Passed
Push — 1.11.x ( 21533e...82ab68 )
by Julito
12:44
created

Event::eventRemoveVirtualCourseTime()   B

Complexity

Conditions 8
Paths 41

Size

Total Lines 102
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 64
nc 41
nop 5
dl 0
loc 102
rs 7.541
c 1
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

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