Event::getAuditItems()   B
last analyzed

Complexity

Conditions 8
Paths 96

Size

Total Lines 53
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 28
dl 0
loc 53
rs 8.4444
c 0
b 0
f 0
cc 8
nc 96
nop 8

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