learnpathItem::__construct()   B
last analyzed

Complexity

Conditions 7
Paths 16

Size

Total Lines 78
Code Lines 67

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 67
nc 16
nop 2
dl 0
loc 78
rs 7.7866
c 0
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
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
                case 'video':
494
                    $table_doc = Database::get_course_table(TABLE_DOCUMENT);
495
                    $sql = 'SELECT path
496
                            FROM '.$table_doc.'
497
                            WHERE iid = '.$path;
498
                    $res = Database::query($sql);
499
                    $row = Database::fetch_array($res);
500
                    $real_path = 'document'.$row['path'];
501
502
                    return $real_path;
503
                case TOOL_STUDENTPUBLICATION:
504
                case TOOL_QUIZ:
505
                case TOOL_FORUM:
506
                case TOOL_THREAD:
507
                case TOOL_LINK:
508
                default:
509
                    return '-1';
510
            }
511
        } else {
512
            if (!empty($path_to_scorm_dir)) {
513
                $path = $path_to_scorm_dir.$path;
514
            }
515
516
            return $path;
517
        }
518
    }
519
520
    /**
521
     * Gets the DB ID.
522
     *
523
     * @return int Database ID for the current item
524
     */
525
    public function get_id()
526
    {
527
        if (!empty($this->db_id)) {
528
            return $this->db_id;
529
        }
530
        // TODO: Check this return value is valid for children classes (SCORM?).
531
        return 0;
532
    }
533
534
    /**
535
     * Loads the interactions into the item object, from the database.
536
     * If object interactions exist, they will be overwritten by this function,
537
     * using the database elements only.
538
     */
539
    public function load_interactions()
540
    {
541
        $this->interactions = [];
542
        $courseId = $this->courseId;
543
        $tbl = Database::get_course_table(TABLE_LP_ITEM_VIEW);
544
        $sql = "SELECT id FROM $tbl
545
                WHERE
546
                    lp_item_id = ".$this->db_id." AND
547
                    lp_view_id = ".$this->view_id." AND
548
                    view_count = ".$this->get_view_count();
549
        $res = Database::query($sql);
550
        if (Database::num_rows($res) > 0) {
551
            $row = Database::fetch_array($res);
552
            $lp_iv_id = $row[0];
553
            $iva_table = Database::get_course_table(TABLE_LP_IV_INTERACTION);
554
            $sql = "SELECT * FROM $iva_table
555
                    WHERE c_id = $courseId AND lp_iv_id = $lp_iv_id ";
556
            $res_sql = Database::query($sql);
557
            while ($row = Database::fetch_array($res_sql)) {
558
                $this->interactions[$row['interaction_id']] = [
559
                    $row['interaction_id'],
560
                    $row['interaction_type'],
561
                    $row['weighting'],
562
                    $row['completion_time'],
563
                    $row['correct_responses'],
564
                    $row['student_responses'],
565
                    $row['result'],
566
                    $row['latency'],
567
                ];
568
            }
569
        }
570
    }
571
572
    /**
573
     * Gets the current count of interactions recorded in the database.
574
     *
575
     * @param bool $checkdb Whether to count from database or not (defaults to no)
576
     *
577
     * @return int The current number of interactions recorder
578
     */
579
    public function get_interactions_count($checkdb = false)
580
    {
581
        $return = 0;
582
        if (api_is_invitee()) {
583
            // If the user is an invitee, we consider there's no interaction
584
            return 0;
585
        }
586
        if ($checkdb) {
587
            $tbl = Database::get_course_table(TABLE_LP_ITEM_VIEW);
588
            $sql = "SELECT iid FROM $tbl
589
                    WHERE
590
                        lp_item_id = ".$this->db_id." AND
591
                        lp_view_id = ".$this->view_id." AND
592
                        view_count = ".$this->get_attempt_id();
593
            $res = Database::query($sql);
594
            if (Database::num_rows($res) > 0) {
595
                $row = Database::fetch_array($res);
596
                $lp_iv_id = $row[0];
597
                $iva_table = Database::get_course_table(TABLE_LP_IV_INTERACTION);
598
                $sql = "SELECT count(iid) as count
599
                        FROM $iva_table
600
                        WHERE lp_iv_id = $lp_iv_id ";
601
                $res_sql = Database::query($sql);
602
                if (Database::num_rows($res_sql) > 0) {
603
                    $row = Database::fetch_array($res_sql);
604
                    $return = (int) $row['count'];
605
                }
606
            }
607
        } else {
608
            if (!empty($this->interactions_count)) {
609
                $return = $this->interactions_count;
610
            }
611
        }
612
613
        return $return;
614
    }
615
616
    /**
617
     * Gets the JavaScript array content to fill the interactions array.
618
     *
619
     * @param bool $checkdb Whether to check directly into the database (default no)
620
     *
621
     * @return string An empty string if no interaction, a JS array definition otherwise
622
     */
623
    public function get_interactions_js_array($checkdb = false)
624
    {
625
        $return = '';
626
        if ($checkdb) {
627
            $this->load_interactions();
628
        }
629
        foreach ($this->interactions as $id => $in) {
630
            $return .= "[
631
                '$id',
632
                '".$in[1]."',
633
                '".$in[2]."',
634
                '".$in[3]."',
635
                '".$in[4]."',
636
                '".$in[5]."',
637
                '".$in[6]."',
638
                '".$in[7]."'],";
639
        }
640
        if (!empty($return)) {
641
            $return = substr($return, 0, -1);
642
        }
643
644
        return $return;
645
    }
646
647
    /**
648
     * Gets the current count of objectives recorded in the database.
649
     *
650
     * @return int The current number of objectives recorder
651
     */
652
    public function get_objectives_count()
653
    {
654
        $res = 0;
655
        if (!empty($this->objectives_count)) {
656
            $res = $this->objectives_count;
657
        }
658
659
        return $res;
660
    }
661
662
    /**
663
     * Gets the launch_data field found in imsmanifests (this is SCORM- or
664
     * AICC-related, really).
665
     *
666
     * @return string Launch data as found in imsmanifest and stored in
667
     *                Chamilo (read only). Defaults to ''.
668
     */
669
    public function get_launch_data()
670
    {
671
        if (!empty($this->launch_data)) {
672
            return str_replace(
673
                ["\r", "\n", "'"],
674
                ['\r', '\n', "\\'"],
675
                $this->launch_data
676
            );
677
        }
678
679
        return '';
680
    }
681
682
    /**
683
     * Gets the lesson location.
684
     *
685
     * @return string lesson location as recorded by the SCORM and AICC
686
     *                elements. Defaults to ''
687
     */
688
    public function get_lesson_location()
689
    {
690
        if (!empty($this->lesson_location)) {
691
            return str_replace(
692
                ["\r", "\n", "'"],
693
                ['\r', '\n', "\\'"],
694
                $this->lesson_location
695
            );
696
        }
697
698
        return '';
699
    }
700
701
    /**
702
     * Gets the lesson_mode (scorm feature, but might be used by aicc as well
703
     * as chamilo paths).
704
     *
705
     * The "browse" mode is not supported yet (because there is no such way of
706
     * seeing a sco in Chamilo)
707
     *
708
     * @return string 'browse','normal' or 'review'. Defaults to 'normal'
709
     */
710
    public function get_lesson_mode()
711
    {
712
        $mode = 'normal';
713
        if (0 != $this->get_prevent_reinit()) {
714
            // If prevent_reinit == 0
715
            $my_status = $this->get_status();
716
            if ($my_status != $this->possible_status[0] && $my_status != $this->possible_status[1]) {
717
                $mode = 'review';
718
            }
719
        }
720
721
        return $mode;
722
    }
723
724
    /**
725
     * Gets the depth level.
726
     *
727
     * @return int Level. Defaults to 0
728
     */
729
    public function get_level()
730
    {
731
        if (empty($this->level)) {
732
            return 0;
733
        }
734
735
        return $this->level;
736
    }
737
738
    /**
739
     * Gets the mastery score.
740
     */
741
    public function get_mastery_score()
742
    {
743
        if (isset($this->mastery_score)) {
744
            return $this->mastery_score;
745
        }
746
747
        return -1;
748
    }
749
750
    /**
751
     * Gets the maximum (score).
752
     *
753
     * @return int Maximum score. Defaults to 100 if nothing else is defined
754
     */
755
    public function get_max()
756
    {
757
        if ('sco' === $this->type) {
758
            if (!empty($this->view_max_score) && $this->view_max_score > 0) {
759
                return $this->view_max_score;
760
            } else {
761
                if (!empty($this->max_score)) {
762
                    return $this->max_score;
763
                }
764
765
                return 100;
766
            }
767
        } else {
768
            if (!empty($this->max_score)) {
769
                return $this->max_score;
770
            }
771
772
            return 100;
773
        }
774
    }
775
776
    /**
777
     * Gets the maximum time allowed for this user in this attempt on this item.
778
     *
779
     * @return string Time string in SCORM format
780
     *                (HH:MM:SS or HH:MM:SS.SS or HHHH:MM:SS.SS)
781
     */
782
    public function get_max_time_allowed()
783
    {
784
        if (!empty($this->max_time_allowed)) {
785
            return $this->max_time_allowed;
786
        }
787
788
        return '';
789
    }
790
791
    /**
792
     * Gets the minimum (score).
793
     *
794
     * @return int Minimum score. Defaults to 0
795
     */
796
    public function get_min()
797
    {
798
        if (!empty($this->min_score)) {
799
            return $this->min_score;
800
        }
801
802
        return 0;
803
    }
804
805
    /**
806
     * Gets the parent ID.
807
     *
808
     * @return int Parent ID. Defaults to null
809
     */
810
    public function get_parent()
811
    {
812
        if (!empty($this->parent)) {
813
            return $this->parent;
814
        }
815
        // TODO: Check this return value is valid for children classes (SCORM?).
816
        return null;
817
    }
818
819
    /**
820
     * Gets the path attribute.
821
     *
822
     * @return string Path. Defaults to ''
823
     */
824
    public function get_path()
825
    {
826
        if (empty($this->path)) {
827
            return '';
828
        }
829
830
        return $this->path;
831
    }
832
833
    /**
834
     * Gets the prerequisites string.
835
     *
836
     * @return string empty string or prerequisites string if defined
837
     */
838
    public function get_prereq_string()
839
    {
840
        if (!empty($this->prereq_string)) {
841
            return $this->prereq_string;
842
        }
843
844
        return '';
845
    }
846
847
    /**
848
     * Gets the prevent_reinit attribute value (and sets it if not set already).
849
     *
850
     * @return int 1 or 0 (defaults to 1)
851
     */
852
    public function get_prevent_reinit()
853
    {
854
        if (self::DEBUG > 2) {
855
            error_log('learnpathItem::get_prevent_reinit()', 0);
856
        }
857
        if (!isset($this->prevent_reinit)) {
858
            if (!empty($this->lp_id)) {
859
                $table = Database::get_course_table(TABLE_LP_MAIN);
860
                $sql = "SELECT prevent_reinit
861
                        FROM $table
862
                        WHERE iid = ".$this->lp_id;
863
                $res = Database::query($sql);
864
                if (Database::num_rows($res) < 1) {
865
                    $this->error = 'Could not find parent learnpath in lp table';
866
                    if (self::DEBUG > 2) {
867
                        error_log(
868
                            'LearnpathItem::get_prevent_reinit() - Returning false',
869
                            0
870
                        );
871
                    }
872
873
                    return false;
874
                } else {
875
                    $row = Database::fetch_array($res);
876
                    $this->prevent_reinit = $row['prevent_reinit'];
877
                }
878
            } else {
879
                // Prevent reinit is always 1 by default - see learnpath.class.php
880
                $this->prevent_reinit = 1;
881
            }
882
        }
883
        if (self::DEBUG > 2) {
884
            error_log('End of learnpathItem::get_prevent_reinit() - Returned '.$this->prevent_reinit);
885
        }
886
887
        return $this->prevent_reinit;
888
    }
889
890
    /**
891
     * Returns 1 if seriousgame_mode is activated, 0 otherwise.
892
     *
893
     * @return int (0 or 1)
894
     *
895
     * @deprecated seriousgame_mode seems not to be used
896
     *
897
     * @author ndiechburg <[email protected]>
898
     */
899
    public function get_seriousgame_mode()
900
    {
901
        if (!isset($this->seriousgame_mode)) {
902
            if (!empty($this->lp_id)) {
903
                $table = Database::get_course_table(TABLE_LP_MAIN);
904
                $sql = "SELECT seriousgame_mode
905
                        FROM $table
906
                        WHERE iid = ".$this->lp_id;
907
                $res = Database::query($sql);
908
                if (Database::num_rows($res) < 1) {
909
                    $this->error = 'Could not find parent learnpath in learnpath table';
910
911
                    return false;
912
                } else {
913
                    $row = Database::fetch_array($res);
914
                    $this->seriousgame_mode = isset($row['seriousgame_mode']) ? $row['seriousgame_mode'] : 0;
915
                }
916
            } else {
917
                $this->seriousgame_mode = 0; //SeriousGame mode is always off by default
918
            }
919
        }
920
921
        return $this->seriousgame_mode;
922
    }
923
924
    /**
925
     * Gets the item's reference column.
926
     *
927
     * @return string The item's reference field (generally used for SCORM identifiers)
928
     */
929
    public function get_ref()
930
    {
931
        return $this->ref;
932
    }
933
934
    /**
935
     * Gets the list of included resources as a list of absolute or relative
936
     * paths of resources included in the current item. This allows for a
937
     * better SCORM export. The list will generally include pictures, flash
938
     * objects, java applets, or any other stuff included in the source of the
939
     * current item. The current item is expected to be an HTML file. If it
940
     * is not, then the function will return and empty list.
941
     *
942
     * @param string $type        (one of the Chamilo tools) - optional (otherwise takes the current item's type)
943
     * @param string $abs_path    absolute file path - optional (otherwise takes the current item's path)
944
     * @param int    $recursivity level of recursivity we're in
945
     *
946
     * @return array List of file paths.
947
     *               An additional field containing 'local' or 'remote' helps determine if
948
     *               the file should be copied into the zip or just linked
949
     */
950
    public function get_resources_from_source(
951
        $type = null,
952
        $abs_path = null,
953
        $recursivity = 1
954
    ) {
955
        $max = 5;
956
        if ($recursivity > $max) {
957
            return [];
958
        }
959
960
        $type = empty($type) ? $this->get_type() : $type;
961
962
        if (!isset($abs_path)) {
963
            $path = $this->get_file_path();
964
            $abs_path = api_get_path(SYS_COURSE_PATH).api_get_course_path().'/'.$path;
0 ignored issues
show
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

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

1128
                                                    /** @scrutinizer ignore-call */ 
1129
                                                    $in_files_list[] = self::get_resources_from_source(
Loading history...
1129
                                                        TOOL_DOCUMENT,
1130
                                                        $second_part,
1131
                                                        $recursivity + 1
1132
                                                    );
1133
                                                    if (count($in_files_list) > 0) {
1134
                                                        $files_list = array_merge(
1135
                                                            $files_list,
1136
                                                            $in_files_list
1137
                                                        );
1138
                                                    }
1139
                                                } else {
1140
                                                    // We didn't find any trace of current portal.
1141
                                                    $files_list[] = [
1142
                                                        $second_part,
1143
                                                        'remote',
1144
                                                        'url',
1145
                                                    ];
1146
                                                }
1147
                                            } elseif (strpos($second_part, '=') > 0) {
1148
                                                if ('/' === substr($second_part, 0, 1)) {
1149
                                                    // Link starts with a /,
1150
                                                    // making it absolute (relative to DocumentRoot).
1151
                                                    $files_list[] = [
1152
                                                        $second_part,
1153
                                                        'local',
1154
                                                        'abs',
1155
                                                    ];
1156
                                                    $in_files_list[] = self::get_resources_from_source(
1157
                                                        TOOL_DOCUMENT,
1158
                                                        $second_part,
1159
                                                        $recursivity + 1
1160
                                                    );
1161
                                                    if (count($in_files_list) > 0) {
1162
                                                        $files_list = array_merge(
1163
                                                            $files_list,
1164
                                                            $in_files_list
1165
                                                        );
1166
                                                    }
1167
                                                } elseif (0 === strstr($second_part, '..')) {
1168
                                                    // Link is relative but going back in the hierarchy.
1169
                                                    $files_list[] = [
1170
                                                        $second_part,
1171
                                                        'local',
1172
                                                        'rel',
1173
                                                    ];
1174
                                                    $dir = dirname(
1175
                                                        $abs_path
1176
                                                    );
1177
                                                    $new_abs_path = realpath(
1178
                                                        $dir.'/'.$second_part
1179
                                                    );
1180
                                                    $in_files_list[] = self::get_resources_from_source(
1181
                                                        TOOL_DOCUMENT,
1182
                                                        $new_abs_path,
1183
                                                        $recursivity + 1
1184
                                                    );
1185
                                                    if (count($in_files_list) > 0) {
1186
                                                        $files_list = array_merge(
1187
                                                            $files_list,
1188
                                                            $in_files_list
1189
                                                        );
1190
                                                    }
1191
                                                } else {
1192
                                                    // No starting '/', making it relative to current document's path.
1193
                                                    if ('./' == substr($second_part, 0, 2)) {
1194
                                                        $second_part = substr(
1195
                                                            $second_part,
1196
                                                            2
1197
                                                        );
1198
                                                    }
1199
                                                    $files_list[] = [
1200
                                                        $second_part,
1201
                                                        'local',
1202
                                                        'rel',
1203
                                                    ];
1204
                                                    $dir = dirname(
1205
                                                        $abs_path
1206
                                                    );
1207
                                                    $new_abs_path = realpath(
1208
                                                        $dir.'/'.$second_part
1209
                                                    );
1210
                                                    $in_files_list[] = self::get_resources_from_source(
1211
                                                        TOOL_DOCUMENT,
1212
                                                        $new_abs_path,
1213
                                                        $recursivity + 1
1214
                                                    );
1215
                                                    if (count($in_files_list) > 0) {
1216
                                                        $files_list = array_merge(
1217
                                                            $files_list,
1218
                                                            $in_files_list
1219
                                                        );
1220
                                                    }
1221
                                                }
1222
                                            }
1223
                                            // Leave that second part behind now.
1224
                                            $source = substr(
1225
                                                $source,
1226
                                                0,
1227
                                                strpos($source, '?')
1228
                                            );
1229
                                            if (strpos($source, '://') > 0) {
1230
                                                if (false !== strpos($source, api_get_path(WEB_PATH))) {
1231
                                                    // We found the current portal url.
1232
                                                    $files_list[] = [
1233
                                                        $source,
1234
                                                        'local',
1235
                                                        'url',
1236
                                                    ];
1237
                                                    $in_files_list[] = self::get_resources_from_source(
1238
                                                        TOOL_DOCUMENT,
1239
                                                        $source,
1240
                                                        $recursivity + 1
1241
                                                    );
1242
                                                    if (count($in_files_list) > 0) {
1243
                                                        $files_list = array_merge(
1244
                                                            $files_list,
1245
                                                            $in_files_list
1246
                                                        );
1247
                                                    }
1248
                                                } else {
1249
                                                    // We didn't find any trace of current portal.
1250
                                                    $files_list[] = [
1251
                                                        $source,
1252
                                                        'remote',
1253
                                                        'url',
1254
                                                    ];
1255
                                                }
1256
                                            } else {
1257
                                                // No protocol found, make link local.
1258
                                                if ('/' === substr($source, 0, 1)) {
1259
                                                    // Link starts with a /, making it absolute (relative to DocumentRoot).
1260
                                                    $files_list[] = [
1261
                                                        $source,
1262
                                                        'local',
1263
                                                        'abs',
1264
                                                    ];
1265
                                                    $in_files_list[] = self::get_resources_from_source(
1266
                                                        TOOL_DOCUMENT,
1267
                                                        $source,
1268
                                                        $recursivity + 1
1269
                                                    );
1270
                                                    if (count($in_files_list) > 0) {
1271
                                                        $files_list = array_merge(
1272
                                                            $files_list,
1273
                                                            $in_files_list
1274
                                                        );
1275
                                                    }
1276
                                                } elseif (0 === strstr($source, '..')) {
1277
                                                    // Link is relative but going back in the hierarchy.
1278
                                                    $files_list[] = [
1279
                                                        $source,
1280
                                                        'local',
1281
                                                        'rel',
1282
                                                    ];
1283
                                                    $dir = dirname(
1284
                                                        $abs_path
1285
                                                    );
1286
                                                    $new_abs_path = realpath(
1287
                                                        $dir.'/'.$source
1288
                                                    );
1289
                                                    $in_files_list[] = self::get_resources_from_source(
1290
                                                        TOOL_DOCUMENT,
1291
                                                        $new_abs_path,
1292
                                                        $recursivity + 1
1293
                                                    );
1294
                                                    if (count($in_files_list) > 0) {
1295
                                                        $files_list = array_merge(
1296
                                                            $files_list,
1297
                                                            $in_files_list
1298
                                                        );
1299
                                                    }
1300
                                                } else {
1301
                                                    // No starting '/', making it relative to current document's path.
1302
                                                    if ('./' == substr($source, 0, 2)) {
1303
                                                        $source = substr(
1304
                                                            $source,
1305
                                                            2
1306
                                                        );
1307
                                                    }
1308
                                                    $files_list[] = [
1309
                                                        $source,
1310
                                                        'local',
1311
                                                        'rel',
1312
                                                    ];
1313
                                                    $dir = dirname(
1314
                                                        $abs_path
1315
                                                    );
1316
                                                    $new_abs_path = realpath(
1317
                                                        $dir.'/'.$source
1318
                                                    );
1319
                                                    $in_files_list[] = self::get_resources_from_source(
1320
                                                        TOOL_DOCUMENT,
1321
                                                        $new_abs_path,
1322
                                                        $recursivity + 1
1323
                                                    );
1324
                                                    if (count($in_files_list) > 0) {
1325
                                                        $files_list = array_merge(
1326
                                                            $files_list,
1327
                                                            $in_files_list
1328
                                                        );
1329
                                                    }
1330
                                                }
1331
                                            }
1332
                                        }
1333
1334
                                        // Found some protocol there.
1335
                                        if (false !== strpos($source, api_get_path(WEB_PATH))) {
1336
                                            // We found the current portal url.
1337
                                            $files_list[] = [
1338
                                                $source,
1339
                                                'local',
1340
                                                'url',
1341
                                            ];
1342
                                            $in_files_list[] = self::get_resources_from_source(
1343
                                                TOOL_DOCUMENT,
1344
                                                $source,
1345
                                                $recursivity + 1
1346
                                            );
1347
                                            if (count($in_files_list) > 0) {
1348
                                                $files_list = array_merge(
1349
                                                    $files_list,
1350
                                                    $in_files_list
1351
                                                );
1352
                                            }
1353
                                        } else {
1354
                                            // We didn't find any trace of current portal.
1355
                                            $files_list[] = [
1356
                                                $source,
1357
                                                'remote',
1358
                                                'url',
1359
                                            ];
1360
                                        }
1361
                                    } else {
1362
                                        // No protocol found, make link local.
1363
                                        if ('/' === substr($source, 0, 1)) {
1364
                                            // Link starts with a /, making it absolute (relative to DocumentRoot).
1365
                                            $files_list[] = [
1366
                                                $source,
1367
                                                'local',
1368
                                                'abs',
1369
                                            ];
1370
                                            $in_files_list[] = self::get_resources_from_source(
1371
                                                TOOL_DOCUMENT,
1372
                                                $source,
1373
                                                $recursivity + 1
1374
                                            );
1375
                                            if (count($in_files_list) > 0) {
1376
                                                $files_list = array_merge(
1377
                                                    $files_list,
1378
                                                    $in_files_list
1379
                                                );
1380
                                            }
1381
                                        } elseif (0 === strstr($source, '..')) {
1382
                                            // Link is relative but going back in the hierarchy.
1383
                                            $files_list[] = [
1384
                                                $source,
1385
                                                'local',
1386
                                                'rel',
1387
                                            ];
1388
                                            $dir = dirname($abs_path);
1389
                                            $new_abs_path = realpath(
1390
                                                $dir.'/'.$source
1391
                                            );
1392
                                            $in_files_list[] = self::get_resources_from_source(
1393
                                                TOOL_DOCUMENT,
1394
                                                $new_abs_path,
1395
                                                $recursivity + 1
1396
                                            );
1397
                                            if (count($in_files_list) > 0) {
1398
                                                $files_list = array_merge(
1399
                                                    $files_list,
1400
                                                    $in_files_list
1401
                                                );
1402
                                            }
1403
                                        } else {
1404
                                            // No starting '/', making it relative to current document's path.
1405
                                            if (strpos($source, 'width=') ||
1406
                                                strpos($source, 'autostart=')
1407
                                            ) {
1408
                                                continue;
1409
                                            }
1410
1411
                                            if ('./' == substr($source, 0, 2)) {
1412
                                                $source = substr(
1413
                                                    $source,
1414
                                                    2
1415
                                                );
1416
                                            }
1417
                                            $files_list[] = [
1418
                                                $source,
1419
                                                'local',
1420
                                                'rel',
1421
                                            ];
1422
                                            $dir = dirname($abs_path);
1423
                                            $new_abs_path = realpath(
1424
                                                $dir.'/'.$source
1425
                                            );
1426
                                            $in_files_list[] = self::get_resources_from_source(
1427
                                                TOOL_DOCUMENT,
1428
                                                $new_abs_path,
1429
                                                $recursivity + 1
1430
                                            );
1431
                                            if (count($in_files_list) > 0) {
1432
                                                $files_list = array_merge(
1433
                                                    $files_list,
1434
                                                    $in_files_list
1435
                                                );
1436
                                            }
1437
                                        }
1438
                                    }
1439
                                }
1440
                            }
1441
                        }
1442
                        break;
1443
                    default:
1444
                        break;
1445
                }
1446
1447
                break;
1448
            default: // Ignore.
1449
                break;
1450
        }
1451
1452
        $checked_files_list = [];
1453
        $checked_array_list = [];
1454
        foreach ($files_list as $idx => $file) {
1455
            if (!empty($file[0])) {
1456
                if (!in_array($file[0], $checked_files_list)) {
1457
                    $checked_files_list[] = $files_list[$idx][0];
1458
                    $checked_array_list[] = $files_list[$idx];
1459
                }
1460
            }
1461
        }
1462
1463
        return $checked_array_list;
1464
    }
1465
1466
    /**
1467
     * Gets the score.
1468
     *
1469
     * @return float The current score or 0 if no score set yet
1470
     */
1471
    public function get_score()
1472
    {
1473
        $res = 0;
1474
        if (!empty($this->current_score)) {
1475
            $res = $this->current_score;
1476
        }
1477
1478
        return $res;
1479
    }
1480
1481
    /**
1482
     * Gets the item status.
1483
     *
1484
     * @param bool $check_db     Do or don't check into the database for the
1485
     *                           latest value. Optional. Default is true
1486
     * @param bool $update_local Do or don't update the local attribute
1487
     *                           value with what's been found in DB
1488
     *
1489
     * @return string Current status or 'Not attempted' if no status set yet
1490
     */
1491
    public function get_status($check_db = true, $update_local = false)
1492
    {
1493
        $debug = self::DEBUG;
1494
        if ($debug) {
1495
            error_log('learnpathItem::get_status() on item '.$this->db_id);
1496
        }
1497
        if ($check_db) {
1498
            if ($debug) {
1499
                error_log('learnpathItem::get_status(): checking db');
1500
            }
1501
            if (!empty($this->db_item_view_id)) {
1502
                $table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
1503
                $sql = "SELECT status FROM $table
1504
                        WHERE
1505
                            iid = '".$this->db_item_view_id."' AND
1506
                            view_count = '".$this->get_attempt_id()."'";
1507
                $res = Database::query($sql);
1508
                if (1 == Database::num_rows($res)) {
1509
                    $row = Database::fetch_array($res);
1510
                    if ($update_local) {
1511
                        $this->set_status($row['status']);
1512
                    }
1513
1514
                    return $row['status'];
1515
                }
1516
            }
1517
        } else {
1518
            if (!empty($this->status)) {
1519
                return $this->status;
1520
            }
1521
        }
1522
1523
        return $this->possible_status[0];
1524
    }
1525
1526
    /**
1527
     * Gets the suspend data.
1528
     */
1529
    public function get_suspend_data()
1530
    {
1531
        // TODO: Improve cleaning of breaklines ... it works but is it really
1532
        // a beautiful way to do it ?
1533
        if (!empty($this->current_data)) {
1534
            return str_replace(
1535
                ["\r", "\n", "'"],
1536
                ['\r', '\n', "\\'"],
1537
                $this->current_data
1538
            );
1539
        }
1540
1541
        return '';
1542
    }
1543
1544
    /**
1545
     * @param string $origin
1546
     * @param string $time
1547
     *
1548
     * @return string
1549
     */
1550
    public static function getScormTimeFromParameter(
1551
        $origin = 'php',
1552
        $time = null
1553
    ) {
1554
        $h = get_lang('h');
1555
        if (!isset($time)) {
1556
            if ('js' == $origin) {
1557
                return '00 : 00: 00';
1558
            }
1559
1560
            return '00 '.$h.' 00 \' 00"';
1561
        }
1562
1563
        return api_format_time($time, $origin);
1564
    }
1565
1566
    /**
1567
     * Gets the total time spent on this item view so far.
1568
     *
1569
     * @param string   $origin     Origin of the request. If coming from PHP,
1570
     *                             send formatted as xxhxx'xx", otherwise use scorm format 00:00:00
1571
     * @param int|null $given_time Given time is a default time to return formatted
1572
     * @param bool     $query_db   Whether to get the value from db or from memory
1573
     *
1574
     * @return string A string with the time in SCORM format
1575
     */
1576
    public function get_scorm_time(
1577
        $origin = 'php',
1578
        $given_time = null,
1579
        $query_db = false
1580
    ) {
1581
        $time = null;
1582
        $courseId = $this->courseId;
1583
        if (empty($courseId)) {
1584
            $courseId = api_get_course_int_id();
1585
        }
1586
1587
        $courseId = (int) $courseId;
1588
        if (!isset($given_time)) {
1589
            if (self::DEBUG > 2) {
1590
                error_log(
1591
                    'learnpathItem::get_scorm_time(): given time empty, current_start_time = '.$this->current_start_time,
1592
                    0
1593
                );
1594
            }
1595
            if (true === $query_db) {
1596
                $table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
1597
                $sql = "SELECT start_time, total_time
1598
                        FROM $table
1599
                        WHERE
1600
                            iid = '".$this->db_item_view_id."' AND
1601
                            view_count = '".$this->get_attempt_id()."'";
1602
                $res = Database::query($sql);
1603
                $row = Database::fetch_array($res);
1604
                $start = $row['start_time'];
1605
                $stop = $start + $row['total_time'];
1606
            } else {
1607
                $start = $this->current_start_time;
1608
                $stop = $this->current_stop_time;
1609
            }
1610
            if (!empty($start)) {
1611
                if (!empty($stop)) {
1612
                    $time = $stop - $start;
1613
                } else {
1614
                    $time = time() - $start;
1615
                }
1616
            }
1617
        } else {
1618
            $time = $given_time;
1619
        }
1620
        if (self::DEBUG > 2) {
1621
            error_log(
1622
                'learnpathItem::get_scorm_time(): intermediate = '.$time,
1623
                0
1624
            );
1625
        }
1626
        $time = api_format_time($time, $origin);
1627
1628
        return $time;
1629
    }
1630
1631
    /**
1632
     * Get the extra terms (tags) that identify this item.
1633
     *
1634
     * @return mixed
1635
     */
1636
    public function get_terms()
1637
    {
1638
        $table = Database::get_course_table(TABLE_LP_ITEM);
1639
        $sql = "SELECT * FROM $table
1640
                WHERE iid = ".intval($this->db_id);
1641
        $res = Database::query($sql);
1642
        $row = Database::fetch_array($res);
1643
1644
        return $row['terms'];
1645
    }
1646
1647
    /**
1648
     * Returns the item's title.
1649
     *
1650
     * @return string Title
1651
     */
1652
    public function get_title()
1653
    {
1654
        if (empty($this->title)) {
1655
            return '';
1656
        }
1657
1658
        return $this->title;
1659
    }
1660
1661
    /**
1662
     * Returns the total time used to see that item.
1663
     *
1664
     * @return int Total time
1665
     */
1666
    public function get_total_time()
1667
    {
1668
        $debug = self::DEBUG;
1669
        if ($debug) {
1670
            error_log(
1671
                'learnpathItem::get_total_time() for item '.$this->db_id.
1672
                ' - Initially, current_start_time = '.$this->current_start_time.
1673
                ' and current_stop_time = '.$this->current_stop_time,
1674
                0
1675
            );
1676
        }
1677
        if (0 == $this->current_start_time) {
1678
            // Shouldn't be necessary thanks to the open() method.
1679
            if ($debug) {
1680
                error_log(
1681
                    'learnpathItem::get_total_time() - Current start time was empty',
1682
                    0
1683
                );
1684
            }
1685
            $this->current_start_time = time();
1686
        }
1687
1688
        if (time() < $this->current_stop_time ||
1689
            0 == $this->current_stop_time
1690
        ) {
1691
            if ($debug) {
1692
                error_log(
1693
                    'learnpathItem::get_total_time() - Current stop time was '
1694
                    .'greater than the current time or was empty',
1695
                    0
1696
                );
1697
            }
1698
            // If this case occurs, then we risk to write huge time data in db.
1699
            // In theory, stop time should be *always* updated here, but it
1700
            // might be used in some unknown goal.
1701
            $this->current_stop_time = time();
1702
        }
1703
1704
        $time = $this->current_stop_time - $this->current_start_time;
1705
1706
        if ($time < 0) {
1707
            if ($debug) {
1708
                error_log(
1709
                    'learnpathItem::get_total_time() - Time smaller than 0. Returning 0',
1710
                    0
1711
                );
1712
            }
1713
1714
            return 0;
1715
        } else {
1716
            $time = $this->fixAbusiveTime($time);
1717
            if ($debug) {
1718
                error_log(
1719
                    'Current start time = '.$this->current_start_time.', current stop time = '.
1720
                    $this->current_stop_time.' Returning '.$time."-----------\n"
1721
                );
1722
            }
1723
1724
            return $time;
1725
        }
1726
    }
1727
1728
    /**
1729
     * Sometimes time recorded for a learning path item is superior to the maximum allowed duration of the session.
1730
     * In this case, this session resets the time for that particular learning path item to 5 minutes
1731
     * (something more realistic, that is also used when leaving the portal without closing one's session).
1732
     *
1733
     * @param int $time
1734
     *
1735
     * @return int
1736
     */
1737
    public function fixAbusiveTime($time)
1738
    {
1739
        // Code based from Event::courseLogout
1740
        $sessionLifetime = api_get_configuration_value('session_lifetime');
1741
        // If session life time too big use 1 hour
1742
        if (empty($sessionLifetime) || $sessionLifetime > 86400) {
1743
            $sessionLifetime = 3600;
1744
        }
1745
1746
        if (!Tracking::minimumTimeAvailable(api_get_session_id(), api_get_course_int_id())) {
1747
            $fixedAddedMinute = 5 * 60; // Add only 5 minutes
1748
            if ($time > $sessionLifetime) {
1749
                error_log("fixAbusiveTime: Total time is too big: $time replaced with: $fixedAddedMinute");
1750
                error_log("item_id : ".$this->db_id." lp_item_view.iid: ".$this->db_item_view_id);
1751
                $time = $fixedAddedMinute;
1752
            }
1753
1754
            return $time;
1755
        } else {
1756
            // Calulate minimum and accumulated time
1757
            $user_id = api_get_user_id();
1758
            $myLP = learnpath::getLpFromSession(api_get_course_int_id(), $this->lp_id, $user_id);
1759
            $timeLp = $myLP->getAccumulateWorkTime();
1760
            $timeTotalCourse = $myLP->getAccumulateWorkTimeTotalCourse();
1761
            /*
1762
            $timeLp = $_SESSION['oLP']->getAccumulateWorkTime();
1763
            $timeTotalCourse = $_SESSION['oLP']->getAccumulateWorkTimeTotalCourse();
1764
            */
1765
            // Minimum connection percentage
1766
            $perc = 100;
1767
            // Time from the course
1768
            $tc = $timeTotalCourse;
1769
            /*if (!empty($sessionId) && $sessionId != 0) {
1770
                $sql = "SELECT hours, perc FROM plugin_licences_course_session WHERE session_id = $sessionId";
1771
                $res = Database::query($sql);
1772
                if (Database::num_rows($res) > 0) {
1773
                    $aux = Database::fetch_assoc($res);
1774
                    $perc = $aux['perc'];
1775
                    $tc = $aux['hours'] * 60;
1776
                }
1777
            }*/
1778
            // Percentage of the learning paths
1779
            $pl = 0;
1780
            if (!empty($timeTotalCourse)) {
1781
                $pl = $timeLp / $timeTotalCourse;
1782
            }
1783
1784
            // Minimum time for each learning path
1785
            $accumulateWorkTime = ($pl * $tc * $perc / 100);
1786
            $time_seg = intval($accumulateWorkTime * 60);
1787
1788
            if ($time_seg < $sessionLifetime) {
1789
                $sessionLifetime = $time_seg;
1790
            }
1791
1792
            if ($time > $sessionLifetime) {
1793
                $fixedAddedMinute = $time_seg + mt_rand(0, 300);
1794
                if (self::DEBUG > 2) {
1795
                    error_log("Total time is too big: $time replaced with: $fixedAddedMinute");
1796
                }
1797
                $time = $fixedAddedMinute;
1798
            }
1799
1800
            return $time;
1801
        }
1802
    }
1803
1804
    /**
1805
     * Gets the item type.
1806
     *
1807
     * @return string The item type (can be doc, dir, sco, asset)
1808
     */
1809
    public function get_type()
1810
    {
1811
        $res = 'asset';
1812
        if (!empty($this->type)) {
1813
            $res = $this->type;
1814
        }
1815
1816
        return $res;
1817
    }
1818
1819
    /**
1820
     * Gets the view count for this item.
1821
     *
1822
     * @return int Number of attempts or 0
1823
     */
1824
    public function get_view_count()
1825
    {
1826
        if (!empty($this->attempt_id)) {
1827
            return $this->attempt_id;
1828
        }
1829
1830
        return 0;
1831
    }
1832
1833
    /**
1834
     * Tells if an item is done ('completed','passed','succeeded') or not.
1835
     *
1836
     * @return bool True if the item is done ('completed','passed','succeeded'),
1837
     *              false otherwise
1838
     */
1839
    public function is_done()
1840
    {
1841
        $completedStatusList = [
1842
            'completed',
1843
            'passed',
1844
            'succeeded',
1845
            'failed',
1846
        ];
1847
1848
        if ($this->status_is($completedStatusList)) {
1849
            return true;
1850
        }
1851
1852
        return false;
1853
    }
1854
1855
    /**
1856
     * Tells if a restart is allowed (take it from $this->prevent_reinit and $this->status).
1857
     *
1858
     * @return int -1 if retaking the sco another time for credit is not allowed,
1859
     *             0 if it is not allowed but the item has to be finished
1860
     *             1 if it is allowed. Defaults to 1
1861
     */
1862
    public function isRestartAllowed()
1863
    {
1864
        $restart = 1;
1865
        $mystatus = $this->get_status(true);
1866
        if ($this->get_prevent_reinit() > 0) {
1867
            // If prevent_reinit == 1 (or more)
1868
            // If status is not attempted or incomplete, authorize retaking (of the same) anyway. Otherwise:
1869
            if ($mystatus != $this->possible_status[0] && $mystatus != $this->possible_status[1]) {
1870
                $restart = -1;
1871
            } else { //status incompleted or not attempted
1872
                $restart = 0;
1873
            }
1874
        } else {
1875
            if ($mystatus == $this->possible_status[0] || $mystatus == $this->possible_status[1]) {
1876
                $restart = -1;
1877
            }
1878
        }
1879
1880
        return $restart;
1881
    }
1882
1883
    /**
1884
     * Opens/launches the item. Initialises runtime values.
1885
     *
1886
     * @param bool $allow_new_attempt
1887
     *
1888
     * @return bool true on success, false on failure
1889
     */
1890
    public function open($allow_new_attempt = false)
1891
    {
1892
        if (0 == $this->prevent_reinit) {
1893
            $this->current_score = 0;
1894
            $this->current_start_time = time();
1895
            // In this case, as we are opening the item, what is important to us
1896
            // is the database status, in order to know if this item has already
1897
            // been used in the past (rather than just loaded and modified by
1898
            // some javascript but not written in the database).
1899
            // If the database status is different from 'not attempted', we can
1900
            // consider this item has already been used, and as such we can
1901
            // open a new attempt. Otherwise, we'll just reuse the current
1902
            // attempt, which is generally created the first time the item is
1903
            // loaded (for example as part of the table of contents).
1904
            $stat = $this->get_status(true);
1905
            if ($allow_new_attempt && isset($stat) && ($stat != $this->possible_status[0])) {
1906
                $this->attempt_id = $this->attempt_id + 1; // Open a new attempt.
1907
            }
1908
            $this->status = $this->possible_status[1];
1909
        } else {
1910
            /*if ($this->current_start_time == 0) {
1911
                // Small exception for start time, to avoid amazing values.
1912
                $this->current_start_time = time();
1913
            }*/
1914
            // If we don't init start time here, the time is sometimes calculated from the last start time.
1915
            $this->current_start_time = time();
1916
        }
1917
    }
1918
1919
    /**
1920
     * Outputs the item contents.
1921
     *
1922
     * @return string HTML file (displayable in an <iframe>) or empty string if no path defined
1923
     */
1924
    public function output()
1925
    {
1926
        if (!empty($this->path) and is_file($this->path)) {
1927
            $output = '';
1928
            $output .= file_get_contents($this->path);
1929
1930
            return $output;
1931
        }
1932
1933
        return '';
1934
    }
1935
1936
    /**
1937
     * Parses the prerequisites string with the AICC logic language.
1938
     *
1939
     * @param string $prereqs_string The prerequisites string as it figures in imsmanifest.xml
1940
     * @param array  $items          Array of items in the current learnpath object.
1941
     *                               Although we're in the learnpathItem object, it's necessary to have
1942
     *                               a list of all items to be able to check the current item's prerequisites
1943
     * @param array  $refs_list      list of references
1944
     *                               (the "ref" column in the lp_item table) that are strings used in the
1945
     *                               expression of prerequisites
1946
     * @param int    $user_id        The user ID. In some cases like Chamilo quizzes,
1947
     *                               it's necessary to have the user ID to query other tables (like the results of quizzes)
1948
     *
1949
     * @return bool True if the list of prerequisites given is entirely satisfied, false otherwise
1950
     */
1951
    public function parse_prereq($prereqs_string, $items, $refs_list, $user_id)
1952
    {
1953
        $debug = self::DEBUG;
1954
        if ($debug > 0) {
1955
            error_log(
1956
                'learnpathItem::parse_prereq() for learnpath '.$this->lp_id.' with string '.$prereqs_string,
1957
                0
1958
            );
1959
        }
1960
1961
        $courseId = $this->courseId;
1962
        $sessionId = api_get_session_id();
1963
1964
        // Deal with &, |, ~, =, <>, {}, ,, X*, () in reverse order.
1965
        $this->prereq_alert = '';
1966
1967
        // First parse all parenthesis by using a sequential loop
1968
        //  (looking for less-inclusives first).
1969
        if ('_true_' == $prereqs_string) {
1970
            return true;
1971
        }
1972
1973
        if ('_false_' == $prereqs_string) {
1974
            if (empty($this->prereq_alert)) {
1975
                $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.');
1976
            }
1977
1978
            return false;
1979
        }
1980
1981
        while (false !== strpos($prereqs_string, '(')) {
1982
            // Remove any () set and replace with its value.
1983
            $matches = [];
1984
            $res = preg_match_all(
1985
                '/(\(([^\(\)]*)\))/',
1986
                $prereqs_string,
1987
                $matches
1988
            );
1989
            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...
1990
                foreach ($matches[2] as $id => $match) {
1991
                    $str_res = $this->parse_prereq(
1992
                        $match,
1993
                        $items,
1994
                        $refs_list,
1995
                        $user_id
1996
                    );
1997
                    if ($str_res) {
1998
                        $prereqs_string = str_replace(
1999
                            $matches[1][$id],
2000
                            '_true_',
2001
                            $prereqs_string
2002
                        );
2003
                    } else {
2004
                        $prereqs_string = str_replace(
2005
                            $matches[1][$id],
2006
                            '_false_',
2007
                            $prereqs_string
2008
                        );
2009
                    }
2010
                }
2011
            }
2012
        }
2013
2014
        // Parenthesis removed, now look for ORs as it is the lesser-priority
2015
        //  binary operator (= always uses one text operand).
2016
        if (false === strpos($prereqs_string, '|')) {
2017
            if ($debug) {
2018
                error_log('New LP - Didnt find any OR, looking for AND', 0);
2019
            }
2020
            if (false !== strpos($prereqs_string, '&')) {
2021
                $list = explode('&', $prereqs_string);
2022
                if (count($list) > 1) {
2023
                    $andstatus = true;
2024
                    foreach ($list as $condition) {
2025
                        $andstatus = $andstatus && $this->parse_prereq(
2026
                            $condition,
2027
                            $items,
2028
                            $refs_list,
2029
                            $user_id
2030
                        );
2031
2032
                        if (!$andstatus) {
2033
                            if ($debug) {
2034
                                error_log(
2035
                                    'New LP - One condition in AND was false, short-circuit',
2036
                                    0
2037
                                );
2038
                            }
2039
                            break;
2040
                        }
2041
                    }
2042
2043
                    if (empty($this->prereq_alert) && !$andstatus) {
2044
                        $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.');
2045
                    }
2046
2047
                    return $andstatus;
2048
                } else {
2049
                    if (isset($items[$refs_list[$list[0]]])) {
2050
                        $status = $items[$refs_list[$list[0]]]->get_status(true);
2051
                        $returnstatus = ($status == $this->possible_status[2]) || ($status == $this->possible_status[3]);
2052
                        if (empty($this->prereq_alert) && !$returnstatus) {
2053
                            $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.');
2054
                        }
2055
2056
                        return $returnstatus;
2057
                    }
2058
                    $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.');
2059
2060
                    return false;
2061
                }
2062
            } else {
2063
                // No ORs found, now look for ANDs.
2064
                if ($debug) {
2065
                    error_log('New LP - Didnt find any AND, looking for =', 0);
2066
                }
2067
2068
                if (false !== strpos($prereqs_string, '=')) {
2069
                    if ($debug) {
2070
                        error_log('New LP - Found =, looking into it', 0);
2071
                    }
2072
                    // We assume '=' signs only appear when there's nothing else around.
2073
                    $params = explode('=', $prereqs_string);
2074
                    if (2 == count($params)) {
2075
                        // Right number of operands.
2076
                        if (isset($items[$refs_list[$params[0]]])) {
2077
                            $status = $items[$refs_list[$params[0]]]->get_status(true);
2078
                            $returnstatus = $status == $params[1];
2079
                            if (empty($this->prereq_alert) && !$returnstatus) {
2080
                                $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.');
2081
                            }
2082
2083
                            return $returnstatus;
2084
                        }
2085
                        $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.');
2086
2087
                        return false;
2088
                    }
2089
                } else {
2090
                    // No ANDs found, look for <>
2091
                    if ($debug) {
2092
                        error_log(
2093
                            'New LP - Didnt find any =, looking for <>',
2094
                            0
2095
                        );
2096
                    }
2097
2098
                    if (false !== strpos($prereqs_string, '<>')) {
2099
                        if ($debug) {
2100
                            error_log('New LP - Found <>, looking into it', 0);
2101
                        }
2102
                        // We assume '<>' signs only appear when there's nothing else around.
2103
                        $params = explode('<>', $prereqs_string);
2104
                        if (2 == count($params)) {
2105
                            // Right number of operands.
2106
                            if (isset($items[$refs_list[$params[0]]])) {
2107
                                $status = $items[$refs_list[$params[0]]]->get_status(true);
2108
                                $returnstatus = $status != $params[1];
2109
                                if (empty($this->prereq_alert) && !$returnstatus) {
2110
                                    $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.');
2111
                                }
2112
2113
                                return $returnstatus;
2114
                            }
2115
                            $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.');
2116
2117
                            return false;
2118
                        }
2119
                    } else {
2120
                        // No <> found, look for ~ (unary)
2121
                        if ($debug) {
2122
                            error_log(
2123
                                'New LP - Didnt find any =, looking for ~',
2124
                                0
2125
                            );
2126
                        }
2127
                        // Only remains: ~ and X*{}
2128
                        if (false !== strpos($prereqs_string, '~')) {
2129
                            // Found NOT.
2130
                            if ($debug) {
2131
                                error_log(
2132
                                    'New LP - Found ~, looking into it',
2133
                                    0
2134
                                );
2135
                            }
2136
                            $list = [];
2137
                            $myres = preg_match(
2138
                                '/~([^(\d+\*)\{]*)/',
2139
                                $prereqs_string,
2140
                                $list
2141
                            );
2142
                            if ($myres) {
2143
                                $returnstatus = !$this->parse_prereq(
2144
                                    $list[1],
2145
                                    $items,
2146
                                    $refs_list,
2147
                                    $user_id
2148
                                );
2149
                                if (empty($this->prereq_alert) && !$returnstatus) {
2150
                                    $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.');
2151
                                }
2152
2153
                                return $returnstatus;
2154
                            } else {
2155
                                // Strange...
2156
                                if ($debug) {
2157
                                    error_log(
2158
                                        'New LP - Found ~ but strange string: '.$prereqs_string,
2159
                                        0
2160
                                    );
2161
                                }
2162
                            }
2163
                        } else {
2164
                            // Finally, look for sets/groups
2165
                            if ($debug) {
2166
                                error_log(
2167
                                    'New LP - Didnt find any ~, looking for groups',
2168
                                    0
2169
                                );
2170
                            }
2171
                            // Only groups here.
2172
                            $groups = [];
2173
                            $groups_there = preg_match_all(
2174
                                '/((\d+\*)?\{([^\}]+)\}+)/',
2175
                                $prereqs_string,
2176
                                $groups
2177
                            );
2178
2179
                            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...
2180
                                foreach ($groups[1] as $gr) {
2181
                                    // Only take the results that correspond to
2182
                                    //  the big brackets-enclosed condition.
2183
                                    if ($debug) {
2184
                                        error_log(
2185
                                            'New LP - Dealing with group '.$gr,
2186
                                            0
2187
                                        );
2188
                                    }
2189
                                    $multi = [];
2190
                                    $mycond = false;
2191
                                    if (preg_match(
2192
                                        '/(\d+)\*\{([^\}]+)\}/',
2193
                                        $gr,
2194
                                        $multi
2195
                                    )
2196
                                    ) {
2197
                                        if ($debug) {
2198
                                            error_log(
2199
                                                'New LP - Found multiplier '.$multi[0],
2200
                                                0
2201
                                            );
2202
                                        }
2203
                                        $count = $multi[1];
2204
                                        $list = explode(',', $multi[2]);
2205
                                        $mytrue = 0;
2206
                                        foreach ($list as $cond) {
2207
                                            if (isset($items[$refs_list[$cond]])) {
2208
                                                $status = $items[$refs_list[$cond]]->get_status(true);
2209
                                                if ($status == $this->possible_status[2] ||
2210
                                                    $status == $this->possible_status[3]
2211
                                                ) {
2212
                                                    $mytrue++;
2213
                                                    if ($debug) {
2214
                                                        error_log(
2215
                                                            'New LP - Found true item, counting.. ('.($mytrue).')',
2216
                                                            0
2217
                                                        );
2218
                                                    }
2219
                                                }
2220
                                            } else {
2221
                                                if ($debug) {
2222
                                                    error_log(
2223
                                                        'New LP - item '.$cond.' does not exist in items list',
2224
                                                        0
2225
                                                    );
2226
                                                }
2227
                                            }
2228
                                        }
2229
                                        if ($mytrue >= $count) {
2230
                                            if ($debug) {
2231
                                                error_log(
2232
                                                    'New LP - Got enough true results, return true',
2233
                                                    0
2234
                                                );
2235
                                            }
2236
                                            $mycond = true;
2237
                                        } else {
2238
                                            if ($debug) {
2239
                                                error_log(
2240
                                                    'New LP - Not enough true results',
2241
                                                    0
2242
                                                );
2243
                                            }
2244
                                        }
2245
                                    } else {
2246
                                        if ($debug) {
2247
                                            error_log(
2248
                                                'New LP - No multiplier',
2249
                                                0
2250
                                            );
2251
                                        }
2252
                                        $list = explode(',', $gr);
2253
                                        $mycond = true;
2254
                                        foreach ($list as $cond) {
2255
                                            if (isset($items[$refs_list[$cond]])) {
2256
                                                $status = $items[$refs_list[$cond]]->get_status(true);
2257
                                                if ($status == $this->possible_status[2] ||
2258
                                                    $status == $this->possible_status[3]
2259
                                                ) {
2260
                                                    $mycond = true;
2261
                                                    if ($debug) {
2262
                                                        error_log(
2263
                                                            'New LP - Found true item',
2264
                                                            0
2265
                                                        );
2266
                                                    }
2267
                                                } else {
2268
                                                    if ($debug) {
2269
                                                        error_log(
2270
                                                            'New LP - '.
2271
                                                            ' Found false item, the set is not true, return false',
2272
                                                            0
2273
                                                        );
2274
                                                    }
2275
                                                    $mycond = false;
2276
                                                    break;
2277
                                                }
2278
                                            } else {
2279
                                                if ($debug) {
2280
                                                    error_log(
2281
                                                        'New LP - item '.$cond.' does not exist in items list',
2282
                                                        0
2283
                                                    );
2284
                                                }
2285
                                                if ($debug) {
2286
                                                    error_log(
2287
                                                        'New LP - Found false item, the set is not true, return false',
2288
                                                        0
2289
                                                    );
2290
                                                }
2291
                                                $mycond = false;
2292
                                                break;
2293
                                            }
2294
                                        }
2295
                                    }
2296
                                    if (!$mycond && empty($this->prereq_alert)) {
2297
                                        $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.');
2298
                                    }
2299
2300
                                    return $mycond;
2301
                                }
2302
                            } else {
2303
                                // Nothing found there either. Now return the
2304
                                // value of the corresponding resource completion status.
2305
                                if (isset($refs_list[$prereqs_string]) &&
2306
                                    isset($items[$refs_list[$prereqs_string]])
2307
                                ) {
2308
                                    /** @var learnpathItem $itemToCheck */
2309
                                    $itemToCheck = $items[$refs_list[$prereqs_string]];
2310
2311
                                    if ('quiz' === $itemToCheck->type) {
2312
                                        // 1. Checking the status in current items.
2313
                                        $status = $itemToCheck->get_status(true);
2314
                                        $returnstatus = $status == $this->possible_status[2] || $status == $this->possible_status[3];
2315
2316
                                        if (!$returnstatus) {
2317
                                            $explanation = sprintf(
2318
                                                get_lang('Item %s blocks this step'),
2319
                                                $itemToCheck->get_title()
2320
                                            );
2321
                                            $this->prereq_alert = $explanation;
2322
                                        }
2323
2324
                                        // For one and first attempt.
2325
                                        if (1 == $this->prevent_reinit) {
2326
                                            // 2. If is completed we check the results in the DB of the quiz.
2327
                                            if ($returnstatus) {
2328
                                                $sql = 'SELECT score, max_score
2329
                                                        FROM '.Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES).'
2330
                                                        WHERE
2331
                                                            exe_exo_id = '.$items[$refs_list[$prereqs_string]]->path.' AND
2332
                                                            exe_user_id = '.$user_id.' AND
2333
                                                            orig_lp_id = '.$this->lp_id.' AND
2334
                                                            orig_lp_item_id = '.$prereqs_string.' AND
2335
                                                            status <> "incomplete" AND
2336
                                                            c_id = '.$courseId.'
2337
                                                        ORDER BY exe_date DESC
2338
                                                        LIMIT 0, 1';
2339
                                                $rs_quiz = Database::query($sql);
2340
                                                if ($quiz = Database::fetch_array($rs_quiz)) {
2341
                                                    /** @var learnpathItem $myItemToCheck */
2342
                                                    $myItemToCheck = $items[$refs_list[$this->get_id()]];
2343
                                                    $minScore = $myItemToCheck->getPrerequisiteMinScore();
2344
                                                    $maxScore = $myItemToCheck->getPrerequisiteMaxScore();
2345
2346
                                                    if (isset($minScore) && isset($minScore)) {
2347
                                                        // Taking min/max prerequisites values see BT#5776
2348
                                                        if ($quiz['score'] >= $minScore &&
2349
                                                            $quiz['score'] <= $maxScore
2350
                                                        ) {
2351
                                                            $returnstatus = true;
2352
                                                        } else {
2353
                                                            $explanation = sprintf(
2354
                                                                get_lang('Your result at %s blocks this step'),
2355
                                                                $itemToCheck->get_title()
2356
                                                            );
2357
                                                            $this->prereq_alert = $explanation;
2358
                                                            $returnstatus = false;
2359
                                                        }
2360
                                                    } else {
2361
                                                        // Classic way
2362
                                                        if ($quiz['score'] >=
2363
                                                            $items[$refs_list[$prereqs_string]]->get_mastery_score()
2364
                                                        ) {
2365
                                                            $returnstatus = true;
2366
                                                        } else {
2367
                                                            $explanation = sprintf(
2368
                                                                get_lang('Your result at %s blocks this step'),
2369
                                                                $itemToCheck->get_title()
2370
                                                            );
2371
                                                            $this->prereq_alert = $explanation;
2372
                                                            $returnstatus = false;
2373
                                                        }
2374
                                                    }
2375
                                                } else {
2376
                                                    $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.');
2377
                                                    $returnstatus = false;
2378
                                                }
2379
                                            }
2380
                                        } else {
2381
                                            // 3. For multiple attempts we check that there are minimum 1 item completed
2382
                                            // Checking in the database.
2383
                                            $sql = 'SELECT score, max_score
2384
                                                    FROM '.Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES).'
2385
                                                    WHERE
2386
                                                        c_id = '.$courseId.' AND
2387
                                                        exe_exo_id = '.$items[$refs_list[$prereqs_string]]->path.' AND
2388
                                                        exe_user_id = '.$user_id.' AND
2389
                                                        orig_lp_id = '.$this->lp_id.' AND
2390
                                                        orig_lp_item_id = '.$prereqs_string;
2391
2392
                                            $rs_quiz = Database::query($sql);
2393
                                            if (Database::num_rows($rs_quiz) > 0) {
2394
                                                while ($quiz = Database::fetch_array($rs_quiz)) {
2395
                                                    /** @var learnpathItem $myItemToCheck */
2396
                                                    $myItemToCheck = $items[$refs_list[$this->get_id()]];
2397
                                                    $minScore = $myItemToCheck->getPrerequisiteMinScore();
2398
                                                    $maxScore = $myItemToCheck->getPrerequisiteMaxScore();
2399
2400
                                                    if (empty($minScore)) {
2401
                                                        // Try with mastery_score
2402
                                                        $masteryScoreAsMin = $myItemToCheck->get_mastery_score();
2403
                                                        if (!empty($masteryScoreAsMin)) {
2404
                                                            $minScore = $masteryScoreAsMin;
2405
                                                        }
2406
                                                    }
2407
2408
                                                    if (isset($minScore) && isset($minScore)) {
2409
                                                        // Taking min/max prerequisites values see BT#5776
2410
                                                        if ($quiz['score'] >= $minScore && $quiz['score'] <= $maxScore) {
2411
                                                            $returnstatus = true;
2412
                                                            break;
2413
                                                        } else {
2414
                                                            $explanation = sprintf(
2415
                                                                get_lang('Your result at %s blocks this step'),
2416
                                                                $itemToCheck->get_title()
2417
                                                            );
2418
                                                            $this->prereq_alert = $explanation;
2419
                                                            $returnstatus = false;
2420
                                                        }
2421
                                                    } else {
2422
                                                        if ($quiz['score'] >=
2423
                                                            $items[$refs_list[$prereqs_string]]->get_mastery_score()
2424
                                                        ) {
2425
                                                            $returnstatus = true;
2426
                                                            break;
2427
                                                        } else {
2428
                                                            $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.');
2429
                                                            $returnstatus = false;
2430
                                                        }
2431
                                                    }
2432
                                                }
2433
                                            } else {
2434
                                                $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.');
2435
                                                $returnstatus = false;
2436
                                            }
2437
                                        }
2438
2439
                                        if (false === $returnstatus) {
2440
                                            // Check results from another sessions.
2441
                                            $checkOtherSessions = ('true' === api_get_setting('lp.validate_lp_prerequisite_from_other_session'));
2442
                                            if ($checkOtherSessions) {
2443
                                                $returnstatus = $this->getStatusFromOtherSessions(
2444
                                                    $user_id,
2445
                                                    $prereqs_string,
2446
                                                    $refs_list
2447
                                                );
2448
                                            }
2449
                                        }
2450
2451
                                        return $returnstatus;
2452
                                    } elseif ('student_publication' === $itemToCheck->type) {
2453
                                        $workId = $items[$refs_list[$prereqs_string]]->path;
2454
                                        $count = get_work_count_by_student($user_id, $workId);
2455
                                        if ($count >= 1) {
2456
                                            $returnstatus = true;
2457
                                        } else {
2458
                                            $returnstatus = false;
2459
                                            $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.');
2460
                                        }
2461
2462
                                        return $returnstatus;
2463
                                    } else {
2464
                                        $status = $itemToCheck->get_status(true);
2465
                                        if (self::DEBUG) {
2466
                                            error_log('Status:'.$status);
2467
                                        }
2468
                                        $returnstatus = $status == $this->possible_status[2] || $status == $this->possible_status[3];
2469
2470
                                        // Check results from another sessions.
2471
                                        $checkOtherSessions = ('true' === api_get_setting('lp.validate_lp_prerequisite_from_other_session'));
2472
                                        if ($checkOtherSessions && !$returnstatus) {
2473
                                            $returnstatus = $this->getStatusFromOtherSessions(
2474
                                                $user_id,
2475
                                                $prereqs_string,
2476
                                                $refs_list
2477
                                            );
2478
                                        }
2479
2480
                                        if (!$returnstatus) {
2481
                                            $explanation = sprintf(
2482
                                                get_lang('Item %s blocks this step'),
2483
                                                $itemToCheck->get_title()
2484
                                            );
2485
                                            $this->prereq_alert = $explanation;
2486
                                        }
2487
2488
                                        $lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW);
2489
                                        $lp_view = Database::get_course_table(TABLE_LP_VIEW);
2490
2491
                                        if ($returnstatus && 1 == $this->prevent_reinit) {
2492
                                            $sql = "SELECT iid FROM $lp_view
2493
                                                    WHERE
2494
                                                        c_id = $courseId AND
2495
                                                        user_id = $user_id  AND
2496
                                                        lp_id = $this->lp_id AND
2497
                                                        session_id = $sessionId
2498
                                                    LIMIT 0, 1";
2499
                                            $rs_lp = Database::query($sql);
2500
                                            if (Database::num_rows($rs_lp)) {
2501
                                                $lp_id = Database::fetch_row($rs_lp);
2502
                                                $my_lp_id = $lp_id[0];
2503
2504
                                                $sql = "SELECT status FROM $lp_item_view
2505
                                                        WHERE
2506
                                                            lp_view_id = $my_lp_id AND
2507
                                                            lp_item_id = $refs_list[$prereqs_string]
2508
                                                        LIMIT 0, 1";
2509
                                                $rs_lp = Database::query($sql);
2510
                                                $status_array = Database::fetch_row($rs_lp);
2511
                                                $status = $status_array[0];
2512
2513
                                                $returnstatus = $status == $this->possible_status[2] || $status == $this->possible_status[3];
2514
                                                if (!$returnstatus && empty($this->prereq_alert)) {
2515
                                                    $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.');
2516
                                                }
2517
                                            }
2518
2519
                                            if ($checkOtherSessions && false === $returnstatus) {
2520
                                                $returnstatus = $returnstatus = $this->getStatusFromOtherSessions(
2521
                                                    $user_id,
2522
                                                    $prereqs_string,
2523
                                                    $refs_list
2524
                                                );
2525
                                            }
2526
                                        }
2527
2528
                                        return $returnstatus;
2529
                                    }
2530
                                }
2531
                            }
2532
                        }
2533
                    }
2534
                }
2535
            }
2536
        } else {
2537
            $list = explode("\|", $prereqs_string);
2538
            if (count($list) > 1) {
2539
                if (self::DEBUG > 1) {
2540
                    error_log('New LP - Found OR, looking into it', 0);
2541
                }
2542
                $orstatus = false;
2543
                foreach ($list as $condition) {
2544
                    if (self::DEBUG) {
2545
                        error_log(
2546
                            'New LP - Found OR, adding it ('.$condition.')',
2547
                            0
2548
                        );
2549
                    }
2550
                    $orstatus = $orstatus || $this->parse_prereq(
2551
                        $condition,
2552
                        $items,
2553
                        $refs_list,
2554
                        $user_id
2555
                    );
2556
                    if ($orstatus) {
2557
                        // Shortcircuit OR.
2558
                        if (self::DEBUG > 1) {
2559
                            error_log(
2560
                                'New LP - One condition in OR was true, short-circuit',
2561
                                0
2562
                            );
2563
                        }
2564
                        break;
2565
                    }
2566
                }
2567
                if (!$orstatus && empty($this->prereq_alert)) {
2568
                    $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.');
2569
                }
2570
2571
                return $orstatus;
2572
            } else {
2573
                if (self::DEBUG > 1) {
2574
                    error_log(
2575
                        'New LP - OR was found but only one elem present !?',
2576
                        0
2577
                    );
2578
                }
2579
                if (isset($items[$refs_list[$list[0]]])) {
2580
                    $status = $items[$refs_list[$list[0]]]->get_status(true);
2581
                    $returnstatus = 'completed' == $status || 'passed' == $status;
2582
                    if (!$returnstatus && empty($this->prereq_alert)) {
2583
                        $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.');
2584
                    }
2585
2586
                    return $returnstatus;
2587
                }
2588
            }
2589
        }
2590
        if (empty($this->prereq_alert)) {
2591
            $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.');
2592
        }
2593
2594
        if (self::DEBUG > 1) {
2595
            error_log(
2596
                'New LP - End of parse_prereq. Error code is now '.$this->prereq_alert,
2597
                0
2598
            );
2599
        }
2600
2601
        return false;
2602
    }
2603
2604
    /**
2605
     * Reinits all local values as the learnpath is restarted.
2606
     *
2607
     * @return bool True on success, false otherwise
2608
     */
2609
    public function restart()
2610
    {
2611
        if (self::DEBUG > 0) {
2612
            error_log('learnpathItem::restart()', 0);
2613
        }
2614
        $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

2614
        $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...
2615
        //For serious game  : We reuse same attempt_id
2616
        if (1 == $seriousGame && 'sco' == $this->type) {
2617
            // If this is a sco, Chamilo can't update the time without an
2618
            //  explicit scorm call
2619
            $this->current_start_time = 0;
2620
            $this->current_stop_time = 0; //Those 0 value have this effect
2621
            $this->last_scorm_session_time = 0;
2622
            $this->save();
2623
2624
            return true;
2625
        }
2626
2627
        $this->save();
2628
2629
        $allowed = $this->isRestartAllowed();
2630
        if (-1 === $allowed) {
2631
            // Nothing allowed, do nothing.
2632
        } elseif (1 === $allowed) {
2633
            // Restart as new attempt is allowed, record a new attempt.
2634
            $this->attempt_id = $this->attempt_id + 1; // Simply reuse the previous attempt_id.
2635
            $this->current_score = 0;
2636
            $this->current_start_time = 0;
2637
            $this->current_stop_time = 0;
2638
            $this->current_data = '';
2639
            $this->status = $this->possible_status[0];
2640
            $this->interactions_count = 0;
2641
            $this->interactions = [];
2642
            $this->objectives_count = 0;
2643
            $this->objectives = [];
2644
            $this->lesson_location = '';
2645
            if (TOOL_QUIZ != $this->type) {
2646
                $this->write_to_db();
2647
            }
2648
        } else {
2649
            // Restart current element is allowed (because it's not finished yet),
2650
            // reinit current.
2651
            //$this->current_score = 0;
2652
            $this->current_start_time = 0;
2653
            $this->current_stop_time = 0;
2654
            $this->interactions_count = $this->get_interactions_count(true);
2655
        }
2656
2657
        return true;
2658
    }
2659
2660
    /**
2661
     * Saves data in the database.
2662
     *
2663
     * @param bool $from_outside     Save from URL params (1) or from object attributes (0)
2664
     * @param bool $prereqs_complete The results of a check on prerequisites for this item.
2665
     *                               True if prerequisites are completed, false otherwise. Defaults to false. Only used if not sco or au
2666
     *
2667
     * @return bool True on success, false on failure
2668
     */
2669
    public function save($from_outside = true, $prereqs_complete = false)
2670
    {
2671
        $debug = self::DEBUG;
2672
        if ($debug) {
2673
            error_log('learnpathItem::save()', 0);
2674
        }
2675
        // First check if parameters passed via GET can be saved here
2676
        // in case it's a SCORM, we should get:
2677
        if ('sco' === $this->type || 'au' === $this->type) {
2678
            $status = $this->get_status(true);
2679
            if (1 == $this->prevent_reinit &&
2680
                $status != $this->possible_status[0] && // not attempted
2681
                $status != $this->possible_status[1]    //incomplete
2682
            ) {
2683
                if ($debug) {
2684
                    error_log(
2685
                        'learnpathItem::save() - save reinit blocked by setting',
2686
                        0
2687
                    );
2688
                }
2689
                // Do nothing because the status has already been set. Don't allow it to change.
2690
                // TODO: Check there isn't a special circumstance where this should be saved.
2691
            } else {
2692
                if ($debug) {
2693
                    error_log(
2694
                        'learnpathItem::save() - SCORM save request received',
2695
                        0
2696
                    );
2697
                }
2698
2699
                if ($from_outside) {
2700
                    if ($debug) {
2701
                        error_log(
2702
                            'learnpathItem::save() - Getting item data from outside',
2703
                            0
2704
                        );
2705
                    }
2706
                    foreach ($_GET as $param => $value) {
2707
                        switch ($param) {
2708
                            case 'score':
2709
                                $this->set_score($value);
2710
                                if ($debug) {
2711
                                    error_log(
2712
                                        'learnpathItem::save() - setting score to '.$value,
2713
                                        0
2714
                                    );
2715
                                }
2716
                                break;
2717
                            case 'max':
2718
                                $this->set_max_score($value);
2719
                                if ($debug) {
2720
                                    error_log(
2721
                                        'learnpathItem::save() - setting view_max_score to '.$value,
2722
                                        0
2723
                                    );
2724
                                }
2725
                                break;
2726
                            case 'min':
2727
                                $this->min_score = $value;
2728
                                if ($debug) {
2729
                                    error_log(
2730
                                        'learnpathItem::save() - setting min_score to '.$value,
2731
                                        0
2732
                                    );
2733
                                }
2734
                                break;
2735
                            case 'lesson_status':
2736
                                if (!empty($value)) {
2737
                                    $this->set_status($value);
2738
                                    if ($debug) {
2739
                                        error_log(
2740
                                            'learnpathItem::save() - setting status to '.$value,
2741
                                            0
2742
                                        );
2743
                                    }
2744
                                }
2745
                                break;
2746
                            case 'time':
2747
                                $this->set_time($value);
2748
                                if ($debug) {
2749
                                    error_log(
2750
                                        'learnpathItem::save() - setting time to '.$value,
2751
                                        0
2752
                                    );
2753
                                }
2754
                                break;
2755
                            case 'suspend_data':
2756
                                $this->current_data = $value;
2757
                                if ($debug) {
2758
                                    error_log(
2759
                                        'learnpathItem::save() - setting suspend_data to '.$value,
2760
                                        0
2761
                                    );
2762
                                }
2763
                                break;
2764
                            case 'lesson_location':
2765
                                $this->set_lesson_location($value);
2766
                                if ($debug) {
2767
                                    error_log(
2768
                                        'learnpathItem::save() - setting lesson_location to '.$value,
2769
                                        0
2770
                                    );
2771
                                }
2772
                                break;
2773
                            case 'core_exit':
2774
                                $this->set_core_exit($value);
2775
                                if ($debug) {
2776
                                    error_log(
2777
                                        'learnpathItem::save() - setting core_exit to '.$value,
2778
                                        0
2779
                                    );
2780
                                }
2781
                                break;
2782
                            case 'interactions':
2783
                                break;
2784
                            case 'objectives':
2785
                                break;
2786
                            default:
2787
                                // Ignore.
2788
                                break;
2789
                        }
2790
                    }
2791
                } else {
2792
                    // Do nothing, just let the local attributes be used.
2793
                    if ($debug) {
2794
                        error_log(
2795
                            'learnpathItem::save() - Using inside item status',
2796
                            0
2797
                        );
2798
                    }
2799
                }
2800
            }
2801
        } else {
2802
            // If not SCO, such messages should not be expected.
2803
            $type = strtolower($this->type);
2804
            if ($debug) {
2805
                error_log("type: $type");
2806
            }
2807
2808
            switch ($type) {
2809
                case 'asset':
2810
                    if ($prereqs_complete) {
2811
                        $this->set_status($this->possible_status[2]);
2812
                    }
2813
                    break;
2814
                case TOOL_HOTPOTATOES:
2815
                    break;
2816
                case TOOL_QUIZ:
2817
                    return false;
2818
                    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...
2819
                default:
2820
                    // For now, everything that is not sco and not asset is set to
2821
                    // completed when saved.
2822
                    if ($prereqs_complete) {
2823
                        $this->set_status($this->possible_status[2]);
2824
                    }
2825
                    break;
2826
            }
2827
        }
2828
2829
        if ($debug) {
2830
            error_log('End of learnpathItem::save() - Calling write_to_db() now');
2831
        }
2832
2833
        return $this->write_to_db();
2834
    }
2835
2836
    /**
2837
     * Sets the number of attempt_id to a given value.
2838
     *
2839
     * @param int $num The given value to set attempt_id to
2840
     *
2841
     * @return bool TRUE on success, FALSE otherwise
2842
     */
2843
    public function set_attempt_id($num)
2844
    {
2845
        if ($num == strval(intval($num)) && $num >= 0) {
2846
            $this->attempt_id = $num;
2847
2848
            return true;
2849
        }
2850
2851
        return false;
2852
    }
2853
2854
    /**
2855
     * Sets the core_exit value to the one given.
2856
     *
2857
     * @return bool $value  True (always)
2858
     */
2859
    public function set_core_exit($value)
2860
    {
2861
        switch ($value) {
2862
            case '':
2863
                $this->core_exit = '';
2864
                break;
2865
            case 'suspend':
2866
                $this->core_exit = 'suspend';
2867
                break;
2868
            default:
2869
                $this->core_exit = 'none';
2870
                break;
2871
        }
2872
2873
        return true;
2874
    }
2875
2876
    /**
2877
     * Sets the item's description.
2878
     *
2879
     * @param string $string Description
2880
     */
2881
    public function set_description($string = '')
2882
    {
2883
        if (!empty($string)) {
2884
            $this->description = $string;
2885
        }
2886
    }
2887
2888
    /**
2889
     * Sets the lesson_location value.
2890
     *
2891
     * @param string $location lesson_location as provided by the SCO
2892
     *
2893
     * @return bool True on success, false otherwise
2894
     */
2895
    public function set_lesson_location($location)
2896
    {
2897
        if (isset($location)) {
2898
            $this->lesson_location = $location;
2899
2900
            return true;
2901
        }
2902
2903
        return false;
2904
    }
2905
2906
    /**
2907
     * Sets the item's depth level in the LP tree (0 is at root).
2908
     *
2909
     * @param int $int Level
2910
     */
2911
    public function set_level($int = 0)
2912
    {
2913
        $this->level = (int) $int;
2914
    }
2915
2916
    /**
2917
     * Sets the lp_view id this item view is registered to.
2918
     *
2919
     * @param int $lp_view_id lp_view DB ID
2920
     *
2921
     * @todo //todo insert into lp_item_view if lp_view not exists
2922
     */
2923
    public function set_lp_view(int $lp_view_id): bool
2924
    {
2925
        $lpItemId = $this->get_id();
2926
2927
        if (empty($lpItemId)) {
2928
            return false;
2929
        }
2930
2931
        if (empty($lp_view_id)) {
2932
            return false;
2933
        }
2934
2935
        if (self::DEBUG > 0) {
2936
            error_log('learnpathItem::set_lp_view('.$lp_view_id.')', 0);
2937
        }
2938
2939
        $this->view_id = $lp_view_id;
2940
2941
        $item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
2942
        // Get the lp_item_view with the highest view_count.
2943
        $sql = "SELECT * FROM $item_view_table
2944
                WHERE
2945
                    lp_item_id = $lpItemId AND
2946
                    lp_view_id = $lp_view_id
2947
                ORDER BY view_count DESC";
2948
2949
        if (self::DEBUG > 2) {
2950
            error_log(
2951
                'learnpathItem::set_lp_view() - Querying lp_item_view: '.$sql,
2952
                0
2953
            );
2954
        }
2955
        $res = Database::query($sql);
2956
        if (Database::num_rows($res) > 0) {
2957
            $row = Database::fetch_array($res);
2958
            $this->db_item_view_id = $row['iid'];
2959
            $this->attempt_id = $row['view_count'];
2960
            $this->current_score = $row['score'];
2961
            $this->current_data = $row['suspend_data'];
2962
            $this->view_max_score = $row['max_score'];
2963
            $this->status = $row['status'];
2964
            $this->current_start_time = $row['start_time'];
2965
            $this->current_stop_time = $this->current_start_time + $row['total_time'];
2966
            $this->lesson_location = $row['lesson_location'];
2967
            $this->core_exit = $row['core_exit'];
2968
2969
            if (self::DEBUG > 2) {
2970
                error_log(
2971
                    'learnpathItem::set_lp_view() - Updated item object with database values',
2972
                    0
2973
                );
2974
            }
2975
2976
            // Now get the number of interactions for this little guy.
2977
            $table = Database::get_course_table(TABLE_LP_IV_INTERACTION);
2978
            $sql = "SELECT * FROM $table
2979
                    WHERE lp_iv_id = ".$this->db_item_view_id;
2980
2981
            $res = Database::query($sql);
2982
            if (false !== $res) {
2983
                $this->interactions_count = Database::num_rows($res);
2984
            } else {
2985
                $this->interactions_count = 0;
2986
            }
2987
            // Now get the number of objectives for this little guy.
2988
            $table = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
2989
            $sql = "SELECT * FROM $table
2990
                    WHERE lp_iv_id = ".$this->db_item_view_id;
2991
2992
            $this->objectives_count = 0;
2993
            $res = Database::query($sql);
2994
            if (false !== $res) {
2995
                $this->objectives_count = Database::num_rows($res);
2996
            }
2997
        }
2998
2999
        return true;
3000
    }
3001
3002
    /**
3003
     * Sets the path.
3004
     *
3005
     * @param string $string Path
3006
     */
3007
    public function set_path($string = '')
3008
    {
3009
        if (!empty($string)) {
3010
            $this->path = $string;
3011
        }
3012
    }
3013
3014
    /**
3015
     * Sets the prevent_reinit attribute.
3016
     * This is based on the LP value and is set at creation time for
3017
     * each learnpathItem. It is a (bad?) way of avoiding
3018
     * a reference to the LP when saving an item.
3019
     *
3020
     * @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...
3021
     * saving freshened values (new "not attempted" status etc)
3022
     */
3023
    public function set_prevent_reinit($prevent)
3024
    {
3025
        $this->prevent_reinit = 0;
3026
        if ($prevent) {
3027
            $this->prevent_reinit = 1;
3028
        }
3029
    }
3030
3031
    /**
3032
     * Sets the score value. If the mastery_score is set and the score reaches
3033
     * it, then set the status to 'passed'.
3034
     *
3035
     * @param float $score Score
3036
     *
3037
     * @return bool True on success, false otherwise
3038
     */
3039
    public function set_score($score)
3040
    {
3041
        $debug = self::DEBUG;
3042
        if ($debug > 0) {
3043
            error_log('learnpathItem::set_score('.$score.')', 0);
3044
        }
3045
        if (($this->max_score <= 0 || $score <= $this->max_score) && ($score >= $this->min_score)) {
3046
            $this->current_score = $score;
3047
            $masteryScore = $this->get_mastery_score();
3048
            $current_status = $this->get_status(false);
3049
3050
            // Fixes bug when SCORM doesn't send a mastery score even if they sent a score!
3051
            if (-1 == $masteryScore) {
3052
                $masteryScore = $this->max_score;
3053
            }
3054
3055
            if ($debug > 0) {
3056
                error_log('get_mastery_score: '.$masteryScore);
3057
                error_log('current_status: '.$current_status);
3058
                error_log('current score : '.$this->current_score);
3059
            }
3060
3061
            // If mastery_score is set AND the current score reaches the mastery
3062
            //  score AND the current status is different from 'completed', then
3063
            //  set it to 'passed'.
3064
            /*
3065
            if ($master != -1 && $this->current_score >= $master && $current_status != $this->possible_status[2]) {
3066
                if ($debug > 0) error_log('Status changed to: '.$this->possible_status[3]);
3067
                $this->set_status($this->possible_status[3]); //passed
3068
            } elseif ($master != -1 && $this->current_score < $master) {
3069
                if ($debug > 0) error_log('Status changed to: '.$this->possible_status[4]);
3070
                $this->set_status($this->possible_status[4]); //failed
3071
            }*/
3072
            return true;
3073
        }
3074
3075
        return false;
3076
    }
3077
3078
    /**
3079
     * Sets the maximum score for this item.
3080
     *
3081
     * @param int $score Maximum score - must be a decimal or an empty string
3082
     *
3083
     * @return bool True on success, false on error
3084
     */
3085
    public function set_max_score($score)
3086
    {
3087
        if (is_int($score) || '' == $score) {
3088
            $this->view_max_score = $score;
3089
3090
            return true;
3091
        }
3092
3093
        return false;
3094
    }
3095
3096
    /**
3097
     * Sets the status for this item.
3098
     *
3099
     * @param string $status Status - must be one of the values defined in $this->possible_status
3100
     *                       (this affects the status setting)
3101
     *
3102
     * @return bool True on success, false on error
3103
     */
3104
    public function set_status($status)
3105
    {
3106
        if (self::DEBUG) {
3107
            error_log('learnpathItem::set_status('.$status.')');
3108
        }
3109
3110
        $found = false;
3111
        foreach ($this->possible_status as $possible) {
3112
            if (preg_match('/^'.$possible.'$/i', $status)) {
3113
                $found = true;
3114
            }
3115
        }
3116
3117
        if ($found) {
3118
            $this->status = $status;
3119
            if (self::DEBUG) {
3120
                error_log(
3121
                    'learnpathItem::set_status() - '.
3122
                        'Updated object status of item '.$this->db_id.
3123
                    ' to '.$this->status
3124
                );
3125
            }
3126
3127
            return true;
3128
        }
3129
3130
        if (self::DEBUG) {
3131
            error_log('Setting status: '.$this->possible_status[0]);
3132
        }
3133
        $this->status = $this->possible_status[0];
3134
3135
        return false;
3136
    }
3137
3138
    /**
3139
     * Set the (indexing) terms for this learnpath item.
3140
     *
3141
     * @param string $terms Terms, as a comma-split list
3142
     *
3143
     * @return bool Always return true
3144
     */
3145
    public function set_terms($terms)
3146
    {
3147
        global $charset;
3148
        $lp_item = Database::get_course_table(TABLE_LP_ITEM);
3149
        $a_terms = preg_split('/,/', $terms);
3150
        $i_terms = preg_split('/,/', $this->get_terms());
3151
        foreach ($i_terms as $term) {
3152
            if (!in_array($term, $a_terms)) {
3153
                array_push($a_terms, $term);
3154
            }
3155
        }
3156
        $new_terms = $a_terms;
3157
        $new_terms_string = implode(',', $new_terms);
3158
3159
        // TODO: Validate csv string.
3160
        $terms = Database::escape_string(api_htmlentities($new_terms_string, ENT_QUOTES));
3161
        $sql = "UPDATE $lp_item
3162
                SET terms = '$terms'
3163
                WHERE iid=".$this->get_id();
3164
        Database::query($sql);
3165
        // Save it to search engine.
3166
        if ('true' == api_get_setting('search_enabled')) {
3167
            $di = new ChamiloIndexer();
3168
            $di->update_terms($this->get_search_did(), $new_terms, 'T');
3169
        }
3170
3171
        return true;
3172
    }
3173
3174
    /**
3175
     * Get the document ID from inside the text index database.
3176
     *
3177
     * @return int Search index database document ID
3178
     */
3179
    public function get_search_did()
3180
    {
3181
        return $this->search_did;
3182
    }
3183
3184
    /**
3185
     * Sets the item viewing time in a usable form, given that SCORM packages
3186
     * often give it as 00:00:00.0000.
3187
     *
3188
     * @param    string    Time as given by SCORM
3189
     * @param string $format
3190
     */
3191
    public function set_time($scorm_time, $format = 'scorm')
3192
    {
3193
        $debug = self::DEBUG;
3194
        if ($debug) {
3195
            error_log("learnpathItem::set_time($scorm_time, $format)");
3196
            error_log("this->type: ".$this->type);
3197
            error_log("this->current_start_time: ".$this->current_start_time);
3198
        }
3199
3200
        if ('0' == $scorm_time &&
3201
            'sco' !== $this->type &&
3202
            0 != $this->current_start_time
3203
        ) {
3204
            $myTime = time() - $this->current_start_time;
3205
            if ($myTime > 0) {
3206
                $this->update_time($myTime);
3207
                if ($debug) {
3208
                    error_log('found asset - set time to '.$myTime);
3209
                }
3210
            } else {
3211
                if ($debug) {
3212
                    error_log('Time not set');
3213
                }
3214
            }
3215
        } else {
3216
            switch ($format) {
3217
                case 'scorm':
3218
                    $res = [];
3219
                    if (preg_match(
3220
                        '/^(\d{1,4}):(\d{2}):(\d{2})(\.\d{1,4})?/',
3221
                        $scorm_time,
3222
                        $res
3223
                    )
3224
                    ) {
3225
                        $hour = $res[1];
3226
                        $min = $res[2];
3227
                        $sec = $res[3];
3228
                        // Getting total number of seconds spent.
3229
                        $totalSec = $hour * 3600 + $min * 60 + $sec;
3230
                        if ($debug) {
3231
                            error_log("totalSec : $totalSec");
3232
                            error_log("Now calling to scorm_update_time()");
3233
                        }
3234
                        $this->scorm_update_time($totalSec);
3235
                    }
3236
                    break;
3237
                case 'int':
3238
                    if ($debug) {
3239
                        error_log("scorm_time = $scorm_time");
3240
                        error_log("Now calling to scorm_update_time()");
3241
                    }
3242
                    $this->scorm_update_time($scorm_time);
3243
                    break;
3244
            }
3245
        }
3246
    }
3247
3248
    /**
3249
     * Sets the item's title.
3250
     *
3251
     * @param string $string Title
3252
     */
3253
    public function set_title($string = '')
3254
    {
3255
        if (!empty($string)) {
3256
            $this->title = $string;
3257
        }
3258
    }
3259
3260
    /**
3261
     * Sets the item's type.
3262
     *
3263
     * @param string $string Type
3264
     */
3265
    public function set_type($string = '')
3266
    {
3267
        if (!empty($string)) {
3268
            $this->type = $string;
3269
        }
3270
    }
3271
3272
    /**
3273
     * Checks if the current status is part of the list of status given.
3274
     *
3275
     * @param array $list An array of status to check for.
3276
     *                    If the current status is one of the strings, return true
3277
     *
3278
     * @return bool True if the status was one of the given strings,
3279
     *              false otherwise
3280
     */
3281
    public function status_is($list = [])
3282
    {
3283
        if (self::DEBUG > 1) {
3284
            error_log(
3285
                '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

3285
                'learnpathItem::status_is('./** @scrutinizer ignore-type */ print_r(
Loading history...
3286
                    $list,
3287
                    true
3288
                ).') on item '.$this->db_id,
3289
                0
3290
            );
3291
        }
3292
        $currentStatus = $this->get_status(true);
3293
        if (empty($currentStatus)) {
3294
            return false;
3295
        }
3296
        $found = false;
3297
        foreach ($list as $status) {
3298
            if (preg_match('/^'.$status.'$/i', $currentStatus)) {
3299
                if (self::DEBUG > 2) {
3300
                    error_log(
3301
                        'New LP - learnpathItem::status_is() - Found status '.
3302
                            $status.' corresponding to current status',
3303
                        0
3304
                    );
3305
                }
3306
                $found = true;
3307
3308
                return $found;
3309
            }
3310
        }
3311
        if (self::DEBUG > 2) {
3312
            error_log(
3313
                'New LP - learnpathItem::status_is() - Status '.
3314
                    $currentStatus.' did not match request',
3315
                0
3316
            );
3317
        }
3318
3319
        return $found;
3320
    }
3321
3322
    /**
3323
     * Updates the time info according to the given session_time.
3324
     *
3325
     * @param int $totalSec Time in seconds
3326
     */
3327
    public function update_time($totalSec = 0)
3328
    {
3329
        if (self::DEBUG > 0) {
3330
            error_log('learnpathItem::update_time('.$totalSec.')');
3331
        }
3332
        if ($totalSec >= 0) {
3333
            // Getting start time from finish time. The only problem in the calculation is it might be
3334
            // modified by the scripts processing time.
3335
            $now = time();
3336
            $start = $now - $totalSec;
3337
            $this->current_start_time = $start;
3338
            $this->current_stop_time = $now;
3339
        }
3340
    }
3341
3342
    /**
3343
     * Special scorm update time function. This function will update time
3344
     * directly into db for scorm objects.
3345
     *
3346
     * @param int $total_sec Total number of seconds
3347
     */
3348
    public function scorm_update_time($total_sec = 0)
3349
    {
3350
        $debug = self::DEBUG;
3351
        if ($debug) {
3352
            error_log('learnpathItem::scorm_update_time()');
3353
            error_log("total_sec: $total_sec");
3354
        }
3355
3356
        // Step 1 : get actual total time stored in db
3357
        $item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3358
3359
        $sql = 'SELECT total_time, status
3360
                FROM '.$item_view_table.'
3361
                WHERE
3362
                    lp_item_id = "'.$this->db_id.'" AND
3363
                    lp_view_id = "'.$this->view_id.'" AND
3364
                    view_count = "'.$this->get_attempt_id().'"';
3365
        $result = Database::query($sql);
3366
        $row = Database::fetch_array($result);
3367
3368
        if (!isset($row['total_time'])) {
3369
            $total_time = 0;
3370
        } else {
3371
            $total_time = $row['total_time'];
3372
        }
3373
        if ($debug) {
3374
            error_log("Original total_time: $total_time");
3375
        }
3376
3377
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3378
        $lp_id = (int) $this->lp_id;
3379
        $sql = "SELECT * FROM $lp_table WHERE iid = $lp_id";
3380
        $res = Database::query($sql);
3381
        $accumulateScormTime = 'false';
3382
        if (Database::num_rows($res) > 0) {
3383
            $row = Database::fetch_assoc($res);
3384
            $accumulateScormTime = $row['accumulate_scorm_time'];
3385
        }
3386
3387
        // Step 2.1 : if normal mode total_time = total_time + total_sec
3388
        if ('sco' === $this->type && 0 != $accumulateScormTime) {
3389
            if ($debug) {
3390
                error_log("accumulateScormTime is on. total_time modified: $total_time + $total_sec");
3391
            }
3392
            $total_time += $total_sec;
3393
        } else {
3394
            // Step 2.2 : if not cumulative mode total_time = total_time - last_update + total_sec
3395
            $total_sec = $this->fixAbusiveTime($total_sec);
3396
            if ($debug) {
3397
                error_log("after fix abusive: $total_sec");
3398
                error_log("total_time: $total_time");
3399
                error_log("this->last_scorm_session_time: ".$this->last_scorm_session_time);
3400
            }
3401
3402
            $total_time = $total_time - $this->last_scorm_session_time + $total_sec;
3403
            $this->last_scorm_session_time = $total_sec;
3404
3405
            if ($total_time < 0) {
3406
                $total_time = $total_sec;
3407
            }
3408
        }
3409
3410
        if ($debug) {
3411
            error_log("accumulate_scorm_time: $accumulateScormTime");
3412
            error_log("total_time modified: $total_time");
3413
        }
3414
3415
        // Step 3 update db only if status != completed, passed, browsed or seriousgamemode not activated
3416
        // @todo complete
3417
        $case_completed = [
3418
            'completed',
3419
            'passed',
3420
            'browsed',
3421
            'failed',
3422
        ];
3423
3424
        if (1 != $this->seriousgame_mode ||
3425
            !in_array($row['status'], $case_completed)
3426
        ) {
3427
            $sql = "UPDATE $item_view_table
3428
                      SET total_time = '$total_time'
3429
                    WHERE
3430
                        lp_item_id = {$this->db_id} AND
3431
                        lp_view_id = {$this->view_id} AND
3432
                        view_count = {$this->get_attempt_id()}";
3433
            if ($debug) {
3434
                error_log('-------------total_time updated ------------------------');
3435
                error_log($sql);
3436
                error_log('-------------------------------------');
3437
            }
3438
            Database::query($sql);
3439
        }
3440
    }
3441
3442
    /**
3443
     * Write objectives to DB. This method is separate from write_to_db() because otherwise
3444
     * objectives are lost as a side effect to AJAX and session concurrent access.
3445
     *
3446
     * @return bool True or false on error
3447
     */
3448
    public function write_objectives_to_db()
3449
    {
3450
        if (self::DEBUG > 0) {
3451
            error_log('learnpathItem::write_objectives_to_db()', 0);
3452
        }
3453
        if (api_is_invitee()) {
3454
            // If the user is an invitee, we don't write anything to DB
3455
            return true;
3456
        }
3457
        $courseId = api_get_course_int_id();
3458
        if (is_array($this->objectives) && count($this->objectives) > 0) {
3459
            // Save objectives.
3460
            $tbl = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3461
            $sql = "SELECT iid
3462
                    FROM $tbl
3463
                    WHERE
3464
                        lp_item_id = ".$this->db_id." AND
3465
                        lp_view_id = ".$this->view_id." AND
3466
                        view_count = ".$this->attempt_id;
3467
            $res = Database::query($sql);
3468
            if (Database::num_rows($res) > 0) {
3469
                $row = Database::fetch_array($res);
3470
                $lp_iv_id = $row[0];
3471
                if (self::DEBUG > 2) {
3472
                    error_log(
3473
                        'learnpathItem::write_to_db() - Got item_view_id '.
3474
                            $lp_iv_id.', now checking objectives ',
3475
                        0
3476
                    );
3477
                }
3478
                foreach ($this->objectives as $index => $objective) {
3479
                    $iva_table = Database::get_course_table(
3480
                        TABLE_LP_IV_OBJECTIVE
3481
                    );
3482
                    $iva_sql = "SELECT iid FROM $iva_table
3483
                                WHERE
3484
                                    c_id = $courseId AND
3485
                                    lp_iv_id = $lp_iv_id AND
3486
                                    objective_id = '".Database::escape_string($objective[0])."'";
3487
                    $iva_res = Database::query($iva_sql);
3488
                    // id(0), type(1), time(2), weighting(3),
3489
                    // correct_responses(4), student_response(5),
3490
                    // result(6), latency(7)
3491
                    if (Database::num_rows($iva_res) > 0) {
3492
                        // Update (or don't).
3493
                        $iva_row = Database::fetch_array($iva_res);
3494
                        $iva_id = $iva_row[0];
3495
                        $ivau_sql = "UPDATE $iva_table ".
3496
                            "SET objective_id = '".Database::escape_string($objective[0])."',".
3497
                            "status = '".Database::escape_string($objective[1])."',".
3498
                            "score_raw = '".Database::escape_string($objective[2])."',".
3499
                            "score_min = '".Database::escape_string($objective[4])."',".
3500
                            "score_max = '".Database::escape_string($objective[3])."' ".
3501
                            "WHERE c_id = $courseId AND iid = $iva_id";
3502
                        Database::query($ivau_sql);
3503
                    } else {
3504
                        // Insert new one.
3505
                        $params = [
3506
                            'c_id' => $courseId,
3507
                            'lp_iv_id' => $lp_iv_id,
3508
                            'order_id' => $index,
3509
                            'objective_id' => $objective[0],
3510
                            'status' => $objective[1],
3511
                            'score_raw' => $objective[2],
3512
                            'score_min' => $objective[4],
3513
                            'score_max' => $objective[3],
3514
                        ];
3515
3516
                        $insertId = Database::insert($iva_table, $params);
3517
                        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...
3518
                            $sql = "UPDATE $iva_table SET id = iid
3519
                                    WHERE iid = $insertId";
3520
                            Database::query($sql);
3521
                        }
3522
                    }
3523
                }
3524
            }
3525
        }
3526
    }
3527
3528
    /**
3529
     * Writes the current data to the database.
3530
     *
3531
     * @return bool Query result
3532
     */
3533
    public function write_to_db()
3534
    {
3535
        $debug = self::DEBUG;
3536
        if ($debug) {
3537
            error_log('------------------------');
3538
            error_log('learnpathItem::write_to_db()');
3539
        }
3540
3541
        // Check the session visibility.
3542
        if (!api_is_allowed_to_session_edit()) {
3543
            if ($debug) {
3544
                error_log('return false api_is_allowed_to_session_edit');
3545
            }
3546
3547
            return false;
3548
        }
3549
        if (api_is_invitee()) {
3550
            if ($debug) {
3551
                error_log('api_is_invitee');
3552
            }
3553
            // If the user is an invitee, we don't write anything to DB
3554
            return true;
3555
        }
3556
3557
        $courseId = api_get_course_int_id();
3558
        $mode = $this->get_lesson_mode();
3559
        $credit = $this->get_credit();
3560
        $total_time = ' ';
3561
        $my_status = ' ';
3562
        $item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3563
        $sql = 'SELECT status, total_time
3564
                FROM '.$item_view_table.'
3565
                WHERE
3566
                    lp_item_id="'.$this->db_id.'" AND
3567
                    lp_view_id="'.$this->view_id.'" AND
3568
                    view_count="'.$this->get_attempt_id().'" ';
3569
        $rs_verified = Database::query($sql);
3570
        $row_verified = Database::fetch_array($rs_verified);
3571
        $my_case_completed = [
3572
            'completed',
3573
            'passed',
3574
            'browsed',
3575
            'failed',
3576
        ];
3577
3578
        $save = true;
3579
3580
        if (!empty($row_verified)) {
3581
            $oldTotalTime = $row_verified['total_time'];
3582
            $this->oldTotalTime = $oldTotalTime;
3583
            if (isset($row_verified['status'])) {
3584
                if (in_array($row_verified['status'], $my_case_completed)) {
3585
                    $save = false;
3586
                }
3587
            }
3588
        }
3589
3590
        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...
3591
           ('sco' === $this->type && ('no-credit' === $credit || 'review' === $mode || 'browse' === $mode))) &&
3592
           (1 != $this->seriousgame_mode && 'sco' === $this->type)
3593
        ) {
3594
            if ($debug) {
3595
                error_log(
3596
                    "This info shouldn't be saved as the credit or lesson mode info prevent it"
3597
                );
3598
                error_log(
3599
                    'learnpathItem::write_to_db() - credit('.$credit.') or'.
3600
                    ' lesson_mode('.$mode.') prevent recording!',
3601
                    0
3602
                );
3603
            }
3604
        } else {
3605
            // Check the row exists.
3606
            $inserted = false;
3607
            // This a special case for multiple attempts and Chamilo exercises.
3608
            if ('quiz' === $this->type &&
3609
                0 == $this->get_prevent_reinit() &&
3610
                'completed' === $this->get_status()
3611
            ) {
3612
                // We force the item to be restarted.
3613
                $this->restart();
3614
                $params = [
3615
                    "c_id" => $courseId,
3616
                    "total_time" => $this->get_total_time(),
3617
                    "start_time" => $this->current_start_time,
3618
                    "score" => $this->get_score(),
3619
                    "status" => $this->get_status(false),
3620
                    "max_score" => $this->get_max(),
3621
                    "lp_item_id" => $this->db_id,
3622
                    "lp_view_id" => $this->view_id,
3623
                    "view_count" => $this->get_attempt_id(),
3624
                    "suspend_data" => $this->current_data,
3625
                    //"max_time_allowed" => ,
3626
                    "lesson_location" => $this->lesson_location,
3627
                ];
3628
                if ($debug) {
3629
                    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

3629
                    error_log('learnpathItem::write_to_db() - Inserting into item_view forced: './** @scrutinizer ignore-type */ print_r($params, 1));
Loading history...
3630
                }
3631
                $this->db_item_view_id = Database::insert($item_view_table, $params);
3632
                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...
3633
                    $inserted = true;
3634
                }
3635
            }
3636
3637
            $sql = "SELECT * FROM $item_view_table
3638
                    WHERE
3639
                        lp_item_id = ".$this->db_id." AND
3640
                        lp_view_id = ".$this->view_id." AND
3641
                        view_count = ".$this->get_attempt_id();
3642
            if ($debug) {
3643
                error_log('learnpathItem::write_to_db() - Querying item_view: '.$sql);
3644
            }
3645
3646
            $check_res = Database::query($sql);
3647
            // Depending on what we want (really), we'll update or insert a new row
3648
            // now save into DB.
3649
            if (!$inserted && Database::num_rows($check_res) < 1) {
3650
                $params = [
3651
                    "c_id" => $courseId,
3652
                    "total_time" => $this->get_total_time(),
3653
                    "start_time" => $this->current_start_time,
3654
                    "score" => $this->get_score(),
3655
                    "status" => $this->get_status(false),
3656
                    "max_score" => $this->get_max(),
3657
                    "lp_item_id" => $this->db_id,
3658
                    "lp_view_id" => $this->view_id,
3659
                    "view_count" => $this->get_attempt_id(),
3660
                    "suspend_data" => $this->current_data,
3661
                    //"max_time_allowed" => ,$this->get_max_time_allowed()
3662
                    "lesson_location" => $this->lesson_location,
3663
                ];
3664
3665
                if ($debug) {
3666
                    error_log(
3667
                        'learnpathItem::write_to_db() - Inserting into item_view forced: '.print_r($params, 1),
3668
                        0
3669
                    );
3670
                }
3671
                $this->db_item_view_id = Database::insert($item_view_table, $params);
3672
            } else {
3673
                if ('hotpotatoes' === $this->type) {
3674
                    $params = [
3675
                        'total_time' => $this->get_total_time(),
3676
                        'start_time' => $this->get_current_start_time(),
3677
                        'score' => $this->get_score(),
3678
                        'status' => $this->get_status(false),
3679
                        'max_score' => $this->get_max(),
3680
                        'suspend_data' => $this->current_data,
3681
                        'lesson_location' => $this->lesson_location,
3682
                    ];
3683
                    $where = [
3684
                        'c_id = ? AND lp_item_id = ? AND lp_view_id = ? AND view_count = ?' => [
3685
                            $courseId,
3686
                            $this->db_id,
3687
                            $this->view_id,
3688
                            $this->get_attempt_id(),
3689
                        ],
3690
                    ];
3691
                    Database::update($item_view_table, $params, $where);
3692
                } else {
3693
                    // For all other content types...
3694
                    if ('quiz' === $this->type) {
3695
                        $my_status = ' ';
3696
                        $total_time = ' ';
3697
                        if (!empty($_REQUEST['exeId'])) {
3698
                            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3699
                            $exeId = (int) $_REQUEST['exeId'];
3700
                            $sql = "SELECT exe_duration
3701
                                    FROM $table
3702
                                    WHERE exe_id = $exeId";
3703
                            $res = Database::query($sql);
3704
                            $exeRow = Database::fetch_array($res);
3705
                            $duration = isset($exeRow['exe_duration']) ? (int) $exeRow['exe_duration'] : 0;
3706
                            $total_time = " total_time = ".$duration.", ";
3707
                            if ($debug) {
3708
                                error_log("quiz: $total_time");
3709
                            }
3710
                        }
3711
                    } else {
3712
                        $my_type_lp = learnpath::get_type_static($this->lp_id);
3713
                        // This is a array containing values finished
3714
                        $case_completed = [
3715
                            'completed',
3716
                            'passed',
3717
                            'browsed',
3718
                            'failed',
3719
                        ];
3720
3721
                        // Is not multiple attempts
3722
                        if (1 == $this->seriousgame_mode && 'sco' === $this->type) {
3723
                            $total_time = " total_time = total_time +".$this->get_total_time().", ";
3724
                            $my_status = " status = '".$this->get_status(false)."' ,";
3725
                            if ($debug) {
3726
                                error_log("seriousgame_mode time changed: $total_time");
3727
                            }
3728
                        } elseif (1 == $this->get_prevent_reinit()) {
3729
                            // Process of status verified into data base.
3730
                            $sql = 'SELECT status FROM '.$item_view_table.'
3731
                                    WHERE
3732
                                        lp_item_id="'.$this->db_id.'" AND
3733
                                        lp_view_id="'.$this->view_id.'" AND
3734
                                        view_count="'.$this->get_attempt_id().'"
3735
                                    ';
3736
                            $rs_verified = Database::query($sql);
3737
                            $row_verified = Database::fetch_array($rs_verified);
3738
3739
                            // Get type lp: 1=lp dokeos and  2=scorm.
3740
                            // If not is completed or passed or browsed and learning path is scorm.
3741
                            if (!in_array($this->get_status(false), $case_completed) &&
3742
                                2 == $my_type_lp
3743
                            ) {
3744
                                $total_time = " total_time = total_time +".$this->get_total_time().", ";
3745
                                $my_status = " status = '".$this->get_status(false)."' ,";
3746
                                if ($debug) {
3747
                                    error_log("get_prevent_reinit = 1 time changed: $total_time");
3748
                                }
3749
                            } else {
3750
                                // Verified into database.
3751
                                if (!in_array($row_verified['status'], $case_completed) &&
3752
                                    2 == $my_type_lp
3753
                                ) {
3754
                                    $total_time = " total_time = total_time +".$this->get_total_time().", ";
3755
                                    $my_status = " status = '".$this->get_status(false)."' ,";
3756
                                    if ($debug) {
3757
                                        error_log("total_time time changed case 1: $total_time");
3758
                                    }
3759
                                } elseif (in_array($row_verified['status'], $case_completed) &&
3760
                                    2 == $my_type_lp && 'sco' != $this->type
3761
                                ) {
3762
                                    $total_time = " total_time = total_time +".$this->get_total_time().", ";
3763
                                    $my_status = " status = '".$this->get_status(false)."' ,";
3764
                                    if ($debug) {
3765
                                        error_log("total_time time changed case 2: $total_time");
3766
                                    }
3767
                                } else {
3768
                                    if ((3 == $my_type_lp && 'au' == $this->type) ||
3769
                                        (1 == $my_type_lp && 'dir' != $this->type)) {
3770
                                        // Is AICC or Chamilo LP
3771
                                        $total_time = " total_time = total_time + ".$this->get_total_time().", ";
3772
                                        $my_status = " status = '".$this->get_status(false)."' ,";
3773
                                        if ($debug) {
3774
                                            error_log("total_time time changed case 3: $total_time");
3775
                                        }
3776
                                    }
3777
                                }
3778
                            }
3779
                        } else {
3780
                            // Multiple attempts are allowed.
3781
                            if (in_array($this->get_status(false), $case_completed) && 2 == $my_type_lp) {
3782
                                // Reset zero new attempt ?
3783
                                $my_status = " status = '".$this->get_status(false)."' ,";
3784
                                if ($debug) {
3785
                                    error_log("total_time time changed Multiple attempt case 1: $total_time");
3786
                                }
3787
                            } elseif (!in_array($this->get_status(false), $case_completed) && 2 == $my_type_lp) {
3788
                                $total_time = " total_time = ".$this->get_total_time().", ";
3789
                                $my_status = " status = '".$this->get_status(false)."' ,";
3790
                                if ($debug) {
3791
                                    error_log("total_time time changed Multiple attempt case 2: $total_time");
3792
                                }
3793
                            } else {
3794
                                // It is chamilo LP.
3795
                                $total_time = " total_time = total_time +".$this->get_total_time().", ";
3796
                                $my_status = " status = '".$this->get_status(false)."' ,";
3797
                                if ($debug) {
3798
                                    error_log("total_time time changed Multiple attempt case 3: $total_time");
3799
                                }
3800
                            }
3801
3802
                            // This code line fixes the problem of wrong status.
3803
                            if (2 == $my_type_lp) {
3804
                                // Verify current status in multiples attempts.
3805
                                $sql = 'SELECT status FROM '.$item_view_table.'
3806
                                        WHERE
3807
                                            c_id = '.$courseId.' AND
3808
                                            lp_item_id="'.$this->db_id.'" AND
3809
                                            lp_view_id="'.$this->view_id.'" AND
3810
                                            view_count="'.$this->get_attempt_id().'" ';
3811
                                $rs_status = Database::query($sql);
3812
                                $current_status = Database::result($rs_status, 0, 'status');
3813
                                if (in_array($current_status, $case_completed)) {
3814
                                    $my_status = '';
3815
                                    $total_time = '';
3816
                                } else {
3817
                                    $total_time = " total_time = total_time + ".$this->get_total_time().", ";
3818
                                }
3819
3820
                                if ($debug) {
3821
                                    error_log("total_time time my_type_lp: $total_time");
3822
                                }
3823
                            }
3824
                        }
3825
                    }
3826
3827
                    if ('sco' === $this->type) {
3828
                        //IF scorm scorm_update_time has already updated total_time in db
3829
                        //" . //start_time = ".$this->get_current_start_time().", " . //scorm_init_time does it
3830
                        ////" max_time_allowed = '".$this->get_max_time_allowed()."'," .
3831
                        $sql = "UPDATE $item_view_table SET
3832
                                    score = ".$this->get_score().",
3833
                                    $my_status
3834
                                    max_score = '".$this->get_max()."',
3835
                                    suspend_data = '".Database::escape_string($this->current_data)."',
3836
                                    lesson_location = '".$this->lesson_location."'
3837
                                WHERE
3838
                                    lp_item_id = ".$this->db_id." AND
3839
                                    lp_view_id = ".$this->view_id."  AND
3840
                                    view_count = ".$this->get_attempt_id();
3841
                    } else {
3842
                        //" max_time_allowed = '".$this->get_max_time_allowed()."'," .
3843
                        $sql = "UPDATE $item_view_table SET
3844
                                    $total_time
3845
                                    start_time = ".$this->get_current_start_time().",
3846
                                    score = ".$this->get_score().",
3847
                                    $my_status
3848
                                    max_score = '".$this->get_max()."',
3849
                                    suspend_data = '".Database::escape_string($this->current_data)."',
3850
                                    lesson_location = '".$this->lesson_location."'
3851
                                WHERE
3852
                                    lp_item_id = ".$this->db_id." AND
3853
                                    lp_view_id = ".$this->view_id." AND
3854
                                    view_count = ".$this->get_attempt_id();
3855
                    }
3856
                    $this->current_start_time = time();
3857
                }
3858
                if ($debug) {
3859
                    error_log('-------------------------------------------');
3860
                    error_log('learnpathItem::write_to_db() - Updating item_view:');
3861
                    error_log($sql);
3862
                    error_log('-------------------------------------------');
3863
                }
3864
                Database::query($sql);
3865
            }
3866
3867
            if (is_array($this->interactions) &&
3868
                count($this->interactions) > 0
3869
            ) {
3870
                $sql = "SELECT iid FROM $item_view_table
3871
                        WHERE
3872
                            lp_item_id = ".$this->db_id." AND
3873
                            lp_view_id = ".$this->view_id." AND
3874
                            view_count = ".$this->get_attempt_id();
3875
                $res = Database::query($sql);
3876
                if (Database::num_rows($res) > 0) {
3877
                    $row = Database::fetch_array($res);
3878
                    $lp_iv_id = $row[0];
3879
                    if ($debug) {
3880
                        error_log(
3881
                            'learnpathItem::write_to_db() - Got item_view_id '.
3882
                            $lp_iv_id.', now checking interactions ',
3883
                            0
3884
                        );
3885
                    }
3886
                    foreach ($this->interactions as $index => $interaction) {
3887
                        $correct_resp = '';
3888
                        if (is_array($interaction[4]) && !empty($interaction[4][0])) {
3889
                            foreach ($interaction[4] as $resp) {
3890
                                $correct_resp .= $resp.',';
3891
                            }
3892
                            $correct_resp = substr(
3893
                                $correct_resp,
3894
                                0,
3895
                                strlen($correct_resp) - 1
3896
                            );
3897
                        }
3898
                        $iva_table = Database::get_course_table(
3899
                            TABLE_LP_IV_INTERACTION
3900
                        );
3901
3902
                        //also check for the interaction ID as it must be unique for this SCO view
3903
                        $iva_sql = "SELECT iid FROM $iva_table
3904
                                    WHERE
3905
                                        c_id = $courseId AND
3906
                                        lp_iv_id = $lp_iv_id AND
3907
                                        (
3908
                                            order_id = $index OR
3909
                                            interaction_id = '".Database::escape_string($interaction[0])."'
3910
                                        )
3911
                                    ";
3912
                        $iva_res = Database::query($iva_sql);
3913
3914
                        $interaction[0] = $interaction[0] ?? '';
3915
                        $interaction[1] = $interaction[1] ?? '';
3916
                        $interaction[2] = $interaction[2] ?? '';
3917
                        $interaction[3] = $interaction[3] ?? '';
3918
                        $interaction[4] = $interaction[4] ?? '';
3919
                        $interaction[5] = $interaction[5] ?? '';
3920
                        $interaction[6] = $interaction[6] ?? '';
3921
                        $interaction[7] = $interaction[7] ?? '';
3922
3923
                        // id(0), type(1), time(2), weighting(3), correct_responses(4), student_response(5), result(6), latency(7)
3924
                        if (Database::num_rows($iva_res) > 0) {
3925
                            // Update (or don't).
3926
                            $iva_row = Database::fetch_array($iva_res);
3927
                            $iva_id = $iva_row[0];
3928
                            // Insert new one.
3929
                            $params = [
3930
                                'interaction_id' => $interaction[0],
3931
                                'interaction_type' => $interaction[1],
3932
                                'weighting' => $interaction[3],
3933
                                'completion_time' => $interaction[2],
3934
                                'correct_responses' => $correct_resp,
3935
                                'student_response' => $interaction[5],
3936
                                'result' => $interaction[6],
3937
                                'latency' => $interaction[7],
3938
                            ];
3939
                            Database::update(
3940
                                $iva_table,
3941
                                $params,
3942
                                [
3943
                                    'c_id = ? AND iid = ?' => [
3944
                                        $courseId,
3945
                                        $iva_id,
3946
                                    ],
3947
                                ]
3948
                            );
3949
                        } else {
3950
                            // Insert new one.
3951
                            $params = [
3952
                                'c_id' => $courseId,
3953
                                'order_id' => $index,
3954
                                'lp_iv_id' => $lp_iv_id,
3955
                                'interaction_id' => $interaction[0],
3956
                                'interaction_type' => $interaction[1],
3957
                                'weighting' => $interaction[3],
3958
                                'completion_time' => $interaction[2],
3959
                                'correct_responses' => $correct_resp,
3960
                                'student_response' => $interaction[5],
3961
                                'result' => $interaction[6],
3962
                                'latency' => $interaction[7],
3963
                            ];
3964
3965
                            $insertId = Database::insert($iva_table, $params);
3966
                            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...
3967
                                $sql = "UPDATE $iva_table SET id = iid
3968
                                        WHERE iid = $insertId";
3969
                                Database::query($sql);
3970
                            }
3971
                        }
3972
                    }
3973
                }
3974
            }
3975
        }
3976
3977
        if ($debug) {
3978
            error_log('End of learnpathItem::write_to_db()', 0);
3979
        }
3980
3981
        return true;
3982
    }
3983
3984
    /**
3985
     * Adds an audio file attached to the current item (store on disk and in db).
3986
     *
3987
     * @return bool
3988
     */
3989
    public function addAudio()
3990
    {
3991
        $course_info = api_get_course_info();
3992
        $userId = api_get_user_id();
3993
3994
        $folderDocument = create_unexisting_directory(
3995
            $course_info,
3996
            $userId,
3997
            0,
3998
            0,
3999
            0,
4000
            null,
4001
            '/audio',
4002
            get_lang('Audio'),
4003
            0,
4004
            false,
4005
            false
4006
        );
4007
4008
        /*$filepath = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document/';
4009
        if (!is_dir($filepath.'audio')) {
4010
            mkdir(
4011
                $filepath.'audio',
4012
                api_get_permissions_for_new_directories()
4013
            );
4014
            DocumentManager::addDocument(
4015
                $course_info,
4016
                '/audio',
4017
                'folder',
4018
                0,
4019
                'audio'
4020
            );
4021
        }*/
4022
4023
        $key = 'file';
4024
        if (!isset($_FILES[$key]['name']) || !isset($_FILES[$key]['tmp_name'])) {
4025
            return false;
4026
        }
4027
4028
        $document = null;
4029
        /*$document = DocumentManager::upload_document(
4030
            $_FILES,
4031
            null,
4032
            null,
4033
            null,
4034
            0,
4035
            'rename',
4036
            false,
4037
            true,
4038
            'file',
4039
            false,
4040
            $folderDocument->getIid(),
4041
        );*/
4042
4043
        if ($document) {
0 ignored issues
show
introduced by
$document is of type null, thus it always evaluated to false.
Loading history...
4044
            $name = '/audio/'.$document->getResourceNode()->getResourceFiles()->first()->getOriginalName();
4045
            // Store the mp3 file in the lp_item table.
4046
            $table = Database::get_course_table(TABLE_LP_ITEM);
4047
            $sql = "UPDATE $table SET
4048
                        audio = '".Database::escape_string($name)."'
4049
                    WHERE iid = ".intval($this->db_id);
4050
            Database::query($sql);
4051
4052
            return true;
4053
        }
4054
4055
        return false;
4056
    }
4057
4058
    /**
4059
     * Removes the relation between the current item and an audio file. The file
4060
     * is only removed from the lp_item table, but remains in the document table
4061
     * and directory.
4062
     *
4063
     * @return bool
4064
     */
4065
    public function removeAudio()
4066
    {
4067
        $courseInfo = api_get_course_info();
4068
4069
        if (empty($this->db_id) || empty($courseInfo)) {
4070
            return false;
4071
        }
4072
4073
        $table = Database::get_course_table(TABLE_LP_ITEM);
4074
        $sql = "UPDATE $table SET
4075
                audio = ''
4076
                WHERE iid = ".$this->db_id;
4077
        Database::query($sql);
4078
    }
4079
4080
    /**
4081
     * Adds an audio file to the current item, using a file already in documents.
4082
     *
4083
     * @param int $documentId
4084
     *
4085
     * @return string
4086
     */
4087
    public function add_audio_from_documents($documentId)
4088
    {
4089
        $courseInfo = api_get_course_info();
4090
        $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

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