Event   F
last analyzed

Complexity

Total Complexity 261

Size/Duplication

Total Lines 2866
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 261
eloc 1375
dl 0
loc 2866
rs 0.8
c 0
b 0
f 0

55 Methods

Rating   Name   Duplication   Size   Complexity  
A get_all_exercises_from_lp() 0 19 2
B get_best_attempt_exercise_results_per_user() 0 50 6
A delete_attempt() 0 32 1
A findUserSubscriptionToCourse() 0 17 1
A event_download() 0 22 2
A getAllExerciseEventByExeId() 0 17 3
B getAttemptQuestionDuration() 0 24 7
A get_exercise_results_by_attempt() 0 37 4
A getLastAttemptDateOfExercise() 0 12 1
A get_attempt_count_not_finished() 0 34 2
A get_event_users() 0 11 1
A get_all_exercise_results_by_user() 0 34 3
A count_exercise_attempts_by_user() 0 35 3
A event_upload() 0 32 2
B eventCourseLoginUpdate() 0 59 8
B saveExerciseAttemptHotspot() 0 68 9
A get_events_by_user_and_type() 0 17 2
A event_send_mail() 0 3 1
A eventCourseLogin() 0 36 4
C addEvent() 0 74 10
A portal_homepage_edited_event_send_mail_filter_func() 0 5 1
A save_event_type_message() 0 67 4
B isSessionLogNeedToBeSave() 0 17 7
A event_send_mail_filter_func() 0 3 1
A get_all_exercise_results_by_course() 0 32 4
A get_all_event_types() 0 19 2
A logSubscribedUserInCourse() 0 21 1
B get_best_exercise_results_by_user() 0 60 8
C courseLogout() 0 69 12
A eventAddVirtualCourseTime() 0 75 3
A get_comments() 0 16 1
B eventLogin() 0 44 8
B get_all_exercise_results() 0 70 9
A open() 0 33 5
F saveQuestionAttempt() 0 182 24
A user_registration_event_send_mail_filter_func() 0 5 1
A getQuestionAttemptByExeIdAndQuestion() 0 18 2
A get_all_exercise_event_from_lp() 0 34 3
A accessCourse() 0 46 5
A get_attempt_count() 0 36 2
A event_link() 0 28 2
B getExerciseResultsByUser() 0 62 6
A eventLoginAttempt() 0 17 2
C delete_student_lp_events() 0 123 10
F registerLog() 0 46 18
B event_access_tool() 0 76 11
A delete_all_incomplete_attempts() 0 29 4
A getLatestQuestionIdFromAttempt() 0 16 2
A count_exercise_result_not_validated() 0 28 1
A logUserSubscribedInCourseSession() 0 31 1
A getAttemptPosition() 0 45 4
A delete_attempt_hotspot() 0 32 2
B eventRemoveVirtualCourseTime() 0 102 8
B updateEventExercise() 0 73 7
B getAuditItems() 0 53 8

How to fix   Complexity   

Complex Class

Complex classes like Event often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Event, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/* See license terms in /license.txt */
4
5
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
6
use Chamilo\CoreBundle\Entity\TrackELoginAttempt;
7
use ChamiloSession as Session;
8
9
/**
10
 * Class Event
11
 * Functions of this library are used to record informations when some kind
12
 * of event occur. Each event has his own types of informations then each event
13
 * use its own function.
14
 */
15
class Event
16
{
17
    /**
18
     * @author Sebastien Piraux <[email protected]>
19
     * @desc Record information for open event (when homepage is opened)
20
     */
21
    public static function open()
22
    {
23
        global $_configuration;
24
        global $TABLETRACK_OPEN;
25
26
        // @getHostByAddr($_SERVER['REMOTE_ADDR']) : will provide host and country information
27
        // $_SERVER['HTTP_USER_AGENT'] :  will provide browser and os information
28
        // $_SERVER['HTTP_REFERER'] : provide information about refering url
29
        if (isset($_SERVER['HTTP_REFERER'])) {
30
            $referer = Database::escape_string($_SERVER['HTTP_REFERER']);
31
        } else {
32
            $referer = '';
33
        }
34
        // record informations only if user comes from another site
35
        //if(!eregi($_configuration['root_web'],$referer))
36
        $pos = strpos($referer, $_configuration['root_web']);
37
        if ($pos === false && $referer != '') {
38
            $ip = api_get_real_ip();
39
            $remhost = @gethostbyaddr($ip);
40
            if ($remhost == $ip) {
41
                $remhost = "Unknown";
42
            } // don't change this
43
            $reallyNow = api_get_utc_datetime();
44
            $params = [
45
                'open_remote_host' => Database::escape_string($remhost),
46
                'open_agent' => $_SERVER['HTTP_USER_AGENT'],
47
                'open_referer' => $referer,
48
                'open_date' => $reallyNow,
49
            ];
50
            Database::insert($TABLETRACK_OPEN, $params);
51
        }
52
53
        return 1;
54
    }
55
56
    /**
57
     * @throws Exception
58
     */
59
    public static function eventLoginAttempt(string $username, bool $success = false)
60
    {
61
        if ((int) api_get_configuration_value('login_max_attempt_before_blocking_account') <= 0) {
62
            return;
63
        }
64
65
        $attempt = new TrackELoginAttempt();
66
        $attempt
67
            ->setUsername($username)
68
            ->setLoginDate(api_get_utc_datetime(null, false, true))
69
            ->setUserIp(api_get_real_ip())
70
            ->setSuccess($success)
71
        ;
72
73
        $em = Database::getManager();
74
        $em->persist($attempt);
75
        $em->flush();
76
    }
77
78
    /**
79
     * @author Sebastien Piraux <[email protected]> old code
80
     * @author Julio Montoya
81
     *
82
     * @param int $userId
83
     *
84
     * @return bool
85
     * @desc Record information for login event when an user identifies himself with username & password
86
     */
87
    public static function eventLogin($userId)
88
    {
89
        $userInfo = api_get_user_info($userId);
90
        $userId = (int) $userId;
91
92
        if (empty($userInfo)) {
93
            return false;
94
        }
95
96
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
97
        $reallyNow = api_get_utc_datetime();
98
        $userIp = Database::escape_string(api_get_real_ip());
99
100
        $sql = "INSERT INTO $table (login_user_id, user_ip, login_date, logout_date) VALUES
101
                    ($userId,
102
                    '$userIp',
103
                    '$reallyNow',
104
                    '$reallyNow'
105
                )";
106
        Database::query($sql);
107
108
        // Auto subscribe
109
        $status = 'student';
110
        if ($userInfo['status'] == SESSIONADMIN) {
111
            $status = 'sessionadmin';
112
        }
113
        if ($userInfo['status'] == COURSEMANAGER) {
114
            $status = 'teacher';
115
        }
116
        if ($userInfo['status'] == DRH) {
117
            $status = 'DRH';
118
        }
119
120
        $autoSubscribe = api_get_setting($status.'_autosubscribe');
121
        if ($autoSubscribe) {
122
            $autoSubscribe = explode('|', $autoSubscribe);
123
            foreach ($autoSubscribe as $code) {
124
                if (CourseManager::course_exists($code)) {
125
                    CourseManager::subscribeUser($userId, $code);
126
                }
127
            }
128
        }
129
130
        return true;
131
    }
132
133
    /**
134
     * @param int $sessionId
135
     *
136
     * @return bool
137
     */
138
    public static function isSessionLogNeedToBeSave($sessionId)
139
    {
140
        if (!empty($sessionId)) {
141
            $visibility = api_get_session_visibility($sessionId);
142
            if (!empty($visibility) && $visibility != SESSION_AVAILABLE) {
143
                $extraFieldValue = new ExtraFieldValue('session');
144
                $value = $extraFieldValue->get_values_by_handler_and_field_variable(
145
                    $sessionId,
146
                    'disable_log_after_session_ends'
147
                );
148
                if (!empty($value) && isset($value['value']) && (int) $value['value'] == 1) {
149
                    return false;
150
                }
151
            }
152
        }
153
154
        return true;
155
    }
156
157
    /**
158
     * @author Sebastien Piraux <[email protected]>
159
     * @desc Record information for access event for courses
160
     *
161
     * @return bool
162
     */
163
    public static function accessCourse()
164
    {
165
        if (Session::read('login_as')) {
166
            return false;
167
        }
168
169
        $TABLETRACK_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
170
        // For "what's new" notification
171
        $TABLETRACK_LASTACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
172
173
        $sessionId = api_get_session_id();
174
        $now = api_get_utc_datetime();
175
        $courseId = api_get_course_int_id();
176
        $userId = api_get_user_id();
177
        $ip = Database::escape_string(api_get_real_ip());
178
179
        if (self::isSessionLogNeedToBeSave($sessionId) === false) {
180
            return false;
181
        }
182
183
        if ($userId) {
184
            $userId = $userId;
185
        } else {
186
            $userId = '0'; // no one
187
        }
188
        $sql = "INSERT INTO $TABLETRACK_ACCESS  (user_ip, access_user_id, c_id, access_date, access_session_id)
189
                VALUES ('$ip', $userId, $courseId, '$now', $sessionId)";
190
191
        Database::query($sql);
192
193
        // added for "what's new" notification
194
        $sql = "UPDATE $TABLETRACK_LASTACCESS  SET access_date = '$now'
195
                WHERE
196
                  access_user_id = $userId AND
197
                  c_id = $courseId AND
198
                  access_tool IS NULL AND
199
                  access_session_id = $sessionId";
200
        $result = Database::query($sql);
201
202
        if (Database::affected_rows($result) == 0) {
203
            $sql = "INSERT INTO $TABLETRACK_LASTACCESS (access_user_id, c_id, access_date, access_session_id)
204
                    VALUES ($userId, $courseId, '$now', $sessionId)";
205
            Database::query($sql);
206
        }
207
208
        return true;
209
    }
210
211
    /**
212
     * @param string $tool name of the tool
213
     *
214
     * @author Sebastien Piraux <[email protected]>
215
     * @desc Record information for access event for tools
216
     *
217
     *  $tool can take this values :
218
     *  Links, Calendar, Document, Announcements,
219
     *  Group, Video, Works, Users, Exercises, Course Desc
220
     *  ...
221
     *  Values can be added if new modules are created (15char max)
222
     *  I encourage to use $nameTool as $tool when calling this function
223
     *
224
     * Functionality for "what's new" notification is added by Toon Van Hoecke
225
     *
226
     * @return bool
227
     */
228
    public static function event_access_tool($tool)
229
    {
230
        if (Session::read('login_as')) {
231
            return false;
232
        }
233
234
        $tool = Database::escape_string($tool);
235
236
        if (empty($tool)) {
237
            return false;
238
        }
239
240
        $courseInfo = api_get_course_info();
241
        $sessionId = api_get_session_id();
242
        $reallyNow = api_get_utc_datetime();
243
        $userId = api_get_user_id();
244
245
        if (empty($courseInfo)) {
246
            return false;
247
        }
248
249
        if (self::isSessionLogNeedToBeSave($sessionId) === false) {
250
            return false;
251
        }
252
253
        $courseId = $courseInfo['real_id'];
254
255
        $tableAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
256
        //for "what's new" notification
257
        $tableLastAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
258
259
        // record information
260
        // only if user comes from the course $_cid
261
        //if( eregi($_configuration['root_web'].$_cid,$_SERVER['HTTP_REFERER'] ) )
262
        //$pos = strpos($_SERVER['HTTP_REFERER'],$_configuration['root_web'].$_cid);
263
        $coursePath = isset($courseInfo['path']) ? $courseInfo['path'] : null;
264
265
        $pos = isset($_SERVER['HTTP_REFERER']) ? strpos(strtolower($_SERVER['HTTP_REFERER']), strtolower(api_get_path(WEB_COURSE_PATH).$coursePath)) : false;
266
        // added for "what's new" notification
267
        $pos2 = isset($_SERVER['HTTP_REFERER']) ? strpos(strtolower($_SERVER['HTTP_REFERER']), strtolower(api_get_path(WEB_PATH)."index")) : false;
268
269
        // end "what's new" notification
270
        if ($pos !== false || $pos2 !== false) {
271
            $params = [
272
                'access_user_id' => $userId,
273
                'c_id' => $courseId,
274
                'access_tool' => $tool,
275
                'access_date' => $reallyNow,
276
                'access_session_id' => $sessionId,
277
                'user_ip' => Database::escape_string(api_get_real_ip()),
278
            ];
279
            Database::insert($tableAccess, $params);
280
        }
281
282
        // "what's new" notification
283
        $sql = "UPDATE $tableLastAccess
284
                SET access_date = '$reallyNow'
285
                WHERE
286
                    access_user_id = $userId AND
287
                    c_id = $courseId AND
288
                    access_tool = '$tool' AND
289
                    access_session_id = $sessionId";
290
        $result = Database::query($sql);
291
292
        if (Database::affected_rows($result) == 0) {
293
            $params = [
294
                'access_user_id' => $userId,
295
                'c_id' => $courseId,
296
                'access_tool' => $tool,
297
                'access_date' => $reallyNow,
298
                'access_session_id' => $sessionId,
299
            ];
300
            Database::insert($tableLastAccess, $params);
301
        }
302
303
        return true;
304
    }
305
306
    /**
307
     * Record information for download event (when an user click to d/l a
308
     * document) it will be used in a redirection page.
309
     *
310
     * @param string $documentUrl
311
     *
312
     * @return int
313
     *
314
     * @author Sebastien Piraux <[email protected]>
315
     * @author Evie Embrechts (bug fixed: The user id is put in single quotes)
316
     */
317
    public static function event_download($documentUrl)
318
    {
319
        if (Session::read('login_as')) {
320
            return false;
321
        }
322
323
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
324
        $documentUrl = Database::escape_string($documentUrl);
325
326
        $reallyNow = api_get_utc_datetime();
327
        $userId = api_get_user_id();
328
        $courseId = api_get_course_int_id();
329
        $sessionId = api_get_session_id();
330
331
        return Database::insert(
332
            $table,
333
            [
334
                'down_user_id' => $userId,
335
                'c_id' => $courseId,
336
                'down_doc_path' => $documentUrl,
337
                'down_date' => $reallyNow,
338
                'down_session_id' => $sessionId,
339
            ]
340
        );
341
    }
342
343
    /**
344
     * @param int $documentId of document (id in mainDb.document table)
345
     *
346
     * @author Sebastien Piraux <[email protected]>
347
     * @desc Record information for upload event
348
     * used in the works tool to record informations when
349
     * an user upload 1 work
350
     *
351
     * @return int
352
     */
353
    public static function event_upload($documentId)
354
    {
355
        if (Session::read('login_as')) {
356
            return false;
357
        }
358
359
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_UPLOADS);
360
        $courseId = api_get_course_int_id();
361
        $reallyNow = api_get_utc_datetime();
362
        $userId = api_get_user_id();
363
        $documentId = (int) $documentId;
364
        $sessionId = api_get_session_id();
365
366
        $sql = "INSERT INTO $table
367
                ( upload_user_id,
368
                  c_id,
369
                  upload_cours_id,
370
                  upload_work_id,
371
                  upload_date,
372
                  upload_session_id
373
                )
374
                VALUES (
375
                 $userId,
376
                 $courseId,
377
                 '',
378
                 $documentId,
379
                 '$reallyNow',
380
                 $sessionId
381
                )";
382
        Database::query($sql);
383
384
        return 1;
385
    }
386
387
    /**
388
     * Record information for link event (when an user click on an added link)
389
     * it will be used in a redirection page.
390
     *
391
     * @param int $linkId (id in c_link table)
392
     *
393
     * @return int
394
     *
395
     * @author Sebastien Piraux <[email protected]>
396
     */
397
    public static function event_link($linkId)
398
    {
399
        if (Session::read('login_as')) {
400
            return false;
401
        }
402
403
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
404
        $reallyNow = api_get_utc_datetime();
405
        $userId = api_get_user_id();
406
        $courseId = api_get_course_int_id();
407
        $linkId = (int) $linkId;
408
        $sessionId = api_get_session_id();
409
        $sql = "INSERT INTO ".$table."
410
                    ( links_user_id,
411
                     c_id,
412
                     links_link_id,
413
                     links_date,
414
                     links_session_id
415
                    ) VALUES (
416
                     $userId,
417
                     $courseId,
418
                     $linkId,
419
                     '$reallyNow',
420
                     $sessionId
421
                    )";
422
        Database::query($sql);
423
424
        return 1;
425
    }
426
427
    /**
428
     * Update the TRACK_E_EXERCICES exercises.
429
     * Record result of user when an exercise was done.
430
     *
431
     * @param int    $exeId
432
     * @param int    $exoId
433
     * @param mixed  $score
434
     * @param int    $weighting
435
     * @param int    $sessionId
436
     * @param int    $learnpathId
437
     * @param int    $learnpathItemId
438
     * @param int    $learnpathItemViewId
439
     * @param int    $duration
440
     * @param array  $questionsList
441
     * @param string $status
442
     * @param array  $remindList
443
     * @param null   $endDate
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $endDate is correct as it would always require null to be passed?
Loading history...
444
     *
445
     * @return bool
446
     *
447
     * @author Sebastien Piraux <[email protected]>
448
     * @author Julio Montoya Armas <[email protected]> Reworked 2010
449
     */
450
    public static function updateEventExercise(
451
        $exeId,
452
        $exoId,
453
        $score,
454
        $weighting,
455
        $sessionId,
456
        $learnpathId = 0,
457
        $learnpathItemId = 0,
458
        $learnpathItemViewId = 0,
459
        $duration = 0,
460
        $questionsList = [],
461
        $status = '',
462
        $remindList = [],
463
        $endDate = null
464
    ) {
465
        if (empty($exeId)) {
466
            return false;
467
        }
468
        if (!isset($status) || empty($status)) {
469
            $status = '';
470
        } else {
471
            $status = Database::escape_string($status);
472
        }
473
474
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
475
476
        if (!empty($questionsList)) {
477
            $questionsList = array_map('intval', $questionsList);
478
        }
479
480
        if (!empty($remindList)) {
481
            $remindList = array_map('intval', $remindList);
482
            $remindList = array_filter($remindList);
483
            $remindList = implode(",", $remindList);
484
        } else {
485
            $remindList = '';
486
        }
487
488
        if (empty($endDate)) {
489
            $endDate = api_get_utc_datetime();
490
        }
491
        $exoId = (int) $exoId;
492
        $sessionId = (int) $sessionId;
493
        $learnpathId = (int) $learnpathId;
494
        $learnpathItemId = (int) $learnpathItemId;
495
        $learnpathItemViewId = (int) $learnpathItemViewId;
496
        $duration = (int) $duration;
497
        $exeId = (int) $exeId;
498
        $score = Database::escape_string($score);
499
        $weighting = Database::escape_string($weighting);
500
        $questions = implode(',', $questionsList);
501
        $userIp = Database::escape_string(api_get_real_ip());
502
503
        $sql = "UPDATE $table SET
504
               exe_exo_id = $exoId,
505
               exe_result = '$score',
506
               exe_weighting = '$weighting',
507
               session_id = $sessionId,
508
               orig_lp_id = $learnpathId,
509
               orig_lp_item_id = $learnpathItemId,
510
               orig_lp_item_view_id = $learnpathItemViewId,
511
               exe_duration = $duration,
512
               exe_date = '$endDate',
513
               status = '$status',
514
               questions_to_check = '$remindList',
515
               data_tracking = '$questions',
516
               user_ip = '$userIp'
517
             WHERE exe_id = $exeId";
518
        Database::query($sql);
519
520
        //Deleting control time session track
521
        //ExerciseLib::exercise_time_control_delete($exo_id);
522
        return true;
523
    }
524
525
    /**
526
     * Record an event for this attempt at answering an exercise.
527
     *
528
     * @param float  $score             Score achieved
529
     * @param string $answer            Answer given
530
     * @param int    $question_id
531
     * @param int    $exe_id            Exercise attempt ID a.k.a exe_id (from track_e_exercise)
532
     * @param int    $position
533
     * @param int    $exercise_id       From c_quiz
534
     * @param bool   $updateResults
535
     * @param int    $duration          Time spent in seconds
536
     * @param string $fileName          Filename (for audio answers - using nanogong)
537
     * @param int    $user_id           The user who's going to get this score.
538
     * @param int    $course_id         Default value of null means "get from context".
539
     * @param int    $session_id        Default value of null means "get from context".
540
     * @param int    $learnpath_id      (from c_lp table). Default value of null means "get from context".
541
     * @param int    $learnpath_item_id (from the c_lp_item table). Default value of null means "get from context".
542
     *
543
     * @return bool Result of the insert query
544
     */
545
    public static function saveQuestionAttempt(
546
        $score,
547
        $answer,
548
        $question_id,
549
        $exe_id,
550
        $position,
551
        $exercise_id = 0,
552
        $updateResults = false,
553
        $questionDuration = 0,
554
        $fileName = null,
555
        $user_id = null,
556
        $course_id = null,
557
        $session_id = null,
558
        $learnpath_id = null,
559
        $learnpath_item_id = null
560
    ) {
561
        global $debug;
562
        $questionDuration = (int) $questionDuration;
563
        $question_id = (int) $question_id;
564
        $exe_id = (int) $exe_id;
565
        $position = (int) $position;
566
        $course_id = (int) $course_id;
567
        $now = api_get_utc_datetime();
568
        $recording = api_get_configuration_value('quiz_answer_extra_recording');
569
570
        // check user_id or get from context
571
        if (empty($user_id)) {
572
            $user_id = api_get_user_id();
573
            // anonymous
574
            if (empty($user_id)) {
575
                $user_id = api_get_anonymous_id();
576
            }
577
        }
578
        // check course_id or get from context
579
        if (empty($course_id)) {
580
            $course_id = api_get_course_int_id();
581
        }
582
        // check session_id or get from context
583
        $session_id = (int) $session_id;
584
        if (empty($session_id)) {
585
            $session_id = api_get_session_id();
586
        }
587
        // check learnpath_id or get from context
588
        if (empty($learnpath_id)) {
589
            global $learnpath_id;
590
        }
591
        // check learnpath_item_id or get from context
592
        if (empty($learnpath_item_id)) {
593
            global $learnpath_item_id;
594
        }
595
596
        $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
597
598
        if ($debug) {
599
            error_log("----- entering saveQuestionAttempt() function ------");
600
            error_log("answer: $answer");
601
            error_log("score: $score");
602
            error_log("question_id : $question_id");
603
            error_log("position: $position");
604
        }
605
606
        // Validation in case of fraud with active control time.
607
        if (!ExerciseLib::exercise_time_control_is_valid($exercise_id, $learnpath_id, $learnpath_item_id)) {
608
            if ($debug) {
609
                error_log("exercise_time_control_is_valid is false");
610
            }
611
            $score = 0;
612
            $answer = 0;
613
        }
614
615
        if (empty($question_id) || empty($exe_id) || empty($user_id)) {
616
            return false;
617
        }
618
619
        if (null === $answer) {
620
            $answer = '';
621
        }
622
623
        if (null === $score) {
624
            $score = 0;
625
        }
626
627
        $attempt = [
628
            'user_id' => $user_id,
629
            'question_id' => $question_id,
630
            'answer' => $answer,
631
            'marks' => $score,
632
            'c_id' => $course_id,
633
            'session_id' => $session_id,
634
            'position' => $position,
635
            'tms' => $now,
636
            'filename' => !empty($fileName) ? basename($fileName) : $fileName,
637
            'teacher_comment' => '',
638
        ];
639
640
        if (api_get_configuration_value('allow_time_per_question')) {
641
            $attempt['seconds_spent'] = $questionDuration;
642
        }
643
644
        // Check if attempt exists.
645
        $sql = "SELECT exe_id FROM $TBL_TRACK_ATTEMPT
646
                WHERE
647
                    c_id = $course_id AND
648
                    session_id = $session_id AND
649
                    exe_id = $exe_id AND
650
                    user_id = $user_id AND
651
                    question_id = $question_id AND
652
                    position = $position";
653
        $result = Database::query($sql);
654
        $attemptData = [];
655
        if (Database::num_rows($result)) {
656
            $attemptData = Database::fetch_array($result, 'ASSOC');
657
            if ($updateResults == false) {
658
                // The attempt already exist do not update use  update_event_exercise() instead
659
                return false;
660
            }
661
        } else {
662
            $attempt['exe_id'] = $exe_id;
663
        }
664
665
        if ($debug) {
666
            error_log("updateResults : $updateResults");
667
            error_log("Saving question attempt: ");
668
            error_log($sql);
669
        }
670
671
        $recording_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
672
        if ($updateResults == false) {
673
            $attempt_id = Database::insert($TBL_TRACK_ATTEMPT, $attempt);
674
            if ($recording) {
675
                $attempt_recording = [
676
                    'exe_id' => $exe_id,
677
                    'question_id' => $question_id,
678
                    'answer' => $answer,
679
                    'marks' => $score,
680
                    'insert_date' => $now,
681
                    'session_id' => $session_id,
682
                ];
683
                Database::insert($recording_table, $attempt_recording);
684
            }
685
        } else {
686
            if (api_get_configuration_value('allow_time_per_question')) {
687
                $attempt['seconds_spent'] = $questionDuration + (int) $attemptData['seconds_spent'];
688
            }
689
            Database::update(
690
                $TBL_TRACK_ATTEMPT,
691
                $attempt,
692
                [
693
                    'exe_id = ? AND question_id = ? AND user_id = ? ' => [
694
                        $exe_id,
695
                        $question_id,
696
                        $user_id,
697
                    ],
698
                ]
699
            );
700
701
            if ($recording) {
702
                $attempt_recording = [
703
                    'exe_id' => $exe_id,
704
                    'question_id' => $question_id,
705
                    'answer' => $answer,
706
                    'marks' => $score,
707
                    'insert_date' => $now,
708
                    'session_id' => $session_id,
709
                ];
710
711
                Database::update(
712
                    $recording_table,
713
                    $attempt_recording,
714
                    [
715
                        'exe_id = ? AND question_id = ? AND session_id = ? ' => [
716
                            $exe_id,
717
                            $question_id,
718
                            $session_id,
719
                        ],
720
                    ]
721
                );
722
            }
723
            $attempt_id = $exe_id;
724
        }
725
726
        return $attempt_id;
727
    }
728
729
    /**
730
     * Record an hotspot spot for this attempt at answering an hotspot question.
731
     *
732
     * @param int    $exeId
733
     * @param int    $questionId    Question ID
734
     * @param int    $answerId      Answer ID
735
     * @param int    $correct
736
     * @param string $coords        Coordinates of this point (e.g. 123;324)
737
     * @param bool   $updateResults
738
     * @param int    $exerciseId
739
     *
740
     * @return bool Result of the insert query
741
     *
742
     * @uses \Course code and user_id from global scope $_cid and $_user
743
     */
744
    public static function saveExerciseAttemptHotspot(
745
        $exeId,
746
        $questionId,
747
        $answerId,
748
        $correct,
749
        $coords,
750
        $updateResults = false,
751
        $exerciseId = 0,
752
        $lpId = 0,
753
        $lpItemId = 0
754
    ) {
755
        $debug = false;
756
757
        if ($updateResults == false) {
758
            // Validation in case of fraud with activated control time
759
            if (!ExerciseLib::exercise_time_control_is_valid($exerciseId, $lpId, $lpItemId)) {
760
                if ($debug) {
761
                    error_log('Attempt is fraud');
762
                }
763
                $correct = 0;
764
            }
765
        }
766
767
        if (empty($exeId)) {
768
            if ($debug) {
769
                error_log('exe id is empty');
770
            }
771
772
            return false;
773
        }
774
775
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
776
        if ($updateResults) {
777
            if ($debug) {
778
                error_log("Insert hotspot results: exeId: $exeId correct: $correct");
779
            }
780
            $params = [
781
                'hotspot_correct' => $correct,
782
                'hotspot_coordinate' => $coords,
783
            ];
784
            Database::update(
785
                $table,
786
                $params,
787
                [
788
                    'hotspot_user_id = ? AND hotspot_exe_id = ? AND hotspot_question_id = ? AND hotspot_answer_id = ? ' => [
789
                        api_get_user_id(),
790
                        $exeId,
791
                        $questionId,
792
                        $answerId,
793
                    ],
794
                ]
795
            );
796
        } else {
797
            if ($debug) {
798
                error_log("Insert hotspot results: exeId: $exeId correct: $correct");
799
            }
800
801
            return Database::insert(
802
                $table,
803
                [
804
                    'hotspot_course_code' => api_get_course_id(),
805
                    'hotspot_user_id' => api_get_user_id(),
806
                    'c_id' => api_get_course_int_id(),
807
                    'hotspot_exe_id' => $exeId,
808
                    'hotspot_question_id' => $questionId,
809
                    'hotspot_answer_id' => $answerId,
810
                    'hotspot_correct' => $correct,
811
                    'hotspot_coordinate' => $coords,
812
                ]
813
            );
814
        }
815
    }
816
817
    /**
818
     * Records information for common (or admin) events (in the track_e_default table).
819
     *
820
     * @author Yannick Warnier <[email protected]>
821
     *
822
     * @param string $event_type       Type of event
823
     * @param string $event_value_type Type of value
824
     * @param mixed  $event_value      Value (string, or array in the case of user info)
825
     * @param string $datetime         Datetime (UTC) (defaults to null)
826
     * @param int    $user_id          User ID (defaults to null)
827
     * @param int    $course_id        Course ID (defaults to null)
828
     * @param int    $sessionId        Session ID
829
     *
830
     * @return bool
831
     * @assert ('','','') === false
832
     */
833
    public static function addEvent(
834
        $event_type,
835
        $event_value_type,
836
        $event_value,
837
        $datetime = null,
838
        $user_id = null,
839
        $course_id = null,
840
        $sessionId = 0
841
    ) {
842
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
843
844
        if (empty($event_type)) {
845
            return false;
846
        }
847
        $event_type = Database::escape_string($event_type);
848
        $event_value_type = Database::escape_string($event_value_type);
849
        if (!empty($course_id)) {
850
            $course_id = (int) $course_id;
851
        } else {
852
            $course_id = api_get_course_int_id();
853
        }
854
        if (!empty($sessionId)) {
855
            $sessionId = (int) $sessionId;
856
        } else {
857
            $sessionId = api_get_session_id();
858
        }
859
860
        //Clean the user_info
861
        if ($event_value_type == LOG_USER_OBJECT) {
862
            if (is_array($event_value)) {
863
                unset($event_value['complete_name']);
864
                unset($event_value['complete_name_with_username']);
865
                unset($event_value['firstName']);
866
                unset($event_value['lastName']);
867
                unset($event_value['avatar_small']);
868
                unset($event_value['avatar']);
869
                unset($event_value['mail']);
870
                unset($event_value['password']);
871
                unset($event_value['last_login']);
872
                unset($event_value['picture_uri']);
873
                $event_value = serialize($event_value);
874
            }
875
        }
876
        // If event is an array then the $event_value_type should finish with
877
        // the suffix _array for example LOG_WORK_DATA = work_data_array
878
        if (is_array($event_value)) {
879
            $event_value = serialize($event_value);
880
        }
881
882
        $event_value = Database::escape_string($event_value);
883
        $sessionId = empty($sessionId) ? api_get_session_id() : (int) $sessionId;
884
885
        if (!isset($datetime)) {
886
            $datetime = api_get_utc_datetime();
887
        }
888
889
        $datetime = Database::escape_string($datetime);
890
891
        if (!isset($user_id)) {
892
            $user_id = api_get_user_id();
893
        }
894
895
        $params = [
896
            'default_user_id' => $user_id,
897
            'c_id' => $course_id,
898
            'default_date' => $datetime,
899
            'default_event_type' => $event_type,
900
            'default_value_type' => $event_value_type,
901
            'default_value' => $event_value,
902
            'session_id' => $sessionId,
903
        ];
904
        Database::insert($table, $params);
905
906
        return true;
907
    }
908
909
    public static function findUserSubscriptionToCourse(int $userId, int $courseId, int $sessionId = 0)
910
    {
911
        $tblTrackEDefault = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
912
913
        return Database::select(
914
            '*',
915
            $tblTrackEDefault,
916
            [
917
                'where' => [
918
                    'default_event_type = ? AND ' => LOG_SUBSCRIBE_USER_TO_COURSE,
919
                    'default_value_type = ? AND ' => LOG_USER_OBJECT,
920
                    'default_value LIKE ? AND ' => '%s:2:\\\\"id\\\\";i:'.$userId.'%',
921
                    'c_id = ? AND ' => $courseId,
922
                    'session_id = ?' => $sessionId,
923
                ],
924
            ],
925
            'first'
926
        );
927
    }
928
929
    /**
930
     * Get every email stored in the database.
931
     *
932
     * @deprecated
933
     *
934
     * @return array
935
     * @assert () !== false
936
     */
937
    public static function get_all_event_types()
938
    {
939
        global $event_config;
940
941
        $sql = 'SELECT etm.id, event_type_name, activated, language_id, message, subject, dokeos_folder
942
                FROM '.Database::get_main_table(TABLE_EVENT_EMAIL_TEMPLATE).' etm
943
                INNER JOIN '.Database::get_main_table(TABLE_MAIN_LANGUAGE).' l
944
                ON etm.language_id = l.id';
945
946
        $events_types = Database::store_result(Database::query($sql), 'ASSOC');
947
948
        $to_return = [];
949
        foreach ($events_types as $et) {
950
            $et['nameLangVar'] = $event_config[$et["event_type_name"]]["name_lang_var"];
951
            $et['descLangVar'] = $event_config[$et["event_type_name"]]["desc_lang_var"];
952
            $to_return[] = $et;
953
        }
954
955
        return $to_return;
956
    }
957
958
    /**
959
     * Get the users related to one event.
960
     *
961
     * @param string $event_name
962
     *
963
     * @return string
964
     */
965
    public static function get_event_users($event_name)
966
    {
967
        $event_name = Database::escape_string($event_name);
968
        $sql = 'SELECT user.user_id,  user.firstname, user.lastname
969
                FROM '.Database::get_main_table(TABLE_MAIN_USER).' user
970
                JOIN '.Database::get_main_table(TABLE_EVENT_TYPE_REL_USER).' relUser
971
                ON relUser.user_id = user.user_id
972
                WHERE user.status <> '.ANONYMOUS.' AND relUser.event_type_name = "'.$event_name.'"';
973
        $user_list = Database::store_result(Database::query($sql), 'ASSOC');
974
975
        return json_encode($user_list);
976
    }
977
978
    /**
979
     * @param int    $user_id
980
     * @param string $event_type
981
     *
982
     * @return array|bool
983
     */
984
    public static function get_events_by_user_and_type($user_id, $event_type)
985
    {
986
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
987
        $user_id = (int) $user_id;
988
        $event_type = Database::escape_string($event_type);
989
990
        $sql = "SELECT * FROM $table
991
                WHERE default_value_type = 'user_id' AND
992
                      default_value = $user_id AND
993
                      default_event_type = '$event_type'
994
                ORDER BY default_date ";
995
        $result = Database::query($sql);
996
        if ($result) {
997
            return Database::store_result($result, 'ASSOC');
998
        }
999
1000
        return false;
1001
    }
1002
1003
    /**
1004
     * Save the new message for one event and for one language.
1005
     *
1006
     * @param string $event_name
1007
     * @param array  $users
1008
     * @param string $message
1009
     * @param string $subject
1010
     * @param string $event_message_language
1011
     * @param int    $activated
1012
     */
1013
    public static function save_event_type_message(
1014
        $event_name,
1015
        $users,
1016
        $message,
1017
        $subject,
1018
        $event_message_language,
1019
        $activated
1020
    ) {
1021
        $event_name = Database::escape_string($event_name);
1022
        $activated = (int) $activated;
1023
        $event_message_language = Database::escape_string($event_message_language);
1024
1025
        // Deletes then re-adds the users linked to the event
1026
        $sql = 'DELETE FROM '.Database::get_main_table(TABLE_EVENT_TYPE_REL_USER).'
1027
                WHERE event_type_name = "'.$event_name.'"	';
1028
        Database::query($sql);
1029
1030
        $eventTable = Database::get_main_table(TABLE_EVENT_TYPE_REL_USER);
1031
1032
        foreach ($users as $user) {
1033
            $user = (int) $user;
1034
            $sql = "INSERT INTO $eventTable (user_id,event_type_name)
1035
                    VALUES($user,'$event_name')";
1036
            Database::query($sql);
1037
        }
1038
        $language_id = api_get_language_id($event_message_language);
1039
        // check if this template in this language already exists or not
1040
        $eventMailTable = Database::get_main_table(TABLE_EVENT_EMAIL_TEMPLATE);
1041
        $sql = "SELECT COUNT(id) as total
1042
                FROM $eventMailTable
1043
                WHERE event_type_name = '$event_name' AND language_id = $language_id";
1044
1045
        $sql = Database::store_result(Database::query($sql), 'ASSOC');
1046
1047
        $languageTable = Database::get_main_table(TABLE_MAIN_LANGUAGE);
1048
        $message = Database::escape_string($message);
1049
        $subject = Database::escape_string($subject);
1050
        // if already exists, we update
1051
        if ($sql[0]["total"] > 0) {
1052
            $sql = "UPDATE $eventMailTable
1053
                SET message = '$message',
1054
                subject = '$subject',
1055
                activated = $activated
1056
                WHERE event_type_name = '$event_name' AND language_id = (
1057
                    SELECT id FROM $languageTable
1058
                    WHERE dokeos_folder = '$event_message_language'
1059
                )";
1060
            Database::query($sql);
1061
        } else { // else we create a new record
1062
            // gets the language_-_id
1063
            $lang_id = "(SELECT id FROM $languageTable
1064
                        WHERE dokeos_folder = '$event_message_language')";
1065
            $lang_id = Database::store_result(Database::query($lang_id), 'ASSOC');
1066
            $lang_id = $lang_id[0]['id'];
1067
1068
            if (!empty($lang_id[0]["id"])) {
1069
                $sql = "INSERT INTO $eventMailTable (event_type_name, language_id, message, subject, activated)
1070
                    VALUES ('$event_name', $lang_id, '$message', '$subject', $activated)";
1071
                Database::query($sql);
1072
            }
1073
        }
1074
1075
        // set activated at every save
1076
        $sql = "UPDATE $eventMailTable
1077
                SET activated = $activated
1078
                WHERE event_type_name = '$event_name'";
1079
        Database::query($sql);
1080
    }
1081
1082
    /**
1083
     * Gets the last attempt of an exercise based in the exe_id.
1084
     *
1085
     * @param int $exeId
1086
     *
1087
     * @return mixed
1088
     */
1089
    public static function getLastAttemptDateOfExercise($exeId)
1090
    {
1091
        $exeId = (int) $exeId;
1092
        $track_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1093
        $sql = "SELECT max(tms) as last_attempt_date
1094
                FROM $track_attempts
1095
                WHERE exe_id = $exeId";
1096
        $rs_last_attempt = Database::query($sql);
1097
        $row_last_attempt = Database::fetch_array($rs_last_attempt);
1098
        $date = $row_last_attempt['last_attempt_date']; //Get the date of last attempt
1099
1100
        return $date;
1101
    }
1102
1103
    /**
1104
     * Gets the last attempt of an exercise based in the exe_id.
1105
     *
1106
     * @param int $exeId
1107
     *
1108
     * @return mixed
1109
     */
1110
    public static function getLatestQuestionIdFromAttempt($exeId)
1111
    {
1112
        $exeId = (int) $exeId;
1113
        $track_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1114
        $sql = "SELECT question_id FROM $track_attempts
1115
                WHERE exe_id = $exeId
1116
                ORDER BY tms DESC
1117
                LIMIT 1";
1118
        $result = Database::query($sql);
1119
        if (Database::num_rows($result)) {
1120
            $row = Database::fetch_array($result);
1121
1122
            return $row['question_id'];
1123
        }
1124
1125
        return false;
1126
    }
1127
1128
    /**
1129
     * Gets how many attempts exists by user, exercise, learning path.
1130
     *
1131
     * @param   int user id
1132
     * @param   int exercise id
1133
     * @param   int lp id
1134
     * @param   int lp item id
1135
     * @param   int lp item view id
1136
     *
1137
     * @return int
1138
     */
1139
    public static function get_attempt_count(
1140
        $user_id,
1141
        $exerciseId,
1142
        $lp_id,
1143
        $lp_item_id,
1144
        $lp_item_view_id
1145
    ) {
1146
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1147
        $user_id = (int) $user_id;
1148
        $exerciseId = (int) $exerciseId;
1149
        $lp_id = (int) $lp_id;
1150
        $lp_item_id = (int) $lp_item_id;
1151
        $lp_item_view_id = (int) $lp_item_view_id;
1152
        $courseId = api_get_course_int_id();
1153
        $sessionId = api_get_session_id();
1154
1155
        $sql = "SELECT count(*) as count
1156
                FROM $table
1157
                WHERE
1158
                    exe_exo_id = $exerciseId AND
1159
                    exe_user_id = $user_id AND
1160
                    status != 'incomplete' AND
1161
                    orig_lp_id = $lp_id AND
1162
                    orig_lp_item_id = $lp_item_id AND
1163
                    orig_lp_item_view_id = $lp_item_view_id AND
1164
                    c_id = $courseId AND
1165
                    session_id = $sessionId";
1166
1167
        $result = Database::query($sql);
1168
        if (Database::num_rows($result) > 0) {
1169
            $attempt = Database::fetch_array($result, 'ASSOC');
1170
1171
            return (int) $attempt['count'];
1172
        }
1173
1174
        return 0;
1175
    }
1176
1177
    public static function getAttemptPosition(
1178
        $exeId,
1179
        $user_id,
1180
        $exerciseId,
1181
        $lp_id,
1182
        $lp_item_id,
1183
        $lp_item_view_id
1184
    ) {
1185
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1186
        $user_id = (int) $user_id;
1187
        $exerciseId = (int) $exerciseId;
1188
        $lp_id = (int) $lp_id;
1189
        $lp_item_id = (int) $lp_item_id;
1190
        $lp_item_view_id = (int) $lp_item_view_id;
1191
        $courseId = api_get_course_int_id();
1192
        $sessionId = api_get_session_id();
1193
1194
        $sql = "SELECT exe_id
1195
                FROM $table
1196
                WHERE
1197
                    exe_exo_id = $exerciseId AND
1198
                    exe_user_id = $user_id AND
1199
                    status = '' AND
1200
                    orig_lp_id = $lp_id AND
1201
                    orig_lp_item_id = $lp_item_id AND
1202
                    orig_lp_item_view_id = $lp_item_view_id AND
1203
                    c_id = $courseId AND
1204
                    session_id = $sessionId
1205
                ORDER by exe_id
1206
                ";
1207
1208
        $result = Database::query($sql);
1209
        if (Database::num_rows($result) > 0) {
1210
            $position = 1;
1211
            while ($row = Database::fetch_array($result, 'ASSOC')) {
1212
                if ($row['exe_id'] === $exeId) {
1213
                    break;
1214
                }
1215
                $position++;
1216
            }
1217
1218
            return $position;
1219
        }
1220
1221
        return 0;
1222
    }
1223
1224
    /**
1225
     * @param $user_id
1226
     * @param $exerciseId
1227
     * @param $lp_id
1228
     * @param $lp_item_id
1229
     *
1230
     * @return int
1231
     */
1232
    public static function get_attempt_count_not_finished(
1233
        $user_id,
1234
        $exerciseId,
1235
        $lp_id,
1236
        $lp_item_id
1237
    ) {
1238
        $stat_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1239
        $user_id = (int) $user_id;
1240
        $exerciseId = (int) $exerciseId;
1241
        $lp_id = (int) $lp_id;
1242
        $lp_item_id = (int) $lp_item_id;
1243
        //$lp_item_view_id = (int) $lp_item_view_id;
1244
        $courseId = api_get_course_int_id();
1245
        $sessionId = api_get_session_id();
1246
1247
        $sql = "SELECT count(*) as count
1248
                FROM $stat_table
1249
                WHERE
1250
                    exe_exo_id 			= $exerciseId AND
1251
                    exe_user_id 		= $user_id AND
1252
                    status 				!= 'incomplete' AND
1253
                    orig_lp_id 			= $lp_id AND
1254
                    orig_lp_item_id 	= $lp_item_id AND
1255
                    c_id = $courseId AND
1256
                    session_id = $sessionId";
1257
1258
        $query = Database::query($sql);
1259
        if (Database::num_rows($query) > 0) {
1260
            $attempt = Database::fetch_array($query, 'ASSOC');
1261
1262
            return (int) $attempt['count'];
1263
        }
1264
1265
        return 0;
1266
    }
1267
1268
    /**
1269
     * @param int   $user_id
1270
     * @param int   $lp_id
1271
     * @param array $course
1272
     * @param int   $session_id
1273
     * @param bool  $disconnectExerciseResultsFromLp (Replace orig_lp_* variables to null)
1274
     *
1275
     * @return bool
1276
     */
1277
    public static function delete_student_lp_events(
1278
        $user_id,
1279
        $lp_id,
1280
        $course,
1281
        $session_id,
1282
        $disconnectExerciseResultsFromLp = false
1283
    ) {
1284
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
1285
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
1286
        $lpInteraction = Database::get_course_table(TABLE_LP_IV_INTERACTION);
1287
        $lpObjective = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
1288
1289
        if (empty($course) || empty($user_id)) {
1290
            return false;
1291
        }
1292
1293
        $course_id = $course['real_id'];
1294
        $user_id = (int) $user_id;
1295
        $lp_id = (int) $lp_id;
1296
        $session_id = (int) $session_id;
1297
1298
        if (empty($course_id)) {
1299
            $course_id = api_get_course_int_id();
1300
        }
1301
1302
        $track_e_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1303
        $track_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1304
        $recording_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
1305
1306
        // Make sure we have the exact lp_view_id
1307
        $sql = "SELECT id FROM $lp_view_table
1308
                WHERE
1309
                    c_id = $course_id AND
1310
                    user_id = $user_id AND
1311
                    lp_id = $lp_id AND
1312
                    session_id = $session_id";
1313
        $result = Database::query($sql);
1314
1315
        if (Database::num_rows($result)) {
1316
            $view = Database::fetch_array($result, 'ASSOC');
1317
            $lp_view_id = $view['id'];
1318
1319
            $sql = "DELETE FROM $lp_item_view_table
1320
                    WHERE c_id = $course_id AND lp_view_id = $lp_view_id";
1321
            Database::query($sql);
1322
1323
            $sql = "DELETE FROM $lpInteraction
1324
                    WHERE c_id = $course_id AND lp_iv_id = $lp_view_id";
1325
            Database::query($sql);
1326
1327
            $sql = "DELETE FROM $lpObjective
1328
                    WHERE c_id = $course_id AND lp_iv_id = $lp_view_id";
1329
            Database::query($sql);
1330
        }
1331
1332
        if (api_get_configuration_value('lp_minimum_time')) {
1333
            $sql = "DELETE FROM track_e_access_complete
1334
                    WHERE
1335
                        tool = 'learnpath' AND
1336
                        c_id = $course_id AND
1337
                        tool_id = $lp_id AND
1338
                        user_id = $user_id AND
1339
                        session_id = $session_id
1340
                    ";
1341
            Database::query($sql);
1342
        }
1343
1344
        $sql = "SELECT exe_id FROM $track_e_exercises
1345
                WHERE
1346
                    exe_user_id = $user_id AND
1347
                    session_id = $session_id AND
1348
                    c_id = $course_id AND
1349
                    orig_lp_id = $lp_id";
1350
        $result = Database::query($sql);
1351
        $exeList = [];
1352
        while ($row = Database::fetch_array($result, 'ASSOC')) {
1353
            $exeList[] = $row['exe_id'];
1354
        }
1355
1356
        if (!empty($exeList) && count($exeList) > 0) {
1357
            $exeListString = implode(',', $exeList);
1358
            if ($disconnectExerciseResultsFromLp) {
1359
                $sql = "UPDATE $track_e_exercises
1360
                        SET orig_lp_id = null,
1361
                            orig_lp_item_id = null,
1362
                            orig_lp_item_view_id = null
1363
                        WHERE exe_id IN ($exeListString)";
1364
                Database::query($sql);
1365
            } else {
1366
                $sql = "DELETE FROM $track_e_exercises
1367
                        WHERE exe_id IN ($exeListString)";
1368
                Database::query($sql);
1369
1370
                $sql = "DELETE FROM $track_attempts
1371
                        WHERE exe_id IN ($exeListString)";
1372
                Database::query($sql);
1373
1374
                $sql = "DELETE FROM $recording_table
1375
                        WHERE exe_id IN ($exeListString)";
1376
                Database::query($sql);
1377
            }
1378
        }
1379
1380
        $sql = "DELETE FROM $lp_view_table
1381
                WHERE
1382
                    c_id = $course_id AND
1383
                    user_id = $user_id AND
1384
                    lp_id= $lp_id AND
1385
                    session_id = $session_id
1386
            ";
1387
        Database::query($sql);
1388
1389
        self::addEvent(
1390
            LOG_LP_ATTEMPT_DELETE,
1391
            LOG_LP_ID,
1392
            $lp_id,
1393
            null,
1394
            null,
1395
            $course_id,
1396
            $session_id
1397
        );
1398
1399
        return true;
1400
    }
1401
1402
    /**
1403
     * Delete all exercise attempts (included in LP or not).
1404
     *
1405
     * @param int user id
1406
     * @param int exercise id
1407
     * @param int $course_id
1408
     * @param int session id
1409
     */
1410
    public static function delete_all_incomplete_attempts(
1411
        $user_id,
1412
        $exercise_id,
1413
        $course_id,
1414
        $session_id = 0
1415
    ) {
1416
        $user_id = (int) $user_id;
1417
        $exercise_id = (int) $exercise_id;
1418
        $course_id = (int) $course_id;
1419
        $session_id = (int) $session_id;
1420
1421
        if (!empty($user_id) && !empty($exercise_id) && !empty($course_id)) {
1422
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1423
            $sql = "DELETE FROM $table
1424
                    WHERE
1425
                        exe_user_id = $user_id AND
1426
                        exe_exo_id = $exercise_id AND
1427
                        c_id = $course_id AND
1428
                        session_id = $session_id AND
1429
                        status = 'incomplete' ";
1430
            Database::query($sql);
1431
            self::addEvent(
1432
                LOG_EXERCISE_RESULT_DELETE_INCOMPLETE,
1433
                LOG_EXERCISE_AND_USER_ID,
1434
                $exercise_id.'-'.$user_id,
1435
                null,
1436
                null,
1437
                $course_id,
1438
                $session_id
1439
            );
1440
        }
1441
    }
1442
1443
    /**
1444
     * Gets all exercise results (NO Exercises in LPs ) from a given exercise id, course, session.
1445
     *
1446
     * @param int $exercise_id
1447
     * @param int $courseId
1448
     * @param int $session_id
1449
     *
1450
     * @return array with the results
1451
     */
1452
    public static function get_all_exercise_results(
1453
        $exercise_id,
1454
        $courseId,
1455
        $session_id = 0,
1456
        $load_question_list = true,
1457
        $user_id = null,
1458
        $groupId = 0,
1459
        $skipLpResults = true
1460
    ) {
1461
        $TABLETRACK_EXERCICES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1462
        $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1463
        $courseId = (int) $courseId;
1464
        $exercise_id = (int) $exercise_id;
1465
        $session_id = (int) $session_id;
1466
        $user_id = (int) $user_id;
1467
        $groupId = (int) $groupId;
1468
1469
        $user_condition = null;
1470
        if (!empty($user_id)) {
1471
            $user_condition = "AND exe_user_id = $user_id ";
1472
        }
1473
1474
        $courseInfo = api_get_course_info_by_id($courseId);
1475
        if (empty($courseInfo)) {
1476
            return [];
1477
        }
1478
1479
        $groupCondition = '';
1480
        if (!empty($groupId)) {
1481
            $users = GroupManager::get_users($groupId, null, null, null, false, $courseId);
1482
            // Check users added.
1483
            if (!empty($users)) {
1484
                $usersToString = implode("', '", $users);
1485
                $groupCondition = " AND exe_user_id IN ('$usersToString') ";
1486
            } else {
1487
                // No users found in the group, then return an empty array.
1488
                return [];
1489
            }
1490
        }
1491
1492
        $skipLpQuery = "";
1493
        if ($skipLpResults) {
1494
            $skipLpQuery = " AND orig_lp_id = 0 AND orig_lp_item_id = 0 ";
1495
        }
1496
1497
        $sql = "SELECT * FROM $TABLETRACK_EXERCICES
1498
                WHERE
1499
                    status = '' AND
1500
                    c_id = $courseId AND
1501
                    exe_exo_id = $exercise_id AND
1502
                    session_id = $session_id
1503
                    $skipLpQuery
1504
                    $user_condition
1505
                    $groupCondition
1506
                ORDER BY exe_id";
1507
        $res = Database::query($sql);
1508
        $list = [];
1509
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1510
            $list[$row['exe_id']] = $row;
1511
            if ($load_question_list) {
1512
                $sql = "SELECT * FROM $TBL_TRACK_ATTEMPT
1513
                        WHERE exe_id = {$row['exe_id']}";
1514
                $res_question = Database::query($sql);
1515
                while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1516
                    $list[$row['exe_id']]['question_list'][$row_q['question_id']] = $row_q;
1517
                }
1518
            }
1519
        }
1520
1521
        return $list;
1522
    }
1523
1524
    /**
1525
     * Gets all exercise results (NO Exercises in LPs ) from a given exercise id, course, session.
1526
     *
1527
     * @param int  $courseId
1528
     * @param int  $session_id
1529
     * @param bool $get_count
1530
     *
1531
     * @return array with the results
1532
     */
1533
    public static function get_all_exercise_results_by_course(
1534
        $courseId,
1535
        $session_id = 0,
1536
        $get_count = true
1537
    ) {
1538
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1539
        $courseId = (int) $courseId;
1540
        $session_id = (int) $session_id;
1541
1542
        $select = '*';
1543
        if ($get_count) {
1544
            $select = 'count(*) as count';
1545
        }
1546
        $sql = "SELECT $select FROM $table_track_exercises
1547
                WHERE   status = ''  AND
1548
                        c_id = $courseId AND
1549
                        session_id = $session_id  AND
1550
                        orig_lp_id = 0 AND
1551
                        orig_lp_item_id = 0
1552
                ORDER BY exe_id";
1553
        $res = Database::query($sql);
1554
        if ($get_count) {
1555
            $row = Database::fetch_array($res, 'ASSOC');
1556
1557
            return $row['count'];
1558
        } else {
1559
            $list = [];
1560
            while ($row = Database::fetch_array($res, 'ASSOC')) {
1561
                $list[$row['exe_id']] = $row;
1562
            }
1563
1564
            return $list;
1565
        }
1566
    }
1567
1568
    /**
1569
     * Gets all exercise results (NO Exercises in LPs) from a given exercise id, course, session.
1570
     *
1571
     * @param int $user_id
1572
     * @param int $courseId
1573
     * @param int $session_id
1574
     *
1575
     * @return array with the results
1576
     */
1577
    public static function get_all_exercise_results_by_user(
1578
        $user_id,
1579
        $courseId,
1580
        $session_id = 0
1581
    ) {
1582
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1583
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1584
        $courseId = (int) $courseId;
1585
        $session_id = (int) $session_id;
1586
        $user_id = (int) $user_id;
1587
1588
        $sql = "SELECT * FROM $table_track_exercises
1589
                WHERE
1590
                    status = '' AND
1591
                    exe_user_id = $user_id AND
1592
                    c_id = $courseId AND
1593
                    session_id = $session_id AND
1594
                    orig_lp_id = 0 AND
1595
                    orig_lp_item_id = 0
1596
                ORDER by exe_id";
1597
1598
        $res = Database::query($sql);
1599
        $list = [];
1600
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1601
            $list[$row['exe_id']] = $row;
1602
            $sql = "SELECT * FROM $table_track_attempt
1603
                    WHERE exe_id = {$row['exe_id']}";
1604
            $res_question = Database::query($sql);
1605
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1606
                $list[$row['exe_id']]['question_list'][$row_q['question_id']] = $row_q;
1607
            }
1608
        }
1609
1610
        return $list;
1611
    }
1612
1613
    /**
1614
     * Gets exercise results (NO Exercises in LPs) from a given exercise id, course, session.
1615
     *
1616
     * @param int    $exe_id attempt id
1617
     * @param string $status
1618
     *
1619
     * @return array with the results
1620
     */
1621
    public static function get_exercise_results_by_attempt($exe_id, $status = null)
1622
    {
1623
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1624
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1625
        $table_track_attempt_recording = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
1626
        $exe_id = (int) $exe_id;
1627
1628
        $status = Database::escape_string($status);
1629
1630
        $sql = "SELECT * FROM $table_track_exercises
1631
                WHERE status = '$status' AND exe_id = $exe_id";
1632
1633
        $res = Database::query($sql);
1634
        $list = [];
1635
        if (Database::num_rows($res)) {
1636
            $row = Database::fetch_array($res, 'ASSOC');
1637
1638
            //Checking if this attempt was revised by a teacher
1639
            $sql_revised = "SELECT exe_id FROM $table_track_attempt_recording
1640
                            WHERE author != '' AND exe_id = $exe_id
1641
                            LIMIT 1";
1642
            $res_revised = Database::query($sql_revised);
1643
            $row['attempt_revised'] = 0;
1644
            if (Database::num_rows($res_revised) > 0) {
1645
                $row['attempt_revised'] = 1;
1646
            }
1647
            $list[$exe_id] = $row;
1648
            $sql = "SELECT * FROM $table_track_attempt
1649
                    WHERE exe_id = $exe_id
1650
                    ORDER BY tms ASC";
1651
            $res_question = Database::query($sql);
1652
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1653
                $list[$exe_id]['question_list'][$row_q['question_id']] = $row_q;
1654
            }
1655
        }
1656
1657
        return $list;
1658
    }
1659
1660
    /**
1661
     * Gets exercise results (NO Exercises in LPs) from a given user, exercise id, course, session, lp_id, lp_item_id.
1662
     *
1663
     * @param   int     user id
1664
     * @param   int     exercise id
1665
     * @param   int     course id
1666
     * @param   int     session id
1667
     * @param   int     lp id
1668
     * @param   int     lp item id
1669
     * @param   string order asc or desc
1670
     *
1671
     * @return array with the results
1672
     */
1673
    public static function getExerciseResultsByUser(
1674
        $user_id,
1675
        $exercise_id,
1676
        $courseId,
1677
        $session_id = 0,
1678
        $lp_id = 0,
1679
        $lp_item_id = 0,
1680
        $order = null
1681
    ) {
1682
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1683
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1684
        $table_track_attempt_recording = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
1685
        $courseId = (int) $courseId;
1686
        $exercise_id = (int) $exercise_id;
1687
        $session_id = (int) $session_id;
1688
        $user_id = (int) $user_id;
1689
        $lp_id = (int) $lp_id;
1690
        $lp_item_id = (int) $lp_item_id;
1691
1692
        if (!in_array(strtolower($order), ['asc', 'desc'])) {
1693
            $order = 'asc';
1694
        }
1695
1696
        $sql = "SELECT * FROM $table_track_exercises
1697
                WHERE
1698
                    status 			= '' AND
1699
                    exe_user_id 	= $user_id AND
1700
                    c_id 	        = $courseId AND
1701
                    exe_exo_id 		= $exercise_id AND
1702
                    session_id 		= $session_id AND
1703
                    orig_lp_id 		= $lp_id AND
1704
                    orig_lp_item_id = $lp_item_id
1705
                ORDER by exe_id $order ";
1706
1707
        $res = Database::query($sql);
1708
        $list = [];
1709
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1710
            // Checking if this attempt was revised by a teacher
1711
            $exeId = $row['exe_id'];
1712
            $sql = "SELECT exe_id FROM $table_track_attempt_recording
1713
                    WHERE author != '' AND exe_id = $exeId
1714
                    LIMIT 1";
1715
            $res_revised = Database::query($sql);
1716
            $row['attempt_revised'] = 0;
1717
            if (Database::num_rows($res_revised) > 0) {
1718
                $row['attempt_revised'] = 1;
1719
            }
1720
            $row['total_percentage'] = 0;
1721
            if (!empty($row['exe_weighting'])) {
1722
                $row['total_percentage'] = ($row['exe_result'] / $row['exe_weighting']) * 100;
1723
            }
1724
1725
            $list[$row['exe_id']] = $row;
1726
            $sql = "SELECT * FROM $table_track_attempt
1727
                    WHERE exe_id = $exeId";
1728
            $res_question = Database::query($sql);
1729
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1730
                $list[$row['exe_id']]['question_list'][$row_q['question_id']][] = $row_q;
1731
            }
1732
        }
1733
1734
        return $list;
1735
    }
1736
1737
    /**
1738
     * Count exercise attempts (NO Exercises in LPs ) from a given exercise id, course, session.
1739
     *
1740
     * @param int $user_id
1741
     * @param int $exercise_id
1742
     * @param int $courseId
1743
     * @param int $session_id
1744
     *
1745
     * @return array with the results
1746
     */
1747
    public static function count_exercise_attempts_by_user(
1748
        $user_id,
1749
        $exercise_id,
1750
        $courseId,
1751
        $session_id = 0,
1752
        $skipLpResults = true
1753
    ) {
1754
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1755
        $courseId = (int) $courseId;
1756
        $exercise_id = (int) $exercise_id;
1757
        $session_id = (int) $session_id;
1758
        $user_id = (int) $user_id;
1759
1760
        $skipLpQuery = "";
1761
        if ($skipLpResults) {
1762
            $skipLpQuery = " AND orig_lp_id = 0 AND orig_lp_item_id = 0 ";
1763
        }
1764
1765
        $sql = "SELECT count(*) as count
1766
                FROM $table
1767
                WHERE status = ''  AND
1768
                    exe_user_id = $user_id AND
1769
                    c_id = $courseId AND
1770
                    exe_exo_id = $exercise_id AND
1771
                    session_id = $session_id
1772
                    $skipLpQuery
1773
                ORDER BY exe_id";
1774
        $res = Database::query($sql);
1775
        $result = 0;
1776
        if (Database::num_rows($res) > 0) {
1777
            $row = Database::fetch_array($res, 'ASSOC');
1778
            $result = $row['count'];
1779
        }
1780
1781
        return $result;
1782
    }
1783
1784
    /**
1785
     * Gets all exercise BEST results attempts (NO Exercises in LPs)
1786
     * from a given exercise id, course, session per user.
1787
     *
1788
     * @param int $exercise_id
1789
     * @param int $courseId
1790
     * @param int $session_id
1791
     * @param int $userId
1792
     *
1793
     * @return array with the results
1794
     *
1795
     * @todo rename this function
1796
     */
1797
    public static function get_best_exercise_results_by_user(
1798
        $exercise_id,
1799
        $courseId,
1800
        $session_id = 0,
1801
        $userId = 0,
1802
        $skipLpResults = true
1803
    ) {
1804
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1805
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1806
        $courseId = (int) $courseId;
1807
        $exercise_id = (int) $exercise_id;
1808
        $session_id = (int) $session_id;
1809
1810
        $skipLpQuery = "";
1811
        if ($skipLpResults) {
1812
            $skipLpQuery = " AND orig_lp_id = 0 AND orig_lp_item_id = 0 ";
1813
        }
1814
1815
        $sql = "SELECT * FROM $table_track_exercises
1816
                WHERE
1817
                    status = '' AND
1818
                    c_id = $courseId AND
1819
                    exe_exo_id = $exercise_id AND
1820
                    session_id = $session_id
1821
                    $skipLpQuery";
1822
1823
        if (!empty($userId)) {
1824
            $userId = (int) $userId;
1825
            $sql .= " AND exe_user_id = $userId ";
1826
        }
1827
        $sql .= ' ORDER BY exe_id';
1828
1829
        $res = Database::query($sql);
1830
        $list = [];
1831
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1832
            $list[$row['exe_id']] = $row;
1833
            $exeId = $row['exe_id'];
1834
            $sql = "SELECT * FROM $table_track_attempt
1835
                    WHERE exe_id = $exeId";
1836
            $res_question = Database::query($sql);
1837
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1838
                $list[$exeId]['question_list'][$row_q['question_id']] = $row_q;
1839
            }
1840
        }
1841
1842
        // Getting the best results of every student
1843
        $best_score_return = [];
1844
        foreach ($list as $student_result) {
1845
            $user_id = $student_result['exe_user_id'];
1846
            $current_best_score[$user_id] = $student_result['exe_result'];
1847
            if (!isset($best_score_return[$user_id]['exe_result'])) {
1848
                $best_score_return[$user_id] = $student_result;
1849
            }
1850
1851
            if ($current_best_score[$user_id] > $best_score_return[$user_id]['exe_result']) {
1852
                $best_score_return[$user_id] = $student_result;
1853
            }
1854
        }
1855
1856
        return $best_score_return;
1857
    }
1858
1859
    /**
1860
     * Get the last best result from all attempts in exercises per user (out of learning paths).
1861
     *
1862
     * @param int  $user_id
1863
     * @param int  $exercise_id
1864
     * @param int  $courseId
1865
     * @param int  $session_id
1866
     * @param bool $skipLpResults
1867
     *
1868
     * @return array
1869
     */
1870
    public static function get_best_attempt_exercise_results_per_user(
1871
        $user_id,
1872
        $exercise_id,
1873
        $courseId,
1874
        $session_id = 0,
1875
        $skipLpResults = true
1876
    ) {
1877
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1878
        $courseId = (int) $courseId;
1879
        $exercise_id = (int) $exercise_id;
1880
        $session_id = (int) $session_id;
1881
        $user_id = (int) $user_id;
1882
1883
        $sql = "SELECT * FROM $table
1884
                WHERE
1885
                    status = ''  AND
1886
                    c_id = $courseId AND
1887
                    exe_exo_id = $exercise_id AND
1888
                    session_id = $session_id  AND
1889
                    exe_user_id = $user_id
1890
                ";
1891
1892
        if ($skipLpResults) {
1893
            $sql .= ' AND
1894
                orig_lp_id = 0 AND
1895
                orig_lp_item_id = 0 ';
1896
        }
1897
1898
        $sql .= ' ORDER BY exe_id ';
1899
1900
        $res = Database::query($sql);
1901
        $list = [];
1902
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1903
            $list[$row['exe_id']] = $row;
1904
        }
1905
        // Getting the best results of every student.
1906
        $best_score_return = [];
1907
        $best_score_return['exe_result'] = 0;
1908
1909
        foreach ($list as $result) {
1910
            $current_best_score = $result;
1911
            if ($current_best_score['exe_result'] > $best_score_return['exe_result']) {
1912
                $best_score_return = $result;
1913
            }
1914
        }
1915
        if (!isset($best_score_return['exe_weighting'])) {
1916
            $best_score_return = [];
1917
        }
1918
1919
        return $best_score_return;
1920
    }
1921
1922
    /**
1923
     * @param int $exercise_id
1924
     * @param int $courseId
1925
     * @param int $session_id
1926
     *
1927
     * @return mixed
1928
     */
1929
    public static function count_exercise_result_not_validated(
1930
        $exercise_id,
1931
        $courseId,
1932
        $session_id = 0
1933
    ) {
1934
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1935
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
1936
        $courseId = (int) $courseId;
1937
        $session_id = (int) $session_id;
1938
        $exercise_id = (int) $exercise_id;
1939
1940
        $sql = "SELECT count(e.exe_id) as count
1941
                FROM $table_track_exercises e
1942
                LEFT JOIN $table_track_attempt a
1943
                ON e.exe_id = a.exe_id
1944
                WHERE
1945
                    exe_exo_id = $exercise_id AND
1946
                    c_id = $courseId AND
1947
                    e.session_id = $session_id  AND
1948
                    orig_lp_id = 0 AND
1949
                    marks IS NULL AND
1950
                    status = '' AND
1951
                    orig_lp_item_id = 0
1952
                ORDER BY e.exe_id";
1953
        $res = Database::query($sql);
1954
        $row = Database::fetch_array($res, 'ASSOC');
1955
1956
        return $row['count'];
1957
    }
1958
1959
    /**
1960
     * Gets all exercise events from a Learning Path within a Course    nd Session.
1961
     *
1962
     * @param int $exercise_id
1963
     * @param int $courseId
1964
     * @param int $session_id
1965
     *
1966
     * @return array
1967
     */
1968
    public static function get_all_exercise_event_from_lp(
1969
        $exercise_id,
1970
        $courseId,
1971
        $session_id = 0
1972
    ) {
1973
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1974
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1975
        $courseId = (int) $courseId;
1976
        $exercise_id = (int) $exercise_id;
1977
        $session_id = (int) $session_id;
1978
1979
        $sql = "SELECT * FROM $table_track_exercises
1980
                WHERE
1981
                    status = '' AND
1982
                    c_id = $courseId AND
1983
                    exe_exo_id = $exercise_id AND
1984
                    session_id = $session_id AND
1985
                    orig_lp_id !=0 AND
1986
                    orig_lp_item_id != 0";
1987
1988
        $res = Database::query($sql);
1989
        $list = [];
1990
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1991
            $exeId = $row['exe_id'];
1992
            $list[$exeId] = $row;
1993
            $sql = "SELECT * FROM $table_track_attempt
1994
                    WHERE exe_id = $exeId";
1995
            $res_question = Database::query($sql);
1996
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1997
                $list[$exeId]['question_list'][$row_q['question_id']] = $row_q;
1998
            }
1999
        }
2000
2001
        return $list;
2002
    }
2003
2004
    /**
2005
     * Get a list of all the exercises in a given learning path.
2006
     *
2007
     * @param int $lp_id
2008
     * @param int $course_id This parameter is probably deprecated as lp_id now is a global iid
2009
     *
2010
     * @return array
2011
     */
2012
    public static function get_all_exercises_from_lp($lp_id, $course_id)
2013
    {
2014
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
2015
        $course_id = (int) $course_id;
2016
        $lp_id = (int) $lp_id;
2017
        $sql = "SELECT * FROM $lp_item_table
2018
                WHERE
2019
                    c_id = $course_id AND
2020
                    lp_id = $lp_id AND
2021
                    item_type = 'quiz'
2022
                ORDER BY parent_item_id, display_order";
2023
        $res = Database::query($sql);
2024
2025
        $list = [];
2026
        while ($row = Database::fetch_array($res, 'ASSOC')) {
2027
            $list[] = $row;
2028
        }
2029
2030
        return $list;
2031
    }
2032
2033
    /**
2034
     * This function gets the comments of an exercise.
2035
     *
2036
     * @param int $exe_id
2037
     * @param int $question_id
2038
     *
2039
     * @return string the comment
2040
     */
2041
    public static function get_comments($exe_id, $question_id)
2042
    {
2043
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2044
        $exe_id = (int) $exe_id;
2045
        $question_id = (int) $question_id;
2046
        $sql = "SELECT teacher_comment
2047
                FROM $table
2048
                WHERE
2049
                    exe_id = $exe_id AND
2050
                    question_id = $question_id
2051
                ORDER by question_id";
2052
        $sqlres = Database::query($sql);
2053
        $comm = strval(Database::result($sqlres, 0, 'teacher_comment'));
2054
        $comm = trim($comm);
2055
2056
        return $comm;
2057
    }
2058
2059
    /**
2060
     * Get all the track_e_attempt records for a given
2061
     * track_e_exercises.exe_id (pk).
2062
     *
2063
     * @param int $exeId The exe_id from an exercise attempt record
2064
     *
2065
     * @return array The complete records from track_e_attempt that match the given exe_id
2066
     */
2067
    public static function getAllExerciseEventByExeId($exeId)
2068
    {
2069
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2070
        $exeId = (int) $exeId;
2071
2072
        $sql = "SELECT * FROM $table
2073
                WHERE exe_id = $exeId
2074
                ORDER BY position";
2075
        $res_question = Database::query($sql);
2076
        $list = [];
2077
        if (Database::num_rows($res_question)) {
2078
            while ($row = Database::fetch_array($res_question, 'ASSOC')) {
2079
                $list[$row['question_id']][] = $row;
2080
            }
2081
        }
2082
2083
        return $list;
2084
    }
2085
2086
    public static function getQuestionAttemptByExeIdAndQuestion($exeId, $questionId)
2087
    {
2088
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2089
        $exeId = (int) $exeId;
2090
        $questionId = (int) $questionId;
2091
2092
        $sql = "SELECT * FROM $table
2093
                WHERE
2094
                    exe_id = $exeId AND
2095
                    question_id = $questionId
2096
                ORDER BY position";
2097
        $result = Database::query($sql);
2098
        $attempt = [];
2099
        if (Database::num_rows($result)) {
2100
            $attempt = Database::fetch_array($result, 'ASSOC');
2101
        }
2102
2103
        return $attempt;
2104
    }
2105
2106
    /**
2107
     * Delete one record from the track_e_attempt table (recorded quiz answer)
2108
     * and register the deletion event (LOG_QUESTION_RESULT_DELETE) in
2109
     * track_e_default.
2110
     *
2111
     * @param int $exeId       The track_e_exercises.exe_id (primary key)
2112
     * @param int $user_id     The user who answered (already contained in exe_id)
2113
     * @param int $courseId    The course in which it happened (already contained in exe_id)
2114
     * @param int $session_id  The session in which it happened (already contained in exe_id)
2115
     * @param int $question_id The c_quiz_question.iid
2116
     */
2117
    public static function delete_attempt(
2118
        $exeId,
2119
        $user_id,
2120
        $courseId,
2121
        $session_id,
2122
        $question_id
2123
    ) {
2124
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2125
2126
        $exeId = (int) $exeId;
2127
        $user_id = (int) $user_id;
2128
        $courseId = (int) $courseId;
2129
        $session_id = (int) $session_id;
2130
        $question_id = (int) $question_id;
2131
2132
        $sql = "DELETE FROM $table
2133
                WHERE
2134
                    exe_id = $exeId AND
2135
                    user_id = $user_id AND
2136
                    c_id = $courseId AND
2137
                    session_id = $session_id AND
2138
                    question_id = $question_id ";
2139
        Database::query($sql);
2140
2141
        self::addEvent(
2142
            LOG_QUESTION_RESULT_DELETE,
2143
            LOG_EXERCISE_ATTEMPT_QUESTION_ID,
2144
            $exeId.'-'.$question_id,
2145
            null,
2146
            null,
2147
            $courseId,
2148
            $session_id
2149
        );
2150
    }
2151
2152
    /**
2153
     * Delete one record from the track_e_hotspot table based on a given
2154
     * track_e_exercises.exe_id.
2155
     *
2156
     * @param $exeId
2157
     * @param $user_id
2158
     * @param int $courseId
2159
     * @param $question_id
2160
     * @param int $sessionId
2161
     */
2162
    public static function delete_attempt_hotspot(
2163
        $exeId,
2164
        $user_id,
2165
        $courseId,
2166
        $question_id,
2167
        $sessionId = null
2168
    ) {
2169
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
2170
2171
        $exeId = (int) $exeId;
2172
        $user_id = (int) $user_id;
2173
        $courseId = (int) $courseId;
2174
        $question_id = (int) $question_id;
2175
        if (!isset($sessionId)) {
2176
            $sessionId = api_get_session_id();
2177
        }
2178
2179
        $sql = "DELETE FROM $table
2180
                WHERE
2181
                    hotspot_exe_id = $exeId AND
2182
                    hotspot_user_id = $user_id AND
2183
                    c_id = $courseId AND
2184
                    hotspot_question_id = $question_id ";
2185
        Database::query($sql);
2186
        self::addEvent(
2187
            LOG_QUESTION_RESULT_DELETE,
2188
            LOG_EXERCISE_ATTEMPT_QUESTION_ID,
2189
            $exeId.'-'.$question_id,
2190
            null,
2191
            null,
2192
            $courseId,
2193
            $sessionId
2194
        );
2195
    }
2196
2197
    /**
2198
     * Registers in track_e_course_access when user logs in for the first time to a course.
2199
     *
2200
     * @param int $courseId  ID of the course
2201
     * @param int $user_id   ID of the user
2202
     * @param int $sessionId ID of the session (if any)
2203
     *
2204
     * @return bool
2205
     */
2206
    public static function eventCourseLogin($courseId, $user_id, $sessionId)
2207
    {
2208
        if (Session::read('login_as')) {
2209
            return false;
2210
        }
2211
2212
        $sessionId = (int) $sessionId;
2213
        if (self::isSessionLogNeedToBeSave($sessionId) === false) {
2214
            return false;
2215
        }
2216
2217
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2218
        $loginDate = $logoutDate = api_get_utc_datetime();
2219
2220
        // $counter represents the number of time this record has been refreshed
2221
        $counter = 1;
2222
        $courseId = (int) $courseId;
2223
        $user_id = (int) $user_id;
2224
        $ip = Database::escape_string(api_get_real_ip());
2225
2226
        $sql = "INSERT INTO $table(c_id, user_ip, user_id, login_course_date, logout_course_date, counter, session_id)
2227
                VALUES($courseId, '$ip', $user_id, '$loginDate', '$logoutDate', $counter, $sessionId)";
2228
        $courseAccessId = Database::query($sql);
2229
2230
        if ($courseAccessId) {
2231
            // Course catalog stats modifications see #4191
2232
            CourseManager::update_course_ranking(
2233
                null,
2234
                null,
2235
                null,
2236
                null,
2237
                true,
2238
                false
2239
            );
2240
2241
            return true;
2242
        }
2243
    }
2244
2245
    /**
2246
     * Updates the user - course - session every X minutes
2247
     * In order to avoid.
2248
     *
2249
     * @param int $courseId
2250
     * @param int $userId
2251
     * @param int $sessionId
2252
     * @param int $minutes
2253
     *
2254
     * @return bool
2255
     */
2256
    public static function eventCourseLoginUpdate(
2257
        $courseId,
2258
        $userId,
2259
        $sessionId,
2260
        $minutes = 5
2261
    ) {
2262
        if (Session::read('login_as')) {
2263
            return false;
2264
        }
2265
2266
        if (empty($courseId) || empty($userId)) {
2267
            return false;
2268
        }
2269
2270
        $sessionId = (int) $sessionId;
2271
2272
        if (self::isSessionLogNeedToBeSave($sessionId) === false) {
2273
            return false;
2274
        }
2275
2276
        $courseId = (int) $courseId;
2277
        $userId = (int) $userId;
2278
2279
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2280
        $sql = "SELECT course_access_id, logout_course_date
2281
                FROM $table
2282
                WHERE
2283
                    c_id = $courseId AND
2284
                    session_id = $sessionId AND
2285
                    user_id = $userId
2286
                ORDER BY login_course_date DESC
2287
                LIMIT 1";
2288
2289
        $result = Database::query($sql);
2290
2291
        // Save every 5 minutes by default
2292
        $seconds = $minutes * 60;
2293
        $maxSeconds = 3600; // Only update if max diff is one hour
2294
        if (Database::num_rows($result)) {
2295
            $row = Database::fetch_array($result);
2296
            $id = $row['course_access_id'];
2297
            $logout = $row['logout_course_date'];
2298
            $now = time();
2299
            $logout = api_strtotime($logout, 'UTC');
2300
            if ($now - $logout > $seconds &&
2301
                $now - $logout < $maxSeconds
2302
            ) {
2303
                $now = api_get_utc_datetime();
2304
                $sql = "UPDATE $table SET
2305
                            logout_course_date = '$now',
2306
                            counter = counter + 1
2307
                        WHERE course_access_id = $id";
2308
                Database::query($sql);
2309
            }
2310
2311
            return true;
2312
        }
2313
2314
        return false;
2315
    }
2316
2317
    /**
2318
     * Register the logout of the course (usually when logging out of the platform)
2319
     * from the track_e_course_access table.
2320
     *
2321
     * @param array $logoutInfo Information stored by local.inc.php
2322
     *                          before new context ['uid'=> x, 'cid'=>y, 'sid'=>z]
2323
     *
2324
     * @return bool
2325
     */
2326
    public static function courseLogout($logoutInfo)
2327
    {
2328
        if (Session::read('login_as')) {
2329
            return false;
2330
        }
2331
2332
        if (empty($logoutInfo['uid']) || empty($logoutInfo['cid'])) {
2333
            return false;
2334
        }
2335
2336
        $sessionLifetime = api_get_configuration_value('session_lifetime');
2337
        /*
2338
         * When $_configuration['session_lifetime'] is larger than ~100 hours
2339
         * (in order to let users take exercises with no problems)
2340
         * the function Tracking::get_time_spent_on_the_course() returns larger values (200h) due the condition:
2341
         * login_course_date > now() - INTERVAL $session_lifetime SECOND
2342
         */
2343
        if (empty($sessionLifetime) || $sessionLifetime > 86400) {
2344
            $sessionLifetime = 3600; // 1 hour
2345
        }
2346
        if (!empty($logoutInfo) && !empty($logoutInfo['cid'])) {
2347
            $sessionId = 0;
2348
            if (!empty($logoutInfo['sid'])) {
2349
                $sessionId = (int) $logoutInfo['sid'];
2350
            }
2351
2352
            if (self::isSessionLogNeedToBeSave($sessionId) === false) {
2353
                return false;
2354
            }
2355
2356
            $tableCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2357
            $userId = (int) $logoutInfo['uid'];
2358
            $courseId = (int) $logoutInfo['cid'];
2359
2360
            $currentDate = api_get_utc_datetime();
2361
            // UTC time
2362
            $diff = time() - $sessionLifetime;
2363
            $time = api_get_utc_datetime($diff);
2364
            $sql = "SELECT course_access_id, logout_course_date
2365
                    FROM $tableCourseAccess
2366
                    WHERE
2367
                        user_id = $userId AND
2368
                        c_id = $courseId  AND
2369
                        session_id = $sessionId AND
2370
                        login_course_date > '$time'
2371
                    ORDER BY login_course_date DESC
2372
                    LIMIT 1";
2373
            $result = Database::query($sql);
2374
            $insert = false;
2375
            if (Database::num_rows($result) > 0) {
2376
                $row = Database::fetch_array($result, 'ASSOC');
2377
                $courseAccessId = $row['course_access_id'];
2378
                $sql = "UPDATE $tableCourseAccess SET
2379
                                logout_course_date = '$currentDate',
2380
                                counter = counter + 1
2381
                            WHERE course_access_id = $courseAccessId";
2382
                Database::query($sql);
2383
            } else {
2384
                $insert = true;
2385
            }
2386
2387
            if ($insert) {
2388
                $ip = Database::escape_string(api_get_real_ip());
2389
                $sql = "INSERT INTO $tableCourseAccess (c_id, user_ip, user_id, login_course_date, logout_course_date, counter, session_id)
2390
                        VALUES ($courseId, '$ip', $userId, '$currentDate', '$currentDate', 1, $sessionId)";
2391
                Database::query($sql);
2392
            }
2393
2394
            return true;
2395
        }
2396
    }
2397
2398
    /**
2399
     * Register a "fake" time spent on the platform, for example to match the
2400
     * estimated time he took to author an assignment/work, see configuration
2401
     * setting considered_working_time.
2402
     * This assumes there is already some connection of the student to the
2403
     * course, otherwise he wouldn't be able to upload an assignment.
2404
     * This works by creating a new record, copy of the current one, then
2405
     * updating the current one to be just the considered_working_time and
2406
     * end at the same second as the user connected to the course.
2407
     *
2408
     * @param int    $courseId    The course in which to add the time
2409
     * @param int    $userId      The user for whom to add the time
2410
     * @param int    $sessionId   The session in which to add the time (if any)
2411
     * @param string $virtualTime The amount of time to be added,
2412
     *                            in a hh:mm:ss format. If int, we consider it is expressed in hours.
2413
     * @param int    $workId      Student publication id result
2414
     *
2415
     * @return true on successful insertion, false otherwise
2416
     */
2417
    public static function eventAddVirtualCourseTime(
2418
        $courseId,
2419
        $userId,
2420
        $sessionId,
2421
        $virtualTime,
2422
        $workId
2423
    ) {
2424
        if (empty($virtualTime)) {
2425
            return false;
2426
        }
2427
2428
        $courseId = (int) $courseId;
2429
        $userId = (int) $userId;
2430
        $sessionId = (int) $sessionId;
2431
2432
        $logoutDate = api_get_utc_datetime();
2433
        $loginDate = ChamiloApi::addOrSubTimeToDateTime(
2434
            $virtualTime,
2435
            $logoutDate,
2436
            false
2437
        );
2438
2439
        $ip = api_get_real_ip();
2440
        $params = [
2441
            'login_course_date' => $loginDate,
2442
            'logout_course_date' => $logoutDate,
2443
            'session_id' => $sessionId,
2444
            'user_id' => $userId,
2445
            'counter' => 0,
2446
            'c_id' => $courseId,
2447
            'user_ip' => $ip,
2448
        ];
2449
        $courseTrackingTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2450
        Database::insert($courseTrackingTable, $params);
2451
2452
        // Time should also be added to the track_e_login table so as to
2453
        // affect total time on the platform
2454
        $params = [
2455
            'login_user_id' => $userId,
2456
            'login_date' => $loginDate,
2457
            'user_ip' => $ip,
2458
            'logout_date' => $logoutDate,
2459
        ];
2460
        $platformTrackingTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2461
        Database::insert($platformTrackingTable, $params);
2462
2463
        if (Tracking::minimumTimeAvailable($sessionId, $courseId)) {
2464
            $workId = (int) $workId;
2465
            $uniqueId = time();
2466
            $logInfo = [
2467
                'c_id' => $courseId,
2468
                'session_id' => $sessionId,
2469
                'tool' => TOOL_STUDENTPUBLICATION,
2470
                'date_reg' => $loginDate,
2471
                'action' => 'add_work_start_'.$workId,
2472
                'action_details' => $virtualTime,
2473
                'user_id' => $userId,
2474
                'current_id' => $uniqueId,
2475
            ];
2476
            self::registerLog($logInfo);
2477
2478
            $logInfo = [
2479
                'c_id' => $courseId,
2480
                'session_id' => $sessionId,
2481
                'tool' => TOOL_STUDENTPUBLICATION,
2482
                'date_reg' => $logoutDate,
2483
                'action' => 'add_work_end_'.$workId,
2484
                'action_details' => $virtualTime,
2485
                'user_id' => $userId,
2486
                'current_id' => $uniqueId,
2487
            ];
2488
            self::registerLog($logInfo);
2489
        }
2490
2491
        return true;
2492
    }
2493
2494
    /**
2495
     * Removes a "fake" time spent on the platform, for example to match the
2496
     * estimated time he took to author an assignment/work, see configuration
2497
     * setting considered_working_time.
2498
     * This method should be called when something that generated a fake
2499
     * time record is removed. Given the database link is weak (no real
2500
     * relationship kept between the deleted item and this record), this
2501
     * method just looks for the latest record that has the same time as the
2502
     * item's fake time, is in the past and in this course+session. If such a
2503
     * record cannot be found, it doesn't do anything.
2504
     * The IP address is not considered a useful filter here.
2505
     *
2506
     * @param int    $courseId    The course in which to add the time
2507
     * @param int    $userId      The user for whom to add the time
2508
     * @param int    $sessionId   The session in which to add the time (if any)
2509
     * @param string $virtualTime The amount of time to be added, in a hh:mm:ss format. If int, we consider it is expressed in hours.
2510
     *
2511
     * @return true on successful removal, false otherwise
2512
     */
2513
    public static function eventRemoveVirtualCourseTime(
2514
        $courseId,
2515
        $userId,
2516
        $sessionId,
2517
        $virtualTime,
2518
        $workId
2519
    ) {
2520
        if (empty($virtualTime)) {
2521
            return false;
2522
        }
2523
2524
        $courseId = (int) $courseId;
2525
        $userId = (int) $userId;
2526
        $sessionId = (int) $sessionId;
2527
        $originalVirtualTime = Database::escape_string($virtualTime);
2528
        $workId = (int) $workId;
2529
2530
        $courseTrackingTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2531
        $platformTrackingTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2532
2533
        // Change $virtualTime format from hh:mm:ss to hhmmss which is the
2534
        // format returned by SQL for a subtraction of two datetime values
2535
        // @todo make sure this is portable between DBMSes
2536
        if (preg_match('/:/', $virtualTime)) {
2537
            [$h, $m, $s] = preg_split('/:/', $virtualTime);
2538
            $virtualTime = $h * 3600 + $m * 60 + $s;
2539
        } else {
2540
            $virtualTime *= 3600;
2541
        }
2542
2543
        // Get the current latest course connection register. We need that
2544
        // record to re-use the data and create a new record.
2545
        $sql = "SELECT course_access_id
2546
                FROM $courseTrackingTable
2547
                WHERE
2548
                    user_id = $userId AND
2549
                    c_id = $courseId  AND
2550
                    session_id  = $sessionId AND
2551
                    counter = 0 AND
2552
                    (UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) = '$virtualTime'
2553
                ORDER BY login_course_date DESC LIMIT 0,1";
2554
        $result = Database::query($sql);
2555
2556
        // Ignore if we didn't find any course connection record in the last
2557
        // hour. In this case it wouldn't be right to add a "fake" time record.
2558
        if (Database::num_rows($result) > 0) {
2559
            // Found the latest connection
2560
            $row = Database::fetch_row($result);
2561
            $courseAccessId = $row[0];
2562
            $sql = "DELETE FROM $courseTrackingTable
2563
                    WHERE course_access_id = $courseAccessId";
2564
            Database::query($sql);
2565
        }
2566
        $sql = "SELECT login_id
2567
                FROM $platformTrackingTable
2568
                WHERE
2569
                    login_user_id = $userId AND
2570
                    (UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date)) = '$virtualTime'
2571
                ORDER BY login_date DESC LIMIT 0,1";
2572
        $result = Database::query($sql);
2573
        if (Database::num_rows($result) > 0) {
2574
            // Found the latest connection
2575
            $row = Database::fetch_row($result);
2576
            $loginAccessId = $row[0];
2577
            $sql = "DELETE FROM $platformTrackingTable
2578
                    WHERE login_id = $loginAccessId";
2579
            Database::query($sql);
2580
        }
2581
2582
        if (Tracking::minimumTimeAvailable($sessionId, $courseId)) {
2583
            $sql = "SELECT id FROM track_e_access_complete
2584
                    WHERE
2585
                        tool = '".TOOL_STUDENTPUBLICATION."' AND
2586
                        c_id = $courseId AND
2587
                        session_id = $sessionId AND
2588
                        user_id = $userId AND
2589
                        action_details = '$originalVirtualTime' AND
2590
                        action = 'add_work_start_$workId' ";
2591
            $result = Database::query($sql);
2592
            $result = Database::fetch_array($result);
2593
            if ($result) {
2594
                $sql = 'DELETE FROM track_e_access_complete WHERE id = '.$result['id'];
2595
                Database::query($sql);
2596
            }
2597
2598
            $sql = "SELECT id FROM track_e_access_complete
2599
                    WHERE
2600
                        tool = '".TOOL_STUDENTPUBLICATION."' AND
2601
                        c_id = $courseId AND
2602
                        session_id = $sessionId AND
2603
                        user_id = $userId AND
2604
                        action_details = '$originalVirtualTime' AND
2605
                        action = 'add_work_end_$workId' ";
2606
            $result = Database::query($sql);
2607
            $result = Database::fetch_array($result);
2608
            if ($result) {
2609
                $sql = 'DELETE FROM track_e_access_complete WHERE id = '.$result['id'];
2610
                Database::query($sql);
2611
            }
2612
        }
2613
2614
        return false;
2615
    }
2616
2617
    /**
2618
     * For the sake of cohesion, this function is a switch.
2619
     * It's called by EventsDispatcher and fires the right function
2620
     * with the right require_once.
2621
     *
2622
     * @deprecated
2623
     *
2624
     * @param string $event_name
2625
     * @param array  $params
2626
     */
2627
    public static function event_send_mail($event_name, $params)
2628
    {
2629
        EventsMail::send_mail($event_name, $params);
2630
    }
2631
2632
    /**
2633
     * Filter EventEmailTemplate Filters see the main/inc/conf/events.conf.dist.php.
2634
     */
2635
2636
    /**
2637
     * Basic template event message filter (to be used by other filters as default).
2638
     *
2639
     * @deprecated
2640
     *
2641
     * @param array $values (passing by reference)     *
2642
     *
2643
     * @return bool True if everything is OK, false otherwise
2644
     */
2645
    public function event_send_mail_filter_func(&$values)
2646
    {
2647
        return true;
2648
    }
2649
2650
    /**
2651
     * user_registration - send_mail filter.
2652
     *
2653
     * @deprecated
2654
     *
2655
     * @param array $values (passing by reference)
2656
     *
2657
     * @return bool True if everything is OK, false otherwise
2658
     */
2659
    public function user_registration_event_send_mail_filter_func(&$values)
2660
    {
2661
        $res = self::event_send_mail_filter_func($values);
2662
        // proper logic for this filter
2663
        return $res;
2664
    }
2665
2666
    /**
2667
     * portal_homepage_edited - send_mail filter.
2668
     *
2669
     * @deprecated
2670
     *
2671
     * @param array $values (passing by reference)
2672
     *
2673
     * @return bool True if everything is OK, false otherwise
2674
     */
2675
    public function portal_homepage_edited_event_send_mail_filter_func(&$values)
2676
    {
2677
        $res = self::event_send_mail_filter_func($values);
2678
        // proper logic for this filter
2679
        return $res;
2680
    }
2681
2682
    /**
2683
     * Register the logout of the course (usually when logging out of the platform)
2684
     * from the track_e_access_complete table.
2685
     *
2686
     * @param array $logInfo Information stored by local.inc.php
2687
     *
2688
     * @return bool
2689
     */
2690
    public static function registerLog($logInfo)
2691
    {
2692
        $sessionId = api_get_session_id();
2693
        $courseId = api_get_course_int_id();
2694
2695
        if (isset($logInfo['c_id']) && !empty($logInfo['c_id'])) {
2696
            $courseId = $logInfo['c_id'];
2697
        }
2698
2699
        if (isset($logInfo['session_id']) && !empty($logInfo['session_id'])) {
2700
            $sessionId = $logInfo['session_id'];
2701
        }
2702
2703
        if (!Tracking::minimumTimeAvailable($sessionId, $courseId)) {
2704
            return false;
2705
        }
2706
2707
        if (self::isSessionLogNeedToBeSave($sessionId) === false) {
2708
            return false;
2709
        }
2710
2711
        $loginAs = (int) Session::read('login_as') === true;
2712
2713
        $logInfo['user_id'] = isset($logInfo['user_id']) ? $logInfo['user_id'] : api_get_user_id();
2714
        $logInfo['date_reg'] = isset($logInfo['date_reg']) ? $logInfo['date_reg'] : api_get_utc_datetime();
2715
        $logInfo['tool'] = !empty($logInfo['tool']) ? $logInfo['tool'] : '';
2716
        $logInfo['tool_id'] = !empty($logInfo['tool_id']) ? (int) $logInfo['tool_id'] : 0;
2717
        $logInfo['tool_id_detail'] = !empty($logInfo['tool_id_detail']) ? (int) $logInfo['tool_id_detail'] : 0;
2718
        $logInfo['action'] = !empty($logInfo['action']) ? $logInfo['action'] : '';
2719
        $logInfo['action_details'] = !empty($logInfo['action_details']) ? $logInfo['action_details'] : '';
2720
        $logInfo['ip_user'] = api_get_real_ip();
2721
        $logInfo['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
2722
        $logInfo['session_id'] = $sessionId;
2723
        $logInfo['c_id'] = $courseId;
2724
        $logInfo['ch_sid'] = session_id();
2725
        $logInfo['login_as'] = $loginAs;
2726
        $logInfo['info'] = !empty($logInfo['info']) ? $logInfo['info'] : '';
2727
        $logInfo['url'] = $_SERVER['REQUEST_URI'];
2728
        $logInfo['current_id'] = isset($logInfo['current_id']) ? $logInfo['current_id'] : Session::read('last_id', 0);
2729
2730
        $id = Database::insert('track_e_access_complete', $logInfo);
2731
        if ($id && empty($logInfo['current_id'])) {
2732
            Session::write('last_id', $id);
2733
        }
2734
2735
        return true;
2736
    }
2737
2738
    public static function getAttemptQuestionDuration($exeId, $questionId)
2739
    {
2740
        // Check current attempt.
2741
        $questionAttempt = self::getQuestionAttemptByExeIdAndQuestion($exeId, $questionId);
2742
        $alreadySpent = 0;
2743
        if (!empty($questionAttempt) && $questionAttempt['seconds_spent']) {
2744
            $alreadySpent = $questionAttempt['seconds_spent'];
2745
        }
2746
        $now = time();
2747
        $questionStart = Session::read('question_start', []);
2748
        if (!empty($questionStart) &&
2749
            isset($questionStart[$questionId]) && !empty($questionStart[$questionId])
2750
        ) {
2751
            $time = $questionStart[$questionId];
2752
        } else {
2753
            $diff = 0;
2754
            if (!empty($alreadySpent)) {
2755
                $diff = $alreadySpent;
2756
            }
2757
            $time = $questionStart[$questionId] = $now - $diff;
2758
            Session::write('question_start', $questionStart);
2759
        }
2760
2761
        return $now - $time;
2762
    }
2763
2764
    public static function logSubscribedUserInCourse(int $subscribedId, int $courseId)
2765
    {
2766
        $dateTime = api_get_utc_datetime();
2767
        $registrantId = api_get_user_id();
2768
2769
        self::addEvent(
2770
            LOG_SUBSCRIBE_USER_TO_COURSE,
2771
            LOG_COURSE_CODE,
2772
            api_get_course_entity($courseId)->getCode(),
2773
            $dateTime,
2774
            $registrantId,
2775
            $courseId
2776
        );
2777
2778
        self::addEvent(
2779
            LOG_SUBSCRIBE_USER_TO_COURSE,
2780
            LOG_USER_OBJECT,
2781
            api_get_user_info($subscribedId),
2782
            $dateTime,
2783
            $registrantId,
2784
            $courseId
2785
        );
2786
    }
2787
2788
    public static function logUserSubscribedInCourseSession(int $subscribedId, int $courseId, int $sessionId)
2789
    {
2790
        $dateTime = api_get_utc_datetime();
2791
        $registrantId = api_get_user_id();
2792
2793
        self::addEvent(
2794
            LOG_SESSION_ADD_USER_COURSE,
2795
            LOG_USER_ID,
2796
            $subscribedId,
2797
            $dateTime,
2798
            $registrantId,
2799
            $courseId,
2800
            $sessionId
2801
        );
2802
        self::addEvent(
2803
            LOG_SUBSCRIBE_USER_TO_COURSE,
2804
            LOG_COURSE_CODE,
2805
            api_get_course_entity($courseId)->getCode(),
2806
            $dateTime,
2807
            $registrantId,
2808
            $courseId,
2809
            $sessionId
2810
        );
2811
        self::addEvent(
2812
            LOG_SUBSCRIBE_USER_TO_COURSE,
2813
            LOG_USER_OBJECT,
2814
            api_get_user_info($subscribedId),
2815
            $dateTime,
2816
            $registrantId,
2817
            $courseId,
2818
            $sessionId
2819
        );
2820
    }
2821
2822
    /**
2823
     * Retrieves audit items from the track_e_default table.
2824
     *
2825
     * This function fetches audit data based on various optional criteria and
2826
     * formats the result to remove the "default_" prefix from each field.
2827
     */
2828
    public static function getAuditItems(
2829
        string $defaultEventType,
2830
        ?int $cId = null,
2831
        ?int $sessionId = null,
2832
        ?string $afterDate = null,
2833
        ?string $beforeDate = null,
2834
        ?int $userId = null,
2835
        int $offset = 0,
2836
        int $limit = 100
2837
    ): array {
2838
        $tblTrackEDefault = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
2839
2840
        $whereConditions = ['default_event_type = ? ' => $defaultEventType];
2841
2842
        if ($cId !== null) {
2843
            $whereConditions[' AND c_id = ? '] = $cId;
2844
        }
2845
        if ($sessionId !== null) {
2846
            $whereConditions[' AND session_id = ? '] = $sessionId;
2847
        }
2848
        if ($afterDate !== null) {
2849
            $whereConditions[' AND default_date >= ? '] = $afterDate;
2850
        }
2851
        if ($beforeDate !== null) {
2852
            $whereConditions[' AND default_date <= ? '] = $beforeDate;
2853
        }
2854
        if ($userId !== null) {
2855
            $whereConditions[' AND default_user_id = ? '] = $userId;
2856
        }
2857
2858
        $conditions = [
2859
            'where' => $whereConditions,
2860
            'order' => 'default_date DESC',
2861
            'limit' => "$offset, $limit",
2862
        ];
2863
2864
        $results = Database::select(
2865
            'default_user_id, c_id, default_date, default_event_type, default_value_type, default_value, session_id',
2866
            $tblTrackEDefault,
2867
            $conditions
2868
        );
2869
2870
        $formattedResults = [];
2871
        foreach ($results as $result) {
2872
            $formattedResult = [];
2873
            foreach ($result as $key => $value) {
2874
                $newKey = str_replace('default_', '', $key);
2875
                $formattedResult[$newKey] = $value;
2876
            }
2877
            $formattedResults[] = $formattedResult;
2878
        }
2879
2880
        return $formattedResults;
2881
    }
2882
}
2883