Passed
Push — 1.11.x ( 6686df...f24d0a )
by Julito
17:18
created

Event::open()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 33
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 20
nc 6
nop 0
dl 0
loc 33
rs 9.2888
c 0
b 0
f 0
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'] = ($row['exe_result'] / $row['exe_weighting']) * 100;
1693
1694
            $list[$row['exe_id']] = $row;
1695
            $sql = "SELECT * FROM $table_track_attempt
1696
                    WHERE exe_id = $exeId";
1697
            $res_question = Database::query($sql);
1698
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1699
                $list[$row['exe_id']]['question_list'][$row_q['question_id']][] = $row_q;
1700
            }
1701
        }
1702
1703
        return $list;
1704
    }
1705
1706
    /**
1707
     * Count exercise attempts (NO Exercises in LPs ) from a given exercise id, course, session.
1708
     *
1709
     * @param int $user_id
1710
     * @param int $exercise_id
1711
     * @param int $courseId
1712
     * @param int $session_id
1713
     *
1714
     * @return array with the results
1715
     */
1716
    public static function count_exercise_attempts_by_user(
1717
        $user_id,
1718
        $exercise_id,
1719
        $courseId,
1720
        $session_id = 0
1721
    ) {
1722
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1723
        $courseId = (int) $courseId;
1724
        $exercise_id = (int) $exercise_id;
1725
        $session_id = (int) $session_id;
1726
        $user_id = (int) $user_id;
1727
1728
        $sql = "SELECT count(*) as count
1729
                FROM $table
1730
                WHERE status = ''  AND
1731
                    exe_user_id = $user_id AND
1732
                    c_id = $courseId AND
1733
                    exe_exo_id = $exercise_id AND
1734
                    session_id = $session_id AND
1735
                    orig_lp_id =0 AND
1736
                    orig_lp_item_id = 0
1737
                ORDER BY exe_id";
1738
        $res = Database::query($sql);
1739
        $result = 0;
1740
        if (Database::num_rows($res) > 0) {
1741
            $row = Database::fetch_array($res, 'ASSOC');
1742
            $result = $row['count'];
1743
        }
1744
1745
        return $result;
1746
    }
1747
1748
    /**
1749
     * Gets all exercise BEST results attempts (NO Exercises in LPs)
1750
     * from a given exercise id, course, session per user.
1751
     *
1752
     * @param int $exercise_id
1753
     * @param int $courseId
1754
     * @param int $session_id
1755
     * @param int $userId
1756
     *
1757
     * @return array with the results
1758
     *
1759
     * @todo rename this function
1760
     */
1761
    public static function get_best_exercise_results_by_user(
1762
        $exercise_id,
1763
        $courseId,
1764
        $session_id = 0,
1765
        $userId = 0
1766
    ) {
1767
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1768
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1769
        $courseId = (int) $courseId;
1770
        $exercise_id = (int) $exercise_id;
1771
        $session_id = (int) $session_id;
1772
1773
        $sql = "SELECT * FROM $table_track_exercises
1774
                WHERE
1775
                    status = '' AND
1776
                    c_id = $courseId AND
1777
                    exe_exo_id = $exercise_id AND
1778
                    session_id = $session_id AND
1779
                    orig_lp_id = 0 AND
1780
                    orig_lp_item_id = 0";
1781
1782
        if (!empty($userId)) {
1783
            $userId = (int) $userId;
1784
            $sql .= " AND exe_user_id = $userId ";
1785
        }
1786
        $sql .= ' ORDER BY exe_id';
1787
1788
        $res = Database::query($sql);
1789
        $list = [];
1790
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1791
            $list[$row['exe_id']] = $row;
1792
            $exeId = $row['exe_id'];
1793
            $sql = "SELECT * FROM $table_track_attempt
1794
                    WHERE exe_id = $exeId";
1795
            $res_question = Database::query($sql);
1796
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1797
                $list[$exeId]['question_list'][$row_q['question_id']] = $row_q;
1798
            }
1799
        }
1800
1801
        // Getting the best results of every student
1802
        $best_score_return = [];
1803
        foreach ($list as $student_result) {
1804
            $user_id = $student_result['exe_user_id'];
1805
            $current_best_score[$user_id] = $student_result['exe_result'];
1806
            if (!isset($best_score_return[$user_id]['exe_result'])) {
1807
                $best_score_return[$user_id] = $student_result;
1808
            }
1809
1810
            if ($current_best_score[$user_id] > $best_score_return[$user_id]['exe_result']) {
1811
                $best_score_return[$user_id] = $student_result;
1812
            }
1813
        }
1814
1815
        return $best_score_return;
1816
    }
1817
1818
    /**
1819
     * Get the last best result from all attempts in exercises per user (out of learning paths).
1820
     *
1821
     * @param int  $user_id
1822
     * @param int  $exercise_id
1823
     * @param int  $courseId
1824
     * @param int  $session_id
1825
     * @param bool $skipLpResults
1826
     *
1827
     * @return array
1828
     */
1829
    public static function get_best_attempt_exercise_results_per_user(
1830
        $user_id,
1831
        $exercise_id,
1832
        $courseId,
1833
        $session_id = 0,
1834
        $skipLpResults = true
1835
    ) {
1836
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1837
        $courseId = (int) $courseId;
1838
        $exercise_id = (int) $exercise_id;
1839
        $session_id = (int) $session_id;
1840
        $user_id = (int) $user_id;
1841
1842
        $sql = "SELECT * FROM $table
1843
                WHERE
1844
                    status = ''  AND
1845
                    c_id = $courseId AND
1846
                    exe_exo_id = $exercise_id AND
1847
                    session_id = $session_id  AND
1848
                    exe_user_id = $user_id
1849
                ";
1850
1851
        if ($skipLpResults) {
1852
            $sql .= ' AND
1853
                orig_lp_id = 0 AND
1854
                orig_lp_item_id = 0 ';
1855
        }
1856
1857
        $sql .= ' ORDER BY exe_id ';
1858
1859
        $res = Database::query($sql);
1860
        $list = [];
1861
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1862
            $list[$row['exe_id']] = $row;
1863
        }
1864
        // Getting the best results of every student.
1865
        $best_score_return = [];
1866
        $best_score_return['exe_result'] = 0;
1867
1868
        foreach ($list as $result) {
1869
            $current_best_score = $result;
1870
            if ($current_best_score['exe_result'] > $best_score_return['exe_result']) {
1871
                $best_score_return = $result;
1872
            }
1873
        }
1874
        if (!isset($best_score_return['exe_weighting'])) {
1875
            $best_score_return = [];
1876
        }
1877
1878
        return $best_score_return;
1879
    }
1880
1881
    /**
1882
     * @param int $exercise_id
1883
     * @param int $courseId
1884
     * @param int $session_id
1885
     *
1886
     * @return mixed
1887
     */
1888
    public static function count_exercise_result_not_validated(
1889
        $exercise_id,
1890
        $courseId,
1891
        $session_id = 0
1892
    ) {
1893
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1894
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
1895
        $courseId = (int) $courseId;
1896
        $session_id = (int) $session_id;
1897
        $exercise_id = (int) $exercise_id;
1898
1899
        $sql = "SELECT count(e.exe_id) as count
1900
                FROM $table_track_exercises e
1901
                LEFT JOIN $table_track_attempt a
1902
                ON e.exe_id = a.exe_id
1903
                WHERE
1904
                    exe_exo_id = $exercise_id AND
1905
                    c_id = $courseId AND
1906
                    e.session_id = $session_id  AND
1907
                    orig_lp_id = 0 AND
1908
                    marks IS NULL AND
1909
                    status = '' AND
1910
                    orig_lp_item_id = 0
1911
                ORDER BY e.exe_id";
1912
        $res = Database::query($sql);
1913
        $row = Database::fetch_array($res, 'ASSOC');
1914
1915
        return $row['count'];
1916
    }
1917
1918
    /**
1919
     * Gets all exercise events from a Learning Path within a Course    nd Session.
1920
     *
1921
     * @param int $exercise_id
1922
     * @param int $courseId
1923
     * @param int $session_id
1924
     *
1925
     * @return array
1926
     */
1927
    public static function get_all_exercise_event_from_lp(
1928
        $exercise_id,
1929
        $courseId,
1930
        $session_id = 0
1931
    ) {
1932
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1933
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1934
        $courseId = (int) $courseId;
1935
        $exercise_id = (int) $exercise_id;
1936
        $session_id = (int) $session_id;
1937
1938
        $sql = "SELECT * FROM $table_track_exercises
1939
                WHERE
1940
                    status = '' AND
1941
                    c_id = $courseId AND
1942
                    exe_exo_id = $exercise_id AND
1943
                    session_id = $session_id AND
1944
                    orig_lp_id !=0 AND
1945
                    orig_lp_item_id != 0";
1946
1947
        $res = Database::query($sql);
1948
        $list = [];
1949
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1950
            $exeId = $row['exe_id'];
1951
            $list[$exeId] = $row;
1952
            $sql = "SELECT * FROM $table_track_attempt
1953
                    WHERE exe_id = $exeId";
1954
            $res_question = Database::query($sql);
1955
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1956
                $list[$exeId]['question_list'][$row_q['question_id']] = $row_q;
1957
            }
1958
        }
1959
1960
        return $list;
1961
    }
1962
1963
    /**
1964
     * Get a list of all the exercises in a given learning path.
1965
     *
1966
     * @param int $lp_id
1967
     * @param int $course_id This parameter is probably deprecated as lp_id now is a global iid
1968
     *
1969
     * @return array
1970
     */
1971
    public static function get_all_exercises_from_lp($lp_id, $course_id)
1972
    {
1973
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
1974
        $course_id = (int) $course_id;
1975
        $lp_id = (int) $lp_id;
1976
        $sql = "SELECT * FROM $lp_item_table
1977
                WHERE
1978
                    c_id = $course_id AND
1979
                    lp_id = $lp_id AND
1980
                    item_type = 'quiz'
1981
                ORDER BY parent_item_id, display_order";
1982
        $res = Database::query($sql);
1983
1984
        $list = [];
1985
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1986
            $list[] = $row;
1987
        }
1988
1989
        return $list;
1990
    }
1991
1992
    /**
1993
     * This function gets the comments of an exercise.
1994
     *
1995
     * @param int $exe_id
1996
     * @param int $question_id
1997
     *
1998
     * @return string the comment
1999
     */
2000
    public static function get_comments($exe_id, $question_id)
2001
    {
2002
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2003
        $exe_id = (int) $exe_id;
2004
        $question_id = (int) $question_id;
2005
        $sql = "SELECT teacher_comment
2006
                FROM $table
2007
                WHERE
2008
                    exe_id = $exe_id AND
2009
                    question_id = $question_id
2010
                ORDER by question_id";
2011
        $sqlres = Database::query($sql);
2012
        $comm = strval(Database::result($sqlres, 0, 'teacher_comment'));
2013
        $comm = trim($comm);
2014
2015
        return $comm;
2016
    }
2017
2018
    /**
2019
     * Get all the track_e_attempt records for a given
2020
     * track_e_exercises.exe_id (pk).
2021
     *
2022
     * @param int $exeId The exe_id from an exercise attempt record
2023
     *
2024
     * @return array The complete records from track_e_attempt that match the given exe_id
2025
     */
2026
    public static function getAllExerciseEventByExeId($exeId)
2027
    {
2028
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2029
        $exeId = (int) $exeId;
2030
2031
        $sql = "SELECT * FROM $table
2032
                WHERE exe_id = $exeId
2033
                ORDER BY position";
2034
        $res_question = Database::query($sql);
2035
        $list = [];
2036
        if (Database::num_rows($res_question)) {
2037
            while ($row = Database::fetch_array($res_question, 'ASSOC')) {
2038
                $list[$row['question_id']][] = $row;
2039
            }
2040
        }
2041
2042
        return $list;
2043
    }
2044
2045
    public static function getQuestionAttemptByExeIdAndQuestion($exeId, $questionId)
2046
    {
2047
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2048
        $exeId = (int) $exeId;
2049
        $questionId = (int) $questionId;
2050
2051
        $sql = "SELECT * FROM $table
2052
                WHERE
2053
                    exe_id = $exeId AND
2054
                    question_id = $questionId
2055
                ORDER BY position";
2056
        $result = Database::query($sql);
2057
        $attempt = [];
2058
        if (Database::num_rows($result)) {
2059
            $attempt = Database::fetch_array($result, 'ASSOC');
2060
        }
2061
2062
        return $attempt;
2063
    }
2064
2065
    /**
2066
     * Delete one record from the track_e_attempt table (recorded quiz answer)
2067
     * and register the deletion event (LOG_QUESTION_RESULT_DELETE) in
2068
     * track_e_default.
2069
     *
2070
     * @param int $exeId       The track_e_exercises.exe_id (primary key)
2071
     * @param int $user_id     The user who answered (already contained in exe_id)
2072
     * @param int $courseId    The course in which it happened (already contained in exe_id)
2073
     * @param int $session_id  The session in which it happened (already contained in exe_id)
2074
     * @param int $question_id The c_quiz_question.iid
2075
     */
2076
    public static function delete_attempt(
2077
        $exeId,
2078
        $user_id,
2079
        $courseId,
2080
        $session_id,
2081
        $question_id
2082
    ) {
2083
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2084
2085
        $exeId = (int) $exeId;
2086
        $user_id = (int) $user_id;
2087
        $courseId = (int) $courseId;
2088
        $session_id = (int) $session_id;
2089
        $question_id = (int) $question_id;
2090
2091
        $sql = "DELETE FROM $table
2092
                WHERE
2093
                    exe_id = $exeId AND
2094
                    user_id = $user_id AND
2095
                    c_id = $courseId AND
2096
                    session_id = $session_id AND
2097
                    question_id = $question_id ";
2098
        Database::query($sql);
2099
2100
        self::addEvent(
2101
            LOG_QUESTION_RESULT_DELETE,
2102
            LOG_EXERCISE_ATTEMPT_QUESTION_ID,
2103
            $exeId.'-'.$question_id,
2104
            null,
2105
            null,
2106
            $courseId,
2107
            $session_id
2108
        );
2109
    }
2110
2111
    /**
2112
     * Delete one record from the track_e_hotspot table based on a given
2113
     * track_e_exercises.exe_id.
2114
     *
2115
     * @param $exeId
2116
     * @param $user_id
2117
     * @param int $courseId
2118
     * @param $question_id
2119
     * @param int $sessionId
2120
     */
2121
    public static function delete_attempt_hotspot(
2122
        $exeId,
2123
        $user_id,
2124
        $courseId,
2125
        $question_id,
2126
        $sessionId = null
2127
    ) {
2128
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
2129
2130
        $exeId = (int) $exeId;
2131
        $user_id = (int) $user_id;
2132
        $courseId = (int) $courseId;
2133
        $question_id = (int) $question_id;
2134
        if (!isset($sessionId)) {
2135
            $sessionId = api_get_session_id();
2136
        }
2137
2138
        $sql = "DELETE FROM $table
2139
                WHERE
2140
                    hotspot_exe_id = $exeId AND
2141
                    hotspot_user_id = $user_id AND
2142
                    c_id = $courseId AND
2143
                    hotspot_question_id = $question_id ";
2144
        Database::query($sql);
2145
        self::addEvent(
2146
            LOG_QUESTION_RESULT_DELETE,
2147
            LOG_EXERCISE_ATTEMPT_QUESTION_ID,
2148
            $exeId.'-'.$question_id,
2149
            null,
2150
            null,
2151
            $courseId,
2152
            $sessionId
2153
        );
2154
    }
2155
2156
    /**
2157
     * Registers in track_e_course_access when user logs in for the first time to a course.
2158
     *
2159
     * @param int $courseId  ID of the course
2160
     * @param int $user_id   ID of the user
2161
     * @param int $sessionId ID of the session (if any)
2162
     *
2163
     * @return bool
2164
     */
2165
    public static function eventCourseLogin($courseId, $user_id, $sessionId)
2166
    {
2167
        if (Session::read('login_as')) {
2168
            return false;
2169
        }
2170
2171
        $sessionId = (int) $sessionId;
2172
        if (self::isSessionLogNeedToBeSave($sessionId) === false) {
2173
            return false;
2174
        }
2175
2176
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2177
        $loginDate = $logoutDate = api_get_utc_datetime();
2178
2179
        // $counter represents the number of time this record has been refreshed
2180
        $counter = 1;
2181
        $courseId = (int) $courseId;
2182
        $user_id = (int) $user_id;
2183
        $ip = Database::escape_string(api_get_real_ip());
2184
2185
        $sql = "INSERT INTO $table(c_id, user_ip, user_id, login_course_date, logout_course_date, counter, session_id)
2186
                VALUES($courseId, '$ip', $user_id, '$loginDate', '$logoutDate', $counter, $sessionId)";
2187
        $courseAccessId = Database::query($sql);
2188
2189
        if ($courseAccessId) {
2190
            // Course catalog stats modifications see #4191
2191
            CourseManager::update_course_ranking(
2192
                null,
2193
                null,
2194
                null,
2195
                null,
2196
                true,
2197
                false
2198
            );
2199
2200
            return true;
2201
        }
2202
    }
2203
2204
    /**
2205
     * Updates the user - course - session every X minutes
2206
     * In order to avoid.
2207
     *
2208
     * @param int $courseId
2209
     * @param int $userId
2210
     * @param int $sessionId
2211
     * @param int $minutes
2212
     *
2213
     * @return bool
2214
     */
2215
    public static function eventCourseLoginUpdate(
2216
        $courseId,
2217
        $userId,
2218
        $sessionId,
2219
        $minutes = 5
2220
    ) {
2221
        if (Session::read('login_as')) {
2222
            return false;
2223
        }
2224
2225
        if (empty($courseId) || empty($userId)) {
2226
            return false;
2227
        }
2228
2229
        $sessionId = (int) $sessionId;
2230
2231
        if (self::isSessionLogNeedToBeSave($sessionId) === false) {
2232
            return false;
2233
        }
2234
2235
        $courseId = (int) $courseId;
2236
        $userId = (int) $userId;
2237
2238
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2239
        $sql = "SELECT course_access_id, logout_course_date
2240
                FROM $table
2241
                WHERE
2242
                    c_id = $courseId AND
2243
                    session_id = $sessionId AND
2244
                    user_id = $userId
2245
                ORDER BY login_course_date DESC
2246
                LIMIT 1";
2247
2248
        $result = Database::query($sql);
2249
2250
        // Save every 5 minutes by default
2251
        $seconds = $minutes * 60;
2252
        $maxSeconds = 3600; // Only update if max diff is one hour
2253
        if (Database::num_rows($result)) {
2254
            $row = Database::fetch_array($result);
2255
            $id = $row['course_access_id'];
2256
            $logout = $row['logout_course_date'];
2257
            $now = time();
2258
            $logout = api_strtotime($logout, 'UTC');
2259
            if ($now - $logout > $seconds &&
2260
                $now - $logout < $maxSeconds
2261
            ) {
2262
                $now = api_get_utc_datetime();
2263
                $sql = "UPDATE $table SET
2264
                            logout_course_date = '$now',
2265
                            counter = counter + 1
2266
                        WHERE course_access_id = $id";
2267
                Database::query($sql);
2268
            }
2269
2270
            return true;
2271
        }
2272
2273
        return false;
2274
    }
2275
2276
    /**
2277
     * Register the logout of the course (usually when logging out of the platform)
2278
     * from the track_e_course_access table.
2279
     *
2280
     * @param array $logoutInfo Information stored by local.inc.php
2281
     *                          before new context ['uid'=> x, 'cid'=>y, 'sid'=>z]
2282
     *
2283
     * @return bool
2284
     */
2285
    public static function courseLogout($logoutInfo)
2286
    {
2287
        if (Session::read('login_as')) {
2288
            return false;
2289
        }
2290
2291
        if (empty($logoutInfo['uid']) || empty($logoutInfo['cid'])) {
2292
            return false;
2293
        }
2294
2295
        $sessionLifetime = api_get_configuration_value('session_lifetime');
2296
        /*
2297
         * When $_configuration['session_lifetime'] is larger than ~100 hours
2298
         * (in order to let users take exercises with no problems)
2299
         * the function Tracking::get_time_spent_on_the_course() returns larger values (200h) due the condition:
2300
         * login_course_date > now() - INTERVAL $session_lifetime SECOND
2301
         */
2302
        if (empty($sessionLifetime) || $sessionLifetime > 86400) {
2303
            $sessionLifetime = 3600; // 1 hour
2304
        }
2305
        if (!empty($logoutInfo) && !empty($logoutInfo['cid'])) {
2306
            $sessionId = 0;
2307
            if (!empty($logoutInfo['sid'])) {
2308
                $sessionId = (int) $logoutInfo['sid'];
2309
            }
2310
2311
            if (self::isSessionLogNeedToBeSave($sessionId) === false) {
2312
                return false;
2313
            }
2314
2315
            $tableCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2316
            $userId = (int) $logoutInfo['uid'];
2317
            $courseId = (int) $logoutInfo['cid'];
2318
2319
            $currentDate = api_get_utc_datetime();
2320
            // UTC time
2321
            $diff = time() - $sessionLifetime;
2322
            $time = api_get_utc_datetime($diff);
2323
            $sql = "SELECT course_access_id, logout_course_date
2324
                    FROM $tableCourseAccess
2325
                    WHERE
2326
                        user_id = $userId AND
2327
                        c_id = $courseId  AND
2328
                        session_id = $sessionId AND
2329
                        login_course_date > '$time'
2330
                    ORDER BY login_course_date DESC
2331
                    LIMIT 1";
2332
            $result = Database::query($sql);
2333
            $insert = false;
2334
            if (Database::num_rows($result) > 0) {
2335
                $row = Database::fetch_array($result, 'ASSOC');
2336
                $courseAccessId = $row['course_access_id'];
2337
                $sql = "UPDATE $tableCourseAccess SET
2338
                                logout_course_date = '$currentDate',
2339
                                counter = counter + 1
2340
                            WHERE course_access_id = $courseAccessId";
2341
                Database::query($sql);
2342
            } else {
2343
                $insert = true;
2344
            }
2345
2346
            if ($insert) {
2347
                $ip = Database::escape_string(api_get_real_ip());
2348
                $sql = "INSERT INTO $tableCourseAccess (c_id, user_ip, user_id, login_course_date, logout_course_date, counter, session_id)
2349
                        VALUES ($courseId, '$ip', $userId, '$currentDate', '$currentDate', 1, $sessionId)";
2350
                Database::query($sql);
2351
            }
2352
2353
            return true;
2354
        }
2355
    }
2356
2357
    /**
2358
     * Register a "fake" time spent on the platform, for example to match the
2359
     * estimated time he took to author an assignment/work, see configuration
2360
     * setting considered_working_time.
2361
     * This assumes there is already some connection of the student to the
2362
     * course, otherwise he wouldn't be able to upload an assignment.
2363
     * This works by creating a new record, copy of the current one, then
2364
     * updating the current one to be just the considered_working_time and
2365
     * end at the same second as the user connected to the course.
2366
     *
2367
     * @param int    $courseId    The course in which to add the time
2368
     * @param int    $userId      The user for whom to add the time
2369
     * @param int    $sessionId   The session in which to add the time (if any)
2370
     * @param string $virtualTime The amount of time to be added,
2371
     *                            in a hh:mm:ss format. If int, we consider it is expressed in hours.
2372
     * @param int    $workId      Student publication id result
2373
     *
2374
     * @return true on successful insertion, false otherwise
2375
     */
2376
    public static function eventAddVirtualCourseTime(
2377
        $courseId,
2378
        $userId,
2379
        $sessionId,
2380
        $virtualTime,
2381
        $workId
2382
    ) {
2383
        if (empty($virtualTime)) {
2384
            return false;
2385
        }
2386
2387
        $courseId = (int) $courseId;
2388
        $userId = (int) $userId;
2389
        $sessionId = (int) $sessionId;
2390
2391
        $logoutDate = api_get_utc_datetime();
2392
        $loginDate = ChamiloApi::addOrSubTimeToDateTime(
2393
            $virtualTime,
2394
            $logoutDate,
2395
            false
2396
        );
2397
2398
        $ip = api_get_real_ip();
2399
        $params = [
2400
            'login_course_date' => $loginDate,
2401
            'logout_course_date' => $logoutDate,
2402
            'session_id' => $sessionId,
2403
            'user_id' => $userId,
2404
            'counter' => 0,
2405
            'c_id' => $courseId,
2406
            'user_ip' => $ip,
2407
        ];
2408
        $courseTrackingTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2409
        Database::insert($courseTrackingTable, $params);
2410
2411
        // Time should also be added to the track_e_login table so as to
2412
        // affect total time on the platform
2413
        $params = [
2414
            'login_user_id' => $userId,
2415
            'login_date' => $loginDate,
2416
            'user_ip' => $ip,
2417
            'logout_date' => $logoutDate,
2418
        ];
2419
        $platformTrackingTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2420
        Database::insert($platformTrackingTable, $params);
2421
2422
        if (Tracking::minimumTimeAvailable($sessionId, $courseId)) {
2423
            $workId = (int) $workId;
2424
            $uniqueId = time();
2425
            $logInfo = [
2426
                'c_id' => $courseId,
2427
                'session_id' => $sessionId,
2428
                'tool' => TOOL_STUDENTPUBLICATION,
2429
                'date_reg' => $loginDate,
2430
                'action' => 'add_work_start_'.$workId,
2431
                'action_details' => $virtualTime,
2432
                'user_id' => $userId,
2433
                'current_id' => $uniqueId,
2434
            ];
2435
            self::registerLog($logInfo);
2436
2437
            $logInfo = [
2438
                'c_id' => $courseId,
2439
                'session_id' => $sessionId,
2440
                'tool' => TOOL_STUDENTPUBLICATION,
2441
                'date_reg' => $logoutDate,
2442
                'action' => 'add_work_end_'.$workId,
2443
                'action_details' => $virtualTime,
2444
                'user_id' => $userId,
2445
                'current_id' => $uniqueId,
2446
            ];
2447
            self::registerLog($logInfo);
2448
        }
2449
2450
        return true;
2451
    }
2452
2453
    /**
2454
     * Removes a "fake" time spent on the platform, for example to match the
2455
     * estimated time he took to author an assignment/work, see configuration
2456
     * setting considered_working_time.
2457
     * This method should be called when something that generated a fake
2458
     * time record is removed. Given the database link is weak (no real
2459
     * relationship kept between the deleted item and this record), this
2460
     * method just looks for the latest record that has the same time as the
2461
     * item's fake time, is in the past and in this course+session. If such a
2462
     * record cannot be found, it doesn't do anything.
2463
     * The IP address is not considered a useful filter here.
2464
     *
2465
     * @param int    $courseId    The course in which to add the time
2466
     * @param int    $userId      The user for whom to add the time
2467
     * @param int    $sessionId   The session in which to add the time (if any)
2468
     * @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.
2469
     *
2470
     * @return true on successful removal, false otherwise
2471
     */
2472
    public static function eventRemoveVirtualCourseTime(
2473
        $courseId,
2474
        $userId,
2475
        $sessionId,
2476
        $virtualTime,
2477
        $workId
2478
    ) {
2479
        if (empty($virtualTime)) {
2480
            return false;
2481
        }
2482
2483
        $courseId = (int) $courseId;
2484
        $userId = (int) $userId;
2485
        $sessionId = (int) $sessionId;
2486
        $originalVirtualTime = Database::escape_string($virtualTime);
2487
        $workId = (int) $workId;
2488
2489
        $courseTrackingTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2490
        $platformTrackingTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2491
2492
        // Change $virtualTime format from hh:mm:ss to hhmmss which is the
2493
        // format returned by SQL for a subtraction of two datetime values
2494
        // @todo make sure this is portable between DBMSes
2495
        if (preg_match('/:/', $virtualTime)) {
2496
            [$h, $m, $s] = preg_split('/:/', $virtualTime);
2497
            $virtualTime = $h * 3600 + $m * 60 + $s;
2498
        } else {
2499
            $virtualTime *= 3600;
2500
        }
2501
2502
        // Get the current latest course connection register. We need that
2503
        // record to re-use the data and create a new record.
2504
        $sql = "SELECT course_access_id
2505
                FROM $courseTrackingTable
2506
                WHERE
2507
                    user_id = $userId AND
2508
                    c_id = $courseId  AND
2509
                    session_id  = $sessionId AND
2510
                    counter = 0 AND
2511
                    (UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) = '$virtualTime'
2512
                ORDER BY login_course_date DESC LIMIT 0,1";
2513
        $result = Database::query($sql);
2514
2515
        // Ignore if we didn't find any course connection record in the last
2516
        // hour. In this case it wouldn't be right to add a "fake" time record.
2517
        if (Database::num_rows($result) > 0) {
2518
            // Found the latest connection
2519
            $row = Database::fetch_row($result);
2520
            $courseAccessId = $row[0];
2521
            $sql = "DELETE FROM $courseTrackingTable
2522
                    WHERE course_access_id = $courseAccessId";
2523
            Database::query($sql);
2524
        }
2525
        $sql = "SELECT login_id
2526
                FROM $platformTrackingTable
2527
                WHERE
2528
                    login_user_id = $userId AND
2529
                    (UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date)) = '$virtualTime'
2530
                ORDER BY login_date DESC LIMIT 0,1";
2531
        $result = Database::query($sql);
2532
        if (Database::num_rows($result) > 0) {
2533
            // Found the latest connection
2534
            $row = Database::fetch_row($result);
2535
            $loginAccessId = $row[0];
2536
            $sql = "DELETE FROM $platformTrackingTable
2537
                    WHERE login_id = $loginAccessId";
2538
            Database::query($sql);
2539
        }
2540
2541
        if (Tracking::minimumTimeAvailable($sessionId, $courseId)) {
2542
            $sql = "SELECT id FROM track_e_access_complete
2543
                    WHERE
2544
                        tool = '".TOOL_STUDENTPUBLICATION."' AND
2545
                        c_id = $courseId AND
2546
                        session_id = $sessionId AND
2547
                        user_id = $userId AND
2548
                        action_details = '$originalVirtualTime' AND
2549
                        action = 'add_work_start_$workId' ";
2550
            $result = Database::query($sql);
2551
            $result = Database::fetch_array($result);
2552
            if ($result) {
2553
                $sql = 'DELETE FROM track_e_access_complete WHERE id = '.$result['id'];
2554
                Database::query($sql);
2555
            }
2556
2557
            $sql = "SELECT id FROM track_e_access_complete
2558
                    WHERE
2559
                        tool = '".TOOL_STUDENTPUBLICATION."' AND
2560
                        c_id = $courseId AND
2561
                        session_id = $sessionId AND
2562
                        user_id = $userId AND
2563
                        action_details = '$originalVirtualTime' AND
2564
                        action = 'add_work_end_$workId' ";
2565
            $result = Database::query($sql);
2566
            $result = Database::fetch_array($result);
2567
            if ($result) {
2568
                $sql = 'DELETE FROM track_e_access_complete WHERE id = '.$result['id'];
2569
                Database::query($sql);
2570
            }
2571
        }
2572
2573
        return false;
2574
    }
2575
2576
    /**
2577
     * For the sake of cohesion, this function is a switch.
2578
     * It's called by EventsDispatcher and fires the right function
2579
     * with the right require_once.
2580
     *
2581
     * @deprecated
2582
     *
2583
     * @param string $event_name
2584
     * @param array  $params
2585
     */
2586
    public static function event_send_mail($event_name, $params)
2587
    {
2588
        EventsMail::send_mail($event_name, $params);
2589
    }
2590
2591
    /**
2592
     * Filter EventEmailTemplate Filters see the main/inc/conf/events.conf.dist.php.
2593
     */
2594
2595
    /**
2596
     * Basic template event message filter (to be used by other filters as default).
2597
     *
2598
     * @deprecated
2599
     *
2600
     * @param array $values (passing by reference)     *
2601
     *
2602
     * @return bool True if everything is OK, false otherwise
2603
     */
2604
    public function event_send_mail_filter_func(&$values)
2605
    {
2606
        return true;
2607
    }
2608
2609
    /**
2610
     * user_registration - send_mail filter.
2611
     *
2612
     * @deprecated
2613
     *
2614
     * @param array $values (passing by reference)
2615
     *
2616
     * @return bool True if everything is OK, false otherwise
2617
     */
2618
    public function user_registration_event_send_mail_filter_func(&$values)
2619
    {
2620
        $res = self::event_send_mail_filter_func($values);
2621
        // proper logic for this filter
2622
        return $res;
2623
    }
2624
2625
    /**
2626
     * portal_homepage_edited - send_mail filter.
2627
     *
2628
     * @deprecated
2629
     *
2630
     * @param array $values (passing by reference)
2631
     *
2632
     * @return bool True if everything is OK, false otherwise
2633
     */
2634
    public function portal_homepage_edited_event_send_mail_filter_func(&$values)
2635
    {
2636
        $res = self::event_send_mail_filter_func($values);
2637
        // proper logic for this filter
2638
        return $res;
2639
    }
2640
2641
    /**
2642
     * Register the logout of the course (usually when logging out of the platform)
2643
     * from the track_e_access_complete table.
2644
     *
2645
     * @param array $logInfo Information stored by local.inc.php
2646
     *
2647
     * @return bool
2648
     */
2649
    public static function registerLog($logInfo)
2650
    {
2651
        $sessionId = api_get_session_id();
2652
        $courseId = api_get_course_int_id();
2653
2654
        if (isset($logInfo['c_id']) && !empty($logInfo['c_id'])) {
2655
            $courseId = $logInfo['c_id'];
2656
        }
2657
2658
        if (isset($logInfo['session_id']) && !empty($logInfo['session_id'])) {
2659
            $sessionId = $logInfo['session_id'];
2660
        }
2661
2662
        if (!Tracking::minimumTimeAvailable($sessionId, $courseId)) {
2663
            return false;
2664
        }
2665
2666
        if (self::isSessionLogNeedToBeSave($sessionId) === false) {
2667
            return false;
2668
        }
2669
2670
        $loginAs = (int) Session::read('login_as') === true;
2671
2672
        $logInfo['user_id'] = isset($logInfo['user_id']) ? $logInfo['user_id'] : api_get_user_id();
2673
        $logInfo['date_reg'] = isset($logInfo['date_reg']) ? $logInfo['date_reg'] : api_get_utc_datetime();
2674
        $logInfo['tool'] = !empty($logInfo['tool']) ? $logInfo['tool'] : '';
2675
        $logInfo['tool_id'] = !empty($logInfo['tool_id']) ? (int) $logInfo['tool_id'] : 0;
2676
        $logInfo['tool_id_detail'] = !empty($logInfo['tool_id_detail']) ? (int) $logInfo['tool_id_detail'] : 0;
2677
        $logInfo['action'] = !empty($logInfo['action']) ? $logInfo['action'] : '';
2678
        $logInfo['action_details'] = !empty($logInfo['action_details']) ? $logInfo['action_details'] : '';
2679
        $logInfo['ip_user'] = api_get_real_ip();
2680
        $logInfo['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
2681
        $logInfo['session_id'] = $sessionId;
2682
        $logInfo['c_id'] = $courseId;
2683
        $logInfo['ch_sid'] = session_id();
2684
        $logInfo['login_as'] = $loginAs;
2685
        $logInfo['info'] = !empty($logInfo['info']) ? $logInfo['info'] : '';
2686
        $logInfo['url'] = $_SERVER['REQUEST_URI'];
2687
        $logInfo['current_id'] = isset($logInfo['current_id']) ? $logInfo['current_id'] : Session::read('last_id', 0);
2688
2689
        $id = Database::insert('track_e_access_complete', $logInfo);
2690
        if ($id && empty($logInfo['current_id'])) {
2691
            Session::write('last_id', $id);
2692
        }
2693
2694
        return true;
2695
    }
2696
2697
    public static function getAttemptQuestionDuration($exeId, $questionId)
2698
    {
2699
        // Check current attempt.
2700
        $questionAttempt = self::getQuestionAttemptByExeIdAndQuestion($exeId, $questionId);
2701
        $alreadySpent = 0;
2702
        if (!empty($questionAttempt) && $questionAttempt['seconds_spent']) {
2703
            $alreadySpent = $questionAttempt['seconds_spent'];
2704
        }
2705
        $now = time();
2706
        $questionStart = Session::read('question_start', []);
2707
        if (!empty($questionStart) &&
2708
            isset($questionStart[$questionId]) && !empty($questionStart[$questionId])
2709
        ) {
2710
            $time = $questionStart[$questionId];
2711
        } else {
2712
            $diff = 0;
2713
            if (!empty($alreadySpent)) {
2714
                $diff = $alreadySpent;
2715
            }
2716
            $time = $questionStart[$questionId] = $now - $diff;
2717
            Session::write('question_start', $questionStart);
2718
        }
2719
2720
        return $now - $time;
2721
    }
2722
2723
    public static function logSubscribedUserInCourse(int $subscribedId, int $courseId)
2724
    {
2725
        $dateTime = api_get_utc_datetime();
2726
        $registrantId = api_get_user_id();
2727
2728
        self::addEvent(
2729
            LOG_SUBSCRIBE_USER_TO_COURSE,
2730
            LOG_COURSE_CODE,
2731
            api_get_course_entity($courseId)->getCode(),
2732
            $dateTime,
2733
            $registrantId,
2734
            $courseId
2735
        );
2736
2737
        self::addEvent(
2738
            LOG_SUBSCRIBE_USER_TO_COURSE,
2739
            LOG_USER_OBJECT,
2740
            api_get_user_info($subscribedId),
2741
            $dateTime,
2742
            $registrantId,
2743
            $courseId
2744
        );
2745
    }
2746
2747
    public static function logUserSubscribedInCourseSession(int $subscribedId, int $courseId, int $sessionId)
2748
    {
2749
        $dateTime = api_get_utc_datetime();
2750
        $registrantId = api_get_user_id();
2751
2752
        self::addEvent(
2753
            LOG_SESSION_ADD_USER_COURSE,
2754
            LOG_USER_ID,
2755
            $subscribedId,
2756
            $dateTime,
2757
            $registrantId,
2758
            $courseId,
2759
            $sessionId
2760
        );
2761
        self::addEvent(
2762
            LOG_SUBSCRIBE_USER_TO_COURSE,
2763
            LOG_COURSE_CODE,
2764
            api_get_course_entity($courseId)->getCode(),
2765
            $dateTime,
2766
            $registrantId,
2767
            $courseId,
2768
            $sessionId
2769
        );
2770
        self::addEvent(
2771
            LOG_SUBSCRIBE_USER_TO_COURSE,
2772
            LOG_USER_OBJECT,
2773
            api_get_user_info($subscribedId),
2774
            $dateTime,
2775
            $registrantId,
2776
            $courseId,
2777
            $sessionId
2778
        );
2779
    }
2780
}
2781