Completed
Push — 1.11.x ( e3784d...d9a004 )
by
unknown
01:50 queued 40s
created

MySpace::duSafeUpdateUserRef()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 37
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 21
dl 0
loc 37
rs 8.6506
c 0
b 0
f 0
cc 7
nc 6
nop 5
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use ChamiloSession as Session;
5
use CpChart\Cache as pCache;
6
use CpChart\Data as pData;
7
use CpChart\Image as pImage;
8
9
/**
10
 * Class MySpace.
11
 */
12
class MySpace
13
{
14
    /**
15
     * Get admin actions.
16
     *
17
     * @return string
18
     */
19
    public static function getAdminActions()
20
    {
21
        $actions = [
22
            ['url' => api_get_path(WEB_CODE_PATH).'mySpace/admin_view.php?display=coaches',         'content' => get_lang('DisplayCoaches')],
23
            ['url' => api_get_path(WEB_CODE_PATH).'mySpace/admin_view.php?display=user',            'content' => get_lang('DisplayUserOverview')],
24
            ['url' => api_get_path(WEB_CODE_PATH).'mySpace/admin_view.php?display=session',         'content' => get_lang('DisplaySessionOverview')],
25
            ['url' => api_get_path(WEB_CODE_PATH).'mySpace/admin_view.php?display=course',          'content' => get_lang('DisplayCourseOverview')],
26
            ['url' => api_get_path(WEB_CODE_PATH).'tracking/question_course_report.php?view=admin', 'content' => get_lang('LPQuestionListResults')],
27
            ['url' => api_get_path(WEB_CODE_PATH).'tracking/course_session_report.php?view=admin',  'content' => get_lang('LPExerciseResultsBySession')],
28
            ['url' => api_get_path(WEB_CODE_PATH).'mySpace/admin_view.php?display=accessoverview',  'content' => get_lang('DisplayAccessOverview').' ('.get_lang('Beta').')'],
29
            ['url' => api_get_path(WEB_CODE_PATH).'mySpace/exercise_category_report.php',           'content' => get_lang('ExerciseCategoryAllSessionsReport')],
30
            ['url' => api_get_path(WEB_CODE_PATH).'mySpace/survey_report.php',                      'content' => get_lang('SurveysReport')],
31
            ['url' => api_get_path(WEB_CODE_PATH).'mySpace/tc_report.php',                          'content' => get_lang('TCReport')],
32
            ['url' => api_get_path(WEB_CODE_PATH).'mySpace/ti_report.php',                          'content' => get_lang('TIReport')],
33
            ['url' => api_get_path(WEB_CODE_PATH).'mySpace/question_stats_global.php',              'content' => get_lang('QuestionStats')],
34
            ['url' => api_get_path(WEB_CODE_PATH).'mySpace/question_stats_global_detail.php',       'content' => get_lang('ExerciseAttemptStatsReport')],
35
            ['url' => api_get_path(WEB_CODE_PATH).'mySpace/duplicated_users.php',                   'content' => 'Duplicated users'],
36
        ];
37
38
        $field = new ExtraField('user');
39
        $companyField = $field->get_handler_field_info_by_field_variable('company');
40
        if (!empty($companyField)) {
41
            $actions[] = [
42
                'url' => api_get_path(WEB_CODE_PATH).'mySpace/admin_view.php?display=company',
43
                'content' => get_lang('UserByEntityReport'),
44
            ];
45
        }
46
        $field = new ExtraField('lp');
47
        $authorsField = $field->get_handler_field_info_by_field_variable('authors');
48
        if (!empty($authorsField)) {
49
            $actions[] = [
50
                'url' => api_get_path(WEB_CODE_PATH).'mySpace/admin_view.php?display=learningPath',
51
                'content' => get_lang('LpByAuthor'),
52
            ];
53
        }
54
        $field = new ExtraField('lp_item');
55
        $authorsItemField = $field->get_handler_field_info_by_field_variable('authorlpitem');
56
        if (!empty($authorsItemField)) {
57
            $actions[] = [
58
                'url' => api_get_path(WEB_CODE_PATH).'mySpace/admin_view.php?display=learningPathByItem',
59
                'content' => get_lang('LearningPathItemByAuthor'),
60
            ];
61
        }
62
63
        $html = '<div class="clearfix" style="margin:8px 0 14px;">';
64
        foreach ($actions as $a) {
65
            $label = $a['content'];
66
67
            if (stripos($label, '(Beta)') !== false) {
68
                $label = str_ireplace('(Beta)', '<small class="text-muted">(Beta)</small>', $label);
69
            }
70
71
            $html .= '<a href="'.$a['url'].'" class="btn btn-default btn-sm" '.
72
                'style="margin:4px 6px; border:1px solid #e1e5eb; border-radius:9999px; background:#fff;">'
73
                .$label.
74
                '</a>';
75
        }
76
        $html .= '</div>';
77
78
        return $html;
79
    }
80
81
    /**
82
     * @return string
83
     */
84
    public static function getTopMenu()
85
    {
86
        $menuItems = [];
87
        $menuItems[] = Display::url(
88
            Display::return_icon(
89
                'statistics.png',
90
                get_lang('MyStats'),
91
                '',
92
                ICON_SIZE_MEDIUM
93
            ),
94
            api_get_path(WEB_CODE_PATH)."auth/my_progress.php"
95
        );
96
        $menuItems[] = Display::url(
97
            Display::return_icon(
98
                'teacher.png',
99
                get_lang('TeacherInterface'),
100
                [],
101
                32
102
            ),
103
            api_get_path(WEB_CODE_PATH).'mySpace/?view=teacher'
104
        );
105
        $menuItems[] = Display::url(
106
            Display::return_icon(
107
                'star_na.png',
108
                get_lang('AdminInterface'),
109
                [],
110
                32
111
            ),
112
            '#'
113
        );
114
        $menuItems[] = Display::url(
115
            Display::return_icon('quiz.png', get_lang('ExamTracking'), [], 32),
116
            api_get_path(WEB_CODE_PATH).'tracking/exams.php'
117
        );
118
        $menu = '';
119
        foreach ($menuItems as $item) {
120
            $menu .= $item;
121
        }
122
        $menu .= '<br />';
123
124
        return $menu;
125
    }
126
127
    /**
128
     * This function serves exporting data in CSV format.
129
     *
130
     * @param array  $header    the header labels
131
     * @param array  $data      the data array
132
     * @param string $file_name the name of the file which contains exported data
133
     *
134
     * @return string mixed             Returns a message (string) if an error occurred
135
     */
136
    public function export_csv($header, $data, $file_name = 'export.csv')
137
    {
138
        $archive_path = api_get_path(SYS_ARCHIVE_PATH);
139
        $archive_url = api_get_path(WEB_CODE_PATH).'course_info/download.php?archive_path=&archive=';
140
        $message = '';
141
        if (!$open = fopen($archive_path.$file_name, 'w+')) {
142
            $message = get_lang('noOpen');
143
        } else {
144
            $info = '';
145
146
            foreach ($header as $value) {
147
                $info .= $value.';';
148
            }
149
            $info .= "\r\n";
150
151
            foreach ($data as $row) {
152
                foreach ($row as $value) {
153
                    $info .= $value.';';
154
                }
155
                $info .= "\r\n";
156
            }
157
158
            fwrite($open, $info);
159
            fclose($open);
160
            @chmod($file_name, api_get_permissions_for_new_files());
161
162
            header("Location:".$archive_url.$file_name);
163
            exit;
164
        }
165
166
        return $message;
167
    }
168
169
    /**
170
     * Gets the connections to a course as an array of login and logout time.
171
     *
172
     * @param int   $userId     User id
173
     * @param array $courseInfo
174
     * @param int   $sessionId  Session id (optional, default = 0)
175
     *
176
     * @return array Connections
177
     */
178
    public static function get_connections_to_course(
179
        $userId,
180
        $courseInfo,
181
        $sessionId = 0
182
    ) {
183
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
184
185
        // protect data
186
        $userId = (int) $userId;
187
        $courseId = (int) $courseInfo['real_id'];
188
        $sessionId = (int) $sessionId;
189
        $sessionCondition = api_get_session_condition($sessionId);
190
191
        $sql = 'SELECT login_course_date, logout_course_date
192
                FROM '.$table.'
193
                WHERE
194
                    user_id = '.$userId.' AND
195
                    c_id = '.$courseId.'
196
                    '.$sessionCondition.'
197
                ORDER BY login_course_date ASC';
198
        $rs = Database::query($sql);
199
        $connections = [];
200
201
        while ($row = Database::fetch_array($rs)) {
202
            $connections[] = [
203
                'login' => $row['login_course_date'],
204
                'logout' => $row['logout_course_date'],
205
            ];
206
        }
207
208
        return $connections;
209
    }
210
211
    /**
212
     * @param $user_id
213
     * @param $course_list
214
     * @param int $session_id
215
     *
216
     * @return array|bool
217
     */
218
    public static function get_connections_from_course_list(
219
        $user_id,
220
        $course_list,
221
        $session_id = 0
222
    ) {
223
        // Database table definitions
224
        $tbl_track_course = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
225
        if (empty($course_list)) {
226
            return false;
227
        }
228
229
        // protect data
230
        $user_id = (int) $user_id;
231
        $session_id = (int) $session_id;
232
        $new_course_list = [];
233
        foreach ($course_list as $course_item) {
234
            $courseInfo = api_get_course_info($course_item['code']);
235
            if ($courseInfo) {
236
                $courseId = $courseInfo['real_id'];
237
                $new_course_list[] = '"'.$courseId.'"';
238
            }
239
        }
240
        $course_list = implode(', ', $new_course_list);
241
242
        if (empty($course_list)) {
243
            return false;
244
        }
245
        $sql = 'SELECT login_course_date, logout_course_date, c_id
246
                FROM '.$tbl_track_course.'
247
                WHERE
248
                    user_id = '.$user_id.' AND
249
                    c_id IN ('.$course_list.') AND
250
                    session_id = '.$session_id.'
251
                ORDER BY login_course_date ASC';
252
        $rs = Database::query($sql);
253
        $connections = [];
254
255
        while ($row = Database::fetch_array($rs)) {
256
            $timestamp_login_date = api_strtotime($row['login_course_date'], 'UTC');
257
            $timestamp_logout_date = api_strtotime($row['logout_course_date'], 'UTC');
258
            $connections[] = [
259
                'login' => $timestamp_login_date,
260
                'logout' => $timestamp_logout_date,
261
                'c_id' => $row['c_id'],
262
            ];
263
        }
264
265
        return $connections;
266
    }
267
268
    /**
269
     * Creates a small table in the last column of the table with the user overview.
270
     *
271
     * @param int $user_id the id of the user
272
     *
273
     * @return array List course
274
     */
275
    public static function returnCourseTracking($user_id)
276
    {
277
        $user_id = (int) $user_id;
278
279
        if (empty($user_id)) {
280
            return [];
281
        }
282
283
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
284
        // getting all the courses of the user
285
        $sql = "SELECT * FROM $tbl_course_user
286
                WHERE
287
                    user_id = $user_id AND
288
                    relation_type <> ".COURSE_RELATION_TYPE_RRHH;
289
        $result = Database::query($sql);
290
291
        $list = [];
292
293
        while ($row = Database::fetch_array($result)) {
294
            $courseInfo = api_get_course_info_by_id($row['c_id']);
295
            $courseId = $courseInfo['real_id'];
296
            $courseCode = $courseInfo['code'];
297
298
            if (empty($courseInfo)) {
299
                continue;
300
            }
301
302
            $avg_score = Tracking::get_avg_student_score($user_id, $courseCode);
303
            if (is_numeric($avg_score)) {
304
                $avg_score = round($avg_score, 2);
305
            } else {
306
                $avg_score = '-';
307
            }
308
309
            // Student exercises results (obtained score, maximum score, number of exercises answered, score percentage)
310
            $exercisesResults = self::exercises_results($user_id, $courseCode);
311
312
            $resultToString = '';
313
            if (!is_null($exercisesResults['percentage'])) {
314
                $resultToString = $exercisesResults['score_obtained'].'/'.$exercisesResults['score_possible'].' ( '.$exercisesResults['percentage'].'% )';
315
            }
316
317
            $item = [
318
                'code' => $courseInfo['code'],
319
                'real_id' => $courseInfo['real_id'],
320
                'title' => $courseInfo['title'],
321
                'category' => $courseInfo['categoryName'],
322
                'image_small' => $courseInfo['course_image'],
323
                'image_large' => $courseInfo['course_image_large'],
324
                'time_spent' => api_time_to_hms(Tracking::get_time_spent_on_the_course($user_id, $courseId)),
325
                'student_progress' => round(Tracking::get_avg_student_progress($user_id, $courseCode)),
326
                'student_score' => $avg_score,
327
                'student_message' => Tracking::count_student_messages($user_id, $courseCode),
328
                'student_assignments' => Tracking::count_student_assignments($user_id, $courseCode),
329
                'student_exercises' => $resultToString,
330
                'questions_answered' => $exercisesResults['questions_answered'],
331
                'last_connection' => Tracking::get_last_connection_date_on_the_course($user_id, $courseInfo),
332
            ];
333
            $list[] = $item;
334
        }
335
336
        return $list;
337
    }
338
339
    /**
340
     * Display a sortable table that contains an overview off all the
341
     * reporting progress of all users and all courses the user is subscribed to.
342
     *
343
     * @author Patrick Cool <[email protected]>, Ghent University, Belgium
344
     *          Alex Aragon <[email protected]>, BeezNest, Perú
345
     *
346
     * @version Chamilo 1.11.8
347
     *
348
     * @since April 2019
349
     */
350
    public static function returnTrackingUserOverviewFilter($user_id)
351
    {
352
        $tpl = new Template('', false, false, false, false, false, false);
353
        $userInfo = api_get_user_info($user_id);
354
355
        $avatar = UserManager::getUserPicture($user_id, USER_IMAGE_SIZE_SMALL);
356
        $user = [
357
            'id' => $user_id,
358
            'code_user' => $userInfo['official_code'],
359
            'complete_name' => $userInfo['complete_name'],
360
            'username' => $userInfo['username'],
361
            'course' => self::returnCourseTracking($user_id),
362
            'avatar' => $avatar,
363
        ];
364
365
        $tpl->assign('item', $user);
366
        $templateName = $tpl->get_template('my_space/partials/tracking_user_overview.tpl');
367
        $content = $tpl->fetch($templateName);
368
369
        return $content;
370
    }
371
372
    /**
373
     * Display a sortable table that contains an overview off all the
374
     * reporting progress of all users and all courses the user is subscribed to.
375
     *
376
     * @author Patrick Cool <[email protected]>, Ghent University, Belgium
377
     *         Alex Aragon <[email protected]>, BeezNest, Perú
378
     *
379
     * @version Chamilo 1.11.8
380
     *
381
     * @since October 2008, Update April 2019
382
     */
383
    public static function display_tracking_user_overview()
384
    {
385
        self::display_user_overview_export_options();
386
387
        $params = ['view' => 'admin', 'display' => 'user'];
388
        $table = new SortableTable(
389
            'tracking_user_overview',
390
            ['MySpace', 'get_number_of_users_tracking_overview'],
391
            ['MySpace', 'get_user_data_tracking_overview'],
392
            0,
393
            20,
394
            'ASC',
395
            null, [
396
                'class' => 'table table-transparent',
397
            ]
398
        );
399
        $table->additional_parameters = $params;
400
401
        $table->set_column_filter(0, ['MySpace', 'returnTrackingUserOverviewFilter']);
402
        $tableContent = $table->return_table();
403
        $tpl = new Template('', false, false, false, false, false, false);
404
        $tpl->assign('table', $tableContent);
405
        $templateName = $tpl->get_template('my_space/user_summary.tpl');
406
        $tpl->display($templateName);
407
    }
408
409
    /**
410
     * @param $export_csv
411
     */
412
    public static function display_tracking_coach_overview($export_csv)
413
    {
414
        if ($export_csv) {
415
            $is_western_name_order = api_is_western_name_order(PERSON_NAME_DATA_EXPORT);
416
        } else {
417
            $is_western_name_order = api_is_western_name_order();
418
        }
419
        $sort_by_first_name = api_sort_by_first_name();
420
421
        if (isset($_GET['tracking_list_coaches_column'])) {
422
            $tracking_column = (int) $_GET['tracking_list_coaches_column'];
423
        } else {
424
            $tracking_column = ($is_western_name_order xor $sort_by_first_name) ? 1 : 0;
425
        }
426
427
        $tracking_direction = (isset($_GET['tracking_list_coaches_direction']) && in_array(strtoupper($_GET['tracking_list_coaches_direction']), ['ASC', 'DESC', 'ASCENDING', 'DESCENDING', '0', '1'])) ? $_GET['tracking_list_coaches_direction'] : 'DESC';
428
        // Prepare array for column order - when impossible, use some of user names.
429
        if ($is_western_name_order) {
430
            $order = [
431
                0 => 'firstname',
432
                1 => 'lastname',
433
                2 => $sort_by_first_name ? 'firstname' : 'lastname',
434
                3 => 'login_date',
435
                4 => $sort_by_first_name ? 'firstname' : 'lastname',
436
                5 => $sort_by_first_name ? 'firstname' : 'lastname',
437
            ];
438
        } else {
439
            $order = [
440
                0 => 'lastname',
441
                1 => 'firstname',
442
                2 => $sort_by_first_name ? 'firstname' : 'lastname',
443
                3 => 'login_date',
444
                4 => $sort_by_first_name ? 'firstname' : 'lastname',
445
                5 => $sort_by_first_name ? 'firstname' : 'lastname',
446
            ];
447
        }
448
        $table = new SortableTable(
449
            'tracking_list_coaches_myspace',
450
            ['MySpace', 'count_coaches'],
451
            null,
452
            ($is_western_name_order xor $sort_by_first_name) ? 1 : 0
453
        );
454
        $parameters['view'] = 'admin';
455
        $table->set_additional_parameters($parameters);
456
        if ($is_western_name_order) {
457
            $table->set_header(0, get_lang('FirstName'), true);
458
            $table->set_header(1, get_lang('LastName'), true);
459
        } else {
460
            $table->set_header(0, get_lang('LastName'), true);
461
            $table->set_header(1, get_lang('FirstName'), true);
462
        }
463
        $table->set_header(2, get_lang('TimeSpentOnThePlatform'), false);
464
        $table->set_header(3, get_lang('LastConnexion'), false);
465
        $table->set_header(4, get_lang('NbStudents'), false);
466
        $table->set_header(5, get_lang('CountCours'), false);
467
        $table->set_header(6, get_lang('NumberOfSessions'), false);
468
        $table->set_header(7, get_lang('Sessions'), false);
469
470
        if ($is_western_name_order) {
471
            $csv_header[] = [
472
                get_lang('FirstName'),
473
                get_lang('LastName'),
474
                get_lang('TimeSpentOnThePlatform'),
475
                get_lang('LastConnexion'),
476
                get_lang('NbStudents'),
477
                get_lang('CountCours'),
478
                get_lang('NumberOfSessions'),
479
            ];
480
        } else {
481
            $csv_header[] = [
482
                get_lang('LastName'),
483
                get_lang('FirstName'),
484
                get_lang('TimeSpentOnThePlatform'),
485
                get_lang('LastConnexion'),
486
                get_lang('NbStudents'),
487
                get_lang('CountCours'),
488
                get_lang('NumberOfSessions'),
489
            ];
490
        }
491
492
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
493
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
494
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
495
        $tbl_sessions = Database::get_main_table(TABLE_MAIN_SESSION);
496
497
        $sqlCoachs = "SELECT DISTINCT
498
                        scu.user_id as id_coach,
499
                        u.id as user_id,
500
                        lastname,
501
                        firstname,
502
                        MAX(login_date) as login_date
503
                        FROM $tbl_user u, $tbl_session_course_user scu, $tbl_track_login
504
                        WHERE
505
                            scu.user_id = u.id AND scu.status=2 AND login_user_id=u.id
506
                        GROUP BY user_id ";
507
508
        if (api_is_multiple_url_enabled()) {
509
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
510
            $access_url_id = api_get_current_access_url_id();
511
            if ($access_url_id != -1) {
512
                $sqlCoachs = "SELECT DISTINCT
513
                                    scu.user_id as id_coach,
514
                                    u.id as user_id,
515
                                    lastname,
516
                                    firstname,
517
                                    MAX(login_date) as login_date
518
                                FROM $tbl_user u,
519
                                $tbl_session_course_user scu,
520
                                $tbl_track_login ,
521
                                $tbl_session_rel_access_url session_rel_url
522
                                WHERE
523
                                    scu.user_id = u.id AND
524
                                    scu.status = 2 AND
525
                                    login_user_id = u.id AND
526
                                    access_url_id = $access_url_id AND
527
                                    session_rel_url.session_id = scu.session_id
528
                                GROUP BY u.id";
529
            }
530
        }
531
        if (!empty($order[$tracking_column])) {
532
            $sqlCoachs .= " ORDER BY `".$order[$tracking_column]."` ".$tracking_direction;
533
        }
534
535
        $result_coaches = Database::query($sqlCoachs);
536
        $global_coaches = [];
537
        while ($coach = Database::fetch_array($result_coaches)) {
538
            $global_coaches[$coach['user_id']] = $coach;
539
        }
540
541
        $sql_session_coach = "SELECT session.id_coach, u.id as user_id, lastname, firstname, MAX(login_date) as login_date
542
                                FROM $tbl_user u , $tbl_sessions as session, $tbl_track_login
543
                                WHERE id_coach = u.id AND login_user_id = u.id
544
                                GROUP BY u.id
545
                                ORDER BY login_date $tracking_direction";
546
547
        if (api_is_multiple_url_enabled()) {
548
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
549
            $access_url_id = api_get_current_access_url_id();
550
            if ($access_url_id != -1) {
551
                $sql_session_coach = "SELECT session.id_coach, u.id as user_id, lastname, firstname, MAX(login_date) as login_date
552
					FROM $tbl_user u , $tbl_sessions as session, $tbl_track_login , $tbl_session_rel_access_url as session_rel_url
553
					WHERE
554
					    id_coach = u.id AND
555
					    login_user_id = u.id  AND
556
					    access_url_id = $access_url_id AND
557
					    session_rel_url.session_id = session.id
558
					GROUP BY  u.id
559
					ORDER BY login_date $tracking_direction";
560
            }
561
        }
562
563
        $result_sessions_coach = Database::query($sql_session_coach);
564
        //$total_no_coaches += Database::num_rows($result_sessions_coach);
565
        while ($coach = Database::fetch_array($result_sessions_coach)) {
566
            $global_coaches[$coach['user_id']] = $coach;
567
        }
568
569
        $all_datas = [];
570
        foreach ($global_coaches as $id_coach => $coaches) {
571
            $time_on_platform = api_time_to_hms(
572
                Tracking::get_time_spent_on_the_platform($coaches['user_id'])
573
            );
574
            $last_connection = Tracking::get_last_connection_date(
575
                $coaches['user_id']
576
            );
577
            $nb_students = count(
578
                Tracking::get_student_followed_by_coach($coaches['user_id'])
579
            );
580
            $nb_courses = count(
581
                Tracking::get_courses_followed_by_coach($coaches['user_id'])
582
            );
583
            $nb_sessions = count(
584
                Tracking::get_sessions_coached_by_user($coaches['user_id'])
585
            );
586
587
            $table_row = [];
588
            if ($is_western_name_order) {
589
                $table_row[] = $coaches['firstname'];
590
                $table_row[] = $coaches['lastname'];
591
            } else {
592
                $table_row[] = $coaches['lastname'];
593
                $table_row[] = $coaches['firstname'];
594
            }
595
            $table_row[] = $time_on_platform;
596
            $table_row[] = $last_connection;
597
            $table_row[] = $nb_students;
598
            $table_row[] = $nb_courses;
599
            $table_row[] = $nb_sessions;
600
            $table_row[] = '<a href="session.php?id_coach='.$coaches['user_id'].'">
601
                '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
602
            </a>';
603
            $all_datas[] = $table_row;
604
605
            if ($is_western_name_order) {
606
                $csv_content[] = [
607
                    api_html_entity_decode($coaches['firstname'], ENT_QUOTES),
608
                    api_html_entity_decode($coaches['lastname'], ENT_QUOTES),
609
                    $time_on_platform,
610
                    $last_connection,
611
                    $nb_students,
612
                    $nb_courses,
613
                    $nb_sessions,
614
                ];
615
            } else {
616
                $csv_content[] = [
617
                    api_html_entity_decode($coaches['lastname'], ENT_QUOTES),
618
                    api_html_entity_decode($coaches['firstname'], ENT_QUOTES),
619
                    $time_on_platform,
620
                    $last_connection,
621
                    $nb_students,
622
                    $nb_courses,
623
                    $nb_sessions,
624
                ];
625
            }
626
        }
627
628
        if ($tracking_column != 3) {
629
            if ($tracking_direction == 'DESC') {
630
                usort($all_datas, ['MySpace', 'rsort_users']);
631
            } else {
632
                usort($all_datas, ['MySpace', 'sort_users']);
633
            }
634
        }
635
636
        if ($export_csv && $tracking_column != 3) {
637
            usort($csv_content, 'sort_users');
638
        }
639
        if ($export_csv) {
640
            $csv_content = array_merge($csv_header, $csv_content);
641
        }
642
643
        foreach ($all_datas as $row) {
644
            $table->addRow($row, 'align="right"');
645
        }
646
        $table->display();
647
    }
648
649
    /**
650
     * @return mixed
651
     */
652
    public static function count_coaches()
653
    {
654
        global $total_no_coaches;
655
656
        return $total_no_coaches;
657
    }
658
659
    public static function sort_users($a, $b)
660
    {
661
        $tracking = Session::read('tracking_column');
662
663
        return api_strcmp(
664
            trim(api_strtolower($a[$tracking])),
665
            trim(api_strtolower($b[$tracking]))
666
        );
667
    }
668
669
    public static function rsort_users($a, $b)
670
    {
671
        $tracking = Session::read('tracking_column');
672
673
        return api_strcmp(
674
            trim(api_strtolower($b[$tracking])),
675
            trim(api_strtolower($a[$tracking]))
676
        );
677
    }
678
679
    /**
680
     * Display a sortable table that contains an overview off all the progress of the user in a session.
681
     *
682
     * @deprecated ?
683
     *
684
     * @author César Perales <[email protected]>, Beeznest Team
685
     */
686
    public static function display_tracking_lp_progress_overview(
687
        $sessionId = '',
688
        $courseId = '',
689
        $date_from,
690
        $date_to
691
    ) {
692
        $course = api_get_course_info_by_id($courseId);
693
        /**
694
         * Column name
695
         * The order is important you need to check the $column variable in the model.ajax.php file.
696
         */
697
        $columns = [
698
            get_lang('Username'),
699
            get_lang('FirstName'),
700
            get_lang('LastName'),
701
        ];
702
        //add lessons of course
703
        $lessons = LearnpathList::get_course_lessons($course['code'], $sessionId);
704
705
        //create columns array
706
        foreach ($lessons as $lesson_id => $lesson) {
707
            $columns[] = $lesson['name'];
708
        }
709
710
        $columns[] = get_lang('Total');
711
712
        /**
713
         * Column config.
714
         */
715
        $column_model = [
716
            [
717
                'name' => 'username',
718
                'index' => 'username',
719
                'align' => 'left',
720
                'search' => 'true',
721
                'wrap_cell' => "true",
722
            ],
723
            [
724
                'name' => 'firstname',
725
                'index' => 'firstname',
726
                'align' => 'left',
727
                'search' => 'true',
728
            ],
729
            [
730
                'name' => 'lastname',
731
                'index' => 'lastname',
732
                'align' => 'left',
733
                'search' => 'true',
734
            ],
735
        ];
736
737
        // Get dinamic column names
738
        foreach ($lessons as $lesson_id => $lesson) {
739
            $column_model[] = [
740
                'name' => $lesson['id'],
741
                'index' => $lesson['id'],
742
                'align' => 'left',
743
                'search' => 'true',
744
            ];
745
        }
746
747
        $column_model[] = [
748
            'name' => 'total',
749
            'index' => 'total',
750
            'align' => 'left',
751
            'search' => 'true',
752
        ];
753
754
        $action_links = '';
755
        // jqgrid will use this URL to do the selects
756
        $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_session_lp_progress&session_id='.$sessionId.'&course_id='.$courseId.'&date_to='.$date_to.'&date_from='.$date_from;
757
758
        // Table Id
759
        $tableId = 'lpProgress';
760
761
        // Autowidth
762
        $extra_params['autowidth'] = 'true';
763
764
        // height auto
765
        $extra_params['height'] = 'auto';
766
767
        $table = Display::grid_js(
768
            $tableId,
769
            $url,
770
            $columns,
771
            $column_model,
772
            $extra_params,
773
            [],
774
            $action_links,
775
            true
776
        );
777
778
        $return = '<script>$(function() {'.$table.
779
            'jQuery("#'.$tableId.'").jqGrid("navGrid","#'.$tableId.'_pager",{view:false, edit:false, add:false, del:false, search:false, excel:true});
780
                jQuery("#'.$tableId.'").jqGrid("navButtonAdd","#'.$tableId.'_pager",{
781
                       caption:"",
782
                       title:"'.get_lang('ExportExcel').'",
783
                       onClickButton : function () {
784
                           jQuery("#'.$tableId.'").jqGrid("excelExport",{"url":"'.$url.'&export_format=xls"});
785
                       }
786
                });
787
            });</script>';
788
        $return .= Display::grid_html($tableId);
789
790
        return $return;
791
    }
792
793
    /**
794
     * Display a sortable table that contains an overview off all the progress of the user in a session.
795
     *
796
     * @param int $sessionId  The session ID
797
     * @param int $courseId   The course ID
798
     * @param int $exerciseId The quiz ID
799
     * @param     $date_from
800
     * @param     $date_to
801
     *
802
     * @return string HTML array of results formatted for gridJS
803
     *
804
     * @deprecated ?
805
     *
806
     * @author César Perales <[email protected]>, Beeznest Team
807
     */
808
    public static function display_tracking_exercise_progress_overview(
809
        $sessionId = 0,
810
        $courseId = 0,
811
        $exerciseId = 0,
812
        $date_from = null,
813
        $date_to = null
814
    ) {
815
        $date_from = Security::remove_XSS($date_from);
816
        $date_to = Security::remove_XSS($date_to);
817
        /**
818
         * Column names
819
         * The column order is important. Check $column variable in the main/inc/ajax/model.ajax.php file.
820
         */
821
        $columns = [
822
            get_lang('Session'),
823
            get_lang('ExerciseId'),
824
            get_lang('ExerciseName'),
825
            get_lang('Username'),
826
            get_lang('LastName'),
827
            get_lang('FirstName'),
828
            get_lang('Time'),
829
            get_lang('QuestionId'),
830
            get_lang('QuestionTitle'),
831
            get_lang('WorkDescription'),
832
            get_lang('Answer'),
833
            get_lang('Correct'),
834
        ];
835
836
        /**
837
         * Column config.
838
         */
839
        $column_model = [
840
            ['name' => 'session', 'index' => 'session', 'align' => 'left', 'search' => 'true', 'wrap_cell' => "true"],
841
            ['name' => 'exercise_id', 'index' => 'exercise_id', 'align' => 'left', 'search' => 'true'],
842
            ['name' => 'quiz_title', 'index' => 'quiz_title', 'align' => 'left', 'search' => 'true'],
843
            ['name' => 'username', 'index' => 'username', 'align' => 'left', 'search' => 'true'],
844
            ['name' => 'lastname', 'index' => 'lastname', 'align' => 'left', 'search' => 'true'],
845
            ['name' => 'firstname', 'index' => 'firstname', 'align' => 'left', 'search' => 'true'],
846
            ['name' => 'time', 'index' => 'time', 'align' => 'left', 'search' => 'true', 'wrap_cell' => "true"],
847
            ['name' => 'question_id', 'index' => 'question_id', 'align' => 'left', 'search' => 'true'],
848
            ['name' => 'question', 'index' => 'question', 'align' => 'left', 'search' => 'true', 'wrap_cell' => "true"],
849
            ['name' => 'description', 'index' => 'description', 'align' => 'left', 'width' => '550', 'search' => 'true', 'wrap_cell' => "true"],
850
            ['name' => 'answer', 'index' => 'answer', 'align' => 'left', 'search' => 'true', 'wrap_cell' => "true"],
851
            ['name' => 'correct', 'index' => 'correct', 'align' => 'left', 'search' => 'true', 'wrap_cell' => "true"],
852
        ];
853
        //get dynamic column names
854
855
        // jqgrid will use this URL to do the selects
856
        $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_exercise_progress&session_id='.$sessionId.'&course_id='.$courseId.'&exercise_id='.$exerciseId.'&date_to='.$date_to.'&date_from='.$date_from;
857
858
        // Autowidth
859
        $extra_params['autowidth'] = 'true';
860
861
        // height auto
862
        $extra_params['height'] = 'auto';
863
864
        $tableId = 'exerciseProgressOverview';
865
        $table = Display::grid_js(
866
            $tableId,
867
            $url,
868
            $columns,
869
            $column_model,
870
            $extra_params,
871
            [],
872
            '',
873
            true
874
        );
875
876
        $return = '<script>$(function() {'.$table.
877
            'jQuery("#'.$tableId.'").jqGrid("navGrid","#'.$tableId.'_pager",{view:false, edit:false, add:false, del:false, search:false, excel:true});
878
                jQuery("#'.$tableId.'").jqGrid("navButtonAdd","#'.$tableId.'_pager",{
879
                       caption:"",
880
                       title:"'.get_lang('ExportExcel').'",
881
                       onClickButton : function () {
882
                           jQuery("#'.$tableId.'").jqGrid("excelExport",{"url":"'.$url.'&export_format=xls"});
883
                       }
884
                });
885
            });</script>';
886
        $return .= Display::grid_html($tableId);
887
888
        return $return;
889
    }
890
891
    /**
892
     * Displays a form with all the additionally defined user fields of the profile
893
     * and give you the opportunity to include these in the CSV export.
894
     *
895
     * @author Patrick Cool <[email protected]>, Ghent University, Belgium
896
     *
897
     * @version 1.8.6
898
     *
899
     * @since November 2008
900
     */
901
    public static function display_user_overview_export_options()
902
    {
903
        $message = '';
904
        $defaults = [];
905
        // include the user manager and formvalidator library
906
        if (isset($_GET['export']) && 'options' == $_GET['export']) {
907
            // get all the defined extra fields
908
            $extrafields = UserManager::get_extra_fields(
909
                0,
910
                50,
911
                5,
912
                'ASC',
913
                false,
914
                1
915
            );
916
917
            // creating the form with all the defined extra fields
918
            $form = new FormValidator(
919
                'exportextrafields',
920
                'post',
921
                api_get_self()."?view=".Security::remove_XSS($_GET['view']).'&display='.Security::remove_XSS($_GET['display']).'&export='.Security::remove_XSS($_GET['export'])
922
            );
923
924
            if (is_array($extrafields) && count($extrafields) > 0) {
925
                foreach ($extrafields as $key => $extra) {
926
                    $form->addElement('checkbox', 'extra_export_field'.$extra[0], '', $extra[3]);
927
                }
928
                $form->addButtonSave(get_lang('Ok'), 'submit');
929
930
                // setting the default values for the form that contains all the extra fields
931
                $exportFields = Session::read('additional_export_fields');
932
                if (is_array($exportFields)) {
933
                    foreach ($exportFields as $key => $value) {
934
                        $defaults['extra_export_field'.$value] = 1;
935
                    }
936
                }
937
                $form->setDefaults($defaults);
938
            } else {
939
                $form->addElement('html', Display::return_message(get_lang('ThereAreNotExtrafieldsAvailable'), 'warning'));
940
            }
941
942
            if ($form->validate()) {
943
                // exporting the form values
944
                $values = $form->exportValues();
945
946
                // re-initialising the session that contains the additional fields that need to be exported
947
                Session::write('additional_export_fields', []);
948
949
                // adding the fields that are checked to the session
950
                $message = '';
951
                $additionalExportFields = [];
952
                foreach ($values as $field_ids => $value) {
953
                    if ($value == 1 && strstr($field_ids, 'extra_export_field')) {
954
                        $additionalExportFields[] = str_replace('extra_export_field', '', $field_ids);
955
                    }
956
                }
957
                Session::write('additional_export_fields', $additionalExportFields);
958
959
                // adding the fields that will be also exported to a message string
960
                $additionalExportFields = Session::read('additional_export_fields');
961
                if (is_array($additionalExportFields)) {
962
                    foreach ($additionalExportFields as $key => $extra_field_export) {
963
                        $message .= '<li>'.$extrafields[$extra_field_export][3].'</li>';
964
                    }
965
                }
966
967
                // Displaying a feedback message
968
                if (!empty($additionalExportFields)) {
969
                    echo Display::return_message(
970
                        get_lang('FollowingFieldsWillAlsoBeExported').': <br /><ul>'.$message.'</ul>',
971
                        'confirm',
972
                        false
973
                    );
974
                } else {
975
                    echo Display::return_message(
976
                        get_lang('NoAdditionalFieldsWillBeExported'),
977
                        'confirm',
978
                        false
979
                    );
980
                }
981
            } else {
982
                $form->display();
983
            }
984
        } else {
985
            $additionalExportFields = Session::read('additional_export_fields');
986
            if (!empty($additionalExportFields)) {
987
                // get all the defined extra fields
988
                $extrafields = UserManager::get_extra_fields(0, 50, 5, 'ASC');
989
990
                foreach ($additionalExportFields as $key => $extra_field_export) {
991
                    $message .= '<li>'.$extrafields[$extra_field_export][3].'</li>';
992
                }
993
994
                echo Display::return_message(
995
                    get_lang('FollowingFieldsWillAlsoBeExported').': <br /><ul>'.$message.'</ul>',
996
                    'normal',
997
                    false
998
                );
999
            }
1000
        }
1001
    }
1002
1003
    /**
1004
     * Export to cvs a list of users who were enrolled in the lessons.
1005
     * It is necessary that in the extra field, a company is defined.
1006
     *
1007
     * @param string|null $startDate
1008
     * @param string|null $endDate
1009
     *
1010
     * @return array
1011
     */
1012
    public static function exportCompanyResumeCsv($startDate, $endDate)
1013
    {
1014
        $companys = self::getCompanyLearnpathSubscription($startDate, $endDate);
1015
        $csv_content = [];
1016
        // Printing table
1017
        $total = 0;
1018
        $displayText = get_lang('Company');
1019
        // the first line of the csv file with the column headers
1020
        $csv_row = [];
1021
        $csv_row[] = $displayText;
1022
1023
        $csv_row[] = get_lang('CountOfSubscribedUsers');
1024
        $csv_content[] = $csv_row;
1025
1026
        foreach ($companys as $entity => $student) {
1027
            $csv_row = [];
1028
            // user official code
1029
            $csv_row[] = $entity;
1030
            $csv_row[] = count($student);
1031
            $total += count($student);
1032
            $csv_content[] = $csv_row;
1033
        }
1034
1035
        $csv_row = [];
1036
        // user official code
1037
        $csv_row[] = get_lang('GeneralTotal');
1038
        $csv_row[] = $total;
1039
        $csv_content[] = $csv_row;
1040
        Export::arrayToCsv($csv_content, 'reporting_company_resume');
1041
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1042
    }
1043
1044
    /**
1045
     * Generates a structure to show the links or names for the authors by lesson report.
1046
     *
1047
     * @param array $students
1048
     * @param array $studentRegistered
1049
     * @param       $lpCourseCode
1050
     */
1051
    public static function getStudentDataToReportByLp($students = [], $studentRegistered = [], $lpCourseCode)
1052
    {
1053
        $data = [];
1054
        $totalStudents = 0;
1055
        $data['csv'] = '';
1056
        $data['html'] = '';
1057
        $icon = Display::return_icon('statistics.png', get_lang('Stats'));
1058
        foreach ($students as $student) {
1059
            $lpSessionId = isset($student['session_id']) ? (int) $student['session_id'] : 0;
1060
            $studentId = (int) $student['id'];
1061
            if (!isset($studentRegistered[$studentId][$lpSessionId])) {
1062
                $url = api_get_path(WEB_CODE_PATH)."mySpace/myStudents.php?details=true&student=$studentId";
1063
                if (0 != $lpSessionId) {
1064
                    $url .= "&id_session=$lpSessionId";
1065
                }
1066
                $url .= "&course=$lpCourseCode";
1067
                $reportLink = Display::url(
1068
                    $icon,
1069
                    $url
1070
                );
1071
                $studentName = $student['complete_name']."(".$student['company'].")";
1072
                $studentRegistered[$studentId][$lpSessionId] = $student;
1073
                $data['csv'] .= $studentName.' / ';
1074
                $data['html'] .= "$reportLink <strong>$studentName</strong><br>";
1075
                $totalStudents++;
1076
            }
1077
        }
1078
        $data['student_registered'] = $studentRegistered;
1079
        $data['total_students'] = $totalStudents;
1080
1081
        return $data;
1082
    }
1083
1084
    /**
1085
     * * Generates a structure to show the names for the authors by lesson report by item.
1086
     *
1087
     * @param array  $students
1088
     * @param array  $studentProcessed
1089
     * @param string $typeReport
1090
     * @param false  $csv
1091
     */
1092
    public static function getStudentDataToReportByLpItem($students = [], $studentProcessed = [], $typeReport = '', $csv = false)
1093
    {
1094
        $totalStudent = count($students);
1095
        $sessionIcon = Display::return_icon(
1096
            'admin_star.png',
1097
            get_lang('StudentInSessionCourse'),
1098
            [],
1099
            ICON_SIZE_MEDIUM
1100
        );
1101
        $classIcon = Display::return_icon(
1102
            'group_summary.png',
1103
            get_lang('UsersInsideClass'),
1104
            '',
1105
            ICON_SIZE_MEDIUM
1106
        );
1107
        /* use 'for' to performance */
1108
        for ($i = 0; $i < $totalStudent; $i++) {
1109
            $student = $students[$i];
1110
            $studentId = $student['id'];
1111
            $lpItemIdStudent = $student['lp_item_id'];
1112
            $sessionId = isset($student['session_id']) ? (int) $student['session_id'] : 0;
1113
            $studentName = $student['complete_name'];
1114
            $studentCompany = $student['company'];
1115
            $studentName = "$studentName($studentCompany)";
1116
            $type = isset($student['type']) ? $student['type'] : null;
1117
            $icon = null;
1118
            if (0 != $sessionId) {
1119
                $icon = $sessionIcon;
1120
            }
1121
            if ('class' == $typeReport) {
1122
                $icon = $classIcon;
1123
            }
1124
            $studentString = "$icon $studentName";
1125
            if (0 != $sessionId) {
1126
                $studentString = "<strong>$studentString</strong>";
1127
            }
1128
            if ($csv == false) {
1129
                $studentProcessed[$lpItemIdStudent][$type][$studentId] = $studentString.'<br>';
1130
            } else {
1131
                $studentProcessed[$lpItemIdStudent][$type][$studentId] = "$studentName / ";
1132
            }
1133
        }
1134
1135
        return $studentProcessed;
1136
    }
1137
1138
    /**
1139
     * Displays a list as a table of users who were enrolled in the lessons.
1140
     * It is necessary that in the extra field, a company is defined.
1141
     *
1142
     * @param string|null $startDate
1143
     * @param string|null $endDate
1144
     */
1145
    public static function displayResumeCompany(
1146
        $startDate = null,
1147
        $endDate = null
1148
    ) {
1149
        $companys = self::getCompanyLearnpathSubscription($startDate, $endDate);
1150
        $tableHtml = '';
1151
        // Printing table
1152
        $total = 0;
1153
        $table = "<div class='table-responsive'><table class='table table-hover table-striped table-bordered data_table'>";
1154
1155
        $displayText = get_lang('Company');
1156
        $table .= "<thead><tr><th class='th-header'>$displayText</th><th class='th-header'> ".get_lang('CountOfSubscribedUsers')." </th></tr></thead><tbody>";
1157
1158
        foreach ($companys as $entity => $student) {
1159
            $table .= "<tr><td>$entity</td><td>".count($student)."</td></tr>";
1160
            $total += count($student);
1161
        }
1162
        $table .= "<tr><td>".get_lang('GeneralTotal')."</td><td>$total</td></tr>";
1163
        $table .= '</tbody></table></div>';
1164
1165
        if (!empty($startDate) or !empty($endDate)) {
1166
            $tableHtml = $table;
1167
        }
1168
1169
        $form = new FormValidator('searchDate', 'get');
1170
        $form->addHidden('display', 'company');
1171
        $today = new DateTime();
1172
        if (empty($startDate)) {
1173
            $startDate = api_get_local_time($today->modify('first day of this month')->format('Y-m-d'));
1174
        }
1175
        if (empty($endDate)) {
1176
            $endDate = api_get_local_time($today->modify('last day of this month')->format('Y-m-d'));
1177
        }
1178
        $form->addDatePicker(
1179
            'startDate',
1180
            get_lang('DateStart'),
1181
            [
1182
                'value' => $startDate,
1183
            ]);
1184
        $form->addDatePicker(
1185
            'endDate',
1186
            get_lang('DateEnd'),
1187
            [
1188
                'value' => $endDate,
1189
            ]);
1190
        $form->addButtonSearch(get_lang('Search'));
1191
        if (count($companys) != 0) {
1192
            //$form->addButtonSave(get_lang('Ok'), 'export');
1193
            $form
1194
                ->addButton(
1195
                    'export_csv',
1196
                    get_lang('ExportAsCSV'),
1197
                    'check',
1198
                    'primary',
1199
                    null,
1200
                    null,
1201
                    [
1202
                    ]
1203
                );
1204
        }
1205
1206
        $tableContent = $form->returnForm();
1207
        $tableContent .= $tableHtml;
1208
        // $tableContent .= $table->return_table();
1209
1210
        $tpl = new Template('', false, false, false, false, false, false);
1211
        $tpl->assign('table', $tableContent);
1212
        $templateName = $tpl->get_template('my_space/course_summary.tpl');
1213
        $tpl->display($templateName);
1214
    }
1215
1216
    /**
1217
     *  Displays a list as a table of teachers who are set authors by a extra_field authors.
1218
     *
1219
     * @param string|null $startDate
1220
     * @param string|null $endDate
1221
     * @param bool        $csv
1222
     */
1223
    public static function displayResumeLP(
1224
        $startDate = null,
1225
        $endDate = null,
1226
        $csv = false
1227
    ) {
1228
        $tblExtraField = Database::get_main_table(TABLE_EXTRA_FIELD);
1229
        $tblCourse = Database::get_main_table(TABLE_MAIN_COURSE);
1230
        $tblExtraFieldValue = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1231
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
1232
        $tblLp = Database::get_course_table(TABLE_LP_MAIN);
1233
        $tblAccessUrlCourse = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
1234
        $accessUrlFilter = '';
1235
        if (api_is_multiple_url_enabled()) {
1236
            $urlId = api_get_current_access_url_id();
1237
            $accessUrlFilter = " INNER JOIN $tblAccessUrlCourse aurc
1238
                      ON (c.id = aurc.c_id AND aurc.access_url_id = $urlId)";
1239
        }
1240
        $query = "
1241
        SELECT DISTINCT
1242
            lp.name,
1243
            lpi.title,
1244
            lp.id as lp_id,
1245
            lpi.id AS lp_item_id,
1246
            REPLACE (efv.value, ';', ',') AS users_id,
1247
            c.title AS course_title,
1248
            c.code AS course_code
1249
        FROM $tblExtraFieldValue AS efv
1250
        INNER JOIN $tblExtraField AS ef
1251
        ON (
1252
            efv.field_id = ef.id AND
1253
            ef.variable = 'authorlpitem' AND
1254
            efv.value != ''
1255
            )
1256
        INNER JOIN $tblLpItem AS lpi
1257
        ON (efv.item_id = lpi.iid)
1258
        INNER JOIN $tblLp AS lp
1259
        ON (lpi.lp_id = lp.iid AND lpi.c_id = lp.c_id)
1260
        INNER JOIN $tblCourse AS c
1261
        ON (lp.c_id = c.id)
1262
        $accessUrlFilter";
1263
        $queryResult = Database::query($query);
1264
        $dataTeachers = Database::store_result($queryResult, 'ASSOC');
1265
        $totalData = count($dataTeachers);
1266
        $lpItems = [];
1267
        $teachers = [];
1268
        $users = [];
1269
        $learningPaths = [];
1270
        $csvContent = [];
1271
        $htmlData = '';
1272
        /* use 'for' to performance */
1273
        for ($i = 0; $i < $totalData; $i++) {
1274
            $row = $dataTeachers[$i];
1275
            $lpId = $row['lp_id'];
1276
            $lpItems[] = $lpId;
1277
            $authorData = $row['users_id'];
1278
            $learningPaths[$lpId] = $row;
1279
            if (strpos($authorData, ",") === false) {
1280
                if (!isset($users[$authorData])) {
1281
                    $users[$authorData] = api_get_user_info($authorData);
1282
                }
1283
                $teachers[$authorData][$lpId] = $users[$authorData];
1284
                $learningPaths[$lpId]['teachers'][$authorData] = $users[$authorData];
1285
            } else {
1286
                $items = explode(',', $authorData);
1287
                $totalItems = count($items);
1288
                for ($j = 0; $j < $totalItems; $j++) {
1289
                    $authorData = $items[$j];
1290
                    if (!isset($users[$authorData])) {
1291
                        $users[$authorData] = api_get_user_info($authorData);
1292
                    }
1293
                    $teachers[$authorData][$lpId] = $users[$authorData];
1294
                    $learningPaths[$lpId]['teachers'][$authorData] = $users[$authorData];
1295
                }
1296
            }
1297
        }
1298
        $lpItems = array_unique($lpItems);
1299
        $whereInLp = implode(',', $lpItems);
1300
        if (count($lpItems) != 0) {
1301
            $registeredUsers = self::getCompanyLearnpathSubscription(
1302
                $startDate,
1303
                $endDate,
1304
                $whereInLp
1305
            );
1306
            foreach ($registeredUsers as $students) {
1307
                $totalStudents = count($students);
1308
                /* use 'for' to performance */
1309
                for ($i = 0; $i < $totalStudents; $i++) {
1310
                    $user = $students[$i];
1311
                    $lpId = $user['lp_item'];
1312
                    $studentId = $user['id'];
1313
                    $learningPaths[$lpId]['courseStudent'][$studentId] = $user;
1314
                }
1315
            }
1316
            $registeredUsersBySession = self::getSessionAddUserCourseFromTrackDefault(
1317
                $startDate,
1318
                $endDate,
1319
                $whereInLp
1320
            );
1321
            foreach ($registeredUsersBySession as $lpId => $student) {
1322
                $totalStudents = count($student);
1323
                /* use 'for' to performance */
1324
                for ($i = 0; $i < $totalStudents; $i++) {
1325
                    $user = $student[$i];
1326
                    $lpId = $user['lp'];
1327
                    $studentId = $user['id'];
1328
                    $learningPaths[$lpId]['sessionStudent'][$studentId] = $user;
1329
                    $learningPaths[$lpId]['sessionStudent'][$studentId]['session_id'] = $user;
1330
                }
1331
            }
1332
            $registeredUsersGroup = self::getCompanyLearnpathSubscription(
1333
                $startDate,
1334
                $endDate,
1335
                $whereInLp,
1336
                true
1337
            );
1338
            foreach ($registeredUsersGroup as $student) {
1339
                $totalStudents = count($student);
1340
                /* use 'for' to performance */
1341
                for ($i = 0; $i < $totalStudents; $i++) {
1342
                    $user = $student[$i];
1343
                    $lpId = $user['lp_item'];
1344
                    $studentId = $user['id'];
1345
                    $learningPaths[$lpId]['courseStudentGroup'][$studentId] = $user;
1346
                }
1347
            }
1348
1349
            $index = 0;
1350
            $iconAdd = Display::return_icon('add.png', get_lang('ShowOrHide'), '', ICON_SIZE_SMALL);
1351
            $iconRemove = Display::return_icon('error.png', get_lang('ShowOrHide'), '', ICON_SIZE_SMALL);
1352
            $htmlData = "<div class='table-responsive'>
1353
            <table class='table table-hover table-striped table-bordered data_table'>
1354
            <thead>
1355
                <tr>
1356
                    <th class='th-header'>".get_lang('Author')."</th>
1357
                    <th class='th-header'>".get_lang('LearningPathList')."</th>
1358
                    <th class='th-header'>".get_lang('CountOfSubscribedUsers')."</th>
1359
                    <th class='th-header'>".get_lang('StudentList')."</th>
1360
                </tr>
1361
            </thead>
1362
                <tbody>";
1363
            $lastTeacher = '';
1364
            /* csv */
1365
            $csv_row = [];
1366
            $csv_row[] = get_lang('Author');
1367
            $csv_row[] = get_lang('LearningPathList');
1368
            $csv_row[] = get_lang('CountOfSubscribedUsers');
1369
            $csv_row[] = get_lang('StudentList');
1370
            $csvContent[] = $csv_row;
1371
            $studentsName = '';
1372
            /* csv */
1373
            foreach ($teachers as $authorLId => $teacher) {
1374
                $totalStudents = 0;
1375
                foreach ($teacher as $lpId => $teacherData) {
1376
                    $lpSessionId = 0;
1377
                    $lpData = $learningPaths[$lpId];
1378
                    $printTeacherName = ($lastTeacher != $teacherData['complete_name']) ? $teacherData['complete_name'] : '';
1379
                    $htmlData .= "<tr><td>$printTeacherName</td>";
1380
                    $hiddenField = 'student_show_'.$index;
1381
                    $hiddenFieldLink = 'student_show_'.$index.'_';
1382
                    $lpCourseCode = $lpData['course_code'];
1383
                    $lpName = $lpData['name'];
1384
                    $courseStudent = isset($lpData['courseStudent']) ? $lpData['courseStudent'] : [];
1385
                    $courseStudentGroup = isset($lpData['courseStudentGroup']) ? $lpData['courseStudentGroup'] : [];
1386
                    $sessionStudent = isset($lpData['sessionStudent']) ? $lpData['sessionStudent'] : [];
1387
                    $htmlData .= "<td>$lpName</td><td>".count($courseStudent)." ( ".count($sessionStudent)." )</td><td>";
1388
                    $csv_row = [];
1389
                    $csv_row[] = $printTeacherName;
1390
                    $csv_row[] = $lpName;
1391
                    $csv_row[] = count($courseStudent).' ( '.count($sessionStudent)." )";
1392
                    if (!empty($courseStudent)
1393
                        || !empty($courseStudentGroup)
1394
                        || !empty($sessionStudent)
1395
                    ) {
1396
                        $htmlData .= "<a href='#!' id='$hiddenFieldLink' onclick='showHideStudent(\"$hiddenField\")'>
1397
                        <div class='icon_add'>$iconAdd</div>
1398
                        <div class='icon_remove hidden'>$iconRemove</div>
1399
                        </a>
1400
                        <div id='$hiddenField' class='hidden'>";
1401
                        $studentRegistered = [];
1402
1403
                        $tempArray = self::getStudentDataToReportByLp($courseStudent, $studentRegistered, $lpCourseCode);
1404
                        $studentsName .= $tempArray['csv'];
1405
                        $htmlData .= $tempArray['html'];
1406
                        $studentRegistered = $tempArray['student_registered'];
1407
                        $totalStudents += $tempArray['total_students'];
1408
1409
                        $tempArray = self::getStudentDataToReportByLp($sessionStudent, $studentRegistered, $lpCourseCode);
1410
                        $studentsName .= $tempArray['csv'];
1411
                        $htmlData .= $tempArray['html'];
1412
                        $studentRegistered = $tempArray['student_registered'];
1413
                        $totalStudents += $tempArray['total_students'];
1414
1415
                        $tempArray = self::getStudentDataToReportByLp($courseStudentGroup, $studentRegistered, $lpCourseCode);
1416
                        $studentsName .= $tempArray['csv'];
1417
                        $htmlData .= $tempArray['html'];
1418
                        $studentRegistered = $tempArray['student_registered'];
1419
                        $totalStudents += $tempArray['total_students'];
1420
1421
                        $htmlData .= "</div>";
1422
                    }
1423
                    $htmlData .= "</td></tr>";
1424
                    $index++;
1425
                    $csv_row[] = trim($studentsName, ' / ');
1426
                    $studentsName = '';
1427
                    $csvContent[] = $csv_row;
1428
                    $lastTeacher = $teacherData['complete_name'];
1429
                }
1430
                $htmlData .= "<tr>
1431
                <td></td>
1432
                <td><strong>".get_lang('LearnpathsTotal')." ".count($teacher)." </strong></td>
1433
                <td><strong>$totalStudents</strong></td>
1434
                <td></td>
1435
                </tr>";
1436
            }
1437
            $htmlData .= "</tbody>
1438
            </table>
1439
            </div>";
1440
        }
1441
        if (false == $csv) {
1442
            $form = new FormValidator('searchDate', 'get');
1443
            $form->addHidden('display', 'learningPath');
1444
            $today = new DateTime();
1445
            if (empty($startDate)) {
1446
                $startDate = $today->modify('first day of this month')->format('Y-m-d');
1447
            }
1448
            if (empty($endDate)) {
1449
                $endDate = $today->modify('last day of this month')->format('Y-m-d');
1450
            }
1451
            $form->addDatePicker(
1452
                'startDate',
1453
                get_lang('DateStart'),
1454
                [
1455
                    'value' => $startDate,
1456
                ]);
1457
            $form->addDatePicker(
1458
                'endDate',
1459
                get_lang('DateEnd'),
1460
                [
1461
                    'value' => $endDate,
1462
                ]);
1463
            $form->addButtonSearch(get_lang('Search'));
1464
            if (0 != count($csvContent)) {
1465
                $form
1466
                    ->addButton(
1467
                        'export_csv',
1468
                        get_lang('ExportAsCSV'),
1469
                        'check',
1470
                        'primary',
1471
                        null,
1472
                        null,
1473
                        [
1474
                        ]
1475
                    );
1476
            }
1477
            $tableContent = $form->returnForm();
1478
            if (!empty($startDate) || !empty($endDate)) {
1479
                $tableContent .= $htmlData;
1480
            }
1481
            $tpl = new Template('', false, false, false, false, false, false);
1482
            $tpl->assign('table', $tableContent);
1483
            $templateName = $tpl->get_template('my_space/course_summary.tpl');
1484
            $tpl->display($templateName);
1485
        } else {
1486
            if (count($csvContent) != 0) {
1487
                Export::arrayToCsv($csvContent, 'reporting_lp_by_authors');
1488
            }
1489
        }
1490
    }
1491
1492
    /**
1493
     *  Displays a list as a table of teachers who are set authors of lp's item by a extra_field authors.
1494
     */
1495
    public static function displayResumeLpByItem(string $startDate = null, string $endDate = null, bool $csv = false)
1496
    {
1497
        $tableHtml = '';
1498
        $table = '';
1499
        $tblExtraField = Database::get_main_table(TABLE_EXTRA_FIELD);
1500
        $tblExtraFieldValue = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1501
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
1502
        $tblLp = Database::get_course_table(TABLE_LP_MAIN);
1503
        $tblAccessUrlCourse = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
1504
        $accessUrlFilter = '';
1505
        if (api_is_multiple_url_enabled()) {
1506
            $urlId = api_get_current_access_url_id();
1507
            $accessUrlFilter = " INNER JOIN $tblAccessUrlCourse aurc
1508
                      ON (lp.c_id = aurc.c_id AND aurc.access_url_id = $urlId)";
1509
        }
1510
        $index = 0;
1511
        $cLpItems = [];
1512
        $cLpItemsAuthor = [];
1513
        $authorArray = [];
1514
        $studentArray = [];
1515
        $whereInLp = [];
1516
        $dataSet = [];
1517
        /** Get lp items only with authors */
1518
        $sql = " SELECT
1519
                efv.item_id AS lp_item_id,
1520
                efv.value AS author
1521
            FROM $tblExtraFieldValue AS efv
1522
            INNER JOIN $tblExtraField AS ef
1523
            ON (
1524
                ef.variable = 'authorlpitem' AND
1525
                efv.field_id = ef.id AND
1526
                efv.value != ''
1527
            )
1528
            ORDER BY efv.item_id ";
1529
        $queryResult = Database::query($sql);
1530
        $data = Database::store_result($queryResult, 'ASSOC');
1531
        $totalData = count($data);
1532
        /* use 'for' to performance */
1533
        for ($i = 0; $i < $totalData; $i++) {
1534
            $cLpItemsAuthor[$data[$i]['lp_item_id']] = $data[$i]['author'];
1535
        }
1536
        /** Get lp items only with price */
1537
        $sql = " SELECT
1538
               lp.iid AS lp_id,
1539
               lp.name AS lp_name,
1540
               efv.item_id AS lp_item_id,
1541
               lpi.title AS title,
1542
               efv.value AS price
1543
            FROM $tblExtraFieldValue AS efv
1544
            INNER JOIN $tblExtraField AS ef
1545
            ON (
1546
                ef.variable = 'price' AND
1547
                efv.field_id = ef.id AND
1548
                efv.value > 0
1549
            )
1550
            INNER JOIN $tblLpItem AS lpi
1551
            ON (lpi.iid = efv.item_id)
1552
            INNER JOIN $tblLp AS lp
1553
            ON (lpi.lp_id = lp.iid AND lpi.c_id = lp.c_id)
1554
            $accessUrlFilter";
1555
        $queryResult = Database::query($sql);
1556
        $data = Database::store_result($queryResult, 'ASSOC');
1557
        $totalData = count($data);
1558
        /* use 'for' to performance */
1559
        for ($i = 0; $i < $totalData; $i++) {
1560
            $item = $data[$i];
1561
            $lpItemId = (int) $item['lp_item_id'];
1562
            $whereInLp[] = $item['lp_id'];
1563
            $author = isset($cLpItemsAuthor[$lpItemId]) ? $cLpItemsAuthor[$lpItemId] : null;
1564
            $item['author'] = $author;
1565
            if (!empty($author)) {
1566
                $cLpItems[count($cLpItems)] = $item;
1567
            }
1568
        }
1569
        $totalLpItems = count($cLpItems);
1570
        $tableNoData = "<div class='table-responsive'>
1571
                <table class='table table-hover table-striped table-bordered data_table'>
1572
                <thead>
1573
                    <tr>
1574
                    <th class='th-header'>".get_lang('NoDataAvailable').'</th>
1575
                </tr>
1576
                </thead>
1577
                </tbody>
1578
                </tbody>
1579
                </table>
1580
                </div>';
1581
        if (0 == $totalLpItems) {
1582
            $tableHtml = $tableNoData;
1583
        } elseif (0 == count($whereInLp)) {
1584
            $tableHtml = $tableNoData;
1585
        } else {
1586
            $whereInLp = array_unique($whereInLp);
1587
            $whereInLp = implode(',', $whereInLp);
1588
            $registeredUsersBySession = self::getSessionAddUserCourseFromTrackDefault(
1589
                $startDate,
1590
                $endDate,
1591
                $whereInLp
1592
            );
1593
            $registeredUsersInCourse = self::getUserSubscribedInCourseByDateAndLp($startDate, $endDate, $whereInLp);
1594
            $registeredUsersInLp = self::getCompanyLearnpathSubscription(
1595
                $startDate,
1596
                $endDate,
1597
                $whereInLp
1598
            );
1599
            $registeredGroupsInLp = self::getCompanyLearnpathSubscription(
1600
                $startDate,
1601
                $endDate,
1602
                $whereInLp,
1603
                true
1604
            );
1605
            /* use 'for' to performance */
1606
            for ($i = 0; $i < $totalLpItems; $i++) {
1607
                $lpItem = $cLpItems[$i];
1608
                $lpItemId = $lpItem['lp_item_id'];
1609
                $author = str_replace(';', ',', $lpItem['author']);
1610
                $tempArrayAuthor = explode(',', $author);
1611
                $byCourse = $registeredUsersInLp[$lpItemId] ?? [];
1612
                $byCourseGroups = $registeredGroupsInLp[$lpItemId] ?? [];
1613
                $bySession = $registeredUsersBySession[$lpItemId] ?? [];
1614
                $byUserInCourse = $registeredUsersInCourse[$lpItemId] ?? [];
1615
                if (is_array($tempArrayAuthor)) {
1616
                    $totalAuthors = count($tempArrayAuthor);
1617
                    for ($j = 0; $j < $totalAuthors; $j++) {
1618
                        if (!isset($authorArray[$tempArrayAuthor[$j]])) {
1619
                            $authorArray[$tempArrayAuthor[$j]] = api_get_user_info($tempArrayAuthor[$j]);
1620
                        }
1621
                        $dataSet[$tempArrayAuthor[$j]][$lpItemId] = [
1622
                            'course' => $byCourse,
1623
                            'courseGroups' => $byCourseGroups,
1624
                            'session' => $bySession,
1625
                            'lp_item' => $lpItem,
1626
                            'course_user' => $byUserInCourse,
1627
                        ];
1628
                    }
1629
                } else {
1630
                    if (!isset($authorArray[$author])) {
1631
                        $authorArray[$author] = api_get_user_info($author);
1632
                    }
1633
                    $dataSet[$author][$lpItemId] = [
1634
                        'course' => $byCourse,
1635
                        'courseGroups' => $byCourseGroups,
1636
                        'session' => $bySession,
1637
                        'lp_item' => $lpItem,
1638
                        'course_user' => $byUserInCourse,
1639
                    ];
1640
                }
1641
            }
1642
        }
1643
        if ($csv == false) {
1644
            if (empty($tableHtml)) {
1645
                $table .= "<div class='table-responsive'>
1646
                    <table class='table table-hover table-striped table-bordered data_table'>
1647
                    <thead>
1648
                    <tr>
1649
                    <th class='th-header'>".get_lang('Author')."</th>
1650
                    <th class='th-header'>".get_lang('ContentList')."</th>
1651
                    <th class='th-header'>".get_lang('Tariff')."</th>
1652
                    <th class='th-header'>".get_lang('CountOfSubscribedUsers')."</th>
1653
                    <th class='th-header'>".get_lang('ToInvoice')."</th>
1654
                    <th class='th-header'>".get_lang('StudentList')."</th>
1655
                    </tr>
1656
                    </thead>
1657
                    <tbody>";
1658
                //Icon Constant
1659
                $iconAdd = Display::return_icon('add.png', get_lang('ShowOrHide'), '', ICON_SIZE_SMALL);
1660
                $iconRemove = Display::return_icon('error.png', get_lang('ShowOrHide'), '', ICON_SIZE_SMALL);
1661
1662
                $lastAuthor = '';
1663
                $total = 0;
1664
                foreach ($dataSet as $authorId => $lpItems) {
1665
                    $authorTemp = $authorArray[$authorId];
1666
                    $totalSudent = 0;
1667
                    foreach ($lpItems as $lpItem) {
1668
                        $totalStudents = 0;
1669
                        $itemLp = $lpItem['lp_item'];
1670
                        $title = $itemLp['title'];
1671
                        $price = $itemLp['price'];
1672
                        $byCourse = $lpItem['course'];
1673
                        $byCourseGroups = $lpItem['courseGroups'];
1674
                        $bySession = $lpItem['session'];
1675
                        $byUserInCourse = $lpItem['course_user'];
1676
                        $hide = "class='author_$authorId hidden' ";
1677
                        $tableTemp = '';
1678
                        if ($lastAuthor != $authorTemp) {
1679
                            $table .= "<tr><td>".$authorTemp['complete_name']."</td>";
1680
                        } else {
1681
                            $table .= "<tr $hide ><td></td>";
1682
                        }
1683
                        $table .= "<td>$title</td><td>$price</td>";
1684
                        $studentRegister = count($byCourse);
1685
                        $studentGroupsRegister = count($byCourseGroups);
1686
                        $studentRegisterBySession = count($bySession);
1687
                        $usersInCourseCount = count($byUserInCourse);
1688
1689
                        $hiddenField = 'student_show_'.$index;
1690
                        $hiddenFieldLink = 'student_show_'.$index.'_';
1691
                        if (0 != $studentRegister ||
1692
                            0 != $studentRegisterBySession ||
1693
                            0 != $studentGroupsRegister ||
1694
                            0 != $usersInCourseCount
1695
                        ) {
1696
                            $tableTemp .= "<td>
1697
                                <a href='#!' id='$hiddenFieldLink' onclick='showHideStudent(\"$hiddenField\")'>
1698
                                <div class='icon_add'>$iconAdd</div>
1699
                                <div class='icon_remove hidden'>$iconRemove</div>
1700
                                </a>
1701
                                <div id='$hiddenField' class='hidden'>";
1702
                            $studentProcessed = [];
1703
                            /* Student by course*/
1704
                            $studentProcessed = self::getStudentDataToReportByLpItem($byCourse, $studentProcessed);
1705
                            /* Student by Class*/
1706
                            $studentProcessed = self::getStudentDataToReportByLpItem($byCourseGroups, $studentProcessed, 'class');
1707
                            /* Student by sessions*/
1708
                            $studentProcessed = self::getStudentDataToReportByLpItem($bySession, $studentProcessed);
1709
                            // Students in course*/
1710
                            $studentProcessed = self::getStudentDataToReportByLpItem($byUserInCourse, $studentProcessed);
1711
                            $index++;
1712
                            foreach ($studentProcessed as $lpItemId => $item) {
1713
                                foreach ($item as $type => $student) {
1714
                                    foreach ($student as $userId => $text) {
1715
                                        if ('LearnpathSubscription' == $type) {
1716
                                            $tableTemp .= $text;
1717
                                            $totalStudents++;
1718
                                        } else {
1719
                                            if (!isset($studentProcessed[$lpItemId]['LearnpathSubscription'])) {
1720
                                                $tableTemp .= $text;
1721
                                                $totalStudents++;
1722
                                            }
1723
                                        }
1724
                                    }
1725
                                }
1726
                            }
1727
                            $tableTemp .= "</div></td>";
1728
                        } else {
1729
                            $tableTemp .= "<td></td>";
1730
                        }
1731
                        $table .= "<td>$totalStudents</td>";
1732
                        $invoicing = ($totalStudents * $price);
1733
                        $table .= "<td>$invoicing</td>";
1734
                        $total += $invoicing;
1735
                        $totalSudent += $totalStudents;
1736
                        $table .= $tableTemp."</tr>";
1737
                        $lastAuthor = $authorTemp;
1738
                    }
1739
                    $hiddenFieldLink = 'student__show_'.$index.'_';
1740
                    $index++;
1741
                    $table .= "<tr>
1742
                    <th class='th-header'></th>
1743
                    <th class='th-header'>
1744
                            <a href='#!' id='$hiddenFieldLink' onclick='ShowMoreAuthor(\"$authorId\")'>
1745
                                <div class='icon_add_author_$authorId'>$iconAdd</div>
1746
                                <div class='icon_remove_author_$authorId hidden'>$iconRemove</div>
1747
                            </a>
1748
                        </th>
1749
                    <th class='th-header'></th>
1750
                    <th class='th-header'>$totalSudent</th>
1751
                    <th class='th-header'>$total</th>
1752
                    <th class='th-header'></tr>";
1753
                    $total = 0;
1754
                }
1755
                $table .= "</tbody></table></div>";
1756
                $tableHtml = $table;
1757
            }
1758
1759
            $form = new FormValidator('searchDate', 'get');
1760
            $form->addHidden('display', 'learningPathByItem');
1761
            $today = new DateTime();
1762
            if (empty($startDate)) {
1763
                $startDate = $today->modify('first day of this month')->format('Y-m-d');
1764
            }
1765
            if (empty($endDate)) {
1766
                $endDate = $today->modify('last day of this month')->format('Y-m-d');
1767
            }
1768
            $form->addDatePicker(
1769
                'startDate',
1770
                get_lang('DateStart'),
1771
                [
1772
                    'value' => $startDate,
1773
                ]
1774
            );
1775
            $form->addDatePicker(
1776
                'endDate',
1777
                get_lang('DateEnd'),
1778
                [
1779
                    'value' => $endDate,
1780
                ]
1781
            );
1782
            $form->addButtonSearch(get_lang('Search'));
1783
1784
            if (count($dataSet) != 0) {
1785
                $form->addButton(
1786
                    'export_csv',
1787
                    get_lang('ExportAsCSV'),
1788
                    'check',
1789
                    'primary',
1790
                    null,
1791
                    null,
1792
                    [
1793
                    ]
1794
                );
1795
            }
1796
            $tableContent = $form->returnForm();
1797
            $tableContent .= $tableHtml;
1798
            $tpl = new Template('', false, false, false, false, false, false);
1799
            $tpl->assign('table', $tableContent);
1800
            $templateName = $tpl->get_template('my_space/course_summary.tpl');
1801
            $tpl->display($templateName);
1802
        } else {
1803
            $csv_content = [];
1804
            $csv_row = [];
1805
            $csv_row[] = get_lang('Author');
1806
            $csv_row[] = get_lang('ContentList');
1807
            $csv_row[] = get_lang('Tariff');
1808
            $csv_row[] = get_lang('CountOfSubscribedUsers');
1809
            $csv_row[] = get_lang('ToInvoice');
1810
            $csv_row[] = get_lang('StudentList');
1811
            $csv_content[] = $csv_row;
1812
            $total = 0;
1813
            foreach ($dataSet as $authorId => $lpItems) {
1814
                $authorTemp = $authorArray[$authorId];
1815
                $totalSudent = 0;
1816
                foreach ($lpItems as $lpItem) {
1817
                    $totalStudents = 0;
1818
                    $itemLp = $lpItem['lp_item'];
1819
                    $itemLpId = $itemLp['lp_item_id'];
1820
                    $title = $itemLp['title'];
1821
                    $price = $itemLp['price'];
1822
                    $byCourse = $lpItem['course'];
1823
                    $bySession = $lpItem['session'];
1824
                    $byCourseGroups = $lpItem['courseGroups'];
1825
                    $byUserInCourse = $lpItem['course_user'];
1826
1827
                    $csv_row = [];
1828
                    $csv_row[] = $authorTemp['complete_name'];
1829
                    $csv_row[] = $title;
1830
                    $csv_row[] = $price;
1831
1832
                    $studentRegister = count($byCourse);
1833
                    $studentRegisterBySession = count($bySession);
1834
                    $studentGroupsRegister = count($byCourseGroups);
1835
                    $usersInCourseCount = count($byUserInCourse);
1836
1837
                    $studentsName = '';
1838
                    if (0 != $studentRegister ||
1839
                        0 != $studentRegisterBySession ||
1840
                        0 != $studentGroupsRegister ||
1841
                        0 != $usersInCourseCount
1842
                    ) {
1843
                        $studentProcessed = [];
1844
                        /* Student by course*/
1845
                        $studentProcessed = self::getStudentDataToReportByLpItem($byCourse, $studentProcessed, '', true);
1846
                        /* Student by Class*/
1847
                        $studentProcessed = self::getStudentDataToReportByLpItem($byCourseGroups, $studentProcessed, 'class', true);
1848
                        /* Student by sessions*/
1849
                        $studentProcessed = self::getStudentDataToReportByLpItem($bySession, $studentProcessed, '', true);
1850
                        // Students in course*/
1851
                        $studentProcessed = self::getStudentDataToReportByLpItem($byUserInCourse, $studentProcessed, '', true);
1852
1853
                        $index++;
1854
                        foreach ($studentProcessed as $lpItemId => $item) {
1855
                            foreach ($item as $type => $student) {
1856
                                foreach ($student as $userId => $text) {
1857
                                    if ('LearnpathSubscription' == $type) {
1858
                                        $studentsName .= $text;
1859
                                        $totalStudents++;
1860
                                    } else {
1861
                                        if (!isset($studentProcessed[$lpItemId]['LearnpathSubscription'])) {
1862
                                            $studentsName .= $text;
1863
                                            $totalStudents++;
1864
                                        }
1865
                                    }
1866
                                }
1867
                            }
1868
                        }
1869
                    }
1870
                    $csv_row[] = $totalStudents;
1871
                    $csv_row[] = $price * $totalStudents;
1872
                    $csv_row[] = trim($studentsName, " / ");
1873
                    $csv_content[] = $csv_row;
1874
                }
1875
            }
1876
            Export::arrayToCsv($csv_content, 'reporting_lp_by_authors');
1877
        }
1878
    }
1879
1880
    public static function getSessionAddUserCourseFromTrackDefault(
1881
        $startDate = null,
1882
        $endDate = null,
1883
        $whereInLp = null
1884
    ) {
1885
        $whereInLp = Database::escape_string($whereInLp);
1886
        $data = [];
1887
        $tblTrackDefault = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
1888
        $tblSessionRelCourseUser = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1889
        $tblLp = Database::get_course_table(TABLE_LP_MAIN);
1890
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
1891
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
1892
        $tblAccessUrlUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1893
        $accessUrlFilter = '';
1894
        if (api_is_multiple_url_enabled()) {
1895
            $urlId = api_get_current_access_url_id();
1896
            $accessUrlFilter = " INNER JOIN $tblAccessUrlUser auru
1897
                      ON (u.id = auru.user_id AND auru.access_url_id = $urlId)";
1898
        }
1899
1900
        if (!empty($startDate)) {
1901
            $startDate = new DateTime($startDate);
1902
        } else {
1903
            $startDate = new DateTime();
1904
        }
1905
        if (!empty($endDate)) {
1906
            $endDate = new DateTime($endDate);
1907
        } else {
1908
            $endDate = new DateTime();
1909
        }
1910
        if (!empty($startDate) and !empty($endDate)) {
1911
            if ($startDate > $endDate) {
1912
                $dateTemp = $endDate;
1913
                $endDate = $startDate;
1914
                $startDate = $dateTemp;
1915
                unset($dateTemp);
1916
            }
1917
        }
1918
        $startDate = api_get_utc_datetime($startDate->setTime(0, 0, 0)->format('Y-m-d H:i:s'));
1919
        $endDate = api_get_utc_datetime($endDate->setTime(0, 0, 0)->format('Y-m-d H:i:s'));
1920
        $extra = '';
1921
        if (!empty($whereInLp)) {
1922
            $extra = " AND lpi.lp_id in ($whereInLp) ";
1923
        }
1924
1925
        $sql = "SELECT DISTINCT
1926
            lp.iid AS lp,
1927
            lpi.iid AS lp_item,
1928
            lpi.iid AS lp_item_id,
1929
            td.default_value AS id,
1930
            srcu.session_id AS session_id,
1931
            u.username AS username,
1932
            td.default_date AS default_date,
1933
            td.default_event_type AS type,
1934
            u.firstname as firstname,
1935
            u.lastname as lastname
1936
        FROM $tblTrackDefault AS td
1937
        INNER JOIN $tblSessionRelCourseUser AS srcu
1938
        ON (td.default_value = srcu.user_id AND td.c_id = srcu.c_id)
1939
        INNER JOIN $tblLp AS lp
1940
        ON (lp.c_id = srcu.c_id)
1941
        INNER JOIN $tblLpItem AS lpi
1942
        ON (
1943
            lpi.c_id = srcu.c_id AND
1944
            lp.id = lpi.lp_id AND
1945
            lpi.c_id = lp.c_id
1946
        )
1947
        INNER JOIN $tblUser AS u
1948
        ON (u.id = srcu.user_id)
1949
        $accessUrlFilter
1950
        WHERE
1951
            td.default_event_type = 'session_add_user_course' AND
1952
            td.default_date >= '$startDate' AND
1953
            td.default_date <= '$endDate'
1954
            $extra
1955
        ORDER BY td.default_value ";
1956
        $queryResult = Database::query($sql);
1957
        $dataTrack = Database::store_result($queryResult, 'ASSOC');
1958
        foreach ($dataTrack as $item) {
1959
            $item['complete_name'] = api_get_person_name($item['firstname'], $item['lastname']);
1960
            $item['company'] = self::getCompanyOfUser($item['id']);
1961
            $data[$item['lp_item_id']][] = $item;
1962
        }
1963
1964
        return $data;
1965
    }
1966
1967
    public static function getUserSubscribedInCourseByDateAndLp(
1968
        $startDate = null,
1969
        $endDate = null,
1970
        $whereInLp = null
1971
    ): array {
1972
        $whereInLp = Database::escape_string($whereInLp);
1973
        $tblTrackDefault = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
1974
        $tblCourseRelUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1975
        $tblLp = Database::get_course_table(TABLE_LP_MAIN);
1976
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
1977
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
1978
        $tblAccessUrlUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1979
        $accessUrlFilter = '';
1980
1981
        if (api_is_multiple_url_enabled()) {
1982
            $urlId = api_get_current_access_url_id();
1983
            $accessUrlFilter = " INNER JOIN $tblAccessUrlUser auru
1984
                ON (u.id = auru.user_id AND auru.access_url_id = $urlId)";
1985
        }
1986
1987
        $startDate = !empty($startDate) ? new DateTime($startDate) : new DateTime();
1988
        $endDate = !empty($endDate) ? new DateTime($endDate) : new DateTime();
1989
1990
        $startDate = api_get_utc_datetime($startDate->setTime(0, 0)->format('Y-m-d H:i:s'));
1991
        $endDate = api_get_utc_datetime($endDate->setTime(0, 0)->format('Y-m-d H:i:s'));
1992
1993
        $extra = '';
1994
1995
        if (!empty($whereInLp)) {
1996
            $extra = " AND lpi.lp_id in ($whereInLp) ";
1997
        }
1998
1999
        $sql = "SELECT DISTINCT
2000
                lp.iid AS lp,
2001
                lpi.iid AS lp_item,
2002
                lpi.iid AS lp_item_id,
2003
                u.id AS id,
2004
                u.username AS username,
2005
                td.default_date AS default_date,
2006
                td.default_event_type AS type,
2007
                u.firstname as firstname,
2008
                u.lastname as lastname
2009
            FROM $tblTrackDefault AS td
2010
            INNER JOIN $tblCourseRelUser AS cru ON td.c_id = cru.c_id
2011
            INNER JOIN $tblLp AS lp ON lp.c_id = cru.c_id
2012
            INNER JOIN $tblLpItem AS lpi
2013
                ON (lpi.c_id = cru.c_id AND lp.id = lpi.lp_id AND lpi.c_id = lp.c_id)
2014
            INNER JOIN $tblUser AS u ON u.id = cru.user_id
2015
            $accessUrlFilter
2016
            WHERE
2017
                td.default_event_type = '".LOG_SUBSCRIBE_USER_TO_COURSE."'
2018
                AND td.default_date >= '$startDate'
2019
                AND td.default_date <= '$endDate'
2020
                AND td.default_value LIKE CONCAT('%s:2:\\\\\\\\\\\"id\\\\\\\\\";i:', cru.user_id, ';%')
2021
                $extra
2022
            ORDER BY u.id";
2023
2024
        $result = Database::query($sql);
2025
2026
        $data = [];
2027
2028
        while ($item = Database::fetch_assoc($result)) {
2029
            $item['complete_name'] = api_get_person_name($item['firstname'], $item['lastname']);
2030
            $item['company'] = self::getCompanyOfUser($item['id']);
2031
2032
            $data[$item['lp_item_id']][] = $item;
2033
        }
2034
2035
        return $data;
2036
    }
2037
2038
    /**
2039
     * Display a sortable table that contains an overview of all the reporting progress of all courses.
2040
     */
2041
    public static function display_tracking_course_overview()
2042
    {
2043
        $params = ['view' => 'admin', 'display' => 'courseoverview'];
2044
        $table = new SortableTable(
2045
            'tracking_session_overview',
2046
            ['MySpace', 'get_total_number_courses'],
2047
            ['MySpace', 'get_course_data_tracking_overview'],
2048
            1,
2049
            20,
2050
            'ASC',
2051
            null, [
2052
                'class' => 'table table-transparent',
2053
            ]
2054
        );
2055
        $table->additional_parameters = $params;
2056
        $table->set_column_filter(0, ['MySpace', 'course_tracking_filter']);
2057
        $tableContent = $table->return_table();
2058
2059
        $tpl = new Template('', false, false, false, false, false, false);
2060
        $tpl->assign('table', $tableContent);
2061
        $templateName = $tpl->get_template('my_space/course_summary.tpl');
2062
        $tpl->display($templateName);
2063
    }
2064
2065
    /**
2066
     * Get the total number of courses.
2067
     *
2068
     * @return int Total number of courses
2069
     */
2070
    public static function get_total_number_courses()
2071
    {
2072
        return CourseManager::count_courses(api_get_current_access_url_id());
2073
    }
2074
2075
    /**
2076
     * Get data for the courses.
2077
     *
2078
     * @param int    $from        Inferior limit
2079
     * @param int    $numberItems Number of items to select
2080
     * @param string $column      Column to order on
2081
     * @param string $direction   Order direction
2082
     *
2083
     * @return array Results
2084
     */
2085
    public static function get_course_data_tracking_overview(
2086
        $from,
2087
        $numberItems,
2088
        $column,
2089
        $direction
2090
    ) {
2091
        switch ($column) {
2092
            default:
2093
            case 1:
2094
                $column = 'title';
2095
                break;
2096
        }
2097
2098
        $courses = CourseManager::get_courses_list(
2099
            $from,
2100
            $numberItems,
2101
            $column,
2102
            $direction,
2103
             -1,
2104
            '',
2105
            api_get_current_access_url_id()
2106
        );
2107
2108
        $list = [];
2109
        foreach ($courses as $course) {
2110
            $list[] = [
2111
                '0' => $course['code'],
2112
                'col0' => $course['code'],
2113
            ];
2114
        }
2115
2116
        return $list;
2117
    }
2118
2119
    /**
2120
     * Fills in course reporting data.
2121
     *
2122
     * @param int course code
2123
     * @param array $url_params additional url parameters
2124
     * @param array $row        the row information (the other columns)
2125
     *
2126
     * @return string html code
2127
     */
2128
    public static function course_tracking_filter($course_code, $url_params, $row)
2129
    {
2130
        $course_code = $row[0];
2131
        $courseInfo = api_get_course_info($course_code);
2132
        $courseId = $courseInfo['real_id'];
2133
2134
        $tpl = new Template('', false, false, false, false, false, false);
2135
        $data = null;
2136
2137
        // database table definition
2138
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2139
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2140
2141
        // getting all the courses of the user
2142
        $sql = "SELECT *
2143
                FROM $tbl_user AS u
2144
                INNER JOIN $tbl_course_rel_user AS cu
2145
                ON cu.user_id = u.user_id
2146
                WHERE cu.c_id = '".$courseId."'";
2147
        $result = Database::query($sql);
2148
        $time_spent = 0;
2149
        $progress = 0;
2150
        $nb_progress_lp = 0;
2151
        $score = 0;
2152
        $nb_score_lp = 0;
2153
        $nb_messages = 0;
2154
        $nb_assignments = 0;
2155
        $last_login_date = false;
2156
        $total_score_obtained = 0;
2157
        $total_score_possible = 0;
2158
        $total_questions_answered = 0;
2159
        while ($row = Database::fetch_object($result)) {
2160
            // get time spent in the course and session
2161
            $time_spent += Tracking::get_time_spent_on_the_course(
2162
                $row->user_id,
2163
                $courseInfo['real_id']
2164
            );
2165
            $progress_tmp = Tracking::get_avg_student_progress(
2166
                $row->user_id,
2167
                $course_code,
2168
                [],
2169
                null,
2170
                true
2171
            );
2172
2173
            if ($progress_tmp) {
2174
                $progress += $progress_tmp[0];
2175
                $nb_progress_lp += $progress_tmp[1];
2176
            }
2177
            $score_tmp = Tracking::get_avg_student_score(
2178
                $row->user_id,
2179
                $course_code,
2180
                [],
2181
                null,
2182
                true
2183
            );
2184
            if (is_array($score_tmp)) {
2185
                $score += $score_tmp[0];
2186
                $nb_score_lp += $score_tmp[1];
2187
            }
2188
            $nb_messages += Tracking::count_student_messages(
2189
                $row->user_id,
2190
                $course_code
2191
            );
2192
            $nb_assignments += Tracking::count_student_assignments(
2193
                $row->user_id,
2194
                $course_code
2195
            );
2196
            $last_login_date_tmp = Tracking::get_last_connection_date_on_the_course(
2197
                $row->user_id,
2198
                $courseInfo,
2199
                null,
2200
                false
2201
            );
2202
            if ($last_login_date_tmp != false &&
2203
                $last_login_date == false
2204
            ) { // TODO: To be cleaned
2205
                $last_login_date = $last_login_date_tmp;
2206
            } elseif ($last_login_date_tmp != false && $last_login_date != false) {
2207
                // TODO: Repeated previous condition. To be cleaned.
2208
                // Find the max and assign it to first_login_date
2209
                if (strtotime($last_login_date_tmp) > strtotime($last_login_date)) {
2210
                    $last_login_date = $last_login_date_tmp;
2211
                }
2212
            }
2213
2214
            $exercise_results_tmp = self::exercises_results($row->user_id, $course_code);
2215
            $total_score_obtained += $exercise_results_tmp['score_obtained'];
2216
            $total_score_possible += $exercise_results_tmp['score_possible'];
2217
            $total_questions_answered += $exercise_results_tmp['questions_answered'];
2218
        }
2219
        if ($nb_progress_lp > 0) {
2220
            $avg_progress = round($progress / $nb_progress_lp, 2);
2221
        } else {
2222
            $avg_progress = 0;
2223
        }
2224
        if ($nb_score_lp > 0) {
2225
            $avg_score = round($score / $nb_score_lp, 2);
2226
        } else {
2227
            $avg_score = '-';
2228
        }
2229
        if ($last_login_date) {
2230
            $last_login_date = api_convert_and_format_date(
2231
                $last_login_date,
2232
                DATE_FORMAT_SHORT,
2233
                date_default_timezone_get()
2234
            );
2235
        } else {
2236
            $last_login_date = '-';
2237
        }
2238
        if ($total_score_possible > 0) {
2239
            $total_score_percentage = round($total_score_obtained / $total_score_possible * 100, 2);
2240
        } else {
2241
            $total_score_percentage = 0;
2242
        }
2243
        if ($total_score_percentage > 0) {
2244
            $total_score = $total_score_obtained.'/'.$total_score_possible.' ('.$total_score_percentage.' %)';
2245
        } else {
2246
            $total_score = '-';
2247
        }
2248
2249
        $data = [
2250
            'course_code' => $course_code,
2251
            'id' => $courseId,
2252
            'image' => $courseInfo['course_image_large'],
2253
            'image_small' => $courseInfo['course_image'],
2254
            'title' => $courseInfo['title'],
2255
            'url' => $courseInfo['course_public_url'],
2256
            'category' => $courseInfo['categoryName'],
2257
            'time_spent' => api_time_to_hms($time_spent),
2258
            'avg_progress' => $avg_progress,
2259
            'avg_score' => $avg_score,
2260
            'number_message' => $nb_messages,
2261
            'number_assignments' => $nb_assignments,
2262
            'total_score' => $total_score,
2263
            'questions_answered' => $total_questions_answered,
2264
            'last_login' => $last_login_date,
2265
        ];
2266
2267
        $tpl->assign('data', $data);
2268
        $layout = $tpl->get_template('my_space/partials/tracking_course_overview.tpl');
2269
        $content = $tpl->fetch($layout);
2270
2271
        return $content;
2272
    }
2273
2274
    /**
2275
     * This function exports the table that we see in display_tracking_course_overview().
2276
     */
2277
    public static function export_tracking_course_overview()
2278
    {
2279
        // database table definition
2280
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2281
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2282
2283
        // the values of the sortable table
2284
        if ($_GET['tracking_course_overview_page_nr']) {
2285
            $from = $_GET['tracking_course_overview_page_nr'];
2286
        } else {
2287
            $from = 0;
2288
        }
2289
        if ($_GET['tracking_course_overview_column']) {
2290
            $orderby = $_GET['tracking_course_overview_column'];
2291
        } else {
2292
            $orderby = 0;
2293
        }
2294
2295
        if ($_GET['tracking_course_overview_direction']) {
2296
            $direction = $_GET['tracking_course_overview_direction'];
2297
        } else {
2298
            $direction = 'ASC';
2299
        }
2300
2301
        $course_data = self::get_course_data_tracking_overview(
2302
            $from,
2303
            1000,
2304
            $orderby,
2305
            $direction
2306
        );
2307
2308
        $csv_content = [];
2309
2310
        // the first line of the csv file with the column headers
2311
        $csv_row = [];
2312
        $csv_row[] = get_lang('Course');
2313
        $csv_row[] = get_lang('AvgTimeSpentInTheCourse');
2314
        $csv_row[] = get_lang('AvgStudentsProgress');
2315
        $csv_row[] = get_lang('AvgCourseScore');
2316
        $csv_row[] = get_lang('TotalNumberOfMessages');
2317
        $csv_row[] = get_lang('TotalNumberOfAssignments');
2318
        $csv_row[] = get_lang('TotalExercisesScoreObtained');
2319
        $csv_row[] = get_lang('TotalExercisesScorePossible');
2320
        $csv_row[] = get_lang('TotalExercisesAnswered');
2321
        $csv_row[] = get_lang('TotalExercisesScorePercentage');
2322
        $csv_row[] = get_lang('LatestLogin');
2323
        $csv_content[] = $csv_row;
2324
2325
        // the other lines (the data)
2326
        foreach ($course_data as $key => $course) {
2327
            $course_code = $course[0];
2328
            $courseInfo = api_get_course_info($course_code);
2329
            $course_title = $courseInfo['title'];
2330
            $courseId = $courseInfo['real_id'];
2331
2332
            $csv_row = [];
2333
            $csv_row[] = $course_title;
2334
2335
            // getting all the courses of the session
2336
            $sql = "SELECT *
2337
                    FROM $tbl_user AS u
2338
                    INNER JOIN $tbl_course_rel_user AS cu
2339
                    ON cu.user_id = u.user_id
2340
                    WHERE cu.c_id = '".$courseId."'";
2341
            $result = Database::query($sql);
2342
            $time_spent = 0;
2343
            $progress = 0;
2344
            $nb_progress_lp = 0;
2345
            $score = 0;
2346
            $nb_score_lp = 0;
2347
            $nb_messages = 0;
2348
            $nb_assignments = 0;
2349
            $last_login_date = false;
2350
            $total_score_obtained = 0;
2351
            $total_score_possible = 0;
2352
            $total_questions_answered = 0;
2353
            while ($row = Database::fetch_object($result)) {
2354
                // get time spent in the course and session
2355
                $time_spent += Tracking::get_time_spent_on_the_course(
2356
                    $row->user_id,
2357
                    $courseId
2358
                );
2359
                $progress_tmp = Tracking::get_avg_student_progress(
2360
                    $row->user_id,
2361
                    $course_code,
2362
                    [],
2363
                    null,
2364
                    true
2365
                );
2366
                $progress += $progress_tmp[0];
2367
                $nb_progress_lp += $progress_tmp[1];
2368
                $score_tmp = Tracking::get_avg_student_score(
2369
                    $row->user_id,
2370
                    $course_code,
2371
                    [],
2372
                    null,
2373
                    true
2374
                );
2375
                if (is_array($score_tmp)) {
2376
                    $score += $score_tmp[0];
2377
                    $nb_score_lp += $score_tmp[1];
2378
                }
2379
                $nb_messages += Tracking::count_student_messages(
2380
                    $row->user_id,
2381
                    $course_code
2382
                );
2383
                $nb_assignments += Tracking::count_student_assignments(
2384
                    $row->user_id,
2385
                    $course_code
2386
                );
2387
2388
                $last_login_date_tmp = Tracking::get_last_connection_date_on_the_course(
2389
                    $row->user_id,
2390
                    $courseInfo,
2391
                    null,
2392
                    false
2393
                );
2394
                if ($last_login_date_tmp != false && $last_login_date == false) {
2395
                    // TODO: To be cleaned.
2396
                    $last_login_date = $last_login_date_tmp;
2397
                } elseif ($last_login_date_tmp != false && $last_login_date == false) {
2398
                    // TODO: Repeated previous condition. To be cleaned.
2399
                    // Find the max and assign it to first_login_date
2400
                    if (strtotime($last_login_date_tmp) > strtotime($last_login_date)) {
2401
                        $last_login_date = $last_login_date_tmp;
2402
                    }
2403
                }
2404
2405
                $exercise_results_tmp = self::exercises_results($row->user_id, $course_code);
2406
                $total_score_obtained += $exercise_results_tmp['score_obtained'];
2407
                $total_score_possible += $exercise_results_tmp['score_possible'];
2408
                $total_questions_answered += $exercise_results_tmp['questions_answered'];
2409
            }
2410
            if ($nb_progress_lp > 0) {
2411
                $avg_progress = round($progress / $nb_progress_lp, 2);
2412
            } else {
2413
                $avg_progress = 0;
2414
            }
2415
            if ($nb_score_lp > 0) {
2416
                $avg_score = round($score / $nb_score_lp, 2);
2417
            } else {
2418
                $avg_score = '-';
2419
            }
2420
            if ($last_login_date) {
2421
                $last_login_date = api_convert_and_format_date(
2422
                    $last_login_date,
2423
                    DATE_FORMAT_SHORT,
2424
                    date_default_timezone_get()
2425
                );
2426
            } else {
2427
                $last_login_date = '-';
2428
            }
2429
            if ($total_score_possible > 0) {
2430
                $total_score_percentage = round($total_score_obtained / $total_score_possible * 100, 2);
2431
            } else {
2432
                $total_score_percentage = 0;
2433
            }
2434
            // time spent in the course
2435
            $csv_row[] = api_time_to_hms($time_spent);
2436
            // student progress in course
2437
            $csv_row[] = $avg_progress;
2438
            // student score
2439
            $csv_row[] = $avg_score;
2440
            // student messages
2441
            $csv_row[] = $nb_messages;
2442
            // student assignments
2443
            $csv_row[] = $nb_assignments;
2444
            // student exercises results (obtained score, maximum score, number of exercises answered, score percentage)
2445
            $csv_row[] = $total_score_obtained;
2446
            $csv_row[] = $total_score_possible;
2447
            $csv_row[] = $total_questions_answered;
2448
            $csv_row[] = $total_score_percentage;
2449
            // last connection
2450
            $csv_row[] = $last_login_date;
2451
            $csv_content[] = $csv_row;
2452
        }
2453
        Export::arrayToCsv($csv_content, 'reporting_course_overview');
2454
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2455
    }
2456
2457
    /**
2458
     * Display a sortable table that contains an overview of all the reporting
2459
     * progress of all sessions and all courses the user is subscribed to.
2460
     *
2461
     * @author Guillaume Viguier <[email protected]>
2462
     */
2463
    public static function display_tracking_session_overview()
2464
    {
2465
        $head = '<table style="width: 100%;border:0;padding:0;border-collapse:collapse;table-layout: fixed">';
2466
        $head .= '<tr>';
2467
        $head .= '<th width="155px" style="border-left:0;border-bottom:0"><span>'.get_lang('Course').'</span></th>';
2468
        $head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('AvgTimeSpentInTheCourse'), 6, true).'</span></th>';
2469
        $head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('AvgStudentsProgress'), 6, true).'</span></th>';
2470
        $head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('AvgCourseScore'), 6, true).'</span></th>';
2471
        $head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('TotalNumberOfMessages'), 6, true).'</span></th>';
2472
        $head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('TotalNumberOfAssignments'), 6, true).'</span></th>';
2473
        $head .= '<th width="105px" style="border-bottom:0"><span>'.get_lang('TotalExercisesScoreObtained').'</span></th>';
2474
        $head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('TotalExercisesAnswered'), 6, true).'</span></th>';
2475
        $head .= '<th style="padding:0;border-bottom:0;border-right:0;"><span>'.get_lang('LatestLogin').'</span></th>';
2476
        $head .= '</tr></table>';
2477
2478
        $params = ['view' => 'admin', 'display' => 'sessionoverview'];
2479
        $table = new SortableTable(
2480
            'tracking_session_overview',
2481
            ['MySpace', 'get_total_number_sessions'],
2482
            ['MySpace', 'get_session_data_tracking_overview'],
2483
            1
2484
        );
2485
        $table->additional_parameters = $params;
2486
2487
        $table->set_header(0, '', false, null, ['style' => 'display: none']);
2488
        $table->set_header(
2489
            1,
2490
            get_lang('Session'),
2491
            true,
2492
            ['style' => 'font-size:8pt'],
2493
            ['style' => 'font-size:8pt']
2494
        );
2495
        $table->set_header(
2496
            2,
2497
            $head,
2498
            false,
2499
            ['style' => 'width:90%;border:0;padding:0;font-size:7.5pt;'],
2500
            ['style' => 'width:90%;padding:0;font-size:7.5pt;']
2501
        );
2502
        $table->set_column_filter(2, ['MySpace', 'session_tracking_filter']);
2503
        $table->display();
2504
    }
2505
2506
    /**
2507
     * Get the total number of sessions.
2508
     *
2509
     * @return int Total number of sessions
2510
     */
2511
    public static function get_total_number_sessions()
2512
    {
2513
        return SessionManager::count_sessions(api_get_current_access_url_id());
2514
    }
2515
2516
    /**
2517
     * Get data for the sessions.
2518
     *
2519
     * @param int    $from        Inferior limit
2520
     * @param int    $numberItems Number of items to select
2521
     * @param string $column      Column to order on
2522
     * @param string $direction   Order direction
2523
     *
2524
     * @return array Results
2525
     */
2526
    public static function get_session_data_tracking_overview(
2527
        $from,
2528
        $numberItems,
2529
        $column,
2530
        $direction
2531
    ) {
2532
        $from = (int) $from;
2533
        $numberItems = (int) $numberItems;
2534
        $direction = Database::escape_string($direction);
2535
        $columnName = 'name';
2536
        if ($column === 1) {
2537
            $columnName = 'id';
2538
        }
2539
2540
        $options = [
2541
            'order' => " $columnName $direction",
2542
            'limit' => " $from,$numberItems",
2543
        ];
2544
        $sessions = SessionManager::formatSessionsAdminForGrid($options);
2545
        $list = [];
2546
        foreach ($sessions as $session) {
2547
            $list[] = [
2548
                '0' => $session['id'],
2549
                'col0' => $session['id'],
2550
                '1' => strip_tags($session['name']),
2551
                'col1' => strip_tags($session['name']),
2552
            ];
2553
        }
2554
2555
        return $list;
2556
    }
2557
2558
    /**
2559
     * Fills in session reporting data.
2560
     *
2561
     * @param int   $session_id the id of the user
2562
     * @param array $url_params additonal url parameters
2563
     * @param array $row        the row information (the other columns)
2564
     *
2565
     * @return string html code
2566
     */
2567
    public static function session_tracking_filter($session_id, $url_params, $row)
2568
    {
2569
        $session_id = $row[0];
2570
        // the table header
2571
        $return = '<table class="table table-hover table-striped data_table" style="width: 100%;border:0;padding:0;border-collapse:collapse;table-layout: fixed">';
2572
2573
        // database table definition
2574
        $tbl_session_rel_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
2575
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2576
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2577
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2578
2579
        // getting all the courses of the user
2580
        $sql = "SELECT * FROM $tbl_course AS c
2581
                INNER JOIN $tbl_session_rel_course AS sc
2582
                ON sc.c_id = c.id
2583
                WHERE sc.session_id = '".$session_id."'";
2584
        $result = Database::query($sql);
2585
        while ($row = Database::fetch_object($result)) {
2586
            $courseId = $row->c_id;
2587
            $courseInfo = api_get_course_info_by_id($courseId);
2588
            $return .= '<tr>';
2589
            // course code
2590
            $return .= '    <td width="157px" >'.$row->title.'</td>';
2591
            // get the users in the course
2592
            $sql = "SELECT u.user_id
2593
                    FROM $tbl_user AS u
2594
                    INNER JOIN $tbl_session_rel_course_rel_user AS scu
2595
                    ON u.user_id = scu.user_id
2596
                    WHERE scu.session_id = '".$session_id."' AND scu.c_id = '".$courseId."'";
2597
            $result_users = Database::query($sql);
2598
            $time_spent = 0;
2599
            $progress = 0;
2600
            $nb_progress_lp = 0;
2601
            $score = 0;
2602
            $nb_score_lp = 0;
2603
            $nb_messages = 0;
2604
            $nb_assignments = 0;
2605
            $last_login_date = false;
2606
            $total_score_obtained = 0;
2607
            $total_score_possible = 0;
2608
            $total_questions_answered = 0;
2609
            while ($row_user = Database::fetch_object($result_users)) {
2610
                // get time spent in the course and session
2611
                $time_spent += Tracking::get_time_spent_on_the_course($row_user->user_id, $courseId, $session_id);
2612
                $progress_tmp = Tracking::get_avg_student_progress($row_user->user_id, $row->code, [], $session_id, true);
2613
                $progress += $progress_tmp[0];
2614
                $nb_progress_lp += $progress_tmp[1];
2615
                $score_tmp = Tracking::get_avg_student_score($row_user->user_id, $row->code, [], $session_id, true);
2616
                if (is_array($score_tmp)) {
2617
                    $score += $score_tmp[0];
2618
                    $nb_score_lp += $score_tmp[1];
2619
                }
2620
                $nb_messages += Tracking::count_student_messages($row_user->user_id, $row->code, $session_id);
2621
                $nb_assignments += Tracking::count_student_assignments($row_user->user_id, $row->code, $session_id);
2622
                $last_login_date_tmp = Tracking::get_last_connection_date_on_the_course(
2623
                    $row_user->user_id,
2624
                    $courseInfo,
2625
                    $session_id,
2626
                    false
2627
                );
2628
                if ($last_login_date_tmp != false && $last_login_date == false) {
2629
                    // TODO: To be cleaned.
2630
                    $last_login_date = $last_login_date_tmp;
2631
                } elseif ($last_login_date_tmp != false && $last_login_date != false) {
2632
                    // TODO: Repeated previous condition! To be cleaned.
2633
                    // Find the max and assign it to first_login_date
2634
                    if (strtotime($last_login_date_tmp) > strtotime($last_login_date)) {
2635
                        $last_login_date = $last_login_date_tmp;
2636
                    }
2637
                }
2638
2639
                $exercise_results_tmp = self::exercises_results($row_user->user_id, $row->code, $session_id);
2640
                $total_score_obtained += $exercise_results_tmp['score_obtained'];
2641
                $total_score_possible += $exercise_results_tmp['score_possible'];
2642
                $total_questions_answered += $exercise_results_tmp['questions_answered'];
2643
            }
2644
            if ($nb_progress_lp > 0) {
2645
                $avg_progress = round($progress / $nb_progress_lp, 2);
2646
            } else {
2647
                $avg_progress = 0;
2648
            }
2649
            if ($nb_score_lp > 0) {
2650
                $avg_score = round($score / $nb_score_lp, 2);
2651
            } else {
2652
                $avg_score = '-';
2653
            }
2654
            if ($last_login_date) {
2655
                $last_login_date = api_convert_and_format_date(
2656
                    $last_login_date,
2657
                    DATE_FORMAT_SHORT,
2658
                    date_default_timezone_get()
2659
                );
2660
            } else {
2661
                $last_login_date = '-';
2662
            }
2663
            if ($total_score_possible > 0) {
2664
                $total_score_percentage = round($total_score_obtained / $total_score_possible * 100, 2);
2665
            } else {
2666
                $total_score_percentage = 0;
2667
            }
2668
            if ($total_score_percentage > 0) {
2669
                $total_score = $total_score_obtained.'/'.$total_score_possible.' ('.$total_score_percentage.' %)';
2670
            } else {
2671
                $total_score = '-';
2672
            }
2673
            // time spent in the course
2674
            $return .= '    <td><div>'.api_time_to_hms($time_spent).'</div></td>';
2675
            // student progress in course
2676
            $return .= '    <td><div>'.$avg_progress.'</div></td>';
2677
            // student score
2678
            $return .= '    <td><div>'.$avg_score.'</div></td>';
2679
            // student messages
2680
            $return .= '    <td><div>'.$nb_messages.'</div></td>';
2681
            // student assignments
2682
            $return .= '    <td><div>'.$nb_assignments.'</div></td>';
2683
            // student exercises results (obtained score, maximum score, number of exercises answered, score percentage)
2684
            $return .= '<td width="105px;">'.$total_score.'</td>';
2685
            $return .= '<td>'.$total_questions_answered.'</td>';
2686
            // last connection
2687
            $return .= '    <td><div>'.$last_login_date.'</div></td>';
2688
            $return .= '<tr>';
2689
        }
2690
        $return .= '</table>';
2691
2692
        return $return;
2693
    }
2694
2695
    /**
2696
     * This function exports the table that we see in display_tracking_session_overview().
2697
     */
2698
    public static function export_tracking_session_overview()
2699
    {
2700
        // database table definition
2701
        $tbl_session_rel_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
2702
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2703
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2704
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2705
2706
        // the values of the sortable table
2707
        $from = 0;
2708
        if ($_GET['tracking_session_overview_page_nr']) {
2709
            $from = $_GET['tracking_session_overview_page_nr'];
2710
        }
2711
2712
        $orderby = 0;
2713
        if ($_GET['tracking_session_overview_column']) {
2714
            $orderby = $_GET['tracking_session_overview_column'];
2715
        }
2716
2717
        $direction = 'ASC';
2718
        if ($_GET['tracking_session_overview_direction']) {
2719
            $direction = $_GET['tracking_session_overview_direction'];
2720
        }
2721
2722
        $session_data = self::get_session_data_tracking_overview($from, 1000, $orderby, $direction);
2723
2724
        $csv_content = [];
2725
2726
        // the first line of the csv file with the column headers
2727
        $csv_row = [];
2728
        $csv_row[] = get_lang('Session');
2729
        $csv_row[] = get_lang('Course');
2730
        $csv_row[] = get_lang('AvgTimeSpentInTheCourse');
2731
        $csv_row[] = get_lang('AvgStudentsProgress');
2732
        $csv_row[] = get_lang('AvgCourseScore');
2733
        $csv_row[] = get_lang('TotalNumberOfMessages');
2734
        $csv_row[] = get_lang('TotalNumberOfAssignments');
2735
        $csv_row[] = get_lang('TotalExercisesScoreObtained');
2736
        $csv_row[] = get_lang('TotalExercisesScorePossible');
2737
        $csv_row[] = get_lang('TotalExercisesAnswered');
2738
        $csv_row[] = get_lang('TotalExercisesScorePercentage');
2739
        $csv_row[] = get_lang('LatestLogin');
2740
        $csv_content[] = $csv_row;
2741
2742
        // the other lines (the data)
2743
        foreach ($session_data as $key => $session) {
2744
            $session_id = $session[0];
2745
            $session_title = $session[1];
2746
2747
            // getting all the courses of the session
2748
            $sql = "SELECT * FROM $tbl_course AS c
2749
                    INNER JOIN $tbl_session_rel_course AS sc
2750
                    ON sc.c_id = c.id
2751
                    WHERE sc.session_id = '".$session_id."';";
2752
            $result = Database::query($sql);
2753
            while ($row = Database::fetch_object($result)) {
2754
                $courseId = $row->c_id;
2755
                $courseInfo = api_get_course_info_by_id($courseId);
2756
                $csv_row = [];
2757
                $csv_row[] = $session_title;
2758
                $csv_row[] = $row->title;
2759
                // get the users in the course
2760
                $sql = "SELECT scu.user_id
2761
                        FROM $tbl_user AS u
2762
                        INNER JOIN $tbl_session_rel_course_rel_user AS scu
2763
                        ON u.user_id = scu.user_id
2764
                        WHERE scu.session_id = '".$session_id."' AND scu.c_id = '".$courseId."'";
2765
                $result_users = Database::query($sql);
2766
                $time_spent = 0;
2767
                $progress = 0;
2768
                $nb_progress_lp = 0;
2769
                $score = 0;
2770
                $nb_score_lp = 0;
2771
                $nb_messages = 0;
2772
                $nb_assignments = 0;
2773
                $last_login_date = false;
2774
                $total_score_obtained = 0;
2775
                $total_score_possible = 0;
2776
                $total_questions_answered = 0;
2777
                while ($row_user = Database::fetch_object($result_users)) {
2778
                    // get time spent in the course and session
2779
                    $time_spent += Tracking::get_time_spent_on_the_course($row_user->user_id, $courseId, $session_id);
2780
                    $progress_tmp = Tracking::get_avg_student_progress(
2781
                        $row_user->user_id,
2782
                        $row->code,
2783
                        [],
2784
                        $session_id,
2785
                        true
2786
                    );
2787
                    $progress += $progress_tmp[0];
2788
                    $nb_progress_lp += $progress_tmp[1];
2789
                    $score_tmp = Tracking::get_avg_student_score(
2790
                        $row_user->user_id,
2791
                        $row->code,
2792
                        [],
2793
                        $session_id,
2794
                        true
2795
                    );
2796
                    if (is_array($score_tmp)) {
2797
                        $score += $score_tmp[0];
2798
                        $nb_score_lp += $score_tmp[1];
2799
                    }
2800
                    $nb_messages += Tracking::count_student_messages(
2801
                        $row_user->user_id,
2802
                        $row->code,
2803
                        $session_id
2804
                    );
2805
2806
                    $nb_assignments += Tracking::count_student_assignments(
2807
                        $row_user->user_id,
2808
                        $row->code,
2809
                        $session_id
2810
                    );
2811
2812
                    $last_login_date_tmp = Tracking::get_last_connection_date_on_the_course(
2813
                        $row_user->user_id,
2814
                        $courseInfo,
2815
                        $session_id,
2816
                        false
2817
                    );
2818
                    if ($last_login_date_tmp != false && $last_login_date == false) {
2819
                        // TODO: To be cleaned.
2820
                        $last_login_date = $last_login_date_tmp;
2821
                    } elseif ($last_login_date_tmp != false && $last_login_date == false) {
2822
                        // TODO: Repeated previous condition. To be cleaned.
2823
                        // Find the max and assign it to first_login_date
2824
                        if (strtotime($last_login_date_tmp) > strtotime($last_login_date)) {
2825
                            $last_login_date = $last_login_date_tmp;
2826
                        }
2827
                    }
2828
2829
                    $exercise_results_tmp = self::exercises_results($row_user->user_id, $row->code, $session_id);
2830
                    $total_score_obtained += $exercise_results_tmp['score_obtained'];
2831
                    $total_score_possible += $exercise_results_tmp['score_possible'];
2832
                    $total_questions_answered += $exercise_results_tmp['questions_answered'];
2833
                }
2834
                if ($nb_progress_lp > 0) {
2835
                    $avg_progress = round($progress / $nb_progress_lp, 2);
2836
                } else {
2837
                    $avg_progress = 0;
2838
                }
2839
                if ($nb_score_lp > 0) {
2840
                    $avg_score = round($score / $nb_score_lp, 2);
2841
                } else {
2842
                    $avg_score = '-';
2843
                }
2844
                if ($last_login_date) {
2845
                    $last_login_date = api_convert_and_format_date(
2846
                        $last_login_date,
2847
                        DATE_FORMAT_SHORT,
2848
                        date_default_timezone_get()
2849
                    );
2850
                } else {
2851
                    $last_login_date = '-';
2852
                }
2853
                if ($total_score_possible > 0) {
2854
                    $total_score_percentage = round($total_score_obtained / $total_score_possible * 100, 2);
2855
                } else {
2856
                    $total_score_percentage = 0;
2857
                }
2858
                if ($total_score_percentage > 0) {
2859
                    $total_score = $total_score_obtained.'/'.$total_score_possible.' ('.$total_score_percentage.' %)';
2860
                } else {
2861
                    $total_score = '-';
2862
                }
2863
                // time spent in the course
2864
                $csv_row[] = api_time_to_hms($time_spent);
2865
                // student progress in course
2866
                $csv_row[] = $avg_progress;
2867
                // student score
2868
                $csv_row[] = $avg_score;
2869
                // student messages
2870
                $csv_row[] = $nb_messages;
2871
                // student assignments
2872
                $csv_row[] = $nb_assignments;
2873
                // student exercises results (obtained score, maximum score, number of exercises answered, score percentage)
2874
                $csv_row[] = $total_score_obtained;
2875
                $csv_row[] = $total_score_possible;
2876
                $csv_row[] = $total_questions_answered;
2877
                $csv_row[] = $total_score_percentage;
2878
                // last connection
2879
                $csv_row[] = $last_login_date;
2880
                $csv_content[] = $csv_row;
2881
            }
2882
        }
2883
        Export::arrayToCsv($csv_content, 'reporting_session_overview');
2884
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2885
    }
2886
2887
    /**
2888
     * Get general information about the exercise performance of the user
2889
     * the total obtained score (all the score on all the questions)
2890
     * the maximum score that could be obtained
2891
     * the number of questions answered
2892
     * the success percentage.
2893
     *
2894
     * @param int    $user_id     the id of the user
2895
     * @param string $course_code the course code
2896
     * @param int    $session_id
2897
     *
2898
     * @return array
2899
     *
2900
     * @author Patrick Cool <[email protected]>, Ghent University, Belgium
2901
     *
2902
     * @version Dokeos 1.8.6
2903
     *
2904
     * @since November 2008
2905
     */
2906
    public static function exercises_results($user_id, $course_code, $session_id = 0)
2907
    {
2908
        $user_id = (int) $user_id;
2909
        $courseId = api_get_course_int_id($course_code);
2910
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2911
2912
        $sql = "SELECT exe_result, exe_weighting
2913
                FROM $table
2914
                WHERE
2915
                    c_id = $courseId AND
2916
                    exe_user_id = $user_id";
2917
2918
        $session_id = (int) $session_id;
2919
        if (!empty($session_id)) {
2920
            $sql .= " AND session_id = '".$session_id."' ";
2921
        }
2922
        $result = Database::query($sql);
2923
        $score_obtained = 0;
2924
        $score_possible = 0;
2925
        $questions_answered = 0;
2926
        while ($row = Database::fetch_array($result)) {
2927
            $score_obtained += $row['exe_result'];
2928
            $score_possible += $row['exe_weighting'];
2929
            $questions_answered++;
2930
        }
2931
2932
        $percentage = null;
2933
        if ($score_possible != 0) {
2934
            $percentage = round(($score_obtained / $score_possible * 100), 2);
2935
        }
2936
2937
        return [
2938
            'score_obtained' => $score_obtained,
2939
            'score_possible' => $score_possible,
2940
            'questions_answered' => $questions_answered,
2941
            'percentage' => $percentage,
2942
        ];
2943
    }
2944
2945
    /**
2946
     * This function exports the table that we see in display_tracking_user_overview().
2947
     *
2948
     * @author Patrick Cool <[email protected]>, Ghent University, Belgium
2949
     *
2950
     * @version Dokeos 1.8.6
2951
     *
2952
     * @since October 2008
2953
     */
2954
    public static function export_tracking_user_overview()
2955
    {
2956
        // database table definitions
2957
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2958
        $is_western_name_order = api_is_western_name_order(PERSON_NAME_DATA_EXPORT);
2959
2960
        // the values of the sortable table
2961
        if ($_GET['tracking_user_overview_page_nr']) {
2962
            $from = (int) $_GET['tracking_user_overview_page_nr'];
2963
        } else {
2964
            $from = 0;
2965
        }
2966
        if ($_GET['tracking_user_overview_column']) {
2967
            $orderby = (int) $_GET['tracking_user_overview_column'];
2968
        } else {
2969
            $orderby = 0;
2970
        }
2971
        if ($is_western_name_order != api_is_western_name_order() && ($orderby == 1 || $orderby == 2)) {
2972
            // Swapping the sorting column if name order for export is different than the common name order.
2973
            $orderby = 3 - $orderby;
2974
        }
2975
        if ($_GET['tracking_user_overview_direction']) {
2976
            $direction = $_GET['tracking_user_overview_direction'];
2977
        } else {
2978
            $direction = 'ASC';
2979
        }
2980
2981
        $user_data = self::get_user_data_tracking_overview(
2982
            $from,
2983
            1000,
2984
            $orderby,
2985
            $direction
2986
        );
2987
2988
        // the first line of the csv file with the column headers
2989
        $csv_row = [];
2990
        $csv_row[] = get_lang('OfficialCode');
2991
        if ($is_western_name_order) {
2992
            $csv_row[] = get_lang('FirstName');
2993
            $csv_row[] = get_lang('LastName');
2994
        } else {
2995
            $csv_row[] = get_lang('LastName');
2996
            $csv_row[] = get_lang('FirstName');
2997
        }
2998
        $csv_row[] = get_lang('LoginName');
2999
        $csv_row[] = get_lang('CourseCode');
3000
3001
        // the additional user defined fields (only those that were selected to be exported)
3002
        $fields = UserManager::get_extra_fields(0, 50, 5, 'ASC');
3003
3004
        $additionalExportFields = Session::read('additional_export_fields');
3005
3006
        if (is_array($additionalExportFields)) {
3007
            foreach ($additionalExportFields as $key => $extra_field_export) {
3008
                $csv_row[] = $fields[$extra_field_export][3];
3009
                $field_names_to_be_exported[] = 'extra_'.$fields[$extra_field_export][1];
3010
            }
3011
        }
3012
        $csv_row[] = get_lang('AvgTimeSpentInTheCourse', '');
3013
        $csv_row[] = get_lang('AvgStudentsProgress', '');
3014
        $csv_row[] = get_lang('AvgCourseScore', '');
3015
        $csv_row[] = get_lang('AvgExercisesScore', '');
3016
        $csv_row[] = get_lang('AvgMessages', '');
3017
        $csv_row[] = get_lang('AvgAssignments', '');
3018
        $csv_row[] = get_lang('TotalExercisesScoreObtained', '');
3019
        $csv_row[] = get_lang('TotalExercisesScorePossible', '');
3020
        $csv_row[] = get_lang('TotalExercisesAnswered', '');
3021
        $csv_row[] = get_lang('TotalExercisesScorePercentage', '');
3022
        $csv_row[] = get_lang('FirstLogin', '');
3023
        $csv_row[] = get_lang('LatestLogin', '');
3024
        $csv_content[] = $csv_row;
3025
3026
        // the other lines (the data)
3027
        foreach ($user_data as $key => $user) {
3028
            // getting all the courses of the user
3029
            $sql = "SELECT * FROM $tbl_course_user
3030
                    WHERE user_id = '".intval($user[4])."' AND relation_type<>".COURSE_RELATION_TYPE_RRHH." ";
3031
            $result = Database::query($sql);
3032
            while ($row = Database::fetch_row($result)) {
3033
                $courseInfo = api_get_course_info($row['course_code']);
3034
                $courseId = $courseInfo['real_id'];
3035
3036
                $csv_row = [];
3037
                // user official code
3038
                $csv_row[] = $user[0];
3039
                // user first|last name
3040
                $csv_row[] = $user[1];
3041
                // user last|first name
3042
                $csv_row[] = $user[2];
3043
                // user login name
3044
                $csv_row[] = $user[3];
3045
                // course code
3046
                $csv_row[] = $row[0];
3047
                // the additional defined user fields
3048
                $extra_fields = self::get_user_overview_export_extra_fields($user[4]);
3049
3050
                if (is_array($field_names_to_be_exported)) {
3051
                    foreach ($field_names_to_be_exported as $key => $extra_field_export) {
3052
                        $csv_row[] = $extra_fields[$extra_field_export];
3053
                    }
3054
                }
3055
                // time spent in the course
3056
                $csv_row[] = api_time_to_hms(Tracking::get_time_spent_on_the_course($user[4], $courseId));
3057
                // student progress in course
3058
                $csv_row[] = round(Tracking::get_avg_student_progress($user[4], $row[0]), 2);
3059
                // student score
3060
                $csv_row[] = round(Tracking::get_avg_student_score($user[4], $row[0]), 2);
3061
                // student tes score
3062
                $csv_row[] = round(Tracking::get_avg_student_exercise_score($user[4], $row[0]), 2);
3063
                // student messages
3064
                $csv_row[] = Tracking::count_student_messages($user[4], $row[0]);
3065
                // student assignments
3066
                $csv_row[] = Tracking::count_student_assignments($user[4], $row[0]);
3067
                // student exercises results
3068
                $exercises_results = self::exercises_results($user[4], $row[0]);
3069
                $csv_row[] = $exercises_results['score_obtained'];
3070
                $csv_row[] = $exercises_results['score_possible'];
3071
                $csv_row[] = $exercises_results['questions_answered'];
3072
                $csv_row[] = $exercises_results['percentage'];
3073
                // first connection
3074
                $csv_row[] = Tracking::get_first_connection_date_on_the_course($user[4], $courseId);
3075
                // last connection
3076
                $csv_row[] = strip_tags(Tracking::get_last_connection_date_on_the_course($user[4], $courseInfo));
3077
3078
                $csv_content[] = $csv_row;
3079
            }
3080
        }
3081
        Export::arrayToCsv($csv_content, 'reporting_user_overview');
3082
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
3083
    }
3084
3085
    /**
3086
     * Get data for courses list in sortable with pagination.
3087
     *
3088
     * @return array
3089
     */
3090
    public static function get_course_data($from, $number_of_items, $column, $direction)
3091
    {
3092
        global $courses, $csv_content, $charset, $session_id;
3093
3094
        // definition database tables
3095
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3096
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3097
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3098
3099
        $course_data = [];
3100
        $courses_code = array_keys($courses);
3101
3102
        foreach ($courses_code as &$code) {
3103
            $code = "'$code'";
3104
        }
3105
3106
        // get all courses with limit
3107
        $sql = "SELECT course.code as col1, course.title as col2
3108
                FROM $tbl_course course
3109
                WHERE course.code IN (".implode(',', $courses_code).")";
3110
3111
        if (!in_array($direction, ['ASC', 'DESC'])) {
3112
            $direction = 'ASC';
3113
        }
3114
3115
        $column = (int) $column;
3116
        $from = (int) $from;
3117
        $number_of_items = (int) $number_of_items;
3118
3119
        $sql .= " ORDER BY col$column $direction ";
3120
        $sql .= " LIMIT $from,$number_of_items";
3121
3122
        $res = Database::query($sql);
3123
        while ($row_course = Database::fetch_row($res)) {
3124
            $course_code = $row_course[0];
3125
            $courseInfo = api_get_course_info($course_code);
3126
            $courseId = $courseInfo['real_id'];
3127
            $avg_assignments_in_course = $avg_messages_in_course = $nb_students_in_course = $avg_progress_in_course = $avg_score_in_course = $avg_time_spent_in_course = $avg_score_in_exercise = 0;
3128
3129
            // students directly subscribed to the course
3130
            if (empty($session_id)) {
3131
                $sql = "SELECT user_id
3132
                        FROM $tbl_course_user as course_rel_user
3133
                        WHERE
3134
                            course_rel_user.status='5' AND
3135
                            course_rel_user.c_id = '$courseId'";
3136
            } else {
3137
                $sql = "SELECT user_id FROM $tbl_session_course_user srcu
3138
                        WHERE
3139
                            c_id = '$courseId' AND
3140
                            session_id = '$session_id' AND
3141
                            status<>2";
3142
            }
3143
            $rs = Database::query($sql);
3144
            $users = [];
3145
            while ($row = Database::fetch_array($rs)) {
3146
                $users[] = $row['user_id'];
3147
            }
3148
3149
            if (count($users) > 0) {
3150
                $nb_students_in_course = count($users);
3151
                $avg_assignments_in_course = Tracking::count_student_assignments($users, $course_code, $session_id);
3152
                $avg_messages_in_course = Tracking::count_student_messages($users, $course_code, $session_id);
3153
                $avg_progress_in_course = Tracking::get_avg_student_progress($users, $course_code, [], $session_id);
3154
                $avg_score_in_course = Tracking::get_avg_student_score($users, $course_code, [], $session_id);
3155
                $avg_score_in_exercise = Tracking::get_avg_student_exercise_score($users, $course_code, 0, $session_id);
3156
                $avg_time_spent_in_course = Tracking::get_time_spent_on_the_course(
3157
                    $users,
3158
                    $courseInfo['real_id'],
3159
                    $session_id
3160
                );
3161
3162
                $avg_progress_in_course = round($avg_progress_in_course / $nb_students_in_course, 2);
3163
                if (is_numeric($avg_score_in_course)) {
3164
                    $avg_score_in_course = round($avg_score_in_course / $nb_students_in_course, 2);
3165
                }
3166
                $avg_time_spent_in_course = api_time_to_hms($avg_time_spent_in_course / $nb_students_in_course);
3167
            } else {
3168
                $avg_time_spent_in_course = null;
3169
                $avg_progress_in_course = null;
3170
                $avg_score_in_course = null;
3171
                $avg_score_in_exercise = null;
3172
                $avg_messages_in_course = null;
3173
                $avg_assignments_in_course = null;
3174
            }
3175
            $table_row = [];
3176
            $table_row[] = $row_course[1];
3177
            $table_row[] = $nb_students_in_course;
3178
            $table_row[] = $avg_time_spent_in_course;
3179
            $table_row[] = is_null($avg_progress_in_course) ? '' : $avg_progress_in_course.'%';
3180
            $table_row[] = is_null($avg_score_in_course) ? '' : $avg_score_in_course.'%';
3181
            $table_row[] = is_null($avg_score_in_exercise) ? '' : $avg_score_in_exercise.'%';
3182
            $table_row[] = $avg_messages_in_course;
3183
            $table_row[] = $avg_assignments_in_course;
3184
3185
            //set the "from" value to know if I access the Reporting by the chamilo tab or the course link
3186
            $table_row[] = '<center><a href="../../tracking/courseLog.php?cidReq='.$course_code.'&from=myspace&id_session='.$session_id.'">
3187
                             '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
3188
                             </a>
3189
                            </center>';
3190
3191
            $scoreInCourse = null;
3192
            if (null !== $avg_score_in_course) {
3193
                if (is_numeric($avg_score_in_course)) {
3194
                    $scoreInCourse = $avg_score_in_course.'%';
3195
                } else {
3196
                    $scoreInCourse = $avg_score_in_course;
3197
                }
3198
            }
3199
3200
            $csv_content[] = [
3201
                api_html_entity_decode($row_course[1], ENT_QUOTES, $charset),
3202
                $nb_students_in_course,
3203
                $avg_time_spent_in_course,
3204
                is_null($avg_progress_in_course) ? null : $avg_progress_in_course.'%',
3205
                $scoreInCourse,
3206
                is_null($avg_score_in_exercise) ? null : $avg_score_in_exercise.'%',
3207
                $avg_messages_in_course,
3208
                $avg_assignments_in_course,
3209
            ];
3210
            $course_data[] = $table_row;
3211
        }
3212
3213
        return $course_data;
3214
    }
3215
3216
    /**
3217
     * Get the number of users of the platform.
3218
     *
3219
     * @return int
3220
     */
3221
    public static function get_number_of_users_tracking_overview()
3222
    {
3223
        return UserManager::get_number_of_users(0, api_get_current_access_url_id());
3224
    }
3225
3226
    /**
3227
     * Get all the data for the sortable table of the reporting progress of
3228
     * all users and all the courses the user is subscribed to.
3229
     *
3230
     * @return array
3231
     */
3232
    public static function get_user_data_tracking_overview(int $from, int $numberItems, int $column, string $direction)
3233
    {
3234
        $isWestern = api_is_western_name_order();
3235
        if ($direction !== 'ASC' && $direction != 'DESC') {
3236
            $direction = 'ASC';
3237
        }
3238
3239
        switch ($column) {
3240
            case '0':
3241
                $column = $isWestern ? 'firstname' : 'lastname';
3242
                break;
3243
        }
3244
3245
        $order = [
3246
            " `$column` $direction",
3247
        ];
3248
        $userList = UserManager::get_user_list([], $order, $from, $numberItems);
3249
        $return = [];
3250
        foreach ($userList as $user) {
3251
            $return[] = [
3252
                '0' => $user['user_id'],
3253
                'col0' => $user['user_id'],
3254
            ];
3255
        }
3256
3257
        return $return;
3258
    }
3259
3260
    /**
3261
     * Get all information that the user with user_id = $user_data has
3262
     * entered in the additionally defined profile fields.
3263
     *
3264
     * @param int $user_id the id of the user
3265
     *
3266
     * @return array
3267
     *
3268
     * @author Patrick Cool <[email protected]>, Ghent University, Belgium
3269
     *
3270
     * @version Dokeos 1.8.6
3271
     *
3272
     * @since November 2008
3273
     */
3274
    public static function get_user_overview_export_extra_fields($user_id)
3275
    {
3276
        // include the user manager
3277
        $data = UserManager::get_extra_user_data($user_id, true);
3278
3279
        return $data;
3280
    }
3281
3282
    /**
3283
     * Checks if a username exist in the DB otherwise it create a "double"
3284
     * i.e. if we look into for jmontoya but the user's name already exist we create the user jmontoya2
3285
     * the return array will be array(username=>'jmontoya', sufix='2').
3286
     *
3287
     * @param string firstname
3288
     * @param string lastname
3289
     * @param string username
3290
     *
3291
     * @return array with the username, the sufix
3292
     *
3293
     * @author Julio Montoya
3294
     */
3295
    public static function make_username($firstname, $lastname, $username, $language = null, $encoding = null)
3296
    {
3297
        // if username exist
3298
        if (!UserManager::is_username_available($username) || empty($username)) {
3299
            $i = 0;
3300
            while (1) {
3301
                if ($i == 0) {
3302
                    $sufix = '';
3303
                } else {
3304
                    $sufix = $i;
3305
                }
3306
                $desired_username = UserManager::create_username(
3307
                    $firstname,
3308
                    $lastname
3309
                );
3310
                if (UserManager::is_username_available($desired_username.$sufix)) {
3311
                    break;
3312
                } else {
3313
                    $i++;
3314
                }
3315
            }
3316
            $username_array = ['username' => $desired_username, 'sufix' => $sufix];
3317
3318
            return $username_array;
3319
        } else {
3320
            $username_array = ['username' => $username, 'sufix' => ''];
3321
3322
            return $username_array;
3323
        }
3324
    }
3325
3326
    /**
3327
     * Checks if there are repeted users in a given array.
3328
     *
3329
     * @param array $usernames  list of the usernames in the uploaded file
3330
     * @param array $user_array $user_array['username'] and $user_array['sufix']
3331
     *                          where suffix is the number part in a login i.e -> jmontoya2
3332
     *
3333
     * @return array with the $usernames array and the $user_array array
3334
     *
3335
     * @author Julio Montoya
3336
     */
3337
    public static function check_user_in_array($usernames, $user_array)
3338
    {
3339
        $user_list = array_keys($usernames);
3340
        $username = $user_array['username'].$user_array['sufix'];
3341
3342
        if (in_array($username, $user_list)) {
3343
            $user_array['sufix'] += $usernames[$username];
3344
            $usernames[$username]++;
3345
        } else {
3346
            $usernames[$username] = 1;
3347
        }
3348
        $result_array = [$usernames, $user_array];
3349
3350
        return $result_array;
3351
    }
3352
3353
    /**
3354
     * Checks whether a username has been already subscribed in a session.
3355
     *
3356
     * @param string $username    a given username
3357
     * @param array  $course_list the array with the course list id
3358
     * @param int    $id_session  the session id
3359
     *
3360
     * @return int 0 if the user is not subscribed otherwise it returns the user_id of the given username
3361
     *
3362
     * @author Julio Montoya
3363
     */
3364
    public static function user_available_in_session($username, $course_list, $id_session)
3365
    {
3366
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3367
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3368
        $id_session = (int) $id_session;
3369
        $username = Database::escape_string($username);
3370
        foreach ($course_list as $courseId) {
3371
            $courseId = (int) $courseId;
3372
            $sql = " SELECT u.user_id FROM $tbl_session_rel_course_rel_user rel
3373
                     INNER JOIN $table_user u
3374
                     ON (rel.user_id = u.user_id)
3375
                     WHERE
3376
                        rel.session_id='$id_session' AND
3377
                        u.status='5' AND
3378
                        u.username ='$username' AND
3379
                        rel.c_id='$courseId'";
3380
            $rs = Database::query($sql);
3381
            if (Database::num_rows($rs) > 0) {
3382
                return Database::result($rs, 0, 0);
3383
            }
3384
        }
3385
3386
        return 0;
3387
    }
3388
3389
    /**
3390
     * This function checks whether some users in the uploaded file
3391
     * repeated and creates unique usernames if necesary.
3392
     * A case: Within the file there is an user repeted twice (Julio Montoya / Julio Montoya)
3393
     * and the username fields are empty.
3394
     * Then, this function would create unique usernames based on the first and the last name.
3395
     * Two users wiould be created - jmontoya and jmontoya2.
3396
     * Of course, if in the database there is a user with the name jmontoya,
3397
     * the newly created two users registered would be jmontoya2 and jmontoya3.
3398
     *
3399
     * @param $users list of users
3400
     *
3401
     * @return array
3402
     *
3403
     * @author Julio Montoya Armas
3404
     */
3405
    public static function check_all_usernames($users, $course_list, $id_session)
3406
    {
3407
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3408
        $usernames = [];
3409
        $new_users = [];
3410
        foreach ($users as $index => $user) {
3411
            $desired_username = [];
3412
            if (empty($user['UserName'])) {
3413
                $desired_username = self::make_username($user['FirstName'], $user['LastName'], '');
3414
                $pre_username = $desired_username['username'].$desired_username['sufix'];
3415
                $user['UserName'] = $pre_username;
3416
                $user['create'] = '1';
3417
            } else {
3418
                if (UserManager::is_username_available($user['UserName'])) {
3419
                    $desired_username = self::make_username($user['FirstName'], $user['LastName'], $user['UserName']);
3420
                    $user['UserName'] = $desired_username['username'].$desired_username['sufix'];
3421
                    $user['create'] = '1';
3422
                } else {
3423
                    $is_session_avail = self::user_available_in_session($user['UserName'], $course_list, $id_session);
3424
                    if (0 == $is_session_avail) {
3425
                        $user_name = $user['UserName'];
3426
                        $sql_select = "SELECT user_id FROM $table_user WHERE username ='$user_name' ";
3427
                        $rs = Database::query($sql_select);
3428
                        $user['create'] = Database::result($rs, 0, 0);
3429
                    } else {
3430
                        $user['create'] = $is_session_avail;
3431
                    }
3432
                }
3433
            }
3434
            // Usernames is the current list of users in the file.
3435
            $result_array = self::check_user_in_array($usernames, $desired_username);
3436
            $usernames = $result_array[0];
3437
            $desired_username = $result_array[1];
3438
            $user['UserName'] = $desired_username['username'].$desired_username['sufix'];
3439
            $new_users[] = $user;
3440
        }
3441
3442
        return $new_users;
3443
    }
3444
3445
    /**
3446
     * This functions checks whether there are users that are already
3447
     * registered in the DB by different creator than the current coach.
3448
     *
3449
     * @param array $users
3450
     *
3451
     * @return array
3452
     *
3453
     * @author Julio Montoya Armas
3454
     */
3455
    public static function get_user_creator($users)
3456
    {
3457
        $errors = [];
3458
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3459
        foreach ($users as $index => $user) {
3460
            $username = Database::escape_string($user['UserName']);
3461
            $sql = "SELECT creator_id FROM $table_user WHERE username='$username' ";
3462
3463
            $rs = Database::query($sql);
3464
            $creator_id = Database::result($rs, 0, 0);
3465
            // check if we are the creators or not
3466
            if ($creator_id != '') {
3467
                if ($creator_id != api_get_user_id()) {
3468
                    $user['error'] = get_lang('UserAlreadyRegisteredByOtherCreator');
3469
                    $errors[] = $user;
3470
                }
3471
            }
3472
        }
3473
3474
        return $errors;
3475
    }
3476
3477
    /**
3478
     * Validates imported data.
3479
     *
3480
     * @param array $users list of users
3481
     */
3482
    public static function validate_data($users, $id_session = null)
3483
    {
3484
        $errors = [];
3485
        $new_users = [];
3486
        foreach ($users as $index => $user) {
3487
            // 1. Check whether mandatory fields are set.
3488
            $mandatory_fields = ['LastName', 'FirstName'];
3489
            if (api_get_setting('registration', 'email') == 'true') {
3490
                $mandatory_fields[] = 'Email';
3491
            }
3492
3493
            foreach ($mandatory_fields as $key => $field) {
3494
                if (!isset($user[$field]) || strlen($user[$field]) == 0) {
3495
                    $user['error'] = get_lang($field.'Mandatory');
3496
                    $errors[] = $user;
3497
                }
3498
            }
3499
            // 2. Check whether the username is too long.
3500
            if (UserManager::is_username_too_long($user['UserName'])) {
3501
                $user['error'] = get_lang('UserNameTooLong');
3502
                $errors[] = $user;
3503
            }
3504
3505
            $user['UserName'] = trim($user['UserName']);
3506
3507
            if (empty($user['UserName'])) {
3508
                $user['UserName'] = UserManager::create_username($user['FirstName'], $user['LastName']);
3509
            }
3510
            $new_users[] = $user;
3511
        }
3512
        $results = ['errors' => $errors, 'users' => $new_users];
3513
3514
        return $results;
3515
    }
3516
3517
    /**
3518
     * Adds missing user-information (which isn't required, like password, etc).
3519
     */
3520
    public static function complete_missing_data($user)
3521
    {
3522
        // 1. Generate a password if it is necessary.
3523
        if (!isset($user['Password']) || strlen($user['Password']) == 0) {
3524
            $user['Password'] = api_generate_password();
3525
        }
3526
3527
        return $user;
3528
    }
3529
3530
    /**
3531
     * Saves imported data.
3532
     */
3533
    public static function save_data($users, $course_list, $id_session)
3534
    {
3535
        $id_session = (int) $id_session;
3536
        $sendMail = $_POST['sendMail'] ? 1 : 0;
3537
3538
        // Adding users to the platform.
3539
        $new_users = [];
3540
        foreach ($users as $index => $user) {
3541
            $user = self::complete_missing_data($user);
3542
            // coach only will registered users
3543
            $default_status = STUDENT;
3544
            if ($user['create'] == COURSEMANAGER) {
3545
                $user['id'] = UserManager::create_user(
3546
                    $user['FirstName'],
3547
                    $user['LastName'],
3548
                    $default_status,
3549
                    $user['Email'],
3550
                    $user['UserName'],
3551
                    $user['Password'],
3552
                    $user['OfficialCode'],
3553
                    api_get_setting('PlatformLanguage'),
3554
                    $user['PhoneNumber'],
3555
                    ''
3556
                );
3557
                $user['added_at_platform'] = 1;
3558
            } else {
3559
                $user['id'] = $user['create'];
3560
                $user['added_at_platform'] = 0;
3561
            }
3562
            $new_users[] = $user;
3563
        }
3564
        // Update user list.
3565
        $users = $new_users;
3566
3567
        // Inserting users.
3568
        SessionManager::insertUsersInCourses(
3569
            array_column($users, 'id'),
3570
            $course_list,
3571
            $id_session
3572
        );
3573
3574
        array_walk(
3575
            $users,
3576
            function (array &$user) {
3577
                $user['added_at_session'] = 1;
3578
            }
3579
        );
3580
3581
        $registered_users = get_lang('FileImported').'<br /> Import file results : <br />';
3582
        // Sending emails.
3583
        $addedto = '';
3584
        if ($sendMail) {
3585
            foreach ($users as $index => $user) {
3586
                $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('YourReg').' '.api_get_setting('siteName');
3587
                $emailbody = get_lang('Dear').' '.
3588
                    api_get_person_name($user['FirstName'], $user['LastName']).",\n\n".
3589
                    get_lang('YouAreReg')." ".api_get_setting('siteName')." ".get_lang('WithTheFollowingSettings')."\n\n".
3590
                    get_lang('Username')." : $user[UserName]\n".
3591
                    get_lang('Pass')." : $user[Password]\n\n".
3592
                    get_lang('Address')." ".api_get_setting('siteName')." ".get_lang('Is')." : ".api_get_path(WEB_PATH)." \n\n".
3593
                    get_lang('Problem')."\n\n".
3594
                    get_lang('SignatureFormula').",\n\n".
3595
                    api_get_person_name(api_get_setting('administratorName'), api_get_setting('administratorSurname'))."\n".
3596
                    get_lang('Manager')." ".api_get_setting('siteName')."\nT. ".
3597
                    api_get_setting('administratorTelephone')."\n".get_lang('Email')." : ".api_get_setting('emailAdministrator');
3598
3599
                $emailbody = nl2br($emailbody);
3600
                MessageManager::send_message_simple($user['id'], $emailsubject, $emailbody, 0, false, false, [], false);
3601
3602
                $userInfo = api_get_user_info($user['id']);
3603
3604
                if (($user['added_at_platform'] == 1 && $user['added_at_session'] == 1) || $user['added_at_session'] == 1) {
3605
                    if ($user['added_at_platform'] == 1) {
3606
                        $addedto = get_lang('UserCreatedPlatform');
3607
                    } else {
3608
                        $addedto = '          ';
3609
                    }
3610
3611
                    if ($user['added_at_session'] == 1) {
3612
                        $addedto .= get_lang('UserInSession');
3613
                    }
3614
                } else {
3615
                    $addedto = get_lang('UserNotAdded');
3616
                }
3617
3618
                $registered_users .= UserManager::getUserProfileLink($userInfo).' - '.$addedto.'<br />';
3619
            }
3620
        } else {
3621
            foreach ($users as $index => $user) {
3622
                $userInfo = api_get_user_info($user['id']);
3623
                if (($user['added_at_platform'] == 1 && $user['added_at_session'] == 1) || $user['added_at_session'] == 1) {
3624
                    if ($user['added_at_platform'] == 1) {
3625
                        $addedto = get_lang('UserCreatedPlatform');
3626
                    } else {
3627
                        $addedto = '          ';
3628
                    }
3629
3630
                    if ($user['added_at_session'] == 1) {
3631
                        $addedto .= ' '.get_lang('UserInSession');
3632
                    }
3633
                } else {
3634
                    $addedto = get_lang('UserNotAdded');
3635
                }
3636
                $registered_users .= "<a href=\"../user/userInfo.php?uInfo=".$user['id']."\">".
3637
                    Security::remove_XSS($userInfo['complete_user_name'])."</a> - ".$addedto.'<br />';
3638
            }
3639
        }
3640
        Display::addFlash(Display::return_message($registered_users, 'normal', false));
3641
        header('Location: course.php?id_session='.$id_session);
3642
        exit;
3643
    }
3644
3645
    /**
3646
     * Reads CSV-file.
3647
     *
3648
     * @param string $file Path to the CSV-file
3649
     *
3650
     * @return array All userinformation read from the file
3651
     */
3652
    public function parse_csv_data($file)
3653
    {
3654
        $users = Import::csvToArray($file);
3655
        foreach ($users as $index => $user) {
3656
            if (isset($user['Courses'])) {
3657
                $user['Courses'] = explode('|', trim($user['Courses']));
3658
            }
3659
            $users[$index] = $user;
3660
        }
3661
3662
        return $users;
3663
    }
3664
3665
    /**
3666
     * Reads XML-file.
3667
     *
3668
     * @param string $file Path to the XML-file
3669
     *
3670
     * @return array All userinformation read from the file
3671
     */
3672
    public static function parse_xml_data($file)
3673
    {
3674
        $crawler = Import::xml($file);
3675
        $crawler = $crawler->filter('Contacts > Contact ');
3676
        $array = [];
3677
        foreach ($crawler as $domElement) {
3678
            $row = [];
3679
            foreach ($domElement->childNodes as $node) {
3680
                if ($node->nodeName != '#text') {
3681
                    $row[$node->nodeName] = $node->nodeValue;
3682
                }
3683
            }
3684
            if (!empty($row)) {
3685
                $array[] = $row;
3686
            }
3687
        }
3688
3689
        return $array;
3690
    }
3691
3692
    /**
3693
     * @param int $courseId
3694
     * @param int $sessionId
3695
     * @param int $studentId
3696
     */
3697
    public static function displayTrackingAccessOverView(
3698
        $courseId,
3699
        $sessionId,
3700
        $studentId,
3701
        $perPage = 20,
3702
        $dates = null
3703
    ) {
3704
        $courseId = (int) $courseId;
3705
        $sessionId = (int) $sessionId;
3706
        $studentId = (int) $studentId;
3707
3708
        $courseList = [];
3709
        $sessionList = [];
3710
        $studentList = [];
3711
3712
        if (!empty($courseId)) {
3713
            $course = api_get_course_entity($courseId);
3714
            if ($course) {
3715
                $courseList[$course->getId()] = $course->getTitle();
3716
            }
3717
        }
3718
3719
        if (!empty($sessionId)) {
3720
            $session = api_get_session_entity($sessionId);
3721
            if ($session) {
3722
                $sessionList[$session->getId()] = $session->getName();
3723
            }
3724
        }
3725
3726
        if (!empty($studentId)) {
3727
            $student = api_get_user_entity($studentId);
3728
            if ($student) {
3729
                $studentList[$student->getId()] = UserManager::formatUserFullName($student);
3730
            }
3731
        }
3732
3733
        $form = new FormValidator('access_overview', 'GET');
3734
        $form->addElement(
3735
            'select_ajax',
3736
            'course_id',
3737
            get_lang('SearchCourse'),
3738
            $courseList,
3739
            [
3740
                'url' => api_get_path(WEB_AJAX_PATH).'course.ajax.php?'.http_build_query(
3741
                    [
3742
                        'a' => 'search_course_by_session_all',
3743
                        'session_id' => $sessionId,
3744
                        'course_id' => $courseId,
3745
                    ]
3746
                ),
3747
            ]
3748
        );
3749
3750
        $form->addElement(
3751
            'select_ajax',
3752
            'session_id',
3753
            get_lang('SearchSession'),
3754
            $sessionList,
3755
            [
3756
                'url_function' => "
3757
                    function () {
3758
                        var params = $.param({
3759
                            a: 'search_session_by_course',
3760
                            course_id: $('#access_overview_course_id').val() || 0
3761
                        });
3762
3763
                        return '".api_get_path(WEB_AJAX_PATH)."session.ajax.php?' + params;
3764
                    }
3765
                ",
3766
            ]
3767
        );
3768
3769
        $form->addSelect(
3770
            'profile',
3771
            get_lang('Profile'),
3772
            [
3773
                '' => get_lang('Select'),
3774
                STUDENT => get_lang('Student'),
3775
                COURSEMANAGER => get_lang('CourseManager'),
3776
                DRH => get_lang('Drh'),
3777
            ],
3778
            ['id' => 'profile']
3779
        );
3780
3781
        $form->addElement(
3782
            'select_ajax',
3783
            'student_id',
3784
            get_lang('SearchUsers'),
3785
            $studentList,
3786
            [
3787
                'placeholder' => get_lang('All'),
3788
                'url_function' => "
3789
                    function () {
3790
                        var params = $.param({
3791
                            a: 'search_user_by_course',
3792
                            session_id: $('#access_overview_session_id').val(),
3793
                            course_id: $('#access_overview_course_id').val()
3794
                        });
3795
3796
                        return '".api_get_path(WEB_AJAX_PATH)."course.ajax.php?' + params;
3797
                    }
3798
                ",
3799
            ]
3800
        );
3801
3802
        $form->addDateRangePicker(
3803
            'date',
3804
            get_lang('DateRange'),
3805
            true,
3806
            [
3807
                'id' => 'date_range',
3808
                'format' => 'YYYY-MM-DD HH:mm',
3809
                'timePicker' => 'true',
3810
                //'validate_format' => 'Y-m-d',
3811
            ]
3812
        );
3813
3814
        $form->addHidden('display', 'accessoverview');
3815
        $form->addRule('course_id', get_lang('Required'), 'required');
3816
        $form->addRule('profile', get_lang('Required'), 'required');
3817
        $form->addButton('submit', get_lang('Generate'), 'gear', 'primary');
3818
3819
        $table = null;
3820
        if (!empty($dates)) {
3821
            //if ($form->validate()) {
3822
            $table = new SortableTable(
3823
                'tracking_access_overview',
3824
                ['MySpace', 'getNumberOfTrackAccessOverview'],
3825
                ['MySpace', 'getUserDataAccessTrackingOverview'],
3826
                0,
3827
                $perPage
3828
            );
3829
            $table->set_additional_parameters(
3830
                [
3831
                    'course_id' => $courseId,
3832
                    'session_id' => $sessionId,
3833
                    'student_id' => $studentId,
3834
                    'date' => $dates,
3835
                    'tracking_access_overview_per_page' => $perPage,
3836
                    'display' => 'accessoverview',
3837
                ]
3838
            );
3839
            $table->set_header(0, get_lang('LoginDate'), true);
3840
            $table->set_header(1, get_lang('Username'), true);
3841
            if (api_is_western_name_order()) {
3842
                $table->set_header(2, get_lang('FirstName'), true);
3843
                $table->set_header(3, get_lang('LastName'), true);
3844
            } else {
3845
                $table->set_header(2, get_lang('LastName'), true);
3846
                $table->set_header(3, get_lang('FirstName'), true);
3847
            }
3848
            //$table->set_header(4, get_lang('Clicks'), false);
3849
            $table->set_header(4, get_lang('IP'), false);
3850
            $table->set_header(5, get_lang('TimeLoggedIn'), false);
3851
        }
3852
3853
        $template = new Template(
3854
            null,
3855
            false,
3856
            false,
3857
            false,
3858
            false,
3859
            false,
3860
            false
3861
        );
3862
        $template->assign('form', $form->returnForm());
3863
        $template->assign('table', $table ? $table->return_table() : null);
3864
3865
        echo $template->fetch(
3866
            $template->get_template('my_space/accessoverview.tpl')
3867
        );
3868
    }
3869
3870
    /**
3871
     * @return int
3872
     */
3873
    public static function getNumberOfTrackAccessOverview()
3874
    {
3875
        $user = Database::get_main_table(TABLE_MAIN_USER);
3876
        $course = Database::get_main_table(TABLE_MAIN_COURSE);
3877
        $trackCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3878
3879
        $sql = "SELECT COUNT(course_access_id) count
3880
                FROM $trackCourseAccess a
3881
                INNER JOIN $user u
3882
                ON a.user_id = u.id
3883
                INNER JOIN $course c
3884
                ON a.c_id = c.id
3885
                ";
3886
        $sql = self::getDataAccessTrackingFilters($sql);
3887
3888
        $result = Database::query($sql);
3889
        $row = Database::fetch_assoc($result);
3890
3891
        if ($row) {
3892
            return $row['count'];
3893
        }
3894
3895
        return 0;
3896
    }
3897
3898
    /**
3899
     * @param $from
3900
     * @param $numberItems
3901
     * @param $column
3902
     * @param $orderDirection
3903
     *
3904
     * @return array
3905
     */
3906
    public static function getUserDataAccessTrackingOverview(
3907
        $from,
3908
        $numberItems,
3909
        $column,
3910
        $orderDirection
3911
    ) {
3912
        $from = (int) $from;
3913
        $numberItems = (int) $numberItems;
3914
        $column = (int) $column;
3915
        $orderDirection = Database::escape_string($orderDirection);
3916
        $orderDirection = !in_array(strtolower(trim($orderDirection)), ['asc', 'desc']) ? 'asc' : $orderDirection;
3917
3918
        $user = Database::get_main_table(TABLE_MAIN_USER);
3919
        $course = Database::get_main_table(TABLE_MAIN_COURSE);
3920
        $track_e_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
3921
        $trackCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3922
3923
        global $export_csv;
3924
        $is_western_name_order = api_is_western_name_order();
3925
        if ($export_csv) {
3926
            $is_western_name_order = api_is_western_name_order(PERSON_NAME_DATA_EXPORT);
3927
        }
3928
3929
        //TODO add course name
3930
        $sql = "SELECT
3931
                a.login_course_date as col0,
3932
                u.username as col1,
3933
                ".(
3934
                    $is_western_name_order ? "
3935
                        u.firstname AS col2,
3936
                        u.lastname AS col3,
3937
                    " : "
3938
                        u.lastname AS col2,
3939
                        u.firstname AS col3,
3940
                "
3941
        )."
3942
                a.login_course_date,
3943
                a.logout_course_date,
3944
                c.title,
3945
                c.code,
3946
                u.id as user_id,
3947
                user_ip
3948
            FROM $trackCourseAccess a
3949
            INNER JOIN $user u
3950
            ON a.user_id = u.id
3951
            INNER JOIN $course c
3952
            ON a.c_id = c.id
3953
            WHERE 1=1 ";
3954
3955
        $sql = self::getDataAccessTrackingFilters($sql);
3956
3957
        $sql .= " ORDER BY col$column $orderDirection ";
3958
        $sql .= " LIMIT $from, $numberItems";
3959
3960
        $result = Database::query($sql);
3961
3962
        $data = [];
3963
        while ($user = Database::fetch_assoc($result)) {
3964
            $data[] = $user;
3965
        }
3966
3967
        $return = [];
3968
        //TODO: Dont use numeric index
3969
        foreach ($data as $key => $info) {
3970
            $return[] = [
3971
                api_get_local_time($info['login_course_date']),
3972
                $info['col1'],
3973
                $info['col2'],
3974
                $info['col3'],
3975
                $info['user_ip'],
3976
                gmdate('H:i:s', strtotime($info['logout_course_date']) - strtotime($info['login_course_date'])),
3977
            ];
3978
        }
3979
3980
        return $return;
3981
    }
3982
3983
    /**
3984
     * Gets the connections to a course as an array of login and logout time.
3985
     *
3986
     * @param int    $user_id
3987
     * @param array  $course_info
3988
     * @param int    $sessionId
3989
     * @param string $start_date
3990
     * @param string $end_date
3991
     * @param bool   $addUserIp
3992
     *
3993
     * @author  Jorge Frisancho Jibaja
3994
     * @author  Julio Montoya <[email protected]> fixing the function
3995
     *
3996
     * @version OCT-22- 2010
3997
     *
3998
     * @return array
3999
     */
4000
    public static function get_connections_to_course_by_date(
4001
        $user_id,
4002
        $course_info,
4003
        $sessionId,
4004
        $start_date,
4005
        $end_date,
4006
        $addUserIp = false
4007
    ) {
4008
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
4009
        $user_id = (int) $user_id;
4010
        $connections = [];
4011
        if (!empty($course_info)) {
4012
            $courseId = (int) $course_info['real_id'];
4013
            $end_date = self::add_day_to($end_date);
4014
4015
            $start_date = Database::escape_string($start_date);
4016
            $end_date = Database::escape_string($end_date);
4017
            $sessionCondition = api_get_session_condition($sessionId);
4018
            $sql = "SELECT
4019
                        login_course_date,
4020
                        logout_course_date,
4021
                        TIMESTAMPDIFF(SECOND, login_course_date, logout_course_date) duration,
4022
                        user_ip
4023
                    FROM $table
4024
                    WHERE
4025
                        user_id = $user_id AND
4026
                        c_id = $courseId AND
4027
                        login_course_date BETWEEN '$start_date' AND '$end_date' AND
4028
                        logout_course_date BETWEEN '$start_date' AND '$end_date'
4029
                        $sessionCondition
4030
                    ORDER BY login_course_date ASC";
4031
            $rs = Database::query($sql);
4032
4033
            while ($row = Database::fetch_array($rs)) {
4034
                $item = [
4035
                    'login' => $row['login_course_date'],
4036
                    'logout' => $row['logout_course_date'],
4037
                    'duration' => $row['duration'],
4038
                ];
4039
                if ($addUserIp) {
4040
                    $item['user_ip'] = $row['user_ip'];
4041
                }
4042
                $connections[] = $item;
4043
            }
4044
        }
4045
4046
        return $connections;
4047
    }
4048
4049
    /**
4050
     * @param int   $user_id
4051
     * @param array $course_info
4052
     * @param int   $sessionId
4053
     * @param null  $start_date
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $start_date is correct as it would always require null to be passed?
Loading history...
4054
     * @param null  $end_date
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $end_date is correct as it would always require null to be passed?
Loading history...
4055
     *
4056
     * @return array
4057
     */
4058
    public static function getStats($user_id, $course_info, $sessionId, $start_date = null, $end_date = null)
4059
    {
4060
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
4061
        $result = [];
4062
        if (!empty($course_info)) {
4063
            $stringStartDate = '';
4064
            $stringEndDate = '';
4065
            if ($start_date != null && $end_date != null) {
4066
                $end_date = self::add_day_to($end_date);
4067
4068
                $start_date = Database::escape_string($start_date);
4069
                $end_date = Database::escape_string($end_date);
4070
4071
                $stringStartDate = "AND login_course_date BETWEEN '$start_date' AND '$end_date'";
4072
                $stringEndDate = "AND logout_course_date BETWEEN '$start_date' AND '$end_date'";
4073
            }
4074
            $user_id = (int) $user_id;
4075
            $courseId = (int) $course_info['real_id'];
4076
            $sessionCondition = api_get_session_condition($sessionId);
4077
            $sql = "SELECT
4078
                SEC_TO_TIME(AVG(time_to_sec(timediff(logout_course_date,login_course_date)))) as avrg,
4079
                SEC_TO_TIME(SUM(time_to_sec(timediff(logout_course_date,login_course_date)))) as total,
4080
                count(user_id) as times
4081
                FROM $table
4082
                WHERE
4083
                    user_id = $user_id AND
4084
                    c_id = $courseId $stringStartDate $stringEndDate
4085
                    $sessionCondition
4086
                ORDER BY login_course_date ASC";
4087
4088
            $rs = Database::query($sql);
4089
            if ($row = Database::fetch_array($rs)) {
4090
                $foo_avg = $row['avrg'];
4091
                $foo_total = $row['total'];
4092
                $foo_times = $row['times'];
4093
                $result = [
4094
                    'avg' => $foo_avg,
4095
                    'total' => $foo_total,
4096
                    'times' => $foo_times,
4097
                ];
4098
            }
4099
        }
4100
4101
        return $result;
4102
    }
4103
4104
    public static function add_day_to($end_date)
4105
    {
4106
        $foo_date = strtotime($end_date);
4107
        $foo_date = strtotime(' +1 day', $foo_date);
4108
        $foo_date = date('Y-m-d', $foo_date);
4109
4110
        return $foo_date;
4111
    }
4112
4113
    /**
4114
     * This function draw the graphic to be displayed on the user view as an image.
4115
     *
4116
     * @param array  $sql_result
4117
     * @param string $start_date
4118
     * @param string $end_date
4119
     * @param string $type
4120
     *
4121
     * @author Jorge Frisancho Jibaja
4122
     *
4123
     * @version OCT-22- 2010
4124
     *
4125
     * @return string
4126
     */
4127
    public static function grapher($sql_result, $start_date, $end_date, $type = '')
4128
    {
4129
        if (empty($start_date)) {
4130
            $start_date = '';
4131
        }
4132
        if (empty($end_date)) {
4133
            $end_date = '';
4134
        }
4135
        if ('' == $type) {
4136
            $type = 'day';
4137
        }
4138
        $main_year = $main_month_year = $main_day = [];
4139
4140
        $period = new DatePeriod(
4141
            new DateTime($start_date),
4142
            new DateInterval('P1D'),
4143
            new DateTime($end_date)
4144
        );
4145
4146
        foreach ($period as $date) {
4147
            $main_day[$date->format('d-m-Y')] = 0;
4148
        }
4149
4150
        $period = new DatePeriod(
4151
            new DateTime($start_date),
4152
            new DateInterval('P1M'),
4153
            new DateTime($end_date)
4154
        );
4155
4156
        foreach ($period as $date) {
4157
            $main_month_year[$date->format('m-Y')] = 0;
4158
        }
4159
4160
        $i = 0;
4161
        if (is_array($sql_result) && count($sql_result) > 0) {
4162
            foreach ($sql_result as $key => $data) {
4163
                $login = api_strtotime($data['login']);
4164
                $logout = api_strtotime($data['logout']);
4165
                //creating the main array
4166
                if (isset($main_month_year[date('m-Y', $login)])) {
4167
                    $main_month_year[date('m-Y', $login)] += (float) ($logout - $login) / 60;
4168
                }
4169
                if (isset($main_day[date('d-m-Y', $login)])) {
4170
                    $main_day[date('d-m-Y', $login)] += (float) ($logout - $login) / 60;
4171
                }
4172
                if ($i > 500) {
4173
                    break;
4174
                }
4175
                $i++;
4176
            }
4177
            switch ($type) {
4178
                case 'day':
4179
                    $main_date = $main_day;
4180
                    break;
4181
                case 'month':
4182
                    $main_date = $main_month_year;
4183
                    break;
4184
                case 'year':
4185
                    $main_date = $main_year;
4186
                    break;
4187
            }
4188
4189
            $labels = array_keys($main_date);
4190
            if (1 == count($main_date)) {
4191
                $labels = $labels[0];
4192
                $main_date = $main_date[$labels];
4193
            }
4194
4195
            /* Create and populate the pData object */
4196
            $myData = new pData();
4197
            $myData->addPoints($main_date, 'Serie1');
4198
            if (count($main_date) != 1) {
4199
                $myData->addPoints($labels, 'Labels');
4200
                $myData->setSerieDescription('Labels', 'Months');
4201
                $myData->setAbscissa('Labels');
4202
            }
4203
            $myData->setSerieWeight('Serie1', 1);
4204
            $myData->setSerieDescription('Serie1', get_lang('MyResults'));
4205
            $myData->setAxisName(0, get_lang('Minutes'));
4206
            $myData->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
4207
4208
            // Cache definition
4209
            $cachePath = api_get_path(SYS_ARCHIVE_PATH);
4210
            $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
4211
            $chartHash = $myCache->getHash($myData);
4212
4213
            if ($myCache->isInCache($chartHash)) {
4214
                //if we already created the img
4215
                $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
4216
                $myCache->saveFromCache($chartHash, $imgPath);
4217
                $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
4218
            } else {
4219
                /* Define width, height and angle */
4220
                $mainWidth = 760;
4221
                $mainHeight = 230;
4222
                $angle = 50;
4223
4224
                /* Create the pChart object */
4225
                $myPicture = new pImage($mainWidth, $mainHeight, $myData);
4226
4227
                /* Turn of Antialiasing */
4228
                $myPicture->Antialias = false;
4229
                /* Draw the background */
4230
                $settings = ["R" => 255, "G" => 255, "B" => 255];
4231
                $myPicture->drawFilledRectangle(0, 0, $mainWidth, $mainHeight, $settings);
4232
4233
                /* Add a border to the picture */
4234
                $myPicture->drawRectangle(
4235
                    0,
4236
                    0,
4237
                    $mainWidth - 1,
4238
                    $mainHeight - 1,
4239
                    ["R" => 0, "G" => 0, "B" => 0]
4240
                );
4241
4242
                /* Set the default font */
4243
                $myPicture->setFontProperties(
4244
                    [
4245
                        "FontName" => api_get_path(SYS_FONTS_PATH).'opensans/OpenSans-Regular.ttf',
4246
                        "FontSize" => 10, ]
4247
                );
4248
                /* Write the chart title */
4249
                $myPicture->drawText(
4250
                    $mainWidth / 2,
4251
                    30,
4252
                    get_lang('TimeSpentInTheCourse'),
4253
                    [
4254
                        "FontSize" => 12,
4255
                        "Align" => TEXT_ALIGN_BOTTOMMIDDLE,
4256
                    ]
4257
                );
4258
4259
                /* Set the default font */
4260
                $myPicture->setFontProperties(
4261
                    [
4262
                        "FontName" => api_get_path(SYS_FONTS_PATH).'opensans/OpenSans-Regular.ttf',
4263
                        "FontSize" => 8,
4264
                    ]
4265
                );
4266
4267
                /* Define the chart area */
4268
                $myPicture->setGraphArea(50, 40, $mainWidth - 40, $mainHeight - 80);
4269
4270
                /* Draw the scale */
4271
                $scaleSettings = [
4272
                    'XMargin' => 10,
4273
                    'YMargin' => 10,
4274
                    'Floating' => true,
4275
                    'GridR' => 200,
4276
                    'GridG' => 200,
4277
                    'GridB' => 200,
4278
                    'DrawSubTicks' => true,
4279
                    'CycleBackground' => true,
4280
                    'LabelRotation' => $angle,
4281
                    'Mode' => SCALE_MODE_ADDALL_START0,
4282
                ];
4283
                $myPicture->drawScale($scaleSettings);
4284
4285
                /* Turn on Antialiasing */
4286
                $myPicture->Antialias = true;
4287
4288
                /* Enable shadow computing */
4289
                $myPicture->setShadow(
4290
                    true,
4291
                    [
4292
                        "X" => 1,
4293
                        "Y" => 1,
4294
                        "R" => 0,
4295
                        "G" => 0,
4296
                        "B" => 0,
4297
                        "Alpha" => 10,
4298
                    ]
4299
                );
4300
4301
                /* Draw the line chart */
4302
                $myPicture->setFontProperties(
4303
                    [
4304
                        "FontName" => api_get_path(SYS_FONTS_PATH).'opensans/OpenSans-Regular.ttf',
4305
                        "FontSize" => 10,
4306
                    ]
4307
                );
4308
                $myPicture->drawSplineChart();
4309
                $myPicture->drawPlotChart(
4310
                    [
4311
                        "DisplayValues" => true,
4312
                        "PlotBorder" => true,
4313
                        "BorderSize" => 1,
4314
                        "Surrounding" => -60,
4315
                        "BorderAlpha" => 80,
4316
                    ]
4317
                );
4318
4319
                /* Do NOT Write the chart legend */
4320
4321
                /* Write and save into cache */
4322
                $myCache->writeToCache($chartHash, $myPicture);
4323
                $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
4324
                $myCache->saveFromCache($chartHash, $imgPath);
4325
                $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
4326
            }
4327
4328
            return '<img src="'.$imgPath.'">';
4329
        } else {
4330
            return api_convert_encoding(
4331
                '<div id="messages" class="warning-message">'.get_lang('GraphicNotAvailable').'</div>',
4332
                'UTF-8'
4333
            );
4334
        }
4335
    }
4336
4337
    /*
4338
     * Gets the company name of a user based on the extra field 'company'.
4339
     *
4340
     * @param int $userId
4341
     *
4342
     * @return string
4343
     */
4344
    public static function getCompanyOfUser($userId = 0)
4345
    {
4346
        $userId = (int) $userId;
4347
        if (0 != $userId) {
4348
            $tblExtraFieldValue = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
4349
            $tblExtraField = Database::get_main_table(TABLE_EXTRA_FIELD);
4350
            $sql = "SELECT
4351
                    extra_field_value.item_id AS userId,
4352
                    extra_field_value.value AS company
4353
                FROM $tblExtraFieldValue AS extra_field_value
4354
                INNER JOIN $tblExtraField AS extra_field
4355
                ON (
4356
                    extra_field_value.field_id = extra_field.id AND
4357
                    extra_field.variable = 'company'
4358
                )
4359
                WHERE
4360
                    extra_field_value.value != '' AND
4361
                    extra_field_value.item_id = $userId ";
4362
            $queryResult = Database::query($sql);
4363
            $data = Database::store_result($queryResult, 'ASSOC');
4364
            $totalData = count($data);
4365
            /* use 'for' to performance */
4366
            for ($i = 0; $i < $totalData; $i++) {
4367
                $row = $data[$i];
4368
                if (isset($row['company']) && !empty($row['company'])) {
4369
                    return $row['company'];
4370
                }
4371
            }
4372
        }
4373
4374
        return get_lang('NoEntity');
4375
    }
4376
4377
    /**
4378
     * Gets a list of users who were enrolled in the lessons.
4379
     * It is necessary that in the extra field, a company is defined.
4380
     *
4381
     *  if lpId is different to 0, this search by lp id too
4382
     *
4383
     * Variable $withGroups determines the consultation of the enrollment in groups. The group in total will be taken
4384
     *
4385
     * @param string|null $startDate
4386
     * @param string|null $endDate
4387
     * @param int         $lpId
4388
     * @param bool        $withGroups
4389
     *
4390
     * @return array
4391
     */
4392
    protected static function getCompanyLearnpathSubscription(
4393
        $startDate = null,
4394
        $endDate = null,
4395
        $whereInLp = null,
4396
        $withGroups = false
4397
    ) {
4398
        $whereInLp = Database::escape_string($whereInLp);
4399
        $tblItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
4400
        $tblLp = Database::get_course_table(TABLE_LP_MAIN);
4401
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
4402
        $tblGroupUser = Database::get_course_table(TABLE_GROUP_USER);
4403
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4404
        $tblAccessUrlUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4405
        $accessUrlFilter = '';
4406
        if (api_is_multiple_url_enabled()) {
4407
            $urlId = api_get_current_access_url_id();
4408
            $accessUrlFilter = " INNER JOIN $tblAccessUrlUser auru
4409
                      ON (u.id = auru.user_id AND auru.access_url_id = $urlId)";
4410
        }
4411
        $whereCondition = '';
4412
4413
        //Validating dates
4414
        if (!empty($startDate)) {
4415
            $startDate = new DateTime($startDate);
4416
        }
4417
        if (!empty($endDate)) {
4418
            $endDate = new DateTime($endDate);
4419
        }
4420
        if (!empty($startDate) && !empty($endDate)) {
4421
            if ($startDate > $endDate) {
4422
                $dateTemp = $endDate;
4423
                $endDate = $startDate;
4424
                $startDate = $dateTemp;
4425
                unset($dateTemp);
4426
            }
4427
        }
4428
4429
        // Settings condition and parametter GET to right date
4430
        if (!empty($startDate)) {
4431
            $startDate = api_get_utc_datetime($startDate->setTime(0, 0, 0)->format('Y-m-d H:i:s'));
4432
            $_GET['startDate'] = $startDate;
4433
            $whereCondition .= " AND ip.lastedit_date >= '$startDate' ";
4434
        }
4435
        if (!empty($endDate)) {
4436
            $endDate = api_get_utc_datetime($endDate->setTime(23, 59, 59)->format('Y-m-d H:i:s'));
4437
            $_GET['endDate'] = $endDate;
4438
            $whereCondition .= " AND ip.lastedit_date <= '$endDate' ";
4439
        }
4440
        if (!empty($whereInLp)) {
4441
            $whereCondition .= " AND ip.ref in ($whereInLp) ";
4442
        }
4443
        $datas = [];
4444
        if (!empty($startDate) or !empty($endDate)) {
4445
            $query = "
4446
            SELECT DISTINCT
4447
                ip.ref AS lp_item,
4448
                lpi.iid AS lp_item_id,
4449
                ip.session_id AS session_id,
4450
                ip.lastedit_type AS type,
4451
                u.username AS username,
4452
                ip.lastedit_date AS lastedit_date,
4453
                ip.to_user_id AS id,
4454
                u.firstname as firstname,
4455
                u.lastname as lastname
4456
            FROM $tblItemProperty AS ip
4457
            INNER JOIN $tblUser AS u
4458
            ON (u.id = ip.to_user_id)
4459
            INNER JOIN $tblLp AS lp
4460
            ON (lp.iid = ip.ref AND lp.c_id = ip.c_id)
4461
            INNER JOIN $tblLpItem AS lpi
4462
            ON (lp.id = lpi.lp_id AND lp.c_id = lpi.c_id)
4463
            $accessUrlFilter
4464
                    WHERE
4465
                ip.lastedit_type = 'LearnpathSubscription' ";
4466
            if (strlen($whereCondition) > 2) {
4467
                $query .= $whereCondition;
4468
            }
4469
            if ($withGroups) {
4470
                $query = "
4471
                SELECT DISTINCT
4472
                    ip.ref AS lp_item,
4473
                    lpi.iid AS lp_item_id,
4474
                    ip.session_id AS session_id,
4475
                    ip.lastedit_type AS type,
4476
                    ip.lastedit_date AS lastedit_date,
4477
                    ip.to_group_id AS group_id,
4478
                    ug.user_id AS id,
4479
                    u.firstname as firstname,
4480
                    u.lastname as lastname
4481
            FROM
4482
                    $tblItemProperty AS ip
4483
                INNER JOIN $tblGroupUser AS ug
4484
                ON (ug.group_id = ip.to_group_id AND ip.c_id = ug.c_id)
4485
                INNER JOIN $tblUser AS u
4486
                ON (u.id = ug.user_id)
4487
                INNER JOIN $tblLp AS lp
4488
                ON (lp.iid = ip.ref AND ug.c_id = lp.c_id)
4489
                INNER JOIN $tblLpItem AS lpi
4490
                ON (lp.id = lpi.lp_id AND lp.c_id = lpi.c_id)
4491
                $accessUrlFilter
4492
            WHERE
4493
                    ip.lastedit_type = 'LearnpathSubscription' AND
4494
                    ip.to_group_id != 0 ";
4495
                if (strlen($whereCondition) > 2) {
4496
                    $query .= $whereCondition;
4497
                }
4498
            }
4499
            $query .= ' ORDER BY ip.ref, ip.session_id ';
4500
            $queryResult = Database::query($query);
4501
            $data = Database::store_result($queryResult, 'ASSOC');
4502
            $totalData = count($data);
4503
            /* use 'for' to performance */
4504
            for ($i = 0; $i < $totalData; $i++) {
4505
                $row = $data[$i];
4506
                $row['complete_name'] = api_get_person_name($row['firstname'], $row['lastname']);
4507
                $row['company'] = self::getCompanyOfUser($row['id']);
4508
                $datas[$row['lp_item_id']][] = $row;
4509
            }
4510
        }
4511
4512
        return $datas;
4513
    }
4514
4515
    private static function getDataAccessTrackingFilters($sql)
4516
    {
4517
        if (isset($_GET['course_id']) && !empty($_GET['course_id'])) {
4518
            $courseId = (int) $_GET['course_id'];
4519
            $sql .= " AND c.id = ".$courseId;
4520
        }
4521
4522
        if (isset($_GET['session_id']) && !empty($_GET['session_id'])) {
4523
            $sessionId = (int) $_GET['session_id'];
4524
            $sql .= " AND a.session_id = ".$sessionId;
4525
        }
4526
4527
        if (isset($_GET['student_id']) && !empty($_GET['student_id'])) {
4528
            $userId = (int) $_GET['student_id'];
4529
            $sql .= " AND u.user_id = ".$userId;
4530
        }
4531
4532
        $sql .= " AND u.status <> ".ANONYMOUS;
4533
4534
        if (isset($_GET['date']) && !empty($_GET['date'])) {
4535
            $dateRangePicker = new DateRangePicker('date', '', ['timePicker' => 'true']);
4536
            $dates = $dateRangePicker->parseDateRange($_GET['date']);
4537
            if (isset($dates['start']) && !empty($dates['start'])) {
4538
                $dates['start'] = Database::escape_string(api_get_utc_datetime($dates['start']));
4539
                $sql .= " AND login_course_date >= '".$dates['start']."'";
4540
            }
4541
            if (isset($dates['end']) && !empty($dates['end'])) {
4542
                $dates['end'] = Database::escape_string(api_get_utc_datetime($dates['end']));
4543
                $sql .= " AND logout_course_date <= '".$dates['end']."'";
4544
            }
4545
        }
4546
4547
        return $sql;
4548
    }
4549
4550
    /** Return all user extra fields as variable => human label. */
4551
    public static function duGetUserExtraFields(): array
4552
    {
4553
        $ef = new ExtraField('user');
4554
        $list = $ef->get_all();
4555
        $out = [];
4556
        if (!empty($list)) {
4557
            foreach ($list as $row) {
4558
                $out[$row['variable']] = $row['display_text'].' ('.$row['variable'].')';
4559
            }
4560
            ksort($out);
4561
        }
4562
        return $out;
4563
    }
4564
4565
    /** Return user extra field info by variable name. */
4566
    public static function duGetUserExtraFieldByVariable(string $var)
4567
    {
4568
        $ef = new ExtraField('user');
4569
        return $ef->get_handler_field_info_by_field_variable($var);
4570
    }
4571
4572
    /** Table exists? (current DB) */
4573
    public static function duTableExists(string $table): bool
4574
    {
4575
        $table = Database::escape_string($table);
4576
        $res = Database::query("SELECT 1 FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = '$table' LIMIT 1");
4577
        return $res && Database::num_rows($res) > 0;
4578
    }
4579
4580
    /** Column exists in table? */
4581
    public static function duColumnExists(string $table, string $column): bool
4582
    {
4583
        $table = Database::escape_string($table);
4584
        $column = Database::escape_string($column);
4585
        $res = Database::query("
4586
        SELECT 1
4587
          FROM information_schema.columns
4588
         WHERE table_schema = DATABASE()
4589
           AND table_name = '$table'
4590
           AND column_name = '$column'
4591
         LIMIT 1
4592
    ");
4593
        return $res && Database::num_rows($res) > 0;
4594
    }
4595
4596
    /**
4597
     * Safe UPDATE (with pre-dedup when composite UNIQUE/PK exists).
4598
     * @param array $uniqueOn Columns (excluding user_id) that are part of a UNIQUE/PK and may collide when moving.
4599
     */
4600
    public static function duSafeUpdateUserRef(string $table, string $column, int $fromUserId, int $toUserId, array $uniqueOn = []): int
4601
    {
4602
        if (!self::duTableExists($table) || !self::duColumnExists($table, $column)) {
4603
            return 0;
4604
        }
4605
4606
        // Pre-deduplicate if composite key exists
4607
        if (!empty($uniqueOn)) {
4608
            $onParts = [];
4609
            foreach ($uniqueOn as $c) {
4610
                if (!self::duColumnExists($table, $c)) {
4611
                    // if any listed unique column does not exist, skip dedup and proceed to UPDATE
4612
                    $onParts = [];
4613
                    break;
4614
                }
4615
                $onParts[] = "a.`$c` = b.`$c`";
4616
            }
4617
            if (!empty($onParts)) {
4618
                $onSql = implode(' AND ', $onParts);
4619
                $sqlDel = "
4620
            DELETE a
4621
              FROM `$table` a
4622
              JOIN `$table` b
4623
                ON $onSql
4624
               AND b.`$column` = $toUserId
4625
             WHERE a.`$column` = $fromUserId
4626
        ";
4627
                $resDel = Database::query($sqlDel);
4628
                // If you need to log deletes: $delRows = Database::affected_rows($resDel);
4629
            }
4630
        }
4631
4632
        // Move references (UPDATE IGNORE avoids duplicates)
4633
        $sqlUpd = "UPDATE IGNORE `$table` SET `$column` = $toUserId WHERE `$column` = $fromUserId";
4634
        $resUpd = Database::query($sqlUpd);
4635
4636
        return Database::affected_rows($resUpd);
4637
    }
4638
4639
    /**
4640
     * Safe UPDATE for tables with two user columns (e.g. user_rel_user).
4641
     * Allows independent dedup per column.
4642
     * @return array [updatedColA, updatedColB]
4643
     */
4644
    public static function duSafeUpdateUserRefDual(
4645
        string $table,
4646
        string $colA,
4647
        string $colB,
4648
        int $fromUserId,
4649
        int $toUserId,
4650
        array $uniqueOnA = [],
4651
        array $uniqueOnB = []
4652
    ): array {
4653
        $totA = 0; $totB = 0;
4654
        if (!self::duTableExists($table)) {
4655
            return [$totA, $totB];
4656
        }
4657
        if (self::duColumnExists($table, $colA)) {
4658
            // Dedup for colA → toUserId
4659
            if (!empty($uniqueOnA)) {
4660
                $onParts = [];
4661
                foreach ($uniqueOnA as $c) {
4662
                    if (!self::duColumnExists($table, $c)) { $onParts = []; break; }
4663
                    $onParts[] = "a.`$c` = b.`$c`";
4664
                }
4665
                if (!empty($onParts)) {
4666
                    $onSql = implode(' AND ', $onParts);
4667
                    $sqlDelA = "
4668
                    DELETE a
4669
                      FROM `$table` a
4670
                      JOIN `$table` b
4671
                        ON $onSql
4672
                       AND b.`$colA` = $toUserId
4673
                     WHERE a.`$colA` = $fromUserId
4674
                ";
4675
                    Database::query($sqlDelA);
4676
                }
4677
            }
4678
            $res = Database::query("UPDATE IGNORE `$table` SET `$colA` = $toUserId WHERE `$colA` = $fromUserId");
4679
            $totA = Database::affected_rows($res);
4680
        }
4681
        if (self::duColumnExists($table, $colB)) {
4682
            if (!empty($uniqueOnB)) {
4683
                $onParts = [];
4684
                foreach ($uniqueOnB as $c) {
4685
                    if (!self::duColumnExists($table, $c)) { $onParts = []; break; }
4686
                    $onParts[] = "a.`$c` = b.`$c`";
4687
                }
4688
                if (!empty($onParts)) {
4689
                    $onSql = implode(' AND ', $onParts);
4690
                    $sqlDelB = "
4691
                    DELETE a
4692
                      FROM `$table` a
4693
                      JOIN `$table` b
4694
                        ON $onSql
4695
                       AND b.`$colB` = $toUserId
4696
                     WHERE a.`$colB` = $fromUserId
4697
                ";
4698
                    Database::query($sqlDelB);
4699
                }
4700
            }
4701
            $res1 = Database::query("UPDATE IGNORE `$table` SET `$colB` = $toUserId WHERE `$colB` = $fromUserId");
4702
            $totB = Database::affected_rows($res1);
4703
        }
4704
        return [$totA, $totB];
4705
    }
4706
4707
    /** REPLACE(search, replace) in a text column (with LIKE filter). */
4708
    public static function duReplaceInColumn(string $table, string $column, string $search, string $replace): int
4709
    {
4710
        if (!self::duTableExists($table) || !self::duColumnExists($table, $column)) {
4711
            return 0;
4712
        }
4713
        $search  = Database::escape_string($search);
4714
        $replace = Database::escape_string($replace);
4715
4716
        $sql = "UPDATE `$table`
4717
           SET `$column` = REPLACE(`$column`, '$search', '$replace')
4718
         WHERE `$column` LIKE '%$search%'";
4719
        $res = Database::query($sql);
4720
4721
        return Database::affected_rows($res);
4722
    }
4723
4724
    /** Duplicated values for a given extra-field on the current portal. */
4725
    public static function duGetDuplicateValues(int $fieldId, int $urlId): array
4726
    {
4727
        $tblVals = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
4728
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4729
        $tblRel  = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4730
4731
        $sql = "SELECT v.value AS the_value, COUNT(DISTINCT u.id) AS qty
4732
        FROM $tblVals v
4733
        JOIN $tblUser u ON u.id = v.item_id
4734
        JOIN $tblRel  r ON r.user_id = u.id AND r.access_url_id = $urlId
4735
        WHERE v.field_id = $fieldId AND v.value <> ''
4736
        GROUP BY v.value
4737
        HAVING COUNT(DISTINCT u.id) > 1
4738
        ORDER BY qty DESC, the_value ASC";
4739
        $res = Database::query($sql);
4740
        return Database::store_result($res, 'ASSOC');
4741
    }
4742
4743
    /** Users having exactly a value for the given extra-field (filtered by portal). */
4744
    public static function duGetUsersByFieldValue(int $fieldId, int $urlId, string $value): array
4745
    {
4746
        $tblVals = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
4747
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4748
        $tblRel  = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4749
        $valueSql = "'".Database::escape_string($value)."'";
4750
4751
        $sql = "SELECT DISTINCT u.id AS user_id, u.username, u.firstname, u.lastname, u.email
4752
        FROM $tblUser u
4753
        JOIN $tblRel  r ON r.user_id = u.id AND r.access_url_id = $urlId
4754
        JOIN $tblVals v ON v.item_id = u.id AND v.field_id = $fieldId
4755
        WHERE v.value = $valueSql
4756
        ORDER BY u.id ASC";
4757
        $res = Database::query($sql);
4758
        return Database::store_result($res, 'ASSOC');
4759
    }
4760
4761
    /** Deactivate (active=0) or delete user with API if available. */
4762
    public static function duDisableOrDeleteUser(int $userId, string $mode): bool
4763
    {
4764
        $mode = $mode === 'delete' ? 'delete' : 'deactivate';
4765
4766
        if ($mode === 'delete' && class_exists('UserManager') && method_exists('UserManager', 'delete_user')) {
4767
            return UserManager::delete_user($userId);
4768
        }
4769
4770
        // Deactivate
4771
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4772
        $sql = "UPDATE $tblUser SET active = 0 WHERE user_id = $userId";
4773
        return (bool) Database::query($sql);
4774
    }
4775
4776
    /** track_e_default: default_value text with user_id in various formats and numeric default_user_id. */
4777
    private static function duUpdateTrackEDefault(int $fromUserId, int $toUserId): int
4778
    {
4779
        if (!self::duTableExists('track_e_default')) { return 0; }
4780
        $moved = 0;
4781
4782
        // default_user_id (if present)
4783
        if (self::duColumnExists('track_e_default','default_user_id')) {
4784
            $result1 = Database::query("UPDATE IGNORE track_e_default SET default_user_id = $toUserId WHERE default_user_id = $fromUserId");
4785
            $moved += Database::affected_rows($result1);
4786
        }
4787
4788
        // Events where default_value == user id as plain text
4789
        $eventsDirect = [
4790
            'user_created','exe_result_deleted','session_add_user_course','session_add_user',
4791
            'user_updated','user_deleted','exe_attempt_deleted','user_disable','user_password_updated',
4792
            'user_enable','session_delete_user','session_delete_user_course','exe_incomplete_results_deleted'
4793
        ];
4794
        if (self::duColumnExists('track_e_default','event_type') && self::duColumnExists('track_e_default','default_value')) {
4795
            $in = "'".implode("','", array_map('Database::escape_string', $eventsDirect))."'";
4796
            $sql = "
4797
            UPDATE track_e_default
4798
               SET default_value = '$toUserId'
4799
             WHERE default_value = '$fromUserId'
4800
               AND event_type IN ($in)
4801
        ";
4802
            $result2 = Database::query($sql);
4803
            $moved += Database::affected_rows($result2);
4804
        }
4805
4806
        // user_subscribed / user_unsubscribed with default_value_type='user_object'
4807
        if (self::duColumnExists('track_e_default','default_value_type') && self::duColumnExists('track_e_default','event_type')) {
4808
            foreach (['user_subscribed','user_unsubscribed'] as $evt) {
4809
                // Replace twice the ID and also the profile.php?u=FROM
4810
                $moved += self::duReplaceInColumn('track_e_default', 'default_value', "profile.php?u=".$fromUserId, "profile.php?u=".$toUserId);
4811
                $moved += self::duReplaceInColumn('track_e_default', 'default_value', (string)$fromUserId, (string)$toUserId);
4812
                // NOTE: REPLACE already uses LIKE; we do not narrow with WHERE to keep cross-install compatibility.
4813
            }
4814
        }
4815
4816
        // soc_gr_u_unsubs / soc_gr_u_subs: "gid: GGG - uid: XXX"
4817
        foreach (['soc_gr_u_unsubs','soc_gr_u_subs'] as $evt) {
4818
            $moved += self::duReplaceInColumn('track_e_default', 'default_value', "uid: ".$fromUserId, "uid: ".$toUserId);
4819
        }
4820
4821
        return $moved;
4822
    }
4823
4824
    /** extra_field_values: for extra_field.extra_field_type = 1 (user), move item_id. */
4825
    private static function duUpdateExtraFieldValuesUserType(int $fromUserId, int $toUserId): int
4826
    {
4827
        if (!self::duTableExists('extra_field') || !self::duTableExists('extra_field_values')) {
4828
            return 0;
4829
        }
4830
4831
        // Remove collisions (same field_id already pointing to target user)
4832
        $sqlDel = "
4833
        DELETE v
4834
          FROM extra_field_values v
4835
          JOIN extra_field f ON f.id = v.field_id AND f.extra_field_type = 1
4836
          JOIN extra_field_values w ON w.field_id = v.field_id AND w.item_id = $toUserId
4837
         WHERE v.item_id = $fromUserId
4838
    ";
4839
        Database::query($sqlDel);
4840
4841
        // Move remaining
4842
        $sqlUpd = "
4843
        UPDATE extra_field_values v
4844
          JOIN extra_field f ON f.id = v.field_id AND f.extra_field_type = 1
4845
           SET v.item_id = $toUserId
4846
         WHERE v.item_id = $fromUserId
4847
    ";
4848
        $result = Database::query($sqlUpd);
4849
        return Database::affected_rows($result);
4850
    }
4851
4852
    /**
4853
     * Move ALL references from $fromUserId → $toUserId, including special cases.
4854
     * Returns a log like "table.col (n)" for UI display.
4855
     */
4856
    public static function duUpdateAllUserRefsList(int $fromUserId, int $toUserId): array
4857
    {
4858
        $log = [];
4859
4860
        // Tables with composite UNIQUE/PK (pre-dedup)
4861
        $composite = [
4862
            // table, column, uniqueOn (exclude the user column)
4863
            ['access_url_rel_user',         'user_id', ['access_url_id']],
4864
            ['fos_user_user_group',         'user_id', ['group_id']],
4865
            ['user_rel_tag',                'user_id', ['tag_id']],
4866
            ['user_rel_course_vote',        'user_id', ['c_id']],
4867
            ['session_rel_user',            'user_id', ['session_id']],
4868
            ['session_rel_course_rel_user', 'user_id', ['session_id','c_id']],
4869
        ];
4870
        foreach ($composite as [$t,$col,$uniqueOn]) {
4871
            $n = self::duSafeUpdateUserRef($t, $col, $fromUserId, $toUserId, $uniqueOn);
4872
            if ($n) { $log[] = "$t.$col ($n)"; }
4873
        }
4874
4875
        // Simple tables (no composite unique with user_id)
4876
        $simple = [
4877
            ['admin','user_id'],
4878
            ['agenda_event_invitation','creator_id'],
4879
            ['agenda_event_invitee','user_id'],
4880
            ['class_user','user_id'],
4881
            ['course_rel_user','user_id'],
4882
            ['course_rel_user_catalogue','user_id'],
4883
            ['course_request','user_id'],
4884
            ['c_attendance_result','user_id'],
4885
            ['c_attendance_sheet','user_id'],
4886
            ['c_attendance_sheet_log','lastedit_user_id'],
4887
            ['c_blog_comment','author_id'],
4888
            ['c_blog_post','author_id'],
4889
            ['c_blog_rating','user_id'],
4890
            ['c_blog_rel_user','user_id'],
4891
            ['c_blog_task_rel_user','user_id'],
4892
            ['c_chat_connected','user_id'],
4893
            ['c_dropbox_category','user_id'],
4894
            ['c_dropbox_feedback','author_user_id'],
4895
            ['c_dropbox_person','user_id'],
4896
            ['c_dropbox_post','dest_user_id'],
4897
            ['c_forum_mailcue','user_id'],
4898
            ['c_forum_notification','user_id'],
4899
            ['c_forum_post','poster_id'],
4900
            ['c_forum_thread','thread_poster_id'],
4901
            ['c_forum_thread_qualify','user_id'],
4902
            ['c_forum_thread_qualify','qualify_user_id'],
4903
            ['c_forum_thread_qualify_log','user_id'],
4904
            ['c_forum_thread_qualify_log','qualify_user_id'],
4905
            ['c_group_rel_tutor','user_id'],
4906
            ['c_group_rel_user','user_id'],
4907
            ['c_item_property','to_user_id'],
4908
            ['c_item_property','insert_user_id'],
4909
            ['c_item_property','lastedit_user_id'],
4910
            ['c_lp_category_user','user_id'],
4911
            ['c_lp_view','user_id'],
4912
            ['c_notebook','user_id'],
4913
            ['c_online_connected','user_id'],
4914
            ['c_permission_user','user_id'],
4915
            ['c_role_user','user_id'],
4916
            ['c_student_publication','user_id'],
4917
            ['c_student_publication_comment','user_id'],
4918
            ['c_student_publication_rel_user','user_id'],
4919
            ['c_survey','author'],
4920
            ['c_survey_answer','user'],
4921
            ['c_survey_invitation','user'],
4922
            ['c_userinfo_content','user_id'],
4923
            ['c_wiki','user_id'],
4924
            ['c_wiki_mailcue','user_id'],
4925
            ['chat','from_user'],
4926
            ['chat','to_user'],
4927
            ['chat_video','from_user'],
4928
            ['chat_video','to_user'],
4929
            ['extra_field_saved_search','user_id'],
4930
            ['gradebook_category','user_id'],
4931
            ['gradebook_certificate','user_id'],
4932
            ['gradebook_evaluation','user_id'],
4933
            ['gradebook_link','user_id'],
4934
            ['gradebook_linkeval_log','user_id_log'],
4935
            ['gradebook_result','user_id'],
4936
            ['gradebook_result_log','user_id'],
4937
            ['gradebook_score_log','user_id'],
4938
            ['justification_document_rel_users','user_id'],
4939
            ['message','user_receiver_id'],
4940
            ['message','user_sender_id'],
4941
            ['notification','dest_user_id'],
4942
            ['personal_agenda','user'],
4943
            ['plugin_bbb_meeting','user_id'],
4944
            ['plugin_bbb_room','participant_id'],
4945
            ['plugin_buycourses_item_rel_beneficiary','user_id'],
4946
            ['plugin_buycourses_paypal_payouts','user_id'],
4947
            ['plugin_buycourses_sale','user_id'],
4948
            ['plugin_buycourses_subscription_rel_sale','user_id'],
4949
            ['plugin_card_game','user_id'],
4950
            ['plugin_h5p','user_id'],
4951
            ['plugin_h5p_import_results','user_id'],
4952
            ['plugin_lti_provider_result','user_id'],
4953
            ['portfolio','user_id'],
4954
            ['portfolio_category','user_id'],
4955
            ['portfolio_comment','author_id'],
4956
            ['sequence_value','user_id'],
4957
            ['shared_survey','author'],
4958
            ['skill_rel_user','argumentation_author_id'],
4959
            ['skill_rel_user','assigned_by'],
4960
            ['skill_rel_user','user_id'],
4961
            ['skill_rel_user_comment','feedback_giver_id'],
4962
            ['templates','user_id'],
4963
            ['ticket_assigned_log','user_id'],
4964
            ['ticket_assigned_log','sys_insert_user_id'],
4965
            ['ticket_category','sys_insert_user_id'],
4966
            ['ticket_category','sys_lastedit_user_id'],
4967
            ['ticket_category_rel_user','user_id'],
4968
            ['ticket_message','sys_insert_user_id'],
4969
            ['ticket_message','sys_lastedit_user_id'],
4970
            ['ticket_message_attachments','sys_lastedit_user_id'],
4971
            ['ticket_message_attachments','sys_insert_user_id'],
4972
            ['ticket_priority','sys_lastedit_user_id'],
4973
            ['ticket_priority','sys_insert_user_id'],
4974
            ['ticket_project','sys_lastedit_user_id'],
4975
            ['ticket_project','sys_insert_user_id'],
4976
            ['ticket_ticket','sys_lastedit_user_id'],
4977
            ['ticket_ticket','sys_insert_user_id'],
4978
            ['track_e_access','access_user_id'],
4979
            ['track_e_attempt','user_id'],
4980
            ['track_e_attempt_recording','author'],
4981
            ['track_e_course_access','user_id'],
4982
            ['track_e_downloads','down_user_id'],
4983
            ['track_e_exercises','exe_user_id'],
4984
            ['track_e_hotpotatoes','exe_user_id'],
4985
            ['track_e_hotspot','hotspot_user_id'],
4986
            ['track_e_item_property','lastedit_user_id'],
4987
            ['track_e_lastaccess','access_user_id'],
4988
            ['track_e_links','links_user_id'],
4989
            ['track_e_login','login_user_id'],
4990
            ['track_e_online','login_user_id'],
4991
            ['track_e_uploads','upload_user_id'],
4992
            ['track_stored_values','user_id'],
4993
            ['track_stored_values_stack','user_id'],
4994
            ['user','creator_id'],
4995
            ['usergroup_rel_user','user_id'],
4996
            ['user_api_key','user_id'],
4997
            ['user_course_category','user_id'],
4998
            ['user_rel_event_type','user_id'],
4999
            ['xapi_internal_log','user_id'],
5000
        ];
5001
        foreach ($simple as [$t,$col]) {
5002
            $n = self::duSafeUpdateUserRef($t, $col, $fromUserId, $toUserId);
5003
            if ($n) { $log[] = "$t.$col ($n)"; }
5004
        }
5005
5006
        // user_rel_user (two user columns)
5007
        if (self::duTableExists('user_rel_user')) {
5008
            // Dedup by user_id
5009
            $sqlDel1 = "
5010
                DELETE a FROM user_rel_user a
5011
                JOIN user_rel_user b
5012
                  ON a.friend_user_id = b.friend_user_id
5013
                 AND b.user_id = $toUserId
5014
               WHERE a.user_id = $fromUserId
5015
            ";
5016
            Database::query($sqlDel1);
5017
5018
            $resUp1 = Database::query("UPDATE IGNORE user_rel_user SET user_id = $toUserId WHERE user_id = $fromUserId");
5019
            $n1 = Database::affected_rows($resUp1);
5020
5021
            // Dedup by friend_user_id
5022
            $sqlDel2 = "
5023
                DELETE a FROM user_rel_user a
5024
                JOIN user_rel_user b
5025
                  ON a.user_id = b.user_id
5026
                 AND b.friend_user_id = $toUserId
5027
               WHERE a.friend_user_id = $fromUserId
5028
            ";
5029
            Database::query($sqlDel2);
5030
5031
            $resUp2 = Database::query("UPDATE IGNORE user_rel_user SET friend_user_id = $toUserId WHERE friend_user_id = $fromUserId");
5032
            $n2 = Database::affected_rows($resUp2);
5033
5034
            if ($n1) { $log[] = "user_rel_user.user_id ($n1)"; }
5035
            if ($n2) { $log[] = "user_rel_user.friend_user_id ($n2)"; }
5036
        }
5037
5038
        // Special cases
5039
        $nDef = self::duUpdateTrackEDefault($fromUserId, $toUserId);
5040
        if ($nDef) { $log[] = "track_e_default (default_user_id/default_value) ($nDef)"; }
5041
5042
        $nEf = self::duUpdateExtraFieldValuesUserType($fromUserId, $toUserId); // idem
5043
        if ($nEf) { $log[] = "extra_field_values.item_id (user-type) ($nEf)"; }
5044
5045
        return $log;
5046
    }
5047
}
5048