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

Event::updateEventExercise()   B

Complexity

Conditions 7
Paths 17

Size

Total Lines 73
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 45
nc 17
nop 13
dl 0
loc 73
rs 8.2666
c 0
b 0
f 0

How to fix   Long Method    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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