learnpathItem::isRestartAllowed()   A
last analyzed

Complexity

Conditions 6
Paths 4

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 11
c 0
b 0
f 0
nc 4
nop 0
dl 0
loc 19
rs 9.2222
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CourseBundle\Entity\CLpItem;
6
7
/**
8
 * Class learnpathItem
9
 * lp_item defines items belonging to a learnpath. Each item has a name,
10
 * a score, a use time and additional information that enables tracking a user's
11
 * progress in a learning path.
12
 *
13
 * @author  Yannick Warnier <[email protected]>
14
 */
15
class learnpathItem
16
{
17
    public const DEBUG = 0; // Logging parameter.
18
    public $iId;
19
    public $attempt_id; // Also called "objectives" SCORM-wise.
20
    public $audio; // The path to an audio file (stored in document/audio/).
21
    public $children = []; // Contains the ids of children items.
22
    public $condition; // If this item has a special condition embedded.
23
    public $current_score;
24
    public $current_start_time;
25
    public $current_stop_time;
26
    public $current_data = '';
27
    public $db_id;
28
    public $db_item_view_id = '';
29
    public $description = '';
30
    public $file;
31
    /**
32
     * At the moment, interactions are just an array of arrays with a structure
33
     * of 8 text fields: id(0), type(1), time(2), weighting(3),
34
     * correct_responses(4), student_response(5), result(6), latency(7).
35
     */
36
    public $interactions = [];
37
    public $interactions_count = 0;
38
    public $objectives = [];
39
    public $objectives_count = 0;
40
    public $launch_data = '';
41
    public $lesson_location = '';
42
    public $level = 0;
43
    public $core_exit = '';
44
    public $lp_id;
45
    public $max_score;
46
    public $mastery_score;
47
    public $min_score;
48
    public $max_time_allowed = '';
49
    public $name;
50
    public $next;
51
    public $parent;
52
    public $path; // In some cases the exo_id = exercise_id in courseDb exercices table.
53
    public $possible_status = [
54
        'not attempted',
55
        'incomplete',
56
        'completed',
57
        'passed',
58
        'failed',
59
        'browsed',
60
    ];
61
    public $prereq_string = '';
62
    public $prereq_alert = '';
63
    public $prereqs = [];
64
    public $previous;
65
    public $prevent_reinit = 1; // 0 =  multiple attempts   1 = one attempt
66
    public $seriousgame_mode;
67
    public $ref;
68
    public $save_on_close = true;
69
    public $search_did = null;
70
    public $status;
71
    public $title;
72
    /**
73
     * Type attribute can contain one of
74
     * link|student_publication|dir|quiz|document|forum|thread.
75
     */
76
    public $type;
77
    public $view_id;
78
    public $oldTotalTime;
79
    public $view_max_score;
80
    public $courseInfo;
81
    public $courseId;
82
    //var used if absolute session time mode is used
83
    private $last_scorm_session_time = 0;
84
    private $prerequisiteMaxScore;
85
    private $prerequisiteMinScore;
86
87
    /**
88
     * Prepares the learning path item for later launch.
89
     * Don't forget to use set_lp_view() if applicable after creating the item.
90
     * Setting an lp_view will finalise the item_view data collection.
91
     *
92
     * @param int                $id           Learning path item ID
93
     * @param CLpItem|array|null $item_content Contents of the item
94
     */
95
    public function __construct($id, $item_content = null)
96
    {
97
        $items_table = Database::get_course_table(TABLE_LP_ITEM);
98
        $id = (int) $id;
99
        $this->courseId = api_get_course_int_id();
100
101
        if (!empty($id)) {
102
            $sql = "SELECT * FROM $items_table
103
                    WHERE iid = $id";
104
            $res = Database::query($sql);
105
            if (Database::num_rows($res) < 1) {
106
                $this->error = 'Could not find given learnpath item in learnpath_item table';
107
            }
108
            $row = Database::fetch_array($res);
109
        } else {
110
            if ($item_content instanceof CLpItem) {
111
                $row = [];
112
                $row['lp_id'] = $item_content->getLp()->getIid();
113
                $row['iid'] = $item_content->getIid();
114
                $row['max_score'] = $item_content->getMaxScore();
115
                $row['min_score'] = $item_content->getMinScore();
116
                $row['title'] = $item_content->getTitle();
117
                $row['item_type'] = $item_content->getItemType();
118
                $row['ref'] = $item_content->getRef();
119
                $row['description'] = $item_content->getDescription();
120
                $row['path'] = $item_content->getPath();
121
                $row['mastery_score'] = $item_content->getMasteryScore();
122
                $row['parent_item_id'] = $item_content->getParentItemId();
123
                $row['next_item_id'] = $item_content->getNextItemId();
124
                $row['previous_item_id'] = $item_content->getPreviousItemId();
125
                $row['display_order'] = $item_content->getDisplayOrder();
126
                $row['prerequisite'] = $item_content->getPrerequisite();
127
                $row['max_time_allowed'] = $item_content->getMaxTimeAllowed();
128
                $row['prerequisite_max_score'] = $item_content->getPrerequisiteMaxScore();
129
                $row['prerequisite_min_score'] = $item_content->getPrerequisiteMinScore();
130
                $row['audio'] = $item_content->getAudio();
131
                $row['launch_data'] = $item_content->getLaunchData();
132
            }
133
        }
134
135
        $this->iId = $row['iid'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $row does not seem to be defined for all execution paths leading up to this point.
Loading history...
136
        $this->lp_id = $row['lp_id'];
137
        $this->max_score = $row['max_score'];
138
        $this->min_score = $row['min_score'];
139
        $this->name = $row['title'];
140
        $this->type = $row['item_type'];
141
        $this->ref = $row['ref'];
142
        $this->title = $row['title'];
143
        $this->description = $row['description'];
144
        $this->path = $row['path'];
145
        $this->mastery_score = $row['mastery_score'];
146
        $this->parent = $row['parent_item_id'];
147
        $this->next = $row['next_item_id'];
148
        $this->previous = $row['previous_item_id'];
149
        $this->display_order = $row['display_order'];
150
        $this->prereq_string = $row['prerequisite'];
151
        $this->max_time_allowed = $row['max_time_allowed'];
152
        $this->setPrerequisiteMaxScore($row['prerequisite_max_score']);
153
        $this->setPrerequisiteMinScore($row['prerequisite_min_score']);
154
        $this->oldTotalTime = 0;
155
        $this->view_max_score = 0;
156
        $this->seriousgame_mode = 0;
157
        //$this->audio = self::fixAudio($row['audio']);
158
        $this->audio = $row['audio'];
159
        $this->launch_data = $row['launch_data'];
160
        $this->save_on_close = true;
161
        $this->db_id = $row['iid'];
162
163
        // Load children list
164
        if (!empty($this->lp_id)) {
165
            $sql = "SELECT iid FROM $items_table
166
                    WHERE
167
                        lp_id = ".$this->lp_id." AND
168
                        parent_item_id = $id";
169
            $res = Database::query($sql);
170
            if (Database::num_rows($res) > 0) {
171
                while ($row = Database::fetch_assoc($res)) {
172
                    $this->children[] = $row['iid'];
173
                }
174
            }
175
176
/*
177
	    // Xapian full text search does not work 
178
	    // and if the option is activated it generates an error
179
	    // So I comment this part of the code to avoid unnecesary errors 
180
            // Get search_did.
181
            if ('true' === api_get_setting('search_enabled')) {
182
                $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
183
                $sql = 'SELECT *
184
                        FROM %s
185
                        WHERE
186
                            course_code=\'%s\' AND
187
                            tool_id=\'%s\' AND
188
                            ref_id_high_level=%s AND
189
                            ref_id_second_level=%d
190
                        LIMIT 1';
191
                // TODO: Verify if it's possible to assume the actual course instead
192
                // of getting it from db.
193
                $sql = sprintf(
194
                    $sql,
195
                    $tbl_se_ref,
196
                    api_get_course_id(),
197
                    TOOL_LEARNPATH,
198
                    $this->lp_id,
199
                    $id
200
                );
201
                $res = Database::query($sql);
202
                if (Database::num_rows($res) > 0) {
203
                    $se_ref = Database::fetch_array($res);
204
                    $this->search_did = (int) $se_ref['search_did'];
205
                }
206
            }
207
*/
208
        }
209
    }
210
211
    public static function fixAudio($audio)
212
    {
213
        $courseInfo = api_get_course_info();
214
        // Do not check in DB as we expect the call to come from the
215
        if (empty($audio) || empty($courseInfo)) {
216
            return '';
217
        }
218
        // learnpath class which should be aware of any fake.
219
        // Old structure
220
        $file = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/audio/'.$audio;
0 ignored issues
show
Bug introduced by
The constant SYS_COURSE_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
221
        if (file_exists($file)) {
222
            $audio = '/audio/'.$audio;
223
            $audio = str_replace('//', '/', $audio);
224
225
            return $audio;
226
        }
227
228
        $file = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document'.$audio;
229
230
        if (file_exists($file)) {
231
            return $audio;
232
        }
233
234
        return '';
235
    }
236
237
    /**
238
     * Adds an interaction to the current item.
239
     *
240
     * @param int   $index  Index (order ID) of the interaction inside this item
241
     * @param array $params Array of parameters:
242
     *                      id(0), type(1), time(2), weighting(3), correct_responses(4),
243
     *                      student_response(5), result(6), latency(7)
244
     */
245
    public function add_interaction($index, $params)
246
    {
247
        $this->interactions[$index] = $params;
248
        // Take the current maximum index to generate the interactions_count.
249
        if (($index + 1) > $this->interactions_count) {
250
            $this->interactions_count = $index + 1;
251
        }
252
    }
253
254
    /**
255
     * Adds an objective to the current item.
256
     *
257
     * @param    array    Array of parameters:
258
     * id(0), status(1), score_raw(2), score_max(3), score_min(4)
259
     */
260
    public function add_objective($index, $params)
261
    {
262
        if (empty($params[0])) {
263
            return null;
264
        }
265
        $this->objectives[$index] = $params;
266
        // Take the current maximum index to generate the objectives_count.
267
        if ((count($this->objectives) + 1) > $this->objectives_count) {
268
            $this->objectives_count = (count($this->objectives) + 1);
269
        }
270
    }
271
272
    /**
273
     * Closes/stops the item viewing. Finalises runtime values.
274
     * If required, save to DB.
275
     *
276
     * @param bool $prerequisitesCheck Needed to check if asset can be set as completed or not
277
     *
278
     * @return bool True on success, false otherwise
279
     */
280
    public function close()
281
    {
282
        $debug = self::DEBUG;
283
        $this->current_stop_time = time();
284
        $type = $this->get_type();
285
        if ($debug) {
286
            error_log('Start - learnpathItem:close');
287
            error_log("Type: ".$type);
288
            error_log("get_id: ".$this->get_id());
289
        }
290
        if ('sco' !== $type) {
291
            if (TOOL_QUIZ == $type || TOOL_HOTPOTATOES == $type) {
292
                $this->get_status(
293
                    true,
294
                    true
295
                ); // Update status (second option forces the update).
296
            } else {
297
                /*$this->status = $this->possible_status[2];
298
                if (self::DEBUG) {
299
                    error_log("STATUS changed to: ".$this->status);
300
                }*/
301
            }
302
        }
303
        if ($this->save_on_close) {
304
            if ($debug) {
305
                error_log('save_on_close');
306
            }
307
            $this->save();
308
        }
309
310
        if ($debug) {
311
            error_log('End - learnpathItem:close');
312
        }
313
314
        return true;
315
    }
316
317
    /**
318
     * Deletes all traces of this item in the database.
319
     *
320
     * @return bool true. Doesn't check for errors yet.
321
     */
322
    public function delete()
323
    {
324
        $lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW);
325
        $lp_item = Database::get_course_table(TABLE_LP_ITEM);
326
327
        $sql = "DELETE FROM $lp_item_view
328
                WHERE lp_item_id = ".$this->db_id;
329
        Database::query($sql);
330
331
        $sql = "SELECT * FROM $lp_item
332
                WHERE iid = ".$this->db_id;
333
        $res_sel = Database::query($sql);
334
        if (Database::num_rows($res_sel) < 1) {
335
            return false;
336
        }
337
338
        $sql = "DELETE FROM $lp_item
339
                WHERE iid = ".$this->db_id;
340
        Database::query($sql);
341
342
        if ('true' == api_get_setting('search_enabled')) {
343
            if (!is_null($this->search_did)) {
344
                $di = new ChamiloIndexer();
345
                $di->remove_document($this->search_did);
346
            }
347
        }
348
349
        return true;
350
    }
351
352
    /**
353
     * Gets the current attempt_id for this user on this item.
354
     *
355
     * @return int attempt_id for this item view by this user or 1 if none defined
356
     */
357
    public function get_attempt_id()
358
    {
359
        $res = 1;
360
        if (!empty($this->attempt_id)) {
361
            $res = (int) $this->attempt_id;
362
        }
363
364
        return $res;
365
    }
366
367
    /**
368
     * Gets a list of the item's children.
369
     *
370
     * @return array Array of children items IDs
371
     */
372
    public function get_children()
373
    {
374
        $list = [];
375
        foreach ($this->children as $child) {
376
            if (!empty($child)) {
377
                $list[] = $child;
378
            }
379
        }
380
381
        return $list;
382
    }
383
384
    /**
385
     * Gets the core_exit value from the database.
386
     */
387
    public function get_core_exit()
388
    {
389
        return $this->core_exit;
390
    }
391
392
    /**
393
     * Gets the credit information (rather scorm-stuff) based on current status
394
     * and reinit autorization. Credit tells the sco(content) if Chamilo will
395
     * record the data it is sent (credit) or not (no-credit).
396
     *
397
     * @return string 'credit' or 'no-credit'. Defaults to 'credit'
398
     *                Because if we don't know enough about this item, it's probably because
399
     *                it was never used before.
400
     */
401
    public function get_credit()
402
    {
403
        if (self::DEBUG > 1) {
404
            error_log('learnpathItem::get_credit()', 0);
405
        }
406
        $credit = 'credit';
407
        // Now check the value of prevent_reinit (if it's 0, return credit as
408
        // the default was).
409
        // If prevent_reinit == 1 (or more).
410
        if (0 != $this->get_prevent_reinit()) {
411
            // If status is not attempted or incomplete, credit anyway.
412
            // Otherwise:
413
            // Check the status in the database rather than in the object, as
414
            // checking in the object would always return "no-credit" when we
415
            // want to set it to completed.
416
            $status = $this->get_status(true);
417
            if (self::DEBUG > 2) {
418
                error_log(
419
                    'learnpathItem::get_credit() - get_prevent_reinit!=0 and '.
420
                    'status is '.$status,
421
                    0
422
                );
423
            }
424
            //0=not attempted - 1 = incomplete
425
            if ($status != $this->possible_status[0] &&
426
                $status != $this->possible_status[1]
427
            ) {
428
                $credit = 'no-credit';
429
            }
430
        }
431
        if (self::DEBUG > 1) {
432
            error_log("learnpathItem::get_credit() returns: $credit");
433
        }
434
435
        return $credit;
436
    }
437
438
    /**
439
     * Gets the current start time property.
440
     *
441
     * @return int Current start time, or current time if none
442
     */
443
    public function get_current_start_time()
444
    {
445
        if (empty($this->current_start_time)) {
446
            return time();
447
        }
448
449
        return $this->current_start_time;
450
    }
451
452
    /**
453
     * Gets the item's description.
454
     *
455
     * @return string Description
456
     */
457
    public function get_description()
458
    {
459
        if (empty($this->description)) {
460
            return '';
461
        }
462
463
        return $this->description;
464
    }
465
466
    /**
467
     * Gets the file path from the course's root directory, no matter what
468
     * tool it is from.
469
     *
470
     * @param string $path_to_scorm_dir
471
     *
472
     * @return string The file path, or an empty string if there is no file
473
     *                attached, or '-1' if the file must be replaced by an error page
474
     */
475
    public function get_file_path($path_to_scorm_dir = '')
476
    {
477
        $courseId = $this->courseId;
478
        $path = $this->get_path();
479
        $type = $this->get_type();
480
481
        if (empty($path)) {
482
            if ('dir' == $type) {
483
                return '';
484
            } else {
485
                return '-1';
486
            }
487
        } elseif ($path == strval(intval($path))) {
488
            // The path is numeric, so it is a reference to a Chamilo object.
489
            switch ($type) {
490
                case 'dir':
491
                    return '';
492
                case TOOL_DOCUMENT:
493
                    $table_doc = Database::get_course_table(TABLE_DOCUMENT);
494
                    $sql = 'SELECT path
495
                            FROM '.$table_doc.'
496
                            WHERE iid = '.$path;
497
                    $res = Database::query($sql);
498
                    $row = Database::fetch_array($res);
499
                    $real_path = 'document'.$row['path'];
500
501
                    return $real_path;
502
                case TOOL_STUDENTPUBLICATION:
503
                case TOOL_QUIZ:
504
                case TOOL_FORUM:
505
                case TOOL_THREAD:
506
                case TOOL_LINK:
507
                default:
508
                    return '-1';
509
            }
510
        } else {
511
            if (!empty($path_to_scorm_dir)) {
512
                $path = $path_to_scorm_dir.$path;
513
            }
514
515
            return $path;
516
        }
517
    }
518
519
    /**
520
     * Gets the DB ID.
521
     *
522
     * @return int Database ID for the current item
523
     */
524
    public function get_id()
525
    {
526
        if (!empty($this->db_id)) {
527
            return $this->db_id;
528
        }
529
        // TODO: Check this return value is valid for children classes (SCORM?).
530
        return 0;
531
    }
532
533
    /**
534
     * Loads the interactions into the item object, from the database.
535
     * If object interactions exist, they will be overwritten by this function,
536
     * using the database elements only.
537
     */
538
    public function load_interactions()
539
    {
540
        $this->interactions = [];
541
        $courseId = $this->courseId;
542
        $tbl = Database::get_course_table(TABLE_LP_ITEM_VIEW);
543
        $sql = "SELECT id FROM $tbl
544
                WHERE
545
                    lp_item_id = ".$this->db_id." AND
546
                    lp_view_id = ".$this->view_id." AND
547
                    view_count = ".$this->get_view_count();
548
        $res = Database::query($sql);
549
        if (Database::num_rows($res) > 0) {
550
            $row = Database::fetch_array($res);
551
            $lp_iv_id = $row[0];
552
            $iva_table = Database::get_course_table(TABLE_LP_IV_INTERACTION);
553
            $sql = "SELECT * FROM $iva_table
554
                    WHERE c_id = $courseId AND lp_iv_id = $lp_iv_id ";
555
            $res_sql = Database::query($sql);
556
            while ($row = Database::fetch_array($res_sql)) {
557
                $this->interactions[$row['interaction_id']] = [
558
                    $row['interaction_id'],
559
                    $row['interaction_type'],
560
                    $row['weighting'],
561
                    $row['completion_time'],
562
                    $row['correct_responses'],
563
                    $row['student_responses'],
564
                    $row['result'],
565
                    $row['latency'],
566
                ];
567
            }
568
        }
569
    }
570
571
    /**
572
     * Gets the current count of interactions recorded in the database.
573
     *
574
     * @param bool $checkdb Whether to count from database or not (defaults to no)
575
     *
576
     * @return int The current number of interactions recorder
577
     */
578
    public function get_interactions_count($checkdb = false)
579
    {
580
        $return = 0;
581
        if (api_is_invitee()) {
582
            // If the user is an invitee, we consider there's no interaction
583
            return 0;
584
        }
585
        if ($checkdb) {
586
            $tbl = Database::get_course_table(TABLE_LP_ITEM_VIEW);
587
            $sql = "SELECT iid FROM $tbl
588
                    WHERE
589
                        lp_item_id = ".$this->db_id." AND
590
                        lp_view_id = ".$this->view_id." AND
591
                        view_count = ".$this->get_attempt_id();
592
            $res = Database::query($sql);
593
            if (Database::num_rows($res) > 0) {
594
                $row = Database::fetch_array($res);
595
                $lp_iv_id = $row[0];
596
                $iva_table = Database::get_course_table(TABLE_LP_IV_INTERACTION);
597
                $sql = "SELECT count(iid) as count
598
                        FROM $iva_table
599
                        WHERE lp_iv_id = $lp_iv_id ";
600
                $res_sql = Database::query($sql);
601
                if (Database::num_rows($res_sql) > 0) {
602
                    $row = Database::fetch_array($res_sql);
603
                    $return = (int) $row['count'];
604
                }
605
            }
606
        } else {
607
            if (!empty($this->interactions_count)) {
608
                $return = $this->interactions_count;
609
            }
610
        }
611
612
        return $return;
613
    }
614
615
    /**
616
     * Gets the JavaScript array content to fill the interactions array.
617
     *
618
     * @param bool $checkdb Whether to check directly into the database (default no)
619
     *
620
     * @return string An empty string if no interaction, a JS array definition otherwise
621
     */
622
    public function get_interactions_js_array($checkdb = false)
623
    {
624
        $return = '';
625
        if ($checkdb) {
626
            $this->load_interactions();
627
        }
628
        foreach ($this->interactions as $id => $in) {
629
            $return .= "[
630
                '$id',
631
                '".$in[1]."',
632
                '".$in[2]."',
633
                '".$in[3]."',
634
                '".$in[4]."',
635
                '".$in[5]."',
636
                '".$in[6]."',
637
                '".$in[7]."'],";
638
        }
639
        if (!empty($return)) {
640
            $return = substr($return, 0, -1);
641
        }
642
643
        return $return;
644
    }
645
646
    /**
647
     * Gets the current count of objectives recorded in the database.
648
     *
649
     * @return int The current number of objectives recorder
650
     */
651
    public function get_objectives_count()
652
    {
653
        $res = 0;
654
        if (!empty($this->objectives_count)) {
655
            $res = $this->objectives_count;
656
        }
657
658
        return $res;
659
    }
660
661
    /**
662
     * Gets the launch_data field found in imsmanifests (this is SCORM- or
663
     * AICC-related, really).
664
     *
665
     * @return string Launch data as found in imsmanifest and stored in
666
     *                Chamilo (read only). Defaults to ''.
667
     */
668
    public function get_launch_data()
669
    {
670
        if (!empty($this->launch_data)) {
671
            return str_replace(
672
                ["\r", "\n", "'"],
673
                ['\r', '\n', "\\'"],
674
                $this->launch_data
675
            );
676
        }
677
678
        return '';
679
    }
680
681
    /**
682
     * Gets the lesson location.
683
     *
684
     * @return string lesson location as recorded by the SCORM and AICC
685
     *                elements. Defaults to ''
686
     */
687
    public function get_lesson_location()
688
    {
689
        if (!empty($this->lesson_location)) {
690
            return str_replace(
691
                ["\r", "\n", "'"],
692
                ['\r', '\n', "\\'"],
693
                $this->lesson_location
694
            );
695
        }
696
697
        return '';
698
    }
699
700
    /**
701
     * Gets the lesson_mode (scorm feature, but might be used by aicc as well
702
     * as chamilo paths).
703
     *
704
     * The "browse" mode is not supported yet (because there is no such way of
705
     * seeing a sco in Chamilo)
706
     *
707
     * @return string 'browse','normal' or 'review'. Defaults to 'normal'
708
     */
709
    public function get_lesson_mode()
710
    {
711
        $mode = 'normal';
712
        if (0 != $this->get_prevent_reinit()) {
713
            // If prevent_reinit == 0
714
            $my_status = $this->get_status();
715
            if ($my_status != $this->possible_status[0] && $my_status != $this->possible_status[1]) {
716
                $mode = 'review';
717
            }
718
        }
719
720
        return $mode;
721
    }
722
723
    /**
724
     * Gets the depth level.
725
     *
726
     * @return int Level. Defaults to 0
727
     */
728
    public function get_level()
729
    {
730
        if (empty($this->level)) {
731
            return 0;
732
        }
733
734
        return $this->level;
735
    }
736
737
    /**
738
     * Gets the mastery score.
739
     */
740
    public function get_mastery_score()
741
    {
742
        if (isset($this->mastery_score)) {
743
            return $this->mastery_score;
744
        }
745
746
        return -1;
747
    }
748
749
    /**
750
     * Gets the maximum (score).
751
     *
752
     * @return int Maximum score. Defaults to 100 if nothing else is defined
753
     */
754
    public function get_max()
755
    {
756
        if ('sco' === $this->type) {
757
            if (!empty($this->view_max_score) && $this->view_max_score > 0) {
758
                return $this->view_max_score;
759
            } else {
760
                if (!empty($this->max_score)) {
761
                    return $this->max_score;
762
                }
763
764
                return 100;
765
            }
766
        } else {
767
            if (!empty($this->max_score)) {
768
                return $this->max_score;
769
            }
770
771
            return 100;
772
        }
773
    }
774
775
    /**
776
     * Gets the maximum time allowed for this user in this attempt on this item.
777
     *
778
     * @return string Time string in SCORM format
779
     *                (HH:MM:SS or HH:MM:SS.SS or HHHH:MM:SS.SS)
780
     */
781
    public function get_max_time_allowed()
782
    {
783
        if (!empty($this->max_time_allowed)) {
784
            return $this->max_time_allowed;
785
        }
786
787
        return '';
788
    }
789
790
    /**
791
     * Gets the minimum (score).
792
     *
793
     * @return int Minimum score. Defaults to 0
794
     */
795
    public function get_min()
796
    {
797
        if (!empty($this->min_score)) {
798
            return $this->min_score;
799
        }
800
801
        return 0;
802
    }
803
804
    /**
805
     * Gets the parent ID.
806
     *
807
     * @return int Parent ID. Defaults to null
808
     */
809
    public function get_parent()
810
    {
811
        if (!empty($this->parent)) {
812
            return $this->parent;
813
        }
814
        // TODO: Check this return value is valid for children classes (SCORM?).
815
        return null;
816
    }
817
818
    /**
819
     * Gets the path attribute.
820
     *
821
     * @return string Path. Defaults to ''
822
     */
823
    public function get_path()
824
    {
825
        if (empty($this->path)) {
826
            return '';
827
        }
828
829
        return $this->path;
830
    }
831
832
    /**
833
     * Gets the prerequisites string.
834
     *
835
     * @return string empty string or prerequisites string if defined
836
     */
837
    public function get_prereq_string()
838
    {
839
        if (!empty($this->prereq_string)) {
840
            return $this->prereq_string;
841
        }
842
843
        return '';
844
    }
845
846
    /**
847
     * Gets the prevent_reinit attribute value (and sets it if not set already).
848
     *
849
     * @return int 1 or 0 (defaults to 1)
850
     */
851
    public function get_prevent_reinit()
852
    {
853
        if (self::DEBUG > 2) {
854
            error_log('learnpathItem::get_prevent_reinit()', 0);
855
        }
856
        if (!isset($this->prevent_reinit)) {
857
            if (!empty($this->lp_id)) {
858
                $table = Database::get_course_table(TABLE_LP_MAIN);
859
                $sql = "SELECT prevent_reinit
860
                        FROM $table
861
                        WHERE iid = ".$this->lp_id;
862
                $res = Database::query($sql);
863
                if (Database::num_rows($res) < 1) {
864
                    $this->error = 'Could not find parent learnpath in lp table';
865
                    if (self::DEBUG > 2) {
866
                        error_log(
867
                            'LearnpathItem::get_prevent_reinit() - Returning false',
868
                            0
869
                        );
870
                    }
871
872
                    return false;
873
                } else {
874
                    $row = Database::fetch_array($res);
875
                    $this->prevent_reinit = $row['prevent_reinit'];
876
                }
877
            } else {
878
                // Prevent reinit is always 1 by default - see learnpath.class.php
879
                $this->prevent_reinit = 1;
880
            }
881
        }
882
        if (self::DEBUG > 2) {
883
            error_log('End of learnpathItem::get_prevent_reinit() - Returned '.$this->prevent_reinit);
884
        }
885
886
        return $this->prevent_reinit;
887
    }
888
889
    /**
890
     * Returns 1 if seriousgame_mode is activated, 0 otherwise.
891
     *
892
     * @return int (0 or 1)
893
     *
894
     * @deprecated seriousgame_mode seems not to be used
895
     *
896
     * @author ndiechburg <[email protected]>
897
     */
898
    public function get_seriousgame_mode()
899
    {
900
        if (!isset($this->seriousgame_mode)) {
901
            if (!empty($this->lp_id)) {
902
                $table = Database::get_course_table(TABLE_LP_MAIN);
903
                $sql = "SELECT seriousgame_mode
904
                        FROM $table
905
                        WHERE iid = ".$this->lp_id;
906
                $res = Database::query($sql);
907
                if (Database::num_rows($res) < 1) {
908
                    $this->error = 'Could not find parent learnpath in learnpath table';
909
910
                    return false;
911
                } else {
912
                    $row = Database::fetch_array($res);
913
                    $this->seriousgame_mode = isset($row['seriousgame_mode']) ? $row['seriousgame_mode'] : 0;
914
                }
915
            } else {
916
                $this->seriousgame_mode = 0; //SeriousGame mode is always off by default
917
            }
918
        }
919
920
        return $this->seriousgame_mode;
921
    }
922
923
    /**
924
     * Gets the item's reference column.
925
     *
926
     * @return string The item's reference field (generally used for SCORM identifiers)
927
     */
928
    public function get_ref()
929
    {
930
        return $this->ref;
931
    }
932
933
    /**
934
     * Gets the list of included resources as a list of absolute or relative
935
     * paths of resources included in the current item. This allows for a
936
     * better SCORM export. The list will generally include pictures, flash
937
     * objects, java applets, or any other stuff included in the source of the
938
     * current item. The current item is expected to be an HTML file. If it
939
     * is not, then the function will return and empty list.
940
     *
941
     * @param string $type        (one of the Chamilo tools) - optional (otherwise takes the current item's type)
942
     * @param string $abs_path    absolute file path - optional (otherwise takes the current item's path)
943
     * @param int    $recursivity level of recursivity we're in
944
     *
945
     * @return array List of file paths.
946
     *               An additional field containing 'local' or 'remote' helps determine if
947
     *               the file should be copied into the zip or just linked
948
     */
949
    public function get_resources_from_source(
950
        $type = null,
951
        $abs_path = null,
952
        $recursivity = 1
953
    ) {
954
        $max = 5;
955
        if ($recursivity > $max) {
956
            return [];
957
        }
958
959
        $type = empty($type) ? $this->get_type() : $type;
960
961
        if (!isset($abs_path)) {
962
            $path = $this->get_file_path();
963
            $abs_path = api_get_path(SYS_COURSE_PATH).api_get_course_path().'/'.$path;
0 ignored issues
show
Bug introduced by
The constant SYS_COURSE_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The function api_get_course_path was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

963
            $abs_path = api_get_path(SYS_COURSE_PATH)./** @scrutinizer ignore-call */ api_get_course_path().'/'.$path;
Loading history...
964
        }
965
966
        $files_list = [];
967
        switch ($type) {
968
            case TOOL_DOCUMENT:
969
            case TOOL_QUIZ:
970
            case 'sco':
971
                // Get the document and, if HTML, open it.
972
                if (!is_file($abs_path)) {
973
                    // The file could not be found.
974
                    return false;
975
                }
976
977
                // for now, read the whole file in one go (that's gonna be
978
                // a problem when the file is too big).
979
                $info = pathinfo($abs_path);
980
                $ext = $info['extension'];
981
982
                switch (strtolower($ext)) {
983
                    case 'html':
984
                    case 'htm':
985
                    case 'shtml':
986
                    case 'css':
987
                        $wantedAttributes = [
988
                            'src',
989
                            'url',
990
                            '@import',
991
                            'href',
992
                            'value',
993
                        ];
994
995
                        // Parse it for included resources.
996
                        $fileContent = file_get_contents($abs_path);
997
                        // Get an array of attributes from the HTML source.
998
                        $attributes = DocumentManager::parse_HTML_attributes(
999
                            $fileContent,
1000
                            $wantedAttributes
1001
                        );
1002
1003
                        // Look at 'src' attributes in this file
1004
                        foreach ($wantedAttributes as $attr) {
1005
                            if (isset($attributes[$attr])) {
1006
                                // Find which kind of path these are (local or remote).
1007
                                $sources = $attributes[$attr];
1008
1009
                                foreach ($sources as $source) {
1010
                                    // Skip what is obviously not a resource.
1011
                                    if (strpos($source, "+this.")) {
1012
                                        continue;
1013
                                    } // javascript code - will still work unaltered.
1014
                                    if (false === strpos($source, '.')) {
1015
                                        continue;
1016
                                    } // No dot, should not be an external file anyway.
1017
                                    if (strpos($source, 'mailto:')) {
1018
                                        continue;
1019
                                    } // mailto link.
1020
                                    if (strpos($source, ';') &&
1021
                                        !strpos($source, '&amp;')
1022
                                    ) {
1023
                                        continue;
1024
                                    } // Avoid code - that should help.
1025
1026
                                    if ('value' == $attr) {
1027
                                        if (strpos($source, 'mp3file')) {
1028
                                            $files_list[] = [
1029
                                                substr(
1030
                                                    $source,
1031
                                                    0,
1032
                                                    strpos(
1033
                                                        $source,
1034
                                                        '.swf'
1035
                                                    ) + 4
1036
                                                ),
1037
                                                'local',
1038
                                                'abs',
1039
                                            ];
1040
                                            $mp3file = substr(
1041
                                                $source,
1042
                                                strpos(
1043
                                                    $source,
1044
                                                    'mp3file='
1045
                                                ) + 8
1046
                                            );
1047
                                            if ('/' == substr($mp3file, 0, 1)) {
1048
                                                $files_list[] = [
1049
                                                    $mp3file,
1050
                                                    'local',
1051
                                                    'abs',
1052
                                                ];
1053
                                            } else {
1054
                                                $files_list[] = [
1055
                                                    $mp3file,
1056
                                                    'local',
1057
                                                    'rel',
1058
                                                ];
1059
                                            }
1060
                                        } elseif (0 === strpos($source, 'flv=')) {
1061
                                            $source = substr($source, 4);
1062
                                            if (strpos($source, '&') > 0) {
1063
                                                $source = substr(
1064
                                                    $source,
1065
                                                    0,
1066
                                                    strpos($source, '&')
1067
                                                );
1068
                                            }
1069
                                            if (strpos($source, '://') > 0) {
1070
                                                if (false !== strpos($source, api_get_path(WEB_PATH))) {
1071
                                                    // We found the current portal url.
1072
                                                    $files_list[] = [
1073
                                                        $source,
1074
                                                        'local',
1075
                                                        'url',
1076
                                                    ];
1077
                                                } else {
1078
                                                    // We didn't find any trace of current portal.
1079
                                                    $files_list[] = [
1080
                                                        $source,
1081
                                                        'remote',
1082
                                                        'url',
1083
                                                    ];
1084
                                                }
1085
                                            } else {
1086
                                                $files_list[] = [
1087
                                                    $source,
1088
                                                    'local',
1089
                                                    'abs',
1090
                                                ];
1091
                                            }
1092
                                            continue; // Skipping anything else to avoid two entries
1093
                                            //(while the others can have sub-files in their url, flv's can't).
1094
                                        }
1095
                                    }
1096
1097
                                    if (strpos($source, '://') > 0) {
1098
                                        // Cut at '?' in a URL with params.
1099
                                        if (strpos($source, '?') > 0) {
1100
                                            $second_part = substr(
1101
                                                $source,
1102
                                                strpos($source, '?')
1103
                                            );
1104
                                            if (strpos($second_part, '://') > 0) {
1105
                                                // If the second part of the url contains a url too,
1106
                                                // treat the second one before cutting.
1107
                                                $pos1 = strpos(
1108
                                                    $second_part,
1109
                                                    '='
1110
                                                );
1111
                                                $pos2 = strpos(
1112
                                                    $second_part,
1113
                                                    '&'
1114
                                                );
1115
                                                $second_part = substr(
1116
                                                    $second_part,
1117
                                                    $pos1 + 1,
1118
                                                    $pos2 - ($pos1 + 1)
1119
                                                );
1120
                                                if (false !== strpos($second_part, api_get_path(WEB_PATH))) {
1121
                                                    // We found the current portal url.
1122
                                                    $files_list[] = [
1123
                                                        $second_part,
1124
                                                        'local',
1125
                                                        'url',
1126
                                                    ];
1127
                                                    $in_files_list[] = self::get_resources_from_source(
0 ignored issues
show
Bug Best Practice introduced by
The method learnpathItem::get_resources_from_source() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1127
                                                    /** @scrutinizer ignore-call */ 
1128
                                                    $in_files_list[] = self::get_resources_from_source(
Loading history...
1128
                                                        TOOL_DOCUMENT,
1129
                                                        $second_part,
1130
                                                        $recursivity + 1
1131
                                                    );
1132
                                                    if (count($in_files_list) > 0) {
1133
                                                        $files_list = array_merge(
1134
                                                            $files_list,
1135
                                                            $in_files_list
1136
                                                        );
1137
                                                    }
1138
                                                } else {
1139
                                                    // We didn't find any trace of current portal.
1140
                                                    $files_list[] = [
1141
                                                        $second_part,
1142
                                                        'remote',
1143
                                                        'url',
1144
                                                    ];
1145
                                                }
1146
                                            } elseif (strpos($second_part, '=') > 0) {
1147
                                                if ('/' === substr($second_part, 0, 1)) {
1148
                                                    // Link starts with a /,
1149
                                                    // making it absolute (relative to DocumentRoot).
1150
                                                    $files_list[] = [
1151
                                                        $second_part,
1152
                                                        'local',
1153
                                                        'abs',
1154
                                                    ];
1155
                                                    $in_files_list[] = self::get_resources_from_source(
1156
                                                        TOOL_DOCUMENT,
1157
                                                        $second_part,
1158
                                                        $recursivity + 1
1159
                                                    );
1160
                                                    if (count($in_files_list) > 0) {
1161
                                                        $files_list = array_merge(
1162
                                                            $files_list,
1163
                                                            $in_files_list
1164
                                                        );
1165
                                                    }
1166
                                                } elseif (0 === strstr($second_part, '..')) {
1167
                                                    // Link is relative but going back in the hierarchy.
1168
                                                    $files_list[] = [
1169
                                                        $second_part,
1170
                                                        'local',
1171
                                                        'rel',
1172
                                                    ];
1173
                                                    $dir = dirname(
1174
                                                        $abs_path
1175
                                                    );
1176
                                                    $new_abs_path = realpath(
1177
                                                        $dir.'/'.$second_part
1178
                                                    );
1179
                                                    $in_files_list[] = self::get_resources_from_source(
1180
                                                        TOOL_DOCUMENT,
1181
                                                        $new_abs_path,
1182
                                                        $recursivity + 1
1183
                                                    );
1184
                                                    if (count($in_files_list) > 0) {
1185
                                                        $files_list = array_merge(
1186
                                                            $files_list,
1187
                                                            $in_files_list
1188
                                                        );
1189
                                                    }
1190
                                                } else {
1191
                                                    // No starting '/', making it relative to current document's path.
1192
                                                    if ('./' == substr($second_part, 0, 2)) {
1193
                                                        $second_part = substr(
1194
                                                            $second_part,
1195
                                                            2
1196
                                                        );
1197
                                                    }
1198
                                                    $files_list[] = [
1199
                                                        $second_part,
1200
                                                        'local',
1201
                                                        'rel',
1202
                                                    ];
1203
                                                    $dir = dirname(
1204
                                                        $abs_path
1205
                                                    );
1206
                                                    $new_abs_path = realpath(
1207
                                                        $dir.'/'.$second_part
1208
                                                    );
1209
                                                    $in_files_list[] = self::get_resources_from_source(
1210
                                                        TOOL_DOCUMENT,
1211
                                                        $new_abs_path,
1212
                                                        $recursivity + 1
1213
                                                    );
1214
                                                    if (count($in_files_list) > 0) {
1215
                                                        $files_list = array_merge(
1216
                                                            $files_list,
1217
                                                            $in_files_list
1218
                                                        );
1219
                                                    }
1220
                                                }
1221
                                            }
1222
                                            // Leave that second part behind now.
1223
                                            $source = substr(
1224
                                                $source,
1225
                                                0,
1226
                                                strpos($source, '?')
1227
                                            );
1228
                                            if (strpos($source, '://') > 0) {
1229
                                                if (false !== strpos($source, api_get_path(WEB_PATH))) {
1230
                                                    // We found the current portal url.
1231
                                                    $files_list[] = [
1232
                                                        $source,
1233
                                                        'local',
1234
                                                        'url',
1235
                                                    ];
1236
                                                    $in_files_list[] = self::get_resources_from_source(
1237
                                                        TOOL_DOCUMENT,
1238
                                                        $source,
1239
                                                        $recursivity + 1
1240
                                                    );
1241
                                                    if (count($in_files_list) > 0) {
1242
                                                        $files_list = array_merge(
1243
                                                            $files_list,
1244
                                                            $in_files_list
1245
                                                        );
1246
                                                    }
1247
                                                } else {
1248
                                                    // We didn't find any trace of current portal.
1249
                                                    $files_list[] = [
1250
                                                        $source,
1251
                                                        'remote',
1252
                                                        'url',
1253
                                                    ];
1254
                                                }
1255
                                            } else {
1256
                                                // No protocol found, make link local.
1257
                                                if ('/' === substr($source, 0, 1)) {
1258
                                                    // Link starts with a /, making it absolute (relative to DocumentRoot).
1259
                                                    $files_list[] = [
1260
                                                        $source,
1261
                                                        'local',
1262
                                                        'abs',
1263
                                                    ];
1264
                                                    $in_files_list[] = self::get_resources_from_source(
1265
                                                        TOOL_DOCUMENT,
1266
                                                        $source,
1267
                                                        $recursivity + 1
1268
                                                    );
1269
                                                    if (count($in_files_list) > 0) {
1270
                                                        $files_list = array_merge(
1271
                                                            $files_list,
1272
                                                            $in_files_list
1273
                                                        );
1274
                                                    }
1275
                                                } elseif (0 === strstr($source, '..')) {
1276
                                                    // Link is relative but going back in the hierarchy.
1277
                                                    $files_list[] = [
1278
                                                        $source,
1279
                                                        'local',
1280
                                                        'rel',
1281
                                                    ];
1282
                                                    $dir = dirname(
1283
                                                        $abs_path
1284
                                                    );
1285
                                                    $new_abs_path = realpath(
1286
                                                        $dir.'/'.$source
1287
                                                    );
1288
                                                    $in_files_list[] = self::get_resources_from_source(
1289
                                                        TOOL_DOCUMENT,
1290
                                                        $new_abs_path,
1291
                                                        $recursivity + 1
1292
                                                    );
1293
                                                    if (count($in_files_list) > 0) {
1294
                                                        $files_list = array_merge(
1295
                                                            $files_list,
1296
                                                            $in_files_list
1297
                                                        );
1298
                                                    }
1299
                                                } else {
1300
                                                    // No starting '/', making it relative to current document's path.
1301
                                                    if ('./' == substr($source, 0, 2)) {
1302
                                                        $source = substr(
1303
                                                            $source,
1304
                                                            2
1305
                                                        );
1306
                                                    }
1307
                                                    $files_list[] = [
1308
                                                        $source,
1309
                                                        'local',
1310
                                                        'rel',
1311
                                                    ];
1312
                                                    $dir = dirname(
1313
                                                        $abs_path
1314
                                                    );
1315
                                                    $new_abs_path = realpath(
1316
                                                        $dir.'/'.$source
1317
                                                    );
1318
                                                    $in_files_list[] = self::get_resources_from_source(
1319
                                                        TOOL_DOCUMENT,
1320
                                                        $new_abs_path,
1321
                                                        $recursivity + 1
1322
                                                    );
1323
                                                    if (count($in_files_list) > 0) {
1324
                                                        $files_list = array_merge(
1325
                                                            $files_list,
1326
                                                            $in_files_list
1327
                                                        );
1328
                                                    }
1329
                                                }
1330
                                            }
1331
                                        }
1332
1333
                                        // Found some protocol there.
1334
                                        if (false !== strpos($source, api_get_path(WEB_PATH))) {
1335
                                            // We found the current portal url.
1336
                                            $files_list[] = [
1337
                                                $source,
1338
                                                'local',
1339
                                                'url',
1340
                                            ];
1341
                                            $in_files_list[] = self::get_resources_from_source(
1342
                                                TOOL_DOCUMENT,
1343
                                                $source,
1344
                                                $recursivity + 1
1345
                                            );
1346
                                            if (count($in_files_list) > 0) {
1347
                                                $files_list = array_merge(
1348
                                                    $files_list,
1349
                                                    $in_files_list
1350
                                                );
1351
                                            }
1352
                                        } else {
1353
                                            // We didn't find any trace of current portal.
1354
                                            $files_list[] = [
1355
                                                $source,
1356
                                                'remote',
1357
                                                'url',
1358
                                            ];
1359
                                        }
1360
                                    } else {
1361
                                        // No protocol found, make link local.
1362
                                        if ('/' === substr($source, 0, 1)) {
1363
                                            // Link starts with a /, making it absolute (relative to DocumentRoot).
1364
                                            $files_list[] = [
1365
                                                $source,
1366
                                                'local',
1367
                                                'abs',
1368
                                            ];
1369
                                            $in_files_list[] = self::get_resources_from_source(
1370
                                                TOOL_DOCUMENT,
1371
                                                $source,
1372
                                                $recursivity + 1
1373
                                            );
1374
                                            if (count($in_files_list) > 0) {
1375
                                                $files_list = array_merge(
1376
                                                    $files_list,
1377
                                                    $in_files_list
1378
                                                );
1379
                                            }
1380
                                        } elseif (0 === strstr($source, '..')) {
1381
                                            // Link is relative but going back in the hierarchy.
1382
                                            $files_list[] = [
1383
                                                $source,
1384
                                                'local',
1385
                                                'rel',
1386
                                            ];
1387
                                            $dir = dirname($abs_path);
1388
                                            $new_abs_path = realpath(
1389
                                                $dir.'/'.$source
1390
                                            );
1391
                                            $in_files_list[] = self::get_resources_from_source(
1392
                                                TOOL_DOCUMENT,
1393
                                                $new_abs_path,
1394
                                                $recursivity + 1
1395
                                            );
1396
                                            if (count($in_files_list) > 0) {
1397
                                                $files_list = array_merge(
1398
                                                    $files_list,
1399
                                                    $in_files_list
1400
                                                );
1401
                                            }
1402
                                        } else {
1403
                                            // No starting '/', making it relative to current document's path.
1404
                                            if (strpos($source, 'width=') ||
1405
                                                strpos($source, 'autostart=')
1406
                                            ) {
1407
                                                continue;
1408
                                            }
1409
1410
                                            if ('./' == substr($source, 0, 2)) {
1411
                                                $source = substr(
1412
                                                    $source,
1413
                                                    2
1414
                                                );
1415
                                            }
1416
                                            $files_list[] = [
1417
                                                $source,
1418
                                                'local',
1419
                                                'rel',
1420
                                            ];
1421
                                            $dir = dirname($abs_path);
1422
                                            $new_abs_path = realpath(
1423
                                                $dir.'/'.$source
1424
                                            );
1425
                                            $in_files_list[] = self::get_resources_from_source(
1426
                                                TOOL_DOCUMENT,
1427
                                                $new_abs_path,
1428
                                                $recursivity + 1
1429
                                            );
1430
                                            if (count($in_files_list) > 0) {
1431
                                                $files_list = array_merge(
1432
                                                    $files_list,
1433
                                                    $in_files_list
1434
                                                );
1435
                                            }
1436
                                        }
1437
                                    }
1438
                                }
1439
                            }
1440
                        }
1441
                        break;
1442
                    default:
1443
                        break;
1444
                }
1445
1446
                break;
1447
            default: // Ignore.
1448
                break;
1449
        }
1450
1451
        $checked_files_list = [];
1452
        $checked_array_list = [];
1453
        foreach ($files_list as $idx => $file) {
1454
            if (!empty($file[0])) {
1455
                if (!in_array($file[0], $checked_files_list)) {
1456
                    $checked_files_list[] = $files_list[$idx][0];
1457
                    $checked_array_list[] = $files_list[$idx];
1458
                }
1459
            }
1460
        }
1461
1462
        return $checked_array_list;
1463
    }
1464
1465
    /**
1466
     * Gets the score.
1467
     *
1468
     * @return float The current score or 0 if no score set yet
1469
     */
1470
    public function get_score()
1471
    {
1472
        $res = 0;
1473
        if (!empty($this->current_score)) {
1474
            $res = $this->current_score;
1475
        }
1476
1477
        return $res;
1478
    }
1479
1480
    /**
1481
     * Gets the item status.
1482
     *
1483
     * @param bool $check_db     Do or don't check into the database for the
1484
     *                           latest value. Optional. Default is true
1485
     * @param bool $update_local Do or don't update the local attribute
1486
     *                           value with what's been found in DB
1487
     *
1488
     * @return string Current status or 'Not attempted' if no status set yet
1489
     */
1490
    public function get_status($check_db = true, $update_local = false)
1491
    {
1492
        $debug = self::DEBUG;
1493
        if ($debug) {
1494
            error_log('learnpathItem::get_status() on item '.$this->db_id);
1495
        }
1496
        if ($check_db) {
1497
            if ($debug) {
1498
                error_log('learnpathItem::get_status(): checking db');
1499
            }
1500
            if (!empty($this->db_item_view_id)) {
1501
                $table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
1502
                $sql = "SELECT status FROM $table
1503
                        WHERE
1504
                            iid = '".$this->db_item_view_id."' AND
1505
                            view_count = '".$this->get_attempt_id()."'";
1506
                $res = Database::query($sql);
1507
                if (1 == Database::num_rows($res)) {
1508
                    $row = Database::fetch_array($res);
1509
                    if ($update_local) {
1510
                        $this->set_status($row['status']);
1511
                    }
1512
1513
                    return $row['status'];
1514
                }
1515
            }
1516
        } else {
1517
            if (!empty($this->status)) {
1518
                return $this->status;
1519
            }
1520
        }
1521
1522
        return $this->possible_status[0];
1523
    }
1524
1525
    /**
1526
     * Gets the suspend data.
1527
     */
1528
    public function get_suspend_data()
1529
    {
1530
        // TODO: Improve cleaning of breaklines ... it works but is it really
1531
        // a beautiful way to do it ?
1532
        if (!empty($this->current_data)) {
1533
            return str_replace(
1534
                ["\r", "\n", "'"],
1535
                ['\r', '\n', "\\'"],
1536
                $this->current_data
1537
            );
1538
        }
1539
1540
        return '';
1541
    }
1542
1543
    /**
1544
     * @param string $origin
1545
     * @param string $time
1546
     *
1547
     * @return string
1548
     */
1549
    public static function getScormTimeFromParameter(
1550
        $origin = 'php',
1551
        $time = null
1552
    ) {
1553
        $h = get_lang('h');
1554
        if (!isset($time)) {
1555
            if ('js' == $origin) {
1556
                return '00 : 00: 00';
1557
            }
1558
1559
            return '00 '.$h.' 00 \' 00"';
1560
        }
1561
1562
        return api_format_time($time, $origin);
1563
    }
1564
1565
    /**
1566
     * Gets the total time spent on this item view so far.
1567
     *
1568
     * @param string   $origin     Origin of the request. If coming from PHP,
1569
     *                             send formatted as xxhxx'xx", otherwise use scorm format 00:00:00
1570
     * @param int|null $given_time Given time is a default time to return formatted
1571
     * @param bool     $query_db   Whether to get the value from db or from memory
1572
     *
1573
     * @return string A string with the time in SCORM format
1574
     */
1575
    public function get_scorm_time(
1576
        $origin = 'php',
1577
        $given_time = null,
1578
        $query_db = false
1579
    ) {
1580
        $time = null;
1581
        $courseId = $this->courseId;
1582
        if (empty($courseId)) {
1583
            $courseId = api_get_course_int_id();
1584
        }
1585
1586
        $courseId = (int) $courseId;
1587
        if (!isset($given_time)) {
1588
            if (self::DEBUG > 2) {
1589
                error_log(
1590
                    'learnpathItem::get_scorm_time(): given time empty, current_start_time = '.$this->current_start_time,
1591
                    0
1592
                );
1593
            }
1594
            if (true === $query_db) {
1595
                $table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
1596
                $sql = "SELECT start_time, total_time
1597
                        FROM $table
1598
                        WHERE
1599
                            iid = '".$this->db_item_view_id."' AND
1600
                            view_count = '".$this->get_attempt_id()."'";
1601
                $res = Database::query($sql);
1602
                $row = Database::fetch_array($res);
1603
                $start = $row['start_time'];
1604
                $stop = $start + $row['total_time'];
1605
            } else {
1606
                $start = $this->current_start_time;
1607
                $stop = $this->current_stop_time;
1608
            }
1609
            if (!empty($start)) {
1610
                if (!empty($stop)) {
1611
                    $time = $stop - $start;
1612
                } else {
1613
                    $time = time() - $start;
1614
                }
1615
            }
1616
        } else {
1617
            $time = $given_time;
1618
        }
1619
        if (self::DEBUG > 2) {
1620
            error_log(
1621
                'learnpathItem::get_scorm_time(): intermediate = '.$time,
1622
                0
1623
            );
1624
        }
1625
        $time = api_format_time($time, $origin);
1626
1627
        return $time;
1628
    }
1629
1630
    /**
1631
     * Get the extra terms (tags) that identify this item.
1632
     *
1633
     * @return mixed
1634
     */
1635
    public function get_terms()
1636
    {
1637
        $table = Database::get_course_table(TABLE_LP_ITEM);
1638
        $sql = "SELECT * FROM $table
1639
                WHERE iid = ".intval($this->db_id);
1640
        $res = Database::query($sql);
1641
        $row = Database::fetch_array($res);
1642
1643
        return $row['terms'];
1644
    }
1645
1646
    /**
1647
     * Returns the item's title.
1648
     *
1649
     * @return string Title
1650
     */
1651
    public function get_title()
1652
    {
1653
        if (empty($this->title)) {
1654
            return '';
1655
        }
1656
1657
        return $this->title;
1658
    }
1659
1660
    /**
1661
     * Returns the total time used to see that item.
1662
     *
1663
     * @return int Total time
1664
     */
1665
    public function get_total_time()
1666
    {
1667
        $debug = self::DEBUG;
1668
        if ($debug) {
1669
            error_log(
1670
                'learnpathItem::get_total_time() for item '.$this->db_id.
1671
                ' - Initially, current_start_time = '.$this->current_start_time.
1672
                ' and current_stop_time = '.$this->current_stop_time,
1673
                0
1674
            );
1675
        }
1676
        if (0 == $this->current_start_time) {
1677
            // Shouldn't be necessary thanks to the open() method.
1678
            if ($debug) {
1679
                error_log(
1680
                    'learnpathItem::get_total_time() - Current start time was empty',
1681
                    0
1682
                );
1683
            }
1684
            $this->current_start_time = time();
1685
        }
1686
1687
        if (time() < $this->current_stop_time ||
1688
            0 == $this->current_stop_time
1689
        ) {
1690
            if ($debug) {
1691
                error_log(
1692
                    'learnpathItem::get_total_time() - Current stop time was '
1693
                    .'greater than the current time or was empty',
1694
                    0
1695
                );
1696
            }
1697
            // If this case occurs, then we risk to write huge time data in db.
1698
            // In theory, stop time should be *always* updated here, but it
1699
            // might be used in some unknown goal.
1700
            $this->current_stop_time = time();
1701
        }
1702
1703
        $time = $this->current_stop_time - $this->current_start_time;
1704
1705
        if ($time < 0) {
1706
            if ($debug) {
1707
                error_log(
1708
                    'learnpathItem::get_total_time() - Time smaller than 0. Returning 0',
1709
                    0
1710
                );
1711
            }
1712
1713
            return 0;
1714
        } else {
1715
            $time = $this->fixAbusiveTime($time);
1716
            if ($debug) {
1717
                error_log(
1718
                    'Current start time = '.$this->current_start_time.', current stop time = '.
1719
                    $this->current_stop_time.' Returning '.$time."-----------\n"
1720
                );
1721
            }
1722
1723
            return $time;
1724
        }
1725
    }
1726
1727
    /**
1728
     * Sometimes time recorded for a learning path item is superior to the maximum allowed duration of the session.
1729
     * In this case, this session resets the time for that particular learning path item to 5 minutes
1730
     * (something more realistic, that is also used when leaving the portal without closing one's session).
1731
     *
1732
     * @param int $time
1733
     *
1734
     * @return int
1735
     */
1736
    public function fixAbusiveTime($time)
1737
    {
1738
        // Code based from Event::courseLogout
1739
        $sessionLifetime = api_get_configuration_value('session_lifetime');
1740
        // If session life time too big use 1 hour
1741
        if (empty($sessionLifetime) || $sessionLifetime > 86400) {
1742
            $sessionLifetime = 3600;
1743
        }
1744
1745
        if (!Tracking::minimumTimeAvailable(api_get_session_id(), api_get_course_int_id())) {
1746
            $fixedAddedMinute = 5 * 60; // Add only 5 minutes
1747
            if ($time > $sessionLifetime) {
1748
                error_log("fixAbusiveTime: Total time is too big: $time replaced with: $fixedAddedMinute");
1749
                error_log("item_id : ".$this->db_id." lp_item_view.iid: ".$this->db_item_view_id);
1750
                $time = $fixedAddedMinute;
1751
            }
1752
1753
            return $time;
1754
        } else {
1755
            // Calulate minimum and accumulated time
1756
            $user_id = api_get_user_id();
1757
            $myLP = learnpath::getLpFromSession(api_get_course_int_id(), $this->lp_id, $user_id);
1758
            $timeLp = $myLP->getAccumulateWorkTime();
1759
            $timeTotalCourse = $myLP->getAccumulateWorkTimeTotalCourse();
1760
            /*
1761
            $timeLp = $_SESSION['oLP']->getAccumulateWorkTime();
1762
            $timeTotalCourse = $_SESSION['oLP']->getAccumulateWorkTimeTotalCourse();
1763
            */
1764
            // Minimum connection percentage
1765
            $perc = 100;
1766
            // Time from the course
1767
            $tc = $timeTotalCourse;
1768
            /*if (!empty($sessionId) && $sessionId != 0) {
1769
                $sql = "SELECT hours, perc FROM plugin_licences_course_session WHERE session_id = $sessionId";
1770
                $res = Database::query($sql);
1771
                if (Database::num_rows($res) > 0) {
1772
                    $aux = Database::fetch_assoc($res);
1773
                    $perc = $aux['perc'];
1774
                    $tc = $aux['hours'] * 60;
1775
                }
1776
            }*/
1777
            // Percentage of the learning paths
1778
            $pl = 0;
1779
            if (!empty($timeTotalCourse)) {
1780
                $pl = $timeLp / $timeTotalCourse;
1781
            }
1782
1783
            // Minimum time for each learning path
1784
            $accumulateWorkTime = ($pl * $tc * $perc / 100);
1785
            $time_seg = intval($accumulateWorkTime * 60);
1786
1787
            if ($time_seg < $sessionLifetime) {
1788
                $sessionLifetime = $time_seg;
1789
            }
1790
1791
            if ($time > $sessionLifetime) {
1792
                $fixedAddedMinute = $time_seg + mt_rand(0, 300);
1793
                if (self::DEBUG > 2) {
1794
                    error_log("Total time is too big: $time replaced with: $fixedAddedMinute");
1795
                }
1796
                $time = $fixedAddedMinute;
1797
            }
1798
1799
            return $time;
1800
        }
1801
    }
1802
1803
    /**
1804
     * Gets the item type.
1805
     *
1806
     * @return string The item type (can be doc, dir, sco, asset)
1807
     */
1808
    public function get_type()
1809
    {
1810
        $res = 'asset';
1811
        if (!empty($this->type)) {
1812
            $res = $this->type;
1813
        }
1814
1815
        return $res;
1816
    }
1817
1818
    /**
1819
     * Gets the view count for this item.
1820
     *
1821
     * @return int Number of attempts or 0
1822
     */
1823
    public function get_view_count()
1824
    {
1825
        if (!empty($this->attempt_id)) {
1826
            return $this->attempt_id;
1827
        }
1828
1829
        return 0;
1830
    }
1831
1832
    /**
1833
     * Tells if an item is done ('completed','passed','succeeded') or not.
1834
     *
1835
     * @return bool True if the item is done ('completed','passed','succeeded'),
1836
     *              false otherwise
1837
     */
1838
    public function is_done()
1839
    {
1840
        $completedStatusList = [
1841
            'completed',
1842
            'passed',
1843
            'succeeded',
1844
            'failed',
1845
        ];
1846
1847
        if ($this->status_is($completedStatusList)) {
1848
            return true;
1849
        }
1850
1851
        return false;
1852
    }
1853
1854
    /**
1855
     * Tells if a restart is allowed (take it from $this->prevent_reinit and $this->status).
1856
     *
1857
     * @return int -1 if retaking the sco another time for credit is not allowed,
1858
     *             0 if it is not allowed but the item has to be finished
1859
     *             1 if it is allowed. Defaults to 1
1860
     */
1861
    public function isRestartAllowed()
1862
    {
1863
        $restart = 1;
1864
        $mystatus = $this->get_status(true);
1865
        if ($this->get_prevent_reinit() > 0) {
1866
            // If prevent_reinit == 1 (or more)
1867
            // If status is not attempted or incomplete, authorize retaking (of the same) anyway. Otherwise:
1868
            if ($mystatus != $this->possible_status[0] && $mystatus != $this->possible_status[1]) {
1869
                $restart = -1;
1870
            } else { //status incompleted or not attempted
1871
                $restart = 0;
1872
            }
1873
        } else {
1874
            if ($mystatus == $this->possible_status[0] || $mystatus == $this->possible_status[1]) {
1875
                $restart = -1;
1876
            }
1877
        }
1878
1879
        return $restart;
1880
    }
1881
1882
    /**
1883
     * Opens/launches the item. Initialises runtime values.
1884
     *
1885
     * @param bool $allow_new_attempt
1886
     *
1887
     * @return bool true on success, false on failure
1888
     */
1889
    public function open($allow_new_attempt = false)
1890
    {
1891
        if (0 == $this->prevent_reinit) {
1892
            $this->current_score = 0;
1893
            $this->current_start_time = time();
1894
            // In this case, as we are opening the item, what is important to us
1895
            // is the database status, in order to know if this item has already
1896
            // been used in the past (rather than just loaded and modified by
1897
            // some javascript but not written in the database).
1898
            // If the database status is different from 'not attempted', we can
1899
            // consider this item has already been used, and as such we can
1900
            // open a new attempt. Otherwise, we'll just reuse the current
1901
            // attempt, which is generally created the first time the item is
1902
            // loaded (for example as part of the table of contents).
1903
            $stat = $this->get_status(true);
1904
            if ($allow_new_attempt && isset($stat) && ($stat != $this->possible_status[0])) {
1905
                $this->attempt_id = $this->attempt_id + 1; // Open a new attempt.
1906
            }
1907
            $this->status = $this->possible_status[1];
1908
        } else {
1909
            /*if ($this->current_start_time == 0) {
1910
                // Small exception for start time, to avoid amazing values.
1911
                $this->current_start_time = time();
1912
            }*/
1913
            // If we don't init start time here, the time is sometimes calculated from the last start time.
1914
            $this->current_start_time = time();
1915
        }
1916
    }
1917
1918
    /**
1919
     * Outputs the item contents.
1920
     *
1921
     * @return string HTML file (displayable in an <iframe>) or empty string if no path defined
1922
     */
1923
    public function output()
1924
    {
1925
        if (!empty($this->path) and is_file($this->path)) {
1926
            $output = '';
1927
            $output .= file_get_contents($this->path);
1928
1929
            return $output;
1930
        }
1931
1932
        return '';
1933
    }
1934
1935
    /**
1936
     * Parses the prerequisites string with the AICC logic language.
1937
     *
1938
     * @param string $prereqs_string The prerequisites string as it figures in imsmanifest.xml
1939
     * @param array  $items          Array of items in the current learnpath object.
1940
     *                               Although we're in the learnpathItem object, it's necessary to have
1941
     *                               a list of all items to be able to check the current item's prerequisites
1942
     * @param array  $refs_list      list of references
1943
     *                               (the "ref" column in the lp_item table) that are strings used in the
1944
     *                               expression of prerequisites
1945
     * @param int    $user_id        The user ID. In some cases like Chamilo quizzes,
1946
     *                               it's necessary to have the user ID to query other tables (like the results of quizzes)
1947
     *
1948
     * @return bool True if the list of prerequisites given is entirely satisfied, false otherwise
1949
     */
1950
    public function parse_prereq($prereqs_string, $items, $refs_list, $user_id)
1951
    {
1952
        $debug = self::DEBUG;
1953
        if ($debug > 0) {
1954
            error_log(
1955
                'learnpathItem::parse_prereq() for learnpath '.$this->lp_id.' with string '.$prereqs_string,
1956
                0
1957
            );
1958
        }
1959
1960
        $courseId = $this->courseId;
1961
        $sessionId = api_get_session_id();
1962
1963
        // Deal with &, |, ~, =, <>, {}, ,, X*, () in reverse order.
1964
        $this->prereq_alert = '';
1965
1966
        // First parse all parenthesis by using a sequential loop
1967
        //  (looking for less-inclusives first).
1968
        if ('_true_' == $prereqs_string) {
1969
            return true;
1970
        }
1971
1972
        if ('_false_' == $prereqs_string) {
1973
            if (empty($this->prereq_alert)) {
1974
                $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
1975
            }
1976
1977
            return false;
1978
        }
1979
1980
        while (false !== strpos($prereqs_string, '(')) {
1981
            // Remove any () set and replace with its value.
1982
            $matches = [];
1983
            $res = preg_match_all(
1984
                '/(\(([^\(\)]*)\))/',
1985
                $prereqs_string,
1986
                $matches
1987
            );
1988
            if ($res) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $res of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1989
                foreach ($matches[2] as $id => $match) {
1990
                    $str_res = $this->parse_prereq(
1991
                        $match,
1992
                        $items,
1993
                        $refs_list,
1994
                        $user_id
1995
                    );
1996
                    if ($str_res) {
1997
                        $prereqs_string = str_replace(
1998
                            $matches[1][$id],
1999
                            '_true_',
2000
                            $prereqs_string
2001
                        );
2002
                    } else {
2003
                        $prereqs_string = str_replace(
2004
                            $matches[1][$id],
2005
                            '_false_',
2006
                            $prereqs_string
2007
                        );
2008
                    }
2009
                }
2010
            }
2011
        }
2012
2013
        // Parenthesis removed, now look for ORs as it is the lesser-priority
2014
        //  binary operator (= always uses one text operand).
2015
        if (false === strpos($prereqs_string, '|')) {
2016
            if ($debug) {
2017
                error_log('New LP - Didnt find any OR, looking for AND', 0);
2018
            }
2019
            if (false !== strpos($prereqs_string, '&')) {
2020
                $list = explode('&', $prereqs_string);
2021
                if (count($list) > 1) {
2022
                    $andstatus = true;
2023
                    foreach ($list as $condition) {
2024
                        $andstatus = $andstatus && $this->parse_prereq(
2025
                            $condition,
2026
                            $items,
2027
                            $refs_list,
2028
                            $user_id
2029
                        );
2030
2031
                        if (!$andstatus) {
2032
                            if ($debug) {
2033
                                error_log(
2034
                                    'New LP - One condition in AND was false, short-circuit',
2035
                                    0
2036
                                );
2037
                            }
2038
                            break;
2039
                        }
2040
                    }
2041
2042
                    if (empty($this->prereq_alert) && !$andstatus) {
2043
                        $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2044
                    }
2045
2046
                    return $andstatus;
2047
                } else {
2048
                    if (isset($items[$refs_list[$list[0]]])) {
2049
                        $status = $items[$refs_list[$list[0]]]->get_status(true);
2050
                        $returnstatus = ($status == $this->possible_status[2]) || ($status == $this->possible_status[3]);
2051
                        if (empty($this->prereq_alert) && !$returnstatus) {
2052
                            $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2053
                        }
2054
2055
                        return $returnstatus;
2056
                    }
2057
                    $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2058
2059
                    return false;
2060
                }
2061
            } else {
2062
                // No ORs found, now look for ANDs.
2063
                if ($debug) {
2064
                    error_log('New LP - Didnt find any AND, looking for =', 0);
2065
                }
2066
2067
                if (false !== strpos($prereqs_string, '=')) {
2068
                    if ($debug) {
2069
                        error_log('New LP - Found =, looking into it', 0);
2070
                    }
2071
                    // We assume '=' signs only appear when there's nothing else around.
2072
                    $params = explode('=', $prereqs_string);
2073
                    if (2 == count($params)) {
2074
                        // Right number of operands.
2075
                        if (isset($items[$refs_list[$params[0]]])) {
2076
                            $status = $items[$refs_list[$params[0]]]->get_status(true);
2077
                            $returnstatus = $status == $params[1];
2078
                            if (empty($this->prereq_alert) && !$returnstatus) {
2079
                                $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2080
                            }
2081
2082
                            return $returnstatus;
2083
                        }
2084
                        $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2085
2086
                        return false;
2087
                    }
2088
                } else {
2089
                    // No ANDs found, look for <>
2090
                    if ($debug) {
2091
                        error_log(
2092
                            'New LP - Didnt find any =, looking for <>',
2093
                            0
2094
                        );
2095
                    }
2096
2097
                    if (false !== strpos($prereqs_string, '<>')) {
2098
                        if ($debug) {
2099
                            error_log('New LP - Found <>, looking into it', 0);
2100
                        }
2101
                        // We assume '<>' signs only appear when there's nothing else around.
2102
                        $params = explode('<>', $prereqs_string);
2103
                        if (2 == count($params)) {
2104
                            // Right number of operands.
2105
                            if (isset($items[$refs_list[$params[0]]])) {
2106
                                $status = $items[$refs_list[$params[0]]]->get_status(true);
2107
                                $returnstatus = $status != $params[1];
2108
                                if (empty($this->prereq_alert) && !$returnstatus) {
2109
                                    $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2110
                                }
2111
2112
                                return $returnstatus;
2113
                            }
2114
                            $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2115
2116
                            return false;
2117
                        }
2118
                    } else {
2119
                        // No <> found, look for ~ (unary)
2120
                        if ($debug) {
2121
                            error_log(
2122
                                'New LP - Didnt find any =, looking for ~',
2123
                                0
2124
                            );
2125
                        }
2126
                        // Only remains: ~ and X*{}
2127
                        if (false !== strpos($prereqs_string, '~')) {
2128
                            // Found NOT.
2129
                            if ($debug) {
2130
                                error_log(
2131
                                    'New LP - Found ~, looking into it',
2132
                                    0
2133
                                );
2134
                            }
2135
                            $list = [];
2136
                            $myres = preg_match(
2137
                                '/~([^(\d+\*)\{]*)/',
2138
                                $prereqs_string,
2139
                                $list
2140
                            );
2141
                            if ($myres) {
2142
                                $returnstatus = !$this->parse_prereq(
2143
                                    $list[1],
2144
                                    $items,
2145
                                    $refs_list,
2146
                                    $user_id
2147
                                );
2148
                                if (empty($this->prereq_alert) && !$returnstatus) {
2149
                                    $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2150
                                }
2151
2152
                                return $returnstatus;
2153
                            } else {
2154
                                // Strange...
2155
                                if ($debug) {
2156
                                    error_log(
2157
                                        'New LP - Found ~ but strange string: '.$prereqs_string,
2158
                                        0
2159
                                    );
2160
                                }
2161
                            }
2162
                        } else {
2163
                            // Finally, look for sets/groups
2164
                            if ($debug) {
2165
                                error_log(
2166
                                    'New LP - Didnt find any ~, looking for groups',
2167
                                    0
2168
                                );
2169
                            }
2170
                            // Only groups here.
2171
                            $groups = [];
2172
                            $groups_there = preg_match_all(
2173
                                '/((\d+\*)?\{([^\}]+)\}+)/',
2174
                                $prereqs_string,
2175
                                $groups
2176
                            );
2177
2178
                            if ($groups_there) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $groups_there of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2179
                                foreach ($groups[1] as $gr) {
2180
                                    // Only take the results that correspond to
2181
                                    //  the big brackets-enclosed condition.
2182
                                    if ($debug) {
2183
                                        error_log(
2184
                                            'New LP - Dealing with group '.$gr,
2185
                                            0
2186
                                        );
2187
                                    }
2188
                                    $multi = [];
2189
                                    $mycond = false;
2190
                                    if (preg_match(
2191
                                        '/(\d+)\*\{([^\}]+)\}/',
2192
                                        $gr,
2193
                                        $multi
2194
                                    )
2195
                                    ) {
2196
                                        if ($debug) {
2197
                                            error_log(
2198
                                                'New LP - Found multiplier '.$multi[0],
2199
                                                0
2200
                                            );
2201
                                        }
2202
                                        $count = $multi[1];
2203
                                        $list = explode(',', $multi[2]);
2204
                                        $mytrue = 0;
2205
                                        foreach ($list as $cond) {
2206
                                            if (isset($items[$refs_list[$cond]])) {
2207
                                                $status = $items[$refs_list[$cond]]->get_status(true);
2208
                                                if ($status == $this->possible_status[2] ||
2209
                                                    $status == $this->possible_status[3]
2210
                                                ) {
2211
                                                    $mytrue++;
2212
                                                    if ($debug) {
2213
                                                        error_log(
2214
                                                            'New LP - Found true item, counting.. ('.($mytrue).')',
2215
                                                            0
2216
                                                        );
2217
                                                    }
2218
                                                }
2219
                                            } else {
2220
                                                if ($debug) {
2221
                                                    error_log(
2222
                                                        'New LP - item '.$cond.' does not exist in items list',
2223
                                                        0
2224
                                                    );
2225
                                                }
2226
                                            }
2227
                                        }
2228
                                        if ($mytrue >= $count) {
2229
                                            if ($debug) {
2230
                                                error_log(
2231
                                                    'New LP - Got enough true results, return true',
2232
                                                    0
2233
                                                );
2234
                                            }
2235
                                            $mycond = true;
2236
                                        } else {
2237
                                            if ($debug) {
2238
                                                error_log(
2239
                                                    'New LP - Not enough true results',
2240
                                                    0
2241
                                                );
2242
                                            }
2243
                                        }
2244
                                    } else {
2245
                                        if ($debug) {
2246
                                            error_log(
2247
                                                'New LP - No multiplier',
2248
                                                0
2249
                                            );
2250
                                        }
2251
                                        $list = explode(',', $gr);
2252
                                        $mycond = true;
2253
                                        foreach ($list as $cond) {
2254
                                            if (isset($items[$refs_list[$cond]])) {
2255
                                                $status = $items[$refs_list[$cond]]->get_status(true);
2256
                                                if ($status == $this->possible_status[2] ||
2257
                                                    $status == $this->possible_status[3]
2258
                                                ) {
2259
                                                    $mycond = true;
2260
                                                    if ($debug) {
2261
                                                        error_log(
2262
                                                            'New LP - Found true item',
2263
                                                            0
2264
                                                        );
2265
                                                    }
2266
                                                } else {
2267
                                                    if ($debug) {
2268
                                                        error_log(
2269
                                                            'New LP - '.
2270
                                                            ' Found false item, the set is not true, return false',
2271
                                                            0
2272
                                                        );
2273
                                                    }
2274
                                                    $mycond = false;
2275
                                                    break;
2276
                                                }
2277
                                            } else {
2278
                                                if ($debug) {
2279
                                                    error_log(
2280
                                                        'New LP - item '.$cond.' does not exist in items list',
2281
                                                        0
2282
                                                    );
2283
                                                }
2284
                                                if ($debug) {
2285
                                                    error_log(
2286
                                                        'New LP - Found false item, the set is not true, return false',
2287
                                                        0
2288
                                                    );
2289
                                                }
2290
                                                $mycond = false;
2291
                                                break;
2292
                                            }
2293
                                        }
2294
                                    }
2295
                                    if (!$mycond && empty($this->prereq_alert)) {
2296
                                        $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2297
                                    }
2298
2299
                                    return $mycond;
2300
                                }
2301
                            } else {
2302
                                // Nothing found there either. Now return the
2303
                                // value of the corresponding resource completion status.
2304
                                if (isset($refs_list[$prereqs_string]) &&
2305
                                    isset($items[$refs_list[$prereqs_string]])
2306
                                ) {
2307
                                    /** @var learnpathItem $itemToCheck */
2308
                                    $itemToCheck = $items[$refs_list[$prereqs_string]];
2309
2310
                                    if ('quiz' === $itemToCheck->type) {
2311
                                        // 1. Checking the status in current items.
2312
                                        $status = $itemToCheck->get_status(true);
2313
                                        $returnstatus = $status == $this->possible_status[2] || $status == $this->possible_status[3];
2314
2315
                                        if (!$returnstatus) {
2316
                                            $explanation = sprintf(
2317
                                                get_lang('Item %s blocks this step'),
2318
                                                $itemToCheck->get_title()
2319
                                            );
2320
                                            $this->prereq_alert = $explanation;
2321
                                        }
2322
2323
                                        // For one and first attempt.
2324
                                        if (1 == $this->prevent_reinit) {
2325
                                            // 2. If is completed we check the results in the DB of the quiz.
2326
                                            if ($returnstatus) {
2327
                                                $sql = 'SELECT score, max_score
2328
                                                        FROM '.Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES).'
2329
                                                        WHERE
2330
                                                            exe_exo_id = '.$items[$refs_list[$prereqs_string]]->path.' AND
2331
                                                            exe_user_id = '.$user_id.' AND
2332
                                                            orig_lp_id = '.$this->lp_id.' AND
2333
                                                            orig_lp_item_id = '.$prereqs_string.' AND
2334
                                                            status <> "incomplete" AND
2335
                                                            c_id = '.$courseId.'
2336
                                                        ORDER BY exe_date DESC
2337
                                                        LIMIT 0, 1';
2338
                                                $rs_quiz = Database::query($sql);
2339
                                                if ($quiz = Database::fetch_array($rs_quiz)) {
2340
                                                    /** @var learnpathItem $myItemToCheck */
2341
                                                    $myItemToCheck = $items[$refs_list[$this->get_id()]];
2342
                                                    $minScore = $myItemToCheck->getPrerequisiteMinScore();
2343
                                                    $maxScore = $myItemToCheck->getPrerequisiteMaxScore();
2344
2345
                                                    if (isset($minScore) && isset($minScore)) {
2346
                                                        // Taking min/max prerequisites values see BT#5776
2347
                                                        if ($quiz['score'] >= $minScore &&
2348
                                                            $quiz['score'] <= $maxScore
2349
                                                        ) {
2350
                                                            $returnstatus = true;
2351
                                                        } else {
2352
                                                            $explanation = sprintf(
2353
                                                                get_lang('Your result at %s blocks this step'),
2354
                                                                $itemToCheck->get_title()
2355
                                                            );
2356
                                                            $this->prereq_alert = $explanation;
2357
                                                            $returnstatus = false;
2358
                                                        }
2359
                                                    } else {
2360
                                                        // Classic way
2361
                                                        if ($quiz['score'] >=
2362
                                                            $items[$refs_list[$prereqs_string]]->get_mastery_score()
2363
                                                        ) {
2364
                                                            $returnstatus = true;
2365
                                                        } else {
2366
                                                            $explanation = sprintf(
2367
                                                                get_lang('Your result at %s blocks this step'),
2368
                                                                $itemToCheck->get_title()
2369
                                                            );
2370
                                                            $this->prereq_alert = $explanation;
2371
                                                            $returnstatus = false;
2372
                                                        }
2373
                                                    }
2374
                                                } else {
2375
                                                    $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2376
                                                    $returnstatus = false;
2377
                                                }
2378
                                            }
2379
                                        } else {
2380
                                            // 3. For multiple attempts we check that there are minimum 1 item completed
2381
                                            // Checking in the database.
2382
                                            $sql = 'SELECT score, max_score
2383
                                                    FROM '.Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES).'
2384
                                                    WHERE
2385
                                                        c_id = '.$courseId.' AND
2386
                                                        exe_exo_id = '.$items[$refs_list[$prereqs_string]]->path.' AND
2387
                                                        exe_user_id = '.$user_id.' AND
2388
                                                        orig_lp_id = '.$this->lp_id.' AND
2389
                                                        orig_lp_item_id = '.$prereqs_string;
2390
2391
                                            $rs_quiz = Database::query($sql);
2392
                                            if (Database::num_rows($rs_quiz) > 0) {
2393
                                                while ($quiz = Database::fetch_array($rs_quiz)) {
2394
                                                    /** @var learnpathItem $myItemToCheck */
2395
                                                    $myItemToCheck = $items[$refs_list[$this->get_id()]];
2396
                                                    $minScore = $myItemToCheck->getPrerequisiteMinScore();
2397
                                                    $maxScore = $myItemToCheck->getPrerequisiteMaxScore();
2398
2399
                                                    if (empty($minScore)) {
2400
                                                        // Try with mastery_score
2401
                                                        $masteryScoreAsMin = $myItemToCheck->get_mastery_score();
2402
                                                        if (!empty($masteryScoreAsMin)) {
2403
                                                            $minScore = $masteryScoreAsMin;
2404
                                                        }
2405
                                                    }
2406
2407
                                                    if (isset($minScore) && isset($minScore)) {
2408
                                                        // Taking min/max prerequisites values see BT#5776
2409
                                                        if ($quiz['score'] >= $minScore && $quiz['score'] <= $maxScore) {
2410
                                                            $returnstatus = true;
2411
                                                            break;
2412
                                                        } else {
2413
                                                            $explanation = sprintf(
2414
                                                                get_lang('Your result at %s blocks this step'),
2415
                                                                $itemToCheck->get_title()
2416
                                                            );
2417
                                                            $this->prereq_alert = $explanation;
2418
                                                            $returnstatus = false;
2419
                                                        }
2420
                                                    } else {
2421
                                                        if ($quiz['score'] >=
2422
                                                            $items[$refs_list[$prereqs_string]]->get_mastery_score()
2423
                                                        ) {
2424
                                                            $returnstatus = true;
2425
                                                            break;
2426
                                                        } else {
2427
                                                            $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2428
                                                            $returnstatus = false;
2429
                                                        }
2430
                                                    }
2431
                                                }
2432
                                            } else {
2433
                                                $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2434
                                                $returnstatus = false;
2435
                                            }
2436
                                        }
2437
2438
                                        if (false === $returnstatus) {
2439
                                            // Check results from another sessions.
2440
                                            $checkOtherSessions = ('true' === api_get_setting('lp.validate_lp_prerequisite_from_other_session'));
2441
                                            if ($checkOtherSessions) {
2442
                                                $returnstatus = $this->getStatusFromOtherSessions(
2443
                                                    $user_id,
2444
                                                    $prereqs_string,
2445
                                                    $refs_list
2446
                                                );
2447
                                            }
2448
                                        }
2449
2450
                                        return $returnstatus;
2451
                                    } elseif ('student_publication' === $itemToCheck->type) {
2452
                                        $workId = $items[$refs_list[$prereqs_string]]->path;
2453
                                        $count = get_work_count_by_student($user_id, $workId);
2454
                                        if ($count >= 1) {
2455
                                            $returnstatus = true;
2456
                                        } else {
2457
                                            $returnstatus = false;
2458
                                            $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2459
                                        }
2460
2461
                                        return $returnstatus;
2462
                                    } else {
2463
                                        $status = $itemToCheck->get_status(true);
2464
                                        if (self::DEBUG) {
2465
                                            error_log('Status:'.$status);
2466
                                        }
2467
                                        $returnstatus = $status == $this->possible_status[2] || $status == $this->possible_status[3];
2468
2469
                                        // Check results from another sessions.
2470
                                        $checkOtherSessions = ('true' === api_get_setting('lp.validate_lp_prerequisite_from_other_session'));
2471
                                        if ($checkOtherSessions && !$returnstatus) {
2472
                                            $returnstatus = $this->getStatusFromOtherSessions(
2473
                                                $user_id,
2474
                                                $prereqs_string,
2475
                                                $refs_list
2476
                                            );
2477
                                        }
2478
2479
                                        if (!$returnstatus) {
2480
                                            $explanation = sprintf(
2481
                                                get_lang('Item %s blocks this step'),
2482
                                                $itemToCheck->get_title()
2483
                                            );
2484
                                            $this->prereq_alert = $explanation;
2485
                                        }
2486
2487
                                        $lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW);
2488
                                        $lp_view = Database::get_course_table(TABLE_LP_VIEW);
2489
2490
                                        if ($returnstatus && 1 == $this->prevent_reinit) {
2491
                                            $sql = "SELECT iid FROM $lp_view
2492
                                                    WHERE
2493
                                                        c_id = $courseId AND
2494
                                                        user_id = $user_id  AND
2495
                                                        lp_id = $this->lp_id AND
2496
                                                        session_id = $sessionId
2497
                                                    LIMIT 0, 1";
2498
                                            $rs_lp = Database::query($sql);
2499
                                            if (Database::num_rows($rs_lp)) {
2500
                                                $lp_id = Database::fetch_row($rs_lp);
2501
                                                $my_lp_id = $lp_id[0];
2502
2503
                                                $sql = "SELECT status FROM $lp_item_view
2504
                                                        WHERE
2505
                                                            lp_view_id = $my_lp_id AND
2506
                                                            lp_item_id = $refs_list[$prereqs_string]
2507
                                                        LIMIT 0, 1";
2508
                                                $rs_lp = Database::query($sql);
2509
                                                $status_array = Database::fetch_row($rs_lp);
2510
                                                $status = $status_array[0];
2511
2512
                                                $returnstatus = $status == $this->possible_status[2] || $status == $this->possible_status[3];
2513
                                                if (!$returnstatus && empty($this->prereq_alert)) {
2514
                                                    $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2515
                                                }
2516
                                            }
2517
2518
                                            if ($checkOtherSessions && false === $returnstatus) {
2519
                                                $returnstatus = $returnstatus = $this->getStatusFromOtherSessions(
2520
                                                    $user_id,
2521
                                                    $prereqs_string,
2522
                                                    $refs_list
2523
                                                );
2524
                                            }
2525
                                        }
2526
2527
                                        return $returnstatus;
2528
                                    }
2529
                                }
2530
                            }
2531
                        }
2532
                    }
2533
                }
2534
            }
2535
        } else {
2536
            $list = explode("\|", $prereqs_string);
2537
            if (count($list) > 1) {
2538
                if (self::DEBUG > 1) {
2539
                    error_log('New LP - Found OR, looking into it', 0);
2540
                }
2541
                $orstatus = false;
2542
                foreach ($list as $condition) {
2543
                    if (self::DEBUG) {
2544
                        error_log(
2545
                            'New LP - Found OR, adding it ('.$condition.')',
2546
                            0
2547
                        );
2548
                    }
2549
                    $orstatus = $orstatus || $this->parse_prereq(
2550
                        $condition,
2551
                        $items,
2552
                        $refs_list,
2553
                        $user_id
2554
                    );
2555
                    if ($orstatus) {
2556
                        // Shortcircuit OR.
2557
                        if (self::DEBUG > 1) {
2558
                            error_log(
2559
                                'New LP - One condition in OR was true, short-circuit',
2560
                                0
2561
                            );
2562
                        }
2563
                        break;
2564
                    }
2565
                }
2566
                if (!$orstatus && empty($this->prereq_alert)) {
2567
                    $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2568
                }
2569
2570
                return $orstatus;
2571
            } else {
2572
                if (self::DEBUG > 1) {
2573
                    error_log(
2574
                        'New LP - OR was found but only one elem present !?',
2575
                        0
2576
                    );
2577
                }
2578
                if (isset($items[$refs_list[$list[0]]])) {
2579
                    $status = $items[$refs_list[$list[0]]]->get_status(true);
2580
                    $returnstatus = 'completed' == $status || 'passed' == $status;
2581
                    if (!$returnstatus && empty($this->prereq_alert)) {
2582
                        $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2583
                    }
2584
2585
                    return $returnstatus;
2586
                }
2587
            }
2588
        }
2589
        if (empty($this->prereq_alert)) {
2590
            $this->prereq_alert = get_lang('This learning object cannot display because the course prerequisites are not completed. This happens when a course imposes that you follow it step by step or get a minimum score in tests before you reach the next steps.');
2591
        }
2592
2593
        if (self::DEBUG > 1) {
2594
            error_log(
2595
                'New LP - End of parse_prereq. Error code is now '.$this->prereq_alert,
2596
                0
2597
            );
2598
        }
2599
2600
        return false;
2601
    }
2602
2603
    /**
2604
     * Reinits all local values as the learnpath is restarted.
2605
     *
2606
     * @return bool True on success, false otherwise
2607
     */
2608
    public function restart()
2609
    {
2610
        if (self::DEBUG > 0) {
2611
            error_log('learnpathItem::restart()', 0);
2612
        }
2613
        $seriousGame = $this->get_seriousgame_mode();
0 ignored issues
show
Deprecated Code introduced by
The function learnpathItem::get_seriousgame_mode() has been deprecated: seriousgame_mode seems not to be used ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

2613
        $seriousGame = /** @scrutinizer ignore-deprecated */ $this->get_seriousgame_mode();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
2614
        //For serious game  : We reuse same attempt_id
2615
        if (1 == $seriousGame && 'sco' == $this->type) {
2616
            // If this is a sco, Chamilo can't update the time without an
2617
            //  explicit scorm call
2618
            $this->current_start_time = 0;
2619
            $this->current_stop_time = 0; //Those 0 value have this effect
2620
            $this->last_scorm_session_time = 0;
2621
            $this->save();
2622
2623
            return true;
2624
        }
2625
2626
        $this->save();
2627
2628
        $allowed = $this->isRestartAllowed();
2629
        if (-1 === $allowed) {
2630
            // Nothing allowed, do nothing.
2631
        } elseif (1 === $allowed) {
2632
            // Restart as new attempt is allowed, record a new attempt.
2633
            $this->attempt_id = $this->attempt_id + 1; // Simply reuse the previous attempt_id.
2634
            $this->current_score = 0;
2635
            $this->current_start_time = 0;
2636
            $this->current_stop_time = 0;
2637
            $this->current_data = '';
2638
            $this->status = $this->possible_status[0];
2639
            $this->interactions_count = 0;
2640
            $this->interactions = [];
2641
            $this->objectives_count = 0;
2642
            $this->objectives = [];
2643
            $this->lesson_location = '';
2644
            if (TOOL_QUIZ != $this->type) {
2645
                $this->write_to_db();
2646
            }
2647
        } else {
2648
            // Restart current element is allowed (because it's not finished yet),
2649
            // reinit current.
2650
            //$this->current_score = 0;
2651
            $this->current_start_time = 0;
2652
            $this->current_stop_time = 0;
2653
            $this->interactions_count = $this->get_interactions_count(true);
2654
        }
2655
2656
        return true;
2657
    }
2658
2659
    /**
2660
     * Saves data in the database.
2661
     *
2662
     * @param bool $from_outside     Save from URL params (1) or from object attributes (0)
2663
     * @param bool $prereqs_complete The results of a check on prerequisites for this item.
2664
     *                               True if prerequisites are completed, false otherwise. Defaults to false. Only used if not sco or au
2665
     *
2666
     * @return bool True on success, false on failure
2667
     */
2668
    public function save($from_outside = true, $prereqs_complete = false)
2669
    {
2670
        $debug = self::DEBUG;
2671
        if ($debug) {
2672
            error_log('learnpathItem::save()', 0);
2673
        }
2674
        // First check if parameters passed via GET can be saved here
2675
        // in case it's a SCORM, we should get:
2676
        if ('sco' === $this->type || 'au' === $this->type) {
2677
            $status = $this->get_status(true);
2678
            if (1 == $this->prevent_reinit &&
2679
                $status != $this->possible_status[0] && // not attempted
2680
                $status != $this->possible_status[1]    //incomplete
2681
            ) {
2682
                if ($debug) {
2683
                    error_log(
2684
                        'learnpathItem::save() - save reinit blocked by setting',
2685
                        0
2686
                    );
2687
                }
2688
                // Do nothing because the status has already been set. Don't allow it to change.
2689
                // TODO: Check there isn't a special circumstance where this should be saved.
2690
            } else {
2691
                if ($debug) {
2692
                    error_log(
2693
                        'learnpathItem::save() - SCORM save request received',
2694
                        0
2695
                    );
2696
                }
2697
2698
                if ($from_outside) {
2699
                    if ($debug) {
2700
                        error_log(
2701
                            'learnpathItem::save() - Getting item data from outside',
2702
                            0
2703
                        );
2704
                    }
2705
                    foreach ($_GET as $param => $value) {
2706
                        switch ($param) {
2707
                            case 'score':
2708
                                $this->set_score($value);
2709
                                if ($debug) {
2710
                                    error_log(
2711
                                        'learnpathItem::save() - setting score to '.$value,
2712
                                        0
2713
                                    );
2714
                                }
2715
                                break;
2716
                            case 'max':
2717
                                $this->set_max_score($value);
2718
                                if ($debug) {
2719
                                    error_log(
2720
                                        'learnpathItem::save() - setting view_max_score to '.$value,
2721
                                        0
2722
                                    );
2723
                                }
2724
                                break;
2725
                            case 'min':
2726
                                $this->min_score = $value;
2727
                                if ($debug) {
2728
                                    error_log(
2729
                                        'learnpathItem::save() - setting min_score to '.$value,
2730
                                        0
2731
                                    );
2732
                                }
2733
                                break;
2734
                            case 'lesson_status':
2735
                                if (!empty($value)) {
2736
                                    $this->set_status($value);
2737
                                    if ($debug) {
2738
                                        error_log(
2739
                                            'learnpathItem::save() - setting status to '.$value,
2740
                                            0
2741
                                        );
2742
                                    }
2743
                                }
2744
                                break;
2745
                            case 'time':
2746
                                $this->set_time($value);
2747
                                if ($debug) {
2748
                                    error_log(
2749
                                        'learnpathItem::save() - setting time to '.$value,
2750
                                        0
2751
                                    );
2752
                                }
2753
                                break;
2754
                            case 'suspend_data':
2755
                                $this->current_data = $value;
2756
                                if ($debug) {
2757
                                    error_log(
2758
                                        'learnpathItem::save() - setting suspend_data to '.$value,
2759
                                        0
2760
                                    );
2761
                                }
2762
                                break;
2763
                            case 'lesson_location':
2764
                                $this->set_lesson_location($value);
2765
                                if ($debug) {
2766
                                    error_log(
2767
                                        'learnpathItem::save() - setting lesson_location to '.$value,
2768
                                        0
2769
                                    );
2770
                                }
2771
                                break;
2772
                            case 'core_exit':
2773
                                $this->set_core_exit($value);
2774
                                if ($debug) {
2775
                                    error_log(
2776
                                        'learnpathItem::save() - setting core_exit to '.$value,
2777
                                        0
2778
                                    );
2779
                                }
2780
                                break;
2781
                            case 'interactions':
2782
                                break;
2783
                            case 'objectives':
2784
                                break;
2785
                            default:
2786
                                // Ignore.
2787
                                break;
2788
                        }
2789
                    }
2790
                } else {
2791
                    // Do nothing, just let the local attributes be used.
2792
                    if ($debug) {
2793
                        error_log(
2794
                            'learnpathItem::save() - Using inside item status',
2795
                            0
2796
                        );
2797
                    }
2798
                }
2799
            }
2800
        } else {
2801
            // If not SCO, such messages should not be expected.
2802
            $type = strtolower($this->type);
2803
            if ($debug) {
2804
                error_log("type: $type");
2805
            }
2806
2807
            switch ($type) {
2808
                case 'asset':
2809
                    if ($prereqs_complete) {
2810
                        $this->set_status($this->possible_status[2]);
2811
                    }
2812
                    break;
2813
                case TOOL_HOTPOTATOES:
2814
                    break;
2815
                case TOOL_QUIZ:
2816
                    return false;
2817
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2818
                default:
2819
                    // For now, everything that is not sco and not asset is set to
2820
                    // completed when saved.
2821
                    if ($prereqs_complete) {
2822
                        $this->set_status($this->possible_status[2]);
2823
                    }
2824
                    break;
2825
            }
2826
        }
2827
2828
        if ($debug) {
2829
            error_log('End of learnpathItem::save() - Calling write_to_db() now');
2830
        }
2831
2832
        return $this->write_to_db();
2833
    }
2834
2835
    /**
2836
     * Sets the number of attempt_id to a given value.
2837
     *
2838
     * @param int $num The given value to set attempt_id to
2839
     *
2840
     * @return bool TRUE on success, FALSE otherwise
2841
     */
2842
    public function set_attempt_id($num)
2843
    {
2844
        if ($num == strval(intval($num)) && $num >= 0) {
2845
            $this->attempt_id = $num;
2846
2847
            return true;
2848
        }
2849
2850
        return false;
2851
    }
2852
2853
    /**
2854
     * Sets the core_exit value to the one given.
2855
     *
2856
     * @return bool $value  True (always)
2857
     */
2858
    public function set_core_exit($value)
2859
    {
2860
        switch ($value) {
2861
            case '':
2862
                $this->core_exit = '';
2863
                break;
2864
            case 'suspend':
2865
                $this->core_exit = 'suspend';
2866
                break;
2867
            default:
2868
                $this->core_exit = 'none';
2869
                break;
2870
        }
2871
2872
        return true;
2873
    }
2874
2875
    /**
2876
     * Sets the item's description.
2877
     *
2878
     * @param string $string Description
2879
     */
2880
    public function set_description($string = '')
2881
    {
2882
        if (!empty($string)) {
2883
            $this->description = $string;
2884
        }
2885
    }
2886
2887
    /**
2888
     * Sets the lesson_location value.
2889
     *
2890
     * @param string $location lesson_location as provided by the SCO
2891
     *
2892
     * @return bool True on success, false otherwise
2893
     */
2894
    public function set_lesson_location($location)
2895
    {
2896
        if (isset($location)) {
2897
            $this->lesson_location = $location;
2898
2899
            return true;
2900
        }
2901
2902
        return false;
2903
    }
2904
2905
    /**
2906
     * Sets the item's depth level in the LP tree (0 is at root).
2907
     *
2908
     * @param int $int Level
2909
     */
2910
    public function set_level($int = 0)
2911
    {
2912
        $this->level = (int) $int;
2913
    }
2914
2915
    /**
2916
     * Sets the lp_view id this item view is registered to.
2917
     *
2918
     * @param int $lp_view_id lp_view DB ID
2919
     *
2920
     * @todo //todo insert into lp_item_view if lp_view not exists
2921
     */
2922
    public function set_lp_view(int $lp_view_id): bool
2923
    {
2924
        $lpItemId = $this->get_id();
2925
2926
        if (empty($lpItemId)) {
2927
            return false;
2928
        }
2929
2930
        if (empty($lp_view_id)) {
2931
            return false;
2932
        }
2933
2934
        if (self::DEBUG > 0) {
2935
            error_log('learnpathItem::set_lp_view('.$lp_view_id.')', 0);
2936
        }
2937
2938
        $this->view_id = $lp_view_id;
2939
2940
        $item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
2941
        // Get the lp_item_view with the highest view_count.
2942
        $sql = "SELECT * FROM $item_view_table
2943
                WHERE
2944
                    lp_item_id = $lpItemId AND
2945
                    lp_view_id = $lp_view_id
2946
                ORDER BY view_count DESC";
2947
2948
        if (self::DEBUG > 2) {
2949
            error_log(
2950
                'learnpathItem::set_lp_view() - Querying lp_item_view: '.$sql,
2951
                0
2952
            );
2953
        }
2954
        $res = Database::query($sql);
2955
        if (Database::num_rows($res) > 0) {
2956
            $row = Database::fetch_array($res);
2957
            $this->db_item_view_id = $row['iid'];
2958
            $this->attempt_id = $row['view_count'];
2959
            $this->current_score = $row['score'];
2960
            $this->current_data = $row['suspend_data'];
2961
            $this->view_max_score = $row['max_score'];
2962
            $this->status = $row['status'];
2963
            $this->current_start_time = $row['start_time'];
2964
            $this->current_stop_time = $this->current_start_time + $row['total_time'];
2965
            $this->lesson_location = $row['lesson_location'];
2966
            $this->core_exit = $row['core_exit'];
2967
2968
            if (self::DEBUG > 2) {
2969
                error_log(
2970
                    'learnpathItem::set_lp_view() - Updated item object with database values',
2971
                    0
2972
                );
2973
            }
2974
2975
            // Now get the number of interactions for this little guy.
2976
            $table = Database::get_course_table(TABLE_LP_IV_INTERACTION);
2977
            $sql = "SELECT * FROM $table
2978
                    WHERE lp_iv_id = ".$this->db_item_view_id;
2979
2980
            $res = Database::query($sql);
2981
            if (false !== $res) {
2982
                $this->interactions_count = Database::num_rows($res);
2983
            } else {
2984
                $this->interactions_count = 0;
2985
            }
2986
            // Now get the number of objectives for this little guy.
2987
            $table = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
2988
            $sql = "SELECT * FROM $table
2989
                    WHERE lp_iv_id = ".$this->db_item_view_id;
2990
2991
            $this->objectives_count = 0;
2992
            $res = Database::query($sql);
2993
            if (false !== $res) {
2994
                $this->objectives_count = Database::num_rows($res);
2995
            }
2996
        }
2997
2998
        return true;
2999
    }
3000
3001
    /**
3002
     * Sets the path.
3003
     *
3004
     * @param string $string Path
3005
     */
3006
    public function set_path($string = '')
3007
    {
3008
        if (!empty($string)) {
3009
            $this->path = $string;
3010
        }
3011
    }
3012
3013
    /**
3014
     * Sets the prevent_reinit attribute.
3015
     * This is based on the LP value and is set at creation time for
3016
     * each learnpathItem. It is a (bad?) way of avoiding
3017
     * a reference to the LP when saving an item.
3018
     *
3019
     * @param int 1 for "prevent", 0 for "don't prevent"
0 ignored issues
show
Documentation Bug introduced by
The doc comment 1 at position 0 could not be parsed: Unknown type name '1' at position 0 in 1.
Loading history...
3020
     * saving freshened values (new "not attempted" status etc)
3021
     */
3022
    public function set_prevent_reinit($prevent)
3023
    {
3024
        $this->prevent_reinit = 0;
3025
        if ($prevent) {
3026
            $this->prevent_reinit = 1;
3027
        }
3028
    }
3029
3030
    /**
3031
     * Sets the score value. If the mastery_score is set and the score reaches
3032
     * it, then set the status to 'passed'.
3033
     *
3034
     * @param float $score Score
3035
     *
3036
     * @return bool True on success, false otherwise
3037
     */
3038
    public function set_score($score)
3039
    {
3040
        $debug = self::DEBUG;
3041
        if ($debug > 0) {
3042
            error_log('learnpathItem::set_score('.$score.')', 0);
3043
        }
3044
        if (($this->max_score <= 0 || $score <= $this->max_score) && ($score >= $this->min_score)) {
3045
            $this->current_score = $score;
3046
            $masteryScore = $this->get_mastery_score();
3047
            $current_status = $this->get_status(false);
3048
3049
            // Fixes bug when SCORM doesn't send a mastery score even if they sent a score!
3050
            if (-1 == $masteryScore) {
3051
                $masteryScore = $this->max_score;
3052
            }
3053
3054
            if ($debug > 0) {
3055
                error_log('get_mastery_score: '.$masteryScore);
3056
                error_log('current_status: '.$current_status);
3057
                error_log('current score : '.$this->current_score);
3058
            }
3059
3060
            // If mastery_score is set AND the current score reaches the mastery
3061
            //  score AND the current status is different from 'completed', then
3062
            //  set it to 'passed'.
3063
            /*
3064
            if ($master != -1 && $this->current_score >= $master && $current_status != $this->possible_status[2]) {
3065
                if ($debug > 0) error_log('Status changed to: '.$this->possible_status[3]);
3066
                $this->set_status($this->possible_status[3]); //passed
3067
            } elseif ($master != -1 && $this->current_score < $master) {
3068
                if ($debug > 0) error_log('Status changed to: '.$this->possible_status[4]);
3069
                $this->set_status($this->possible_status[4]); //failed
3070
            }*/
3071
            return true;
3072
        }
3073
3074
        return false;
3075
    }
3076
3077
    /**
3078
     * Sets the maximum score for this item.
3079
     *
3080
     * @param int $score Maximum score - must be a decimal or an empty string
3081
     *
3082
     * @return bool True on success, false on error
3083
     */
3084
    public function set_max_score($score)
3085
    {
3086
        if (is_int($score) || '' == $score) {
3087
            $this->view_max_score = $score;
3088
3089
            return true;
3090
        }
3091
3092
        return false;
3093
    }
3094
3095
    /**
3096
     * Sets the status for this item.
3097
     *
3098
     * @param string $status Status - must be one of the values defined in $this->possible_status
3099
     *                       (this affects the status setting)
3100
     *
3101
     * @return bool True on success, false on error
3102
     */
3103
    public function set_status($status)
3104
    {
3105
        if (self::DEBUG) {
3106
            error_log('learnpathItem::set_status('.$status.')');
3107
        }
3108
3109
        $found = false;
3110
        foreach ($this->possible_status as $possible) {
3111
            if (preg_match('/^'.$possible.'$/i', $status)) {
3112
                $found = true;
3113
            }
3114
        }
3115
3116
        if ($found) {
3117
            $this->status = $status;
3118
            if (self::DEBUG) {
3119
                error_log(
3120
                    'learnpathItem::set_status() - '.
3121
                        'Updated object status of item '.$this->db_id.
3122
                    ' to '.$this->status
3123
                );
3124
            }
3125
3126
            return true;
3127
        }
3128
3129
        if (self::DEBUG) {
3130
            error_log('Setting status: '.$this->possible_status[0]);
3131
        }
3132
        $this->status = $this->possible_status[0];
3133
3134
        return false;
3135
    }
3136
3137
    /**
3138
     * Set the (indexing) terms for this learnpath item.
3139
     *
3140
     * @param string $terms Terms, as a comma-split list
3141
     *
3142
     * @return bool Always return true
3143
     */
3144
    public function set_terms($terms)
3145
    {
3146
        global $charset;
3147
        $lp_item = Database::get_course_table(TABLE_LP_ITEM);
3148
        $a_terms = preg_split('/,/', $terms);
3149
        $i_terms = preg_split('/,/', $this->get_terms());
3150
        foreach ($i_terms as $term) {
3151
            if (!in_array($term, $a_terms)) {
3152
                array_push($a_terms, $term);
3153
            }
3154
        }
3155
        $new_terms = $a_terms;
3156
        $new_terms_string = implode(',', $new_terms);
3157
3158
        // TODO: Validate csv string.
3159
        $terms = Database::escape_string(api_htmlentities($new_terms_string, ENT_QUOTES));
3160
        $sql = "UPDATE $lp_item
3161
                SET terms = '$terms'
3162
                WHERE iid=".$this->get_id();
3163
        Database::query($sql);
3164
        // Save it to search engine.
3165
        if ('true' == api_get_setting('search_enabled')) {
3166
            $di = new ChamiloIndexer();
3167
            $di->update_terms($this->get_search_did(), $new_terms, 'T');
3168
        }
3169
3170
        return true;
3171
    }
3172
3173
    /**
3174
     * Get the document ID from inside the text index database.
3175
     *
3176
     * @return int Search index database document ID
3177
     */
3178
    public function get_search_did()
3179
    {
3180
        return $this->search_did;
3181
    }
3182
3183
    /**
3184
     * Sets the item viewing time in a usable form, given that SCORM packages
3185
     * often give it as 00:00:00.0000.
3186
     *
3187
     * @param    string    Time as given by SCORM
3188
     * @param string $format
3189
     */
3190
    public function set_time($scorm_time, $format = 'scorm')
3191
    {
3192
        $debug = self::DEBUG;
3193
        if ($debug) {
3194
            error_log("learnpathItem::set_time($scorm_time, $format)");
3195
            error_log("this->type: ".$this->type);
3196
            error_log("this->current_start_time: ".$this->current_start_time);
3197
        }
3198
3199
        if ('0' == $scorm_time &&
3200
            'sco' !== $this->type &&
3201
            0 != $this->current_start_time
3202
        ) {
3203
            $myTime = time() - $this->current_start_time;
3204
            if ($myTime > 0) {
3205
                $this->update_time($myTime);
3206
                if ($debug) {
3207
                    error_log('found asset - set time to '.$myTime);
3208
                }
3209
            } else {
3210
                if ($debug) {
3211
                    error_log('Time not set');
3212
                }
3213
            }
3214
        } else {
3215
            switch ($format) {
3216
                case 'scorm':
3217
                    $res = [];
3218
                    if (preg_match(
3219
                        '/^(\d{1,4}):(\d{2}):(\d{2})(\.\d{1,4})?/',
3220
                        $scorm_time,
3221
                        $res
3222
                    )
3223
                    ) {
3224
                        $hour = $res[1];
3225
                        $min = $res[2];
3226
                        $sec = $res[3];
3227
                        // Getting total number of seconds spent.
3228
                        $totalSec = $hour * 3600 + $min * 60 + $sec;
3229
                        if ($debug) {
3230
                            error_log("totalSec : $totalSec");
3231
                            error_log("Now calling to scorm_update_time()");
3232
                        }
3233
                        $this->scorm_update_time($totalSec);
3234
                    }
3235
                    break;
3236
                case 'int':
3237
                    if ($debug) {
3238
                        error_log("scorm_time = $scorm_time");
3239
                        error_log("Now calling to scorm_update_time()");
3240
                    }
3241
                    $this->scorm_update_time($scorm_time);
3242
                    break;
3243
            }
3244
        }
3245
    }
3246
3247
    /**
3248
     * Sets the item's title.
3249
     *
3250
     * @param string $string Title
3251
     */
3252
    public function set_title($string = '')
3253
    {
3254
        if (!empty($string)) {
3255
            $this->title = $string;
3256
        }
3257
    }
3258
3259
    /**
3260
     * Sets the item's type.
3261
     *
3262
     * @param string $string Type
3263
     */
3264
    public function set_type($string = '')
3265
    {
3266
        if (!empty($string)) {
3267
            $this->type = $string;
3268
        }
3269
    }
3270
3271
    /**
3272
     * Checks if the current status is part of the list of status given.
3273
     *
3274
     * @param array $list An array of status to check for.
3275
     *                    If the current status is one of the strings, return true
3276
     *
3277
     * @return bool True if the status was one of the given strings,
3278
     *              false otherwise
3279
     */
3280
    public function status_is($list = [])
3281
    {
3282
        if (self::DEBUG > 1) {
3283
            error_log(
3284
                'learnpathItem::status_is('.print_r(
0 ignored issues
show
Bug introduced by
Are you sure print_r($list, true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3284
                'learnpathItem::status_is('./** @scrutinizer ignore-type */ print_r(
Loading history...
3285
                    $list,
3286
                    true
3287
                ).') on item '.$this->db_id,
3288
                0
3289
            );
3290
        }
3291
        $currentStatus = $this->get_status(true);
3292
        if (empty($currentStatus)) {
3293
            return false;
3294
        }
3295
        $found = false;
3296
        foreach ($list as $status) {
3297
            if (preg_match('/^'.$status.'$/i', $currentStatus)) {
3298
                if (self::DEBUG > 2) {
3299
                    error_log(
3300
                        'New LP - learnpathItem::status_is() - Found status '.
3301
                            $status.' corresponding to current status',
3302
                        0
3303
                    );
3304
                }
3305
                $found = true;
3306
3307
                return $found;
3308
            }
3309
        }
3310
        if (self::DEBUG > 2) {
3311
            error_log(
3312
                'New LP - learnpathItem::status_is() - Status '.
3313
                    $currentStatus.' did not match request',
3314
                0
3315
            );
3316
        }
3317
3318
        return $found;
3319
    }
3320
3321
    /**
3322
     * Updates the time info according to the given session_time.
3323
     *
3324
     * @param int $totalSec Time in seconds
3325
     */
3326
    public function update_time($totalSec = 0)
3327
    {
3328
        if (self::DEBUG > 0) {
3329
            error_log('learnpathItem::update_time('.$totalSec.')');
3330
        }
3331
        if ($totalSec >= 0) {
3332
            // Getting start time from finish time. The only problem in the calculation is it might be
3333
            // modified by the scripts processing time.
3334
            $now = time();
3335
            $start = $now - $totalSec;
3336
            $this->current_start_time = $start;
3337
            $this->current_stop_time = $now;
3338
        }
3339
    }
3340
3341
    /**
3342
     * Special scorm update time function. This function will update time
3343
     * directly into db for scorm objects.
3344
     *
3345
     * @param int $total_sec Total number of seconds
3346
     */
3347
    public function scorm_update_time($total_sec = 0)
3348
    {
3349
        $debug = self::DEBUG;
3350
        if ($debug) {
3351
            error_log('learnpathItem::scorm_update_time()');
3352
            error_log("total_sec: $total_sec");
3353
        }
3354
3355
        // Step 1 : get actual total time stored in db
3356
        $item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3357
3358
        $sql = 'SELECT total_time, status
3359
                FROM '.$item_view_table.'
3360
                WHERE
3361
                    lp_item_id = "'.$this->db_id.'" AND
3362
                    lp_view_id = "'.$this->view_id.'" AND
3363
                    view_count = "'.$this->get_attempt_id().'"';
3364
        $result = Database::query($sql);
3365
        $row = Database::fetch_array($result);
3366
3367
        if (!isset($row['total_time'])) {
3368
            $total_time = 0;
3369
        } else {
3370
            $total_time = $row['total_time'];
3371
        }
3372
        if ($debug) {
3373
            error_log("Original total_time: $total_time");
3374
        }
3375
3376
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3377
        $lp_id = (int) $this->lp_id;
3378
        $sql = "SELECT * FROM $lp_table WHERE iid = $lp_id";
3379
        $res = Database::query($sql);
3380
        $accumulateScormTime = 'false';
3381
        if (Database::num_rows($res) > 0) {
3382
            $row = Database::fetch_assoc($res);
3383
            $accumulateScormTime = $row['accumulate_scorm_time'];
3384
        }
3385
3386
        // Step 2.1 : if normal mode total_time = total_time + total_sec
3387
        if ('sco' === $this->type && 0 != $accumulateScormTime) {
3388
            if ($debug) {
3389
                error_log("accumulateScormTime is on. total_time modified: $total_time + $total_sec");
3390
            }
3391
            $total_time += $total_sec;
3392
        } else {
3393
            // Step 2.2 : if not cumulative mode total_time = total_time - last_update + total_sec
3394
            $total_sec = $this->fixAbusiveTime($total_sec);
3395
            if ($debug) {
3396
                error_log("after fix abusive: $total_sec");
3397
                error_log("total_time: $total_time");
3398
                error_log("this->last_scorm_session_time: ".$this->last_scorm_session_time);
3399
            }
3400
3401
            $total_time = $total_time - $this->last_scorm_session_time + $total_sec;
3402
            $this->last_scorm_session_time = $total_sec;
3403
3404
            if ($total_time < 0) {
3405
                $total_time = $total_sec;
3406
            }
3407
        }
3408
3409
        if ($debug) {
3410
            error_log("accumulate_scorm_time: $accumulateScormTime");
3411
            error_log("total_time modified: $total_time");
3412
        }
3413
3414
        // Step 3 update db only if status != completed, passed, browsed or seriousgamemode not activated
3415
        // @todo complete
3416
        $case_completed = [
3417
            'completed',
3418
            'passed',
3419
            'browsed',
3420
            'failed',
3421
        ];
3422
3423
        if (1 != $this->seriousgame_mode ||
3424
            !in_array($row['status'], $case_completed)
3425
        ) {
3426
            $sql = "UPDATE $item_view_table
3427
                      SET total_time = '$total_time'
3428
                    WHERE
3429
                        lp_item_id = {$this->db_id} AND
3430
                        lp_view_id = {$this->view_id} AND
3431
                        view_count = {$this->get_attempt_id()}";
3432
            if ($debug) {
3433
                error_log('-------------total_time updated ------------------------');
3434
                error_log($sql);
3435
                error_log('-------------------------------------');
3436
            }
3437
            Database::query($sql);
3438
        }
3439
    }
3440
3441
    /**
3442
     * Write objectives to DB. This method is separate from write_to_db() because otherwise
3443
     * objectives are lost as a side effect to AJAX and session concurrent access.
3444
     *
3445
     * @return bool True or false on error
3446
     */
3447
    public function write_objectives_to_db()
3448
    {
3449
        if (self::DEBUG > 0) {
3450
            error_log('learnpathItem::write_objectives_to_db()', 0);
3451
        }
3452
        if (api_is_invitee()) {
3453
            // If the user is an invitee, we don't write anything to DB
3454
            return true;
3455
        }
3456
        $courseId = api_get_course_int_id();
3457
        if (is_array($this->objectives) && count($this->objectives) > 0) {
3458
            // Save objectives.
3459
            $tbl = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3460
            $sql = "SELECT iid
3461
                    FROM $tbl
3462
                    WHERE
3463
                        lp_item_id = ".$this->db_id." AND
3464
                        lp_view_id = ".$this->view_id." AND
3465
                        view_count = ".$this->attempt_id;
3466
            $res = Database::query($sql);
3467
            if (Database::num_rows($res) > 0) {
3468
                $row = Database::fetch_array($res);
3469
                $lp_iv_id = $row[0];
3470
                if (self::DEBUG > 2) {
3471
                    error_log(
3472
                        'learnpathItem::write_to_db() - Got item_view_id '.
3473
                            $lp_iv_id.', now checking objectives ',
3474
                        0
3475
                    );
3476
                }
3477
                foreach ($this->objectives as $index => $objective) {
3478
                    $iva_table = Database::get_course_table(
3479
                        TABLE_LP_IV_OBJECTIVE
3480
                    );
3481
                    $iva_sql = "SELECT iid FROM $iva_table
3482
                                WHERE
3483
                                    c_id = $courseId AND
3484
                                    lp_iv_id = $lp_iv_id AND
3485
                                    objective_id = '".Database::escape_string($objective[0])."'";
3486
                    $iva_res = Database::query($iva_sql);
3487
                    // id(0), type(1), time(2), weighting(3),
3488
                    // correct_responses(4), student_response(5),
3489
                    // result(6), latency(7)
3490
                    if (Database::num_rows($iva_res) > 0) {
3491
                        // Update (or don't).
3492
                        $iva_row = Database::fetch_array($iva_res);
3493
                        $iva_id = $iva_row[0];
3494
                        $ivau_sql = "UPDATE $iva_table ".
3495
                            "SET objective_id = '".Database::escape_string($objective[0])."',".
3496
                            "status = '".Database::escape_string($objective[1])."',".
3497
                            "score_raw = '".Database::escape_string($objective[2])."',".
3498
                            "score_min = '".Database::escape_string($objective[4])."',".
3499
                            "score_max = '".Database::escape_string($objective[3])."' ".
3500
                            "WHERE c_id = $courseId AND iid = $iva_id";
3501
                        Database::query($ivau_sql);
3502
                    } else {
3503
                        // Insert new one.
3504
                        $params = [
3505
                            'c_id' => $courseId,
3506
                            'lp_iv_id' => $lp_iv_id,
3507
                            'order_id' => $index,
3508
                            'objective_id' => $objective[0],
3509
                            'status' => $objective[1],
3510
                            'score_raw' => $objective[2],
3511
                            'score_min' => $objective[4],
3512
                            'score_max' => $objective[3],
3513
                        ];
3514
3515
                        $insertId = Database::insert($iva_table, $params);
3516
                        if ($insertId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $insertId of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
3517
                            $sql = "UPDATE $iva_table SET id = iid
3518
                                    WHERE iid = $insertId";
3519
                            Database::query($sql);
3520
                        }
3521
                    }
3522
                }
3523
            }
3524
        }
3525
    }
3526
3527
    /**
3528
     * Writes the current data to the database.
3529
     *
3530
     * @return bool Query result
3531
     */
3532
    public function write_to_db()
3533
    {
3534
        $debug = self::DEBUG;
3535
        if ($debug) {
3536
            error_log('------------------------');
3537
            error_log('learnpathItem::write_to_db()');
3538
        }
3539
3540
        // Check the session visibility.
3541
        if (!api_is_allowed_to_session_edit()) {
3542
            if ($debug) {
3543
                error_log('return false api_is_allowed_to_session_edit');
3544
            }
3545
3546
            return false;
3547
        }
3548
        if (api_is_invitee()) {
3549
            if ($debug) {
3550
                error_log('api_is_invitee');
3551
            }
3552
            // If the user is an invitee, we don't write anything to DB
3553
            return true;
3554
        }
3555
3556
        $courseId = api_get_course_int_id();
3557
        $mode = $this->get_lesson_mode();
3558
        $credit = $this->get_credit();
3559
        $total_time = ' ';
3560
        $my_status = ' ';
3561
        $item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3562
        $sql = 'SELECT status, total_time
3563
                FROM '.$item_view_table.'
3564
                WHERE
3565
                    lp_item_id="'.$this->db_id.'" AND
3566
                    lp_view_id="'.$this->view_id.'" AND
3567
                    view_count="'.$this->get_attempt_id().'" ';
3568
        $rs_verified = Database::query($sql);
3569
        $row_verified = Database::fetch_array($rs_verified);
3570
        $my_case_completed = [
3571
            'completed',
3572
            'passed',
3573
            'browsed',
3574
            'failed',
3575
        ];
3576
3577
        $save = true;
3578
3579
        if (!empty($row_verified)) {
3580
            $oldTotalTime = $row_verified['total_time'];
3581
            $this->oldTotalTime = $oldTotalTime;
3582
            if (isset($row_verified['status'])) {
3583
                if (in_array($row_verified['status'], $my_case_completed)) {
3584
                    $save = false;
3585
                }
3586
            }
3587
        }
3588
3589
        if (((false === $save && 'sco' === $this->type) ||
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (false === $save && 'sco...& 'sco' === $this->type, Probably Intended Meaning: false === $save && 'sco'... 'sco' === $this->type)
Loading history...
3590
           ('sco' === $this->type && ('no-credit' === $credit || 'review' === $mode || 'browse' === $mode))) &&
3591
           (1 != $this->seriousgame_mode && 'sco' === $this->type)
3592
        ) {
3593
            if ($debug) {
3594
                error_log(
3595
                    "This info shouldn't be saved as the credit or lesson mode info prevent it"
3596
                );
3597
                error_log(
3598
                    'learnpathItem::write_to_db() - credit('.$credit.') or'.
3599
                    ' lesson_mode('.$mode.') prevent recording!',
3600
                    0
3601
                );
3602
            }
3603
        } else {
3604
            // Check the row exists.
3605
            $inserted = false;
3606
            // This a special case for multiple attempts and Chamilo exercises.
3607
            if ('quiz' === $this->type &&
3608
                0 == $this->get_prevent_reinit() &&
3609
                'completed' === $this->get_status()
3610
            ) {
3611
                // We force the item to be restarted.
3612
                $this->restart();
3613
                $params = [
3614
                    "c_id" => $courseId,
3615
                    "total_time" => $this->get_total_time(),
3616
                    "start_time" => $this->current_start_time,
3617
                    "score" => $this->get_score(),
3618
                    "status" => $this->get_status(false),
3619
                    "max_score" => $this->get_max(),
3620
                    "lp_item_id" => $this->db_id,
3621
                    "lp_view_id" => $this->view_id,
3622
                    "view_count" => $this->get_attempt_id(),
3623
                    "suspend_data" => $this->current_data,
3624
                    //"max_time_allowed" => ,
3625
                    "lesson_location" => $this->lesson_location,
3626
                ];
3627
                if ($debug) {
3628
                    error_log('learnpathItem::write_to_db() - Inserting into item_view forced: '.print_r($params, 1));
0 ignored issues
show
Bug introduced by
Are you sure print_r($params, 1) of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3628
                    error_log('learnpathItem::write_to_db() - Inserting into item_view forced: './** @scrutinizer ignore-type */ print_r($params, 1));
Loading history...
3629
                }
3630
                $this->db_item_view_id = Database::insert($item_view_table, $params);
3631
                if ($this->db_item_view_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->db_item_view_id of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
3632
                    $inserted = true;
3633
                }
3634
            }
3635
3636
            $sql = "SELECT * FROM $item_view_table
3637
                    WHERE
3638
                        lp_item_id = ".$this->db_id." AND
3639
                        lp_view_id = ".$this->view_id." AND
3640
                        view_count = ".$this->get_attempt_id();
3641
            if ($debug) {
3642
                error_log('learnpathItem::write_to_db() - Querying item_view: '.$sql);
3643
            }
3644
3645
            $check_res = Database::query($sql);
3646
            // Depending on what we want (really), we'll update or insert a new row
3647
            // now save into DB.
3648
            if (!$inserted && Database::num_rows($check_res) < 1) {
3649
                $params = [
3650
                    "c_id" => $courseId,
3651
                    "total_time" => $this->get_total_time(),
3652
                    "start_time" => $this->current_start_time,
3653
                    "score" => $this->get_score(),
3654
                    "status" => $this->get_status(false),
3655
                    "max_score" => $this->get_max(),
3656
                    "lp_item_id" => $this->db_id,
3657
                    "lp_view_id" => $this->view_id,
3658
                    "view_count" => $this->get_attempt_id(),
3659
                    "suspend_data" => $this->current_data,
3660
                    //"max_time_allowed" => ,$this->get_max_time_allowed()
3661
                    "lesson_location" => $this->lesson_location,
3662
                ];
3663
3664
                if ($debug) {
3665
                    error_log(
3666
                        'learnpathItem::write_to_db() - Inserting into item_view forced: '.print_r($params, 1),
3667
                        0
3668
                    );
3669
                }
3670
                $this->db_item_view_id = Database::insert($item_view_table, $params);
3671
            } else {
3672
                if ('hotpotatoes' === $this->type) {
3673
                    $params = [
3674
                        'total_time' => $this->get_total_time(),
3675
                        'start_time' => $this->get_current_start_time(),
3676
                        'score' => $this->get_score(),
3677
                        'status' => $this->get_status(false),
3678
                        'max_score' => $this->get_max(),
3679
                        'suspend_data' => $this->current_data,
3680
                        'lesson_location' => $this->lesson_location,
3681
                    ];
3682
                    $where = [
3683
                        'c_id = ? AND lp_item_id = ? AND lp_view_id = ? AND view_count = ?' => [
3684
                            $courseId,
3685
                            $this->db_id,
3686
                            $this->view_id,
3687
                            $this->get_attempt_id(),
3688
                        ],
3689
                    ];
3690
                    Database::update($item_view_table, $params, $where);
3691
                } else {
3692
                    // For all other content types...
3693
                    if ('quiz' === $this->type) {
3694
                        $my_status = ' ';
3695
                        $total_time = ' ';
3696
                        if (!empty($_REQUEST['exeId'])) {
3697
                            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3698
                            $exeId = (int) $_REQUEST['exeId'];
3699
                            $sql = "SELECT exe_duration
3700
                                    FROM $table
3701
                                    WHERE exe_id = $exeId";
3702
                            $res = Database::query($sql);
3703
                            $exeRow = Database::fetch_array($res);
3704
                            $duration = isset($exeRow['exe_duration']) ? (int) $exeRow['exe_duration'] : 0;
3705
                            $total_time = " total_time = ".$duration.", ";
3706
                            if ($debug) {
3707
                                error_log("quiz: $total_time");
3708
                            }
3709
                        }
3710
                    } else {
3711
                        $my_type_lp = learnpath::get_type_static($this->lp_id);
3712
                        // This is a array containing values finished
3713
                        $case_completed = [
3714
                            'completed',
3715
                            'passed',
3716
                            'browsed',
3717
                            'failed',
3718
                        ];
3719
3720
                        // Is not multiple attempts
3721
                        if (1 == $this->seriousgame_mode && 'sco' === $this->type) {
3722
                            $total_time = " total_time = total_time +".$this->get_total_time().", ";
3723
                            $my_status = " status = '".$this->get_status(false)."' ,";
3724
                            if ($debug) {
3725
                                error_log("seriousgame_mode time changed: $total_time");
3726
                            }
3727
                        } elseif (1 == $this->get_prevent_reinit()) {
3728
                            // Process of status verified into data base.
3729
                            $sql = 'SELECT status FROM '.$item_view_table.'
3730
                                    WHERE
3731
                                        lp_item_id="'.$this->db_id.'" AND
3732
                                        lp_view_id="'.$this->view_id.'" AND
3733
                                        view_count="'.$this->get_attempt_id().'"
3734
                                    ';
3735
                            $rs_verified = Database::query($sql);
3736
                            $row_verified = Database::fetch_array($rs_verified);
3737
3738
                            // Get type lp: 1=lp dokeos and  2=scorm.
3739
                            // If not is completed or passed or browsed and learning path is scorm.
3740
                            if (!in_array($this->get_status(false), $case_completed) &&
3741
                                2 == $my_type_lp
3742
                            ) {
3743
                                $total_time = " total_time = total_time +".$this->get_total_time().", ";
3744
                                $my_status = " status = '".$this->get_status(false)."' ,";
3745
                                if ($debug) {
3746
                                    error_log("get_prevent_reinit = 1 time changed: $total_time");
3747
                                }
3748
                            } else {
3749
                                // Verified into database.
3750
                                if (!in_array($row_verified['status'], $case_completed) &&
3751
                                    2 == $my_type_lp
3752
                                ) {
3753
                                    $total_time = " total_time = total_time +".$this->get_total_time().", ";
3754
                                    $my_status = " status = '".$this->get_status(false)."' ,";
3755
                                    if ($debug) {
3756
                                        error_log("total_time time changed case 1: $total_time");
3757
                                    }
3758
                                } elseif (in_array($row_verified['status'], $case_completed) &&
3759
                                    2 == $my_type_lp && 'sco' != $this->type
3760
                                ) {
3761
                                    $total_time = " total_time = total_time +".$this->get_total_time().", ";
3762
                                    $my_status = " status = '".$this->get_status(false)."' ,";
3763
                                    if ($debug) {
3764
                                        error_log("total_time time changed case 2: $total_time");
3765
                                    }
3766
                                } else {
3767
                                    if ((3 == $my_type_lp && 'au' == $this->type) ||
3768
                                        (1 == $my_type_lp && 'dir' != $this->type)) {
3769
                                        // Is AICC or Chamilo LP
3770
                                        $total_time = " total_time = total_time + ".$this->get_total_time().", ";
3771
                                        $my_status = " status = '".$this->get_status(false)."' ,";
3772
                                        if ($debug) {
3773
                                            error_log("total_time time changed case 3: $total_time");
3774
                                        }
3775
                                    }
3776
                                }
3777
                            }
3778
                        } else {
3779
                            // Multiple attempts are allowed.
3780
                            if (in_array($this->get_status(false), $case_completed) && 2 == $my_type_lp) {
3781
                                // Reset zero new attempt ?
3782
                                $my_status = " status = '".$this->get_status(false)."' ,";
3783
                                if ($debug) {
3784
                                    error_log("total_time time changed Multiple attempt case 1: $total_time");
3785
                                }
3786
                            } elseif (!in_array($this->get_status(false), $case_completed) && 2 == $my_type_lp) {
3787
                                $total_time = " total_time = ".$this->get_total_time().", ";
3788
                                $my_status = " status = '".$this->get_status(false)."' ,";
3789
                                if ($debug) {
3790
                                    error_log("total_time time changed Multiple attempt case 2: $total_time");
3791
                                }
3792
                            } else {
3793
                                // It is chamilo LP.
3794
                                $total_time = " total_time = total_time +".$this->get_total_time().", ";
3795
                                $my_status = " status = '".$this->get_status(false)."' ,";
3796
                                if ($debug) {
3797
                                    error_log("total_time time changed Multiple attempt case 3: $total_time");
3798
                                }
3799
                            }
3800
3801
                            // This code line fixes the problem of wrong status.
3802
                            if (2 == $my_type_lp) {
3803
                                // Verify current status in multiples attempts.
3804
                                $sql = 'SELECT status FROM '.$item_view_table.'
3805
                                        WHERE
3806
                                            c_id = '.$courseId.' AND
3807
                                            lp_item_id="'.$this->db_id.'" AND
3808
                                            lp_view_id="'.$this->view_id.'" AND
3809
                                            view_count="'.$this->get_attempt_id().'" ';
3810
                                $rs_status = Database::query($sql);
3811
                                $current_status = Database::result($rs_status, 0, 'status');
3812
                                if (in_array($current_status, $case_completed)) {
3813
                                    $my_status = '';
3814
                                    $total_time = '';
3815
                                } else {
3816
                                    $total_time = " total_time = total_time + ".$this->get_total_time().", ";
3817
                                }
3818
3819
                                if ($debug) {
3820
                                    error_log("total_time time my_type_lp: $total_time");
3821
                                }
3822
                            }
3823
                        }
3824
                    }
3825
3826
                    if ('sco' === $this->type) {
3827
                        //IF scorm scorm_update_time has already updated total_time in db
3828
                        //" . //start_time = ".$this->get_current_start_time().", " . //scorm_init_time does it
3829
                        ////" max_time_allowed = '".$this->get_max_time_allowed()."'," .
3830
                        $sql = "UPDATE $item_view_table SET
3831
                                    score = ".$this->get_score().",
3832
                                    $my_status
3833
                                    max_score = '".$this->get_max()."',
3834
                                    suspend_data = '".Database::escape_string($this->current_data)."',
3835
                                    lesson_location = '".$this->lesson_location."'
3836
                                WHERE
3837
                                    lp_item_id = ".$this->db_id." AND
3838
                                    lp_view_id = ".$this->view_id."  AND
3839
                                    view_count = ".$this->get_attempt_id();
3840
                    } else {
3841
                        //" max_time_allowed = '".$this->get_max_time_allowed()."'," .
3842
                        $sql = "UPDATE $item_view_table SET
3843
                                    $total_time
3844
                                    start_time = ".$this->get_current_start_time().",
3845
                                    score = ".$this->get_score().",
3846
                                    $my_status
3847
                                    max_score = '".$this->get_max()."',
3848
                                    suspend_data = '".Database::escape_string($this->current_data)."',
3849
                                    lesson_location = '".$this->lesson_location."'
3850
                                WHERE
3851
                                    lp_item_id = ".$this->db_id." AND
3852
                                    lp_view_id = ".$this->view_id." AND
3853
                                    view_count = ".$this->get_attempt_id();
3854
                    }
3855
                    $this->current_start_time = time();
3856
                }
3857
                if ($debug) {
3858
                    error_log('-------------------------------------------');
3859
                    error_log('learnpathItem::write_to_db() - Updating item_view:');
3860
                    error_log($sql);
3861
                    error_log('-------------------------------------------');
3862
                }
3863
                Database::query($sql);
3864
            }
3865
3866
            if (is_array($this->interactions) &&
3867
                count($this->interactions) > 0
3868
            ) {
3869
                $sql = "SELECT iid FROM $item_view_table
3870
                        WHERE
3871
                            lp_item_id = ".$this->db_id." AND
3872
                            lp_view_id = ".$this->view_id." AND
3873
                            view_count = ".$this->get_attempt_id();
3874
                $res = Database::query($sql);
3875
                if (Database::num_rows($res) > 0) {
3876
                    $row = Database::fetch_array($res);
3877
                    $lp_iv_id = $row[0];
3878
                    if ($debug) {
3879
                        error_log(
3880
                            'learnpathItem::write_to_db() - Got item_view_id '.
3881
                            $lp_iv_id.', now checking interactions ',
3882
                            0
3883
                        );
3884
                    }
3885
                    foreach ($this->interactions as $index => $interaction) {
3886
                        $correct_resp = '';
3887
                        if (is_array($interaction[4]) && !empty($interaction[4][0])) {
3888
                            foreach ($interaction[4] as $resp) {
3889
                                $correct_resp .= $resp.',';
3890
                            }
3891
                            $correct_resp = substr(
3892
                                $correct_resp,
3893
                                0,
3894
                                strlen($correct_resp) - 1
3895
                            );
3896
                        }
3897
                        $iva_table = Database::get_course_table(
3898
                            TABLE_LP_IV_INTERACTION
3899
                        );
3900
3901
                        //also check for the interaction ID as it must be unique for this SCO view
3902
                        $iva_sql = "SELECT iid FROM $iva_table
3903
                                    WHERE
3904
                                        c_id = $courseId AND
3905
                                        lp_iv_id = $lp_iv_id AND
3906
                                        (
3907
                                            order_id = $index OR
3908
                                            interaction_id = '".Database::escape_string($interaction[0])."'
3909
                                        )
3910
                                    ";
3911
                        $iva_res = Database::query($iva_sql);
3912
3913
                        $interaction[0] = $interaction[0] ?? '';
3914
                        $interaction[1] = $interaction[1] ?? '';
3915
                        $interaction[2] = $interaction[2] ?? '';
3916
                        $interaction[3] = $interaction[3] ?? '';
3917
                        $interaction[4] = $interaction[4] ?? '';
3918
                        $interaction[5] = $interaction[5] ?? '';
3919
                        $interaction[6] = $interaction[6] ?? '';
3920
                        $interaction[7] = $interaction[7] ?? '';
3921
3922
                        // id(0), type(1), time(2), weighting(3), correct_responses(4), student_response(5), result(6), latency(7)
3923
                        if (Database::num_rows($iva_res) > 0) {
3924
                            // Update (or don't).
3925
                            $iva_row = Database::fetch_array($iva_res);
3926
                            $iva_id = $iva_row[0];
3927
                            // Insert new one.
3928
                            $params = [
3929
                                'interaction_id' => $interaction[0],
3930
                                'interaction_type' => $interaction[1],
3931
                                'weighting' => $interaction[3],
3932
                                'completion_time' => $interaction[2],
3933
                                'correct_responses' => $correct_resp,
3934
                                'student_response' => $interaction[5],
3935
                                'result' => $interaction[6],
3936
                                'latency' => $interaction[7],
3937
                            ];
3938
                            Database::update(
3939
                                $iva_table,
3940
                                $params,
3941
                                [
3942
                                    'c_id = ? AND iid = ?' => [
3943
                                        $courseId,
3944
                                        $iva_id,
3945
                                    ],
3946
                                ]
3947
                            );
3948
                        } else {
3949
                            // Insert new one.
3950
                            $params = [
3951
                                'c_id' => $courseId,
3952
                                'order_id' => $index,
3953
                                'lp_iv_id' => $lp_iv_id,
3954
                                'interaction_id' => $interaction[0],
3955
                                'interaction_type' => $interaction[1],
3956
                                'weighting' => $interaction[3],
3957
                                'completion_time' => $interaction[2],
3958
                                'correct_responses' => $correct_resp,
3959
                                'student_response' => $interaction[5],
3960
                                'result' => $interaction[6],
3961
                                'latency' => $interaction[7],
3962
                            ];
3963
3964
                            $insertId = Database::insert($iva_table, $params);
3965
                            if ($insertId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $insertId of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
3966
                                $sql = "UPDATE $iva_table SET id = iid
3967
                                        WHERE iid = $insertId";
3968
                                Database::query($sql);
3969
                            }
3970
                        }
3971
                    }
3972
                }
3973
            }
3974
        }
3975
3976
        if ($debug) {
3977
            error_log('End of learnpathItem::write_to_db()', 0);
3978
        }
3979
3980
        return true;
3981
    }
3982
3983
    /**
3984
     * Adds an audio file attached to the current item (store on disk and in db).
3985
     *
3986
     * @return bool
3987
     */
3988
    public function addAudio()
3989
    {
3990
        $course_info = api_get_course_info();
3991
        $userId = api_get_user_id();
3992
3993
        $folderDocument = create_unexisting_directory(
3994
            $course_info,
3995
            $userId,
3996
            0,
3997
            0,
3998
            0,
3999
            null,
4000
            '/audio',
4001
            get_lang('Audio'),
4002
            0,
4003
            false,
4004
            false
4005
        );
4006
4007
        /*$filepath = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document/';
4008
        if (!is_dir($filepath.'audio')) {
4009
            mkdir(
4010
                $filepath.'audio',
4011
                api_get_permissions_for_new_directories()
4012
            );
4013
            DocumentManager::addDocument(
4014
                $course_info,
4015
                '/audio',
4016
                'folder',
4017
                0,
4018
                'audio'
4019
            );
4020
        }*/
4021
4022
        $key = 'file';
4023
        if (!isset($_FILES[$key]['name']) || !isset($_FILES[$key]['tmp_name'])) {
4024
            return false;
4025
        }
4026
4027
        $document = null;
4028
        /*$document = DocumentManager::upload_document(
4029
            $_FILES,
4030
            null,
4031
            null,
4032
            null,
4033
            0,
4034
            'rename',
4035
            false,
4036
            true,
4037
            'file',
4038
            false,
4039
            $folderDocument->getIid(),
4040
        );*/
4041
4042
        if ($document) {
0 ignored issues
show
introduced by
$document is of type null, thus it always evaluated to false.
Loading history...
4043
            $name = '/audio/'.$document->getResourceNode()->getResourceFiles()->first()->getOriginalName();
4044
            // Store the mp3 file in the lp_item table.
4045
            $table = Database::get_course_table(TABLE_LP_ITEM);
4046
            $sql = "UPDATE $table SET
4047
                        audio = '".Database::escape_string($name)."'
4048
                    WHERE iid = ".intval($this->db_id);
4049
            Database::query($sql);
4050
4051
            return true;
4052
        }
4053
4054
        return false;
4055
    }
4056
4057
    /**
4058
     * Removes the relation between the current item and an audio file. The file
4059
     * is only removed from the lp_item table, but remains in the document table
4060
     * and directory.
4061
     *
4062
     * @return bool
4063
     */
4064
    public function removeAudio()
4065
    {
4066
        $courseInfo = api_get_course_info();
4067
4068
        if (empty($this->db_id) || empty($courseInfo)) {
4069
            return false;
4070
        }
4071
4072
        $table = Database::get_course_table(TABLE_LP_ITEM);
4073
        $sql = "UPDATE $table SET
4074
                audio = ''
4075
                WHERE iid = ".$this->db_id;
4076
        Database::query($sql);
4077
    }
4078
4079
    /**
4080
     * Adds an audio file to the current item, using a file already in documents.
4081
     *
4082
     * @param int $documentId
4083
     *
4084
     * @return string
4085
     */
4086
    public function add_audio_from_documents($documentId)
4087
    {
4088
        $courseInfo = api_get_course_info();
4089
        $documentData = DocumentManager::get_document_data_by_id($documentId, $courseInfo['code']);
0 ignored issues
show
Deprecated Code introduced by
The function DocumentManager::get_document_data_by_id() has been deprecated: use $repo->find() ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

4089
        $documentData = /** @scrutinizer ignore-deprecated */ DocumentManager::get_document_data_by_id($documentId, $courseInfo['code']);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
4090
4091
        $path = '';
4092
        if (!empty($documentData)) {
4093
            $path = $documentData['path'];
4094
            // Store the mp3 file in the lp_item table.
4095
            $table = Database::get_course_table(TABLE_LP_ITEM);
4096
            $sql = "UPDATE $table SET
4097
                        audio = '".Database::escape_string($path)."'
4098
                    WHERE iid = ".$this->db_id;
4099
            Database::query($sql);
4100
        }
4101
4102
        return $path;
4103
    }
4104
4105
    /**
4106
     * Transform the SCORM status to a string that can be translated by Chamilo
4107
     * in different user languages.
4108
     *
4109
     * @param $status
4110
     * @param bool   $decorate
4111
     * @param string $type     classic|simple
4112
     *
4113
     * @return array|string
4114
     */
4115
    public static function humanize_status($status, $decorate = true, $type = 'classic')
4116
    {
4117
        $statusList = [
4118
            'completed' => 'Completed',
4119
            'incomplete' => 'Incomplete',
4120
            'failed' => 'Failed',
4121
            'passed' => 'Passed',
4122
            'browsed' => 'Browsed',
4123
            'not attempted' => 'Not attempted',
4124
        ];
4125
4126
        $myLessonStatus = get_lang($statusList[$status]);
4127
4128
        switch ($status) {
4129
            case 'completed':
4130
            case 'browsed':
4131
                $classStatus = 'info';
4132
                break;
4133
            case 'incomplete':
4134
                $classStatus = 'warning';
4135
                break;
4136
            case 'passed':
4137
                $classStatus = 'success';
4138
                break;
4139
            case 'failed':
4140
                $classStatus = 'important';
4141
                break;
4142
            default:
4143
                $classStatus = 'default';
4144
                break;
4145
        }
4146
4147
        if ('simple' === $type) {
4148
            if (in_array($status, ['failed', 'passed', 'browsed'])) {
4149
                $myLessonStatus = get_lang('Incomplete');
4150
4151
                $classStatus = 'warning';
4152
            }
4153
        }
4154
4155
        if ($decorate) {
4156
            return Display::label($myLessonStatus, $classStatus);
4157
        }
4158
4159
        return $myLessonStatus;
4160
    }
4161
4162
    /**
4163
     * @return float
4164
     */
4165
    public function getPrerequisiteMaxScore()
4166
    {
4167
        return $this->prerequisiteMaxScore;
4168
    }
4169
4170
    /**
4171
     * @param float $prerequisiteMaxScore
4172
     */
4173
    public function setPrerequisiteMaxScore($prerequisiteMaxScore)
4174
    {
4175
        $this->prerequisiteMaxScore = $prerequisiteMaxScore;
4176
    }
4177
4178
    /**
4179
     * @return float
4180
     */
4181
    public function getPrerequisiteMinScore()
4182
    {
4183
        return $this->prerequisiteMinScore;
4184
    }
4185
4186
    /**
4187
     * @param float $prerequisiteMinScore
4188
     */
4189
    public function setPrerequisiteMinScore($prerequisiteMinScore)
4190
    {
4191
        $this->prerequisiteMinScore = $prerequisiteMinScore;
4192
    }
4193
4194
    /**
4195
     * @return int
4196
     */
4197
    public function getLastScormSessionTime()
4198
    {
4199
        return $this->last_scorm_session_time;
4200
    }
4201
4202
    /**
4203
     * @return int
4204
     */
4205
    public function getIid()
4206
    {
4207
        return $this->iId;
4208
    }
4209
4210
    /**
4211
     * @param int    $user_id
4212
     * @param string $prereqs_string
4213
     * @param array  $refs_list
4214
     *
4215
     * @return bool
4216
     */
4217
    public function getStatusFromOtherSessions($user_id, $prereqs_string, $refs_list)
4218
    {
4219
        $lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW);
4220
        $lp_view = Database::get_course_table(TABLE_LP_VIEW);
4221
        $courseId = api_get_course_int_id();
4222
        $user_id = (int) $user_id;
4223
4224
        // Check results from another sessions:
4225
        $checkOtherSessions = ('true' === api_get_setting('lp.validate_lp_prerequisite_from_other_session'));
4226
        if ($checkOtherSessions) {
4227
            // Check items
4228
            $sql = "SELECT iid FROM $lp_view
4229
                    WHERE
4230
                        c_id = $courseId AND
4231
                        user_id = $user_id  AND
4232
                        lp_id = $this->lp_id AND
4233
                        session_id <> 0
4234
                    ";
4235
            $result = Database::query($sql);
4236
            $resultFromOtherSessions = false;
4237
            while ($row = Database::fetch_array($result)) {
4238
                $lpIid = $row['iid'];
4239
                $sql = "SELECT status FROM $lp_item_view
4240
                        WHERE
4241
                            lp_view_id = $lpIid AND
4242
                            lp_item_id = $refs_list[$prereqs_string]
4243
                        LIMIT 1";
4244
                $resultRow = Database::query($sql);
4245
                if (Database::num_rows($resultRow)) {
4246
                    $statusResult = Database::fetch_array($resultRow);
4247
                    $status = $statusResult['status'];
4248
                    $checked = $status == $this->possible_status[2] || $status == $this->possible_status[3];
4249
                    if ($checked) {
4250
                        $resultFromOtherSessions = true;
4251
                        break;
4252
                    }
4253
                }
4254
            }
4255
4256
            return $resultFromOtherSessions;
4257
        }
4258
    }
4259
}
4260