Passed
Push — 1.11.x ( 9b16ad...f94d9f )
by Julito
12:00
created

Event::getExerciseResultsByUser()   B

Complexity

Conditions 6
Paths 18

Size

Total Lines 62
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 39
c 1
b 0
f 0
nc 18
nop 7
dl 0
loc 62
rs 8.6737

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

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