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

Event::updateEventExercise()   B

Complexity

Conditions 7
Paths 17

Size

Total Lines 82
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Long Method    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
/* 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