Passed
Push — master ( 2338dd...fbaf94 )
by Julito
09:45
created

learnpathItem::lpItemHasThread()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 34
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 20
nc 2
nop 1
dl 0
loc 34
rs 9.6
c 0
b 0
f 0

1 Method

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

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

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

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

1127
                                                    /** @scrutinizer ignore-call */ 
1128
                                                    $in_files_list[] = self::get_resources_from_source(
Loading history...
1128
                                                        TOOL_DOCUMENT,
1129
                                                        $second_part,
1130
                                                        $recursivity + 1
1131
                                                    );
1132
                                                    if (count($in_files_list) > 0) {
1133
                                                        $files_list = array_merge(
1134
                                                            $files_list,
1135
                                                            $in_files_list
1136
                                                        );
1137
                                                    }
1138
                                                } else {
1139
                                                    // We didn't find any trace of current portal.
1140
                                                    $files_list[] = [
1141
                                                        $second_part,
1142
                                                        'remote',
1143
                                                        'url',
1144
                                                    ];
1145
                                                }
1146
                                            } elseif (strpos($second_part, '=') > 0) {
1147
                                                if ('/' === substr($second_part, 0, 1)) {
1148
                                                    // Link starts with a /,
1149
                                                    // making it absolute (relative to DocumentRoot).
1150
                                                    $files_list[] = [
1151
                                                        $second_part,
1152
                                                        'local',
1153
                                                        'abs',
1154
                                                    ];
1155
                                                    $in_files_list[] = self::get_resources_from_source(
1156
                                                        TOOL_DOCUMENT,
1157
                                                        $second_part,
1158
                                                        $recursivity + 1
1159
                                                    );
1160
                                                    if (count($in_files_list) > 0) {
1161
                                                        $files_list = array_merge(
1162
                                                            $files_list,
1163
                                                            $in_files_list
1164
                                                        );
1165
                                                    }
1166
                                                } elseif (0 === strstr($second_part, '..')) {
1167
                                                    // Link is relative but going back in the hierarchy.
1168
                                                    $files_list[] = [
1169
                                                        $second_part,
1170
                                                        'local',
1171
                                                        'rel',
1172
                                                    ];
1173
                                                    $dir = dirname(
1174
                                                        $abs_path
1175
                                                    );
1176
                                                    $new_abs_path = realpath(
1177
                                                        $dir.'/'.$second_part
1178
                                                    );
1179
                                                    $in_files_list[] = self::get_resources_from_source(
1180
                                                        TOOL_DOCUMENT,
1181
                                                        $new_abs_path,
1182
                                                        $recursivity + 1
1183
                                                    );
1184
                                                    if (count($in_files_list) > 0) {
1185
                                                        $files_list = array_merge(
1186
                                                            $files_list,
1187
                                                            $in_files_list
1188
                                                        );
1189
                                                    }
1190
                                                } else {
1191
                                                    // No starting '/', making it relative to current document's path.
1192
                                                    if ('./' == substr($second_part, 0, 2)) {
1193
                                                        $second_part = substr(
1194
                                                            $second_part,
1195
                                                            2
1196
                                                        );
1197
                                                    }
1198
                                                    $files_list[] = [
1199
                                                        $second_part,
1200
                                                        'local',
1201
                                                        'rel',
1202
                                                    ];
1203
                                                    $dir = dirname(
1204
                                                        $abs_path
1205
                                                    );
1206
                                                    $new_abs_path = realpath(
1207
                                                        $dir.'/'.$second_part
1208
                                                    );
1209
                                                    $in_files_list[] = self::get_resources_from_source(
1210
                                                        TOOL_DOCUMENT,
1211
                                                        $new_abs_path,
1212
                                                        $recursivity + 1
1213
                                                    );
1214
                                                    if (count($in_files_list) > 0) {
1215
                                                        $files_list = array_merge(
1216
                                                            $files_list,
1217
                                                            $in_files_list
1218
                                                        );
1219
                                                    }
1220
                                                }
1221
                                            }
1222
                                            // Leave that second part behind now.
1223
                                            $source = substr(
1224
                                                $source,
1225
                                                0,
1226
                                                strpos($source, '?')
1227
                                            );
1228
                                            if (strpos($source, '://') > 0) {
1229
                                                if (false !== strpos($source, api_get_path(WEB_PATH))) {
1230
                                                    // We found the current portal url.
1231
                                                    $files_list[] = [
1232
                                                        $source,
1233
                                                        'local',
1234
                                                        'url',
1235
                                                    ];
1236
                                                    $in_files_list[] = self::get_resources_from_source(
1237
                                                        TOOL_DOCUMENT,
1238
                                                        $source,
1239
                                                        $recursivity + 1
1240
                                                    );
1241
                                                    if (count($in_files_list) > 0) {
1242
                                                        $files_list = array_merge(
1243
                                                            $files_list,
1244
                                                            $in_files_list
1245
                                                        );
1246
                                                    }
1247
                                                } else {
1248
                                                    // We didn't find any trace of current portal.
1249
                                                    $files_list[] = [
1250
                                                        $source,
1251
                                                        'remote',
1252
                                                        'url',
1253
                                                    ];
1254
                                                }
1255
                                            } else {
1256
                                                // No protocol found, make link local.
1257
                                                if ('/' === substr($source, 0, 1)) {
1258
                                                    // Link starts with a /, making it absolute (relative to DocumentRoot).
1259
                                                    $files_list[] = [
1260
                                                        $source,
1261
                                                        'local',
1262
                                                        'abs',
1263
                                                    ];
1264
                                                    $in_files_list[] = self::get_resources_from_source(
1265
                                                        TOOL_DOCUMENT,
1266
                                                        $source,
1267
                                                        $recursivity + 1
1268
                                                    );
1269
                                                    if (count($in_files_list) > 0) {
1270
                                                        $files_list = array_merge(
1271
                                                            $files_list,
1272
                                                            $in_files_list
1273
                                                        );
1274
                                                    }
1275
                                                } elseif (0 === strstr($source, '..')) {
1276
                                                    // Link is relative but going back in the hierarchy.
1277
                                                    $files_list[] = [
1278
                                                        $source,
1279
                                                        'local',
1280
                                                        'rel',
1281
                                                    ];
1282
                                                    $dir = dirname(
1283
                                                        $abs_path
1284
                                                    );
1285
                                                    $new_abs_path = realpath(
1286
                                                        $dir.'/'.$source
1287
                                                    );
1288
                                                    $in_files_list[] = self::get_resources_from_source(
1289
                                                        TOOL_DOCUMENT,
1290
                                                        $new_abs_path,
1291
                                                        $recursivity + 1
1292
                                                    );
1293
                                                    if (count($in_files_list) > 0) {
1294
                                                        $files_list = array_merge(
1295
                                                            $files_list,
1296
                                                            $in_files_list
1297
                                                        );
1298
                                                    }
1299
                                                } else {
1300
                                                    // No starting '/', making it relative to current document's path.
1301
                                                    if ('./' == substr($source, 0, 2)) {
1302
                                                        $source = substr(
1303
                                                            $source,
1304
                                                            2
1305
                                                        );
1306
                                                    }
1307
                                                    $files_list[] = [
1308
                                                        $source,
1309
                                                        'local',
1310
                                                        'rel',
1311
                                                    ];
1312
                                                    $dir = dirname(
1313
                                                        $abs_path
1314
                                                    );
1315
                                                    $new_abs_path = realpath(
1316
                                                        $dir.'/'.$source
1317
                                                    );
1318
                                                    $in_files_list[] = self::get_resources_from_source(
1319
                                                        TOOL_DOCUMENT,
1320
                                                        $new_abs_path,
1321
                                                        $recursivity + 1
1322
                                                    );
1323
                                                    if (count($in_files_list) > 0) {
1324
                                                        $files_list = array_merge(
1325
                                                            $files_list,
1326
                                                            $in_files_list
1327
                                                        );
1328
                                                    }
1329
                                                }
1330
                                            }
1331
                                        }
1332
1333
                                        // Found some protocol there.
1334
                                        if (false !== strpos($source, api_get_path(WEB_PATH))) {
1335
                                            // We found the current portal url.
1336
                                            $files_list[] = [
1337
                                                $source,
1338
                                                'local',
1339
                                                'url',
1340
                                            ];
1341
                                            $in_files_list[] = self::get_resources_from_source(
1342
                                                TOOL_DOCUMENT,
1343
                                                $source,
1344
                                                $recursivity + 1
1345
                                            );
1346
                                            if (count($in_files_list) > 0) {
1347
                                                $files_list = array_merge(
1348
                                                    $files_list,
1349
                                                    $in_files_list
1350
                                                );
1351
                                            }
1352
                                        } else {
1353
                                            // We didn't find any trace of current portal.
1354
                                            $files_list[] = [
1355
                                                $source,
1356
                                                'remote',
1357
                                                'url',
1358
                                            ];
1359
                                        }
1360
                                    } else {
1361
                                        // No protocol found, make link local.
1362
                                        if ('/' === substr($source, 0, 1)) {
1363
                                            // Link starts with a /, making it absolute (relative to DocumentRoot).
1364
                                            $files_list[] = [
1365
                                                $source,
1366
                                                'local',
1367
                                                'abs',
1368
                                            ];
1369
                                            $in_files_list[] = self::get_resources_from_source(
1370
                                                TOOL_DOCUMENT,
1371
                                                $source,
1372
                                                $recursivity + 1
1373
                                            );
1374
                                            if (count($in_files_list) > 0) {
1375
                                                $files_list = array_merge(
1376
                                                    $files_list,
1377
                                                    $in_files_list
1378
                                                );
1379
                                            }
1380
                                        } elseif (0 === strstr($source, '..')) {
1381
                                            // Link is relative but going back in the hierarchy.
1382
                                            $files_list[] = [
1383
                                                $source,
1384
                                                'local',
1385
                                                'rel',
1386
                                            ];
1387
                                            $dir = dirname($abs_path);
1388
                                            $new_abs_path = realpath(
1389
                                                $dir.'/'.$source
1390
                                            );
1391
                                            $in_files_list[] = self::get_resources_from_source(
1392
                                                TOOL_DOCUMENT,
1393
                                                $new_abs_path,
1394
                                                $recursivity + 1
1395
                                            );
1396
                                            if (count($in_files_list) > 0) {
1397
                                                $files_list = array_merge(
1398
                                                    $files_list,
1399
                                                    $in_files_list
1400
                                                );
1401
                                            }
1402
                                        } else {
1403
                                            // No starting '/', making it relative to current document's path.
1404
                                            if (strpos($source, 'width=') ||
1405
                                                strpos($source, 'autostart=')
1406
                                            ) {
1407
                                                continue;
1408
                                            }
1409
1410
                                            if ('./' == substr($source, 0, 2)) {
1411
                                                $source = substr(
1412
                                                    $source,
1413
                                                    2
1414
                                                );
1415
                                            }
1416
                                            $files_list[] = [
1417
                                                $source,
1418
                                                'local',
1419
                                                'rel',
1420
                                            ];
1421
                                            $dir = dirname($abs_path);
1422
                                            $new_abs_path = realpath(
1423
                                                $dir.'/'.$source
1424
                                            );
1425
                                            $in_files_list[] = self::get_resources_from_source(
1426
                                                TOOL_DOCUMENT,
1427
                                                $new_abs_path,
1428
                                                $recursivity + 1
1429
                                            );
1430
                                            if (count($in_files_list) > 0) {
1431
                                                $files_list = array_merge(
1432
                                                    $files_list,
1433
                                                    $in_files_list
1434
                                                );
1435
                                            }
1436
                                        }
1437
                                    }
1438
                                }
1439
                            }
1440
                        }
1441
                        break;
1442
                    default:
1443
                        break;
1444
                }
1445
1446
                break;
1447
            default: // Ignore.
1448
                break;
1449
        }
1450
1451
        $checked_files_list = [];
1452
        $checked_array_list = [];
1453
        foreach ($files_list as $idx => $file) {
1454
            if (!empty($file[0])) {
1455
                if (!in_array($file[0], $checked_files_list)) {
1456
                    $checked_files_list[] = $files_list[$idx][0];
1457
                    $checked_array_list[] = $files_list[$idx];
1458
                }
1459
            }
1460
        }
1461
1462
        return $checked_array_list;
1463
    }
1464
1465
    /**
1466
     * Gets the score.
1467
     *
1468
     * @return float The current score or 0 if no score set yet
1469
     */
1470
    public function get_score()
1471
    {
1472
        $res = 0;
1473
        if (!empty($this->current_score)) {
1474
            $res = $this->current_score;
1475
        }
1476
1477
        return $res;
1478
    }
1479
1480
    /**
1481
     * Gets the item status.
1482
     *
1483
     * @param bool $check_db     Do or don't check into the database for the
1484
     *                           latest value. Optional. Default is true
1485
     * @param bool $update_local Do or don't update the local attribute
1486
     *                           value with what's been found in DB
1487
     *
1488
     * @return string Current status or 'Not attempted' if no status set yet
1489
     */
1490
    public function get_status($check_db = true, $update_local = false)
1491
    {
1492
        $courseId = $this->courseId;
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) && !empty($courseId)) {
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_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) {
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) {
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 = api_get_configuration_value('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 = api_get_configuration_value('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
            if (!WhispeakAuthPlugin::isAllowedToSaveLpItem($this->iId)) {
2809
                return false;
2810
            }
2811
2812
            switch ($type) {
2813
                case 'asset':
2814
                    if ($prereqs_complete) {
2815
                        $this->set_status($this->possible_status[2]);
2816
                    }
2817
                    break;
2818
                case TOOL_HOTPOTATOES:
2819
                    break;
2820
                case TOOL_QUIZ:
2821
                    return false;
2822
                    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...
2823
                default:
2824
                    // For now, everything that is not sco and not asset is set to
2825
                    // completed when saved.
2826
                    if ($prereqs_complete) {
2827
                        $this->set_status($this->possible_status[2]);
2828
                    }
2829
                    break;
2830
            }
2831
        }
2832
2833
        if ($debug) {
2834
            error_log('End of learnpathItem::save() - Calling write_to_db() now');
2835
        }
2836
2837
        return $this->write_to_db();
2838
    }
2839
2840
    /**
2841
     * Sets the number of attempt_id to a given value.
2842
     *
2843
     * @param int $num The given value to set attempt_id to
2844
     *
2845
     * @return bool TRUE on success, FALSE otherwise
2846
     */
2847
    public function set_attempt_id($num)
2848
    {
2849
        if ($num == strval(intval($num)) && $num >= 0) {
2850
            $this->attempt_id = $num;
2851
2852
            return true;
2853
        }
2854
2855
        return false;
2856
    }
2857
2858
    /**
2859
     * Sets the core_exit value to the one given.
2860
     *
2861
     * @return bool $value  True (always)
2862
     */
2863
    public function set_core_exit($value)
2864
    {
2865
        switch ($value) {
2866
            case '':
2867
                $this->core_exit = '';
2868
                break;
2869
            case 'suspend':
2870
                $this->core_exit = 'suspend';
2871
                break;
2872
            default:
2873
                $this->core_exit = 'none';
2874
                break;
2875
        }
2876
2877
        return true;
2878
    }
2879
2880
    /**
2881
     * Sets the item's description.
2882
     *
2883
     * @param string $string Description
2884
     */
2885
    public function set_description($string = '')
2886
    {
2887
        if (!empty($string)) {
2888
            $this->description = $string;
2889
        }
2890
    }
2891
2892
    /**
2893
     * Sets the lesson_location value.
2894
     *
2895
     * @param string $location lesson_location as provided by the SCO
2896
     *
2897
     * @return bool True on success, false otherwise
2898
     */
2899
    public function set_lesson_location($location)
2900
    {
2901
        if (isset($location)) {
2902
            $this->lesson_location = $location;
2903
2904
            return true;
2905
        }
2906
2907
        return false;
2908
    }
2909
2910
    /**
2911
     * Sets the item's depth level in the LP tree (0 is at root).
2912
     *
2913
     * @param int $int Level
2914
     */
2915
    public function set_level($int = 0)
2916
    {
2917
        $this->level = (int) $int;
2918
    }
2919
2920
    /**
2921
     * Sets the lp_view id this item view is registered to.
2922
     *
2923
     * @param int $lp_view_id lp_view DB ID
2924
     * @param int $courseId
2925
     *
2926
     * @return bool
2927
     *
2928
     * @todo //todo insert into lp_item_view if lp_view not exists
2929
     */
2930
    public function set_lp_view($lp_view_id, $courseId = null)
2931
    {
2932
        $lp_view_id = (int) $lp_view_id;
2933
        $courseId = (int) $courseId;
2934
2935
        if (empty($courseId)) {
2936
            $courseId = api_get_course_int_id();
2937
        }
2938
2939
        $lpItemId = $this->get_id();
2940
2941
        if (empty($lpItemId)) {
2942
            return false;
2943
        }
2944
2945
        if (empty($lp_view_id)) {
2946
            return false;
2947
        }
2948
2949
        if (self::DEBUG > 0) {
2950
            error_log('learnpathItem::set_lp_view('.$lp_view_id.')', 0);
2951
        }
2952
2953
        $this->view_id = $lp_view_id;
2954
2955
        $item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
2956
        // Get the lp_item_view with the highest view_count.
2957
        $sql = "SELECT * FROM $item_view_table
2958
                WHERE
2959
                    lp_item_id = $lpItemId AND
2960
                    lp_view_id = $lp_view_id
2961
                ORDER BY view_count DESC";
2962
2963
        if (self::DEBUG > 2) {
2964
            error_log(
2965
                'learnpathItem::set_lp_view() - Querying lp_item_view: '.$sql,
2966
                0
2967
            );
2968
        }
2969
        $res = Database::query($sql);
2970
        if (Database::num_rows($res) > 0) {
2971
            $row = Database::fetch_array($res);
2972
            $this->db_item_view_id = $row['iid'];
2973
            $this->attempt_id = $row['view_count'];
2974
            $this->current_score = $row['score'];
2975
            $this->current_data = $row['suspend_data'];
2976
            $this->view_max_score = $row['max_score'];
2977
            $this->status = $row['status'];
2978
            $this->current_start_time = $row['start_time'];
2979
            $this->current_stop_time = $this->current_start_time + $row['total_time'];
2980
            $this->lesson_location = $row['lesson_location'];
2981
            $this->core_exit = $row['core_exit'];
2982
2983
            if (self::DEBUG > 2) {
2984
                error_log(
2985
                    'learnpathItem::set_lp_view() - Updated item object with database values',
2986
                    0
2987
                );
2988
            }
2989
2990
            // Now get the number of interactions for this little guy.
2991
            $table = Database::get_course_table(TABLE_LP_IV_INTERACTION);
2992
            $sql = "SELECT * FROM $table
2993
                    WHERE
2994
                        c_id = $courseId AND
2995
                        lp_iv_id = '".$this->db_item_view_id."'";
2996
2997
            $res = Database::query($sql);
2998
            if (false !== $res) {
2999
                $this->interactions_count = Database::num_rows($res);
3000
            } else {
3001
                $this->interactions_count = 0;
3002
            }
3003
            // Now get the number of objectives for this little guy.
3004
            $table = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
3005
            $sql = "SELECT * FROM $table
3006
                    WHERE
3007
                        c_id = $courseId AND
3008
                        lp_iv_id = '".$this->db_item_view_id."'";
3009
3010
            $this->objectives_count = 0;
3011
            $res = Database::query($sql);
3012
            if (false !== $res) {
3013
                $this->objectives_count = Database::num_rows($res);
3014
            }
3015
        }
3016
3017
        return true;
3018
    }
3019
3020
    /**
3021
     * Sets the path.
3022
     *
3023
     * @param string $string Path
3024
     */
3025
    public function set_path($string = '')
3026
    {
3027
        if (!empty($string)) {
3028
            $this->path = $string;
3029
        }
3030
    }
3031
3032
    /**
3033
     * Sets the prevent_reinit attribute.
3034
     * This is based on the LP value and is set at creation time for
3035
     * each learnpathItem. It is a (bad?) way of avoiding
3036
     * a reference to the LP when saving an item.
3037
     *
3038
     * @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...
3039
     * saving freshened values (new "not attempted" status etc)
3040
     */
3041
    public function set_prevent_reinit($prevent)
3042
    {
3043
        $this->prevent_reinit = 0;
3044
        if ($prevent) {
3045
            $this->prevent_reinit = 1;
3046
        }
3047
    }
3048
3049
    /**
3050
     * Sets the score value. If the mastery_score is set and the score reaches
3051
     * it, then set the status to 'passed'.
3052
     *
3053
     * @param float $score Score
3054
     *
3055
     * @return bool True on success, false otherwise
3056
     */
3057
    public function set_score($score)
3058
    {
3059
        $debug = self::DEBUG;
3060
        if ($debug > 0) {
3061
            error_log('learnpathItem::set_score('.$score.')', 0);
3062
        }
3063
        if (($this->max_score <= 0 || $score <= $this->max_score) && ($score >= $this->min_score)) {
3064
            $this->current_score = $score;
3065
            $masteryScore = $this->get_mastery_score();
3066
            $current_status = $this->get_status(false);
3067
3068
            // Fixes bug when SCORM doesn't send a mastery score even if they sent a score!
3069
            if (-1 == $masteryScore) {
3070
                $masteryScore = $this->max_score;
3071
            }
3072
3073
            if ($debug > 0) {
3074
                error_log('get_mastery_score: '.$masteryScore);
3075
                error_log('current_status: '.$current_status);
3076
                error_log('current score : '.$this->current_score);
3077
            }
3078
3079
            // If mastery_score is set AND the current score reaches the mastery
3080
            //  score AND the current status is different from 'completed', then
3081
            //  set it to 'passed'.
3082
            /*
3083
            if ($master != -1 && $this->current_score >= $master && $current_status != $this->possible_status[2]) {
3084
                if ($debug > 0) error_log('Status changed to: '.$this->possible_status[3]);
3085
                $this->set_status($this->possible_status[3]); //passed
3086
            } elseif ($master != -1 && $this->current_score < $master) {
3087
                if ($debug > 0) error_log('Status changed to: '.$this->possible_status[4]);
3088
                $this->set_status($this->possible_status[4]); //failed
3089
            }*/
3090
            return true;
3091
        }
3092
3093
        return false;
3094
    }
3095
3096
    /**
3097
     * Sets the maximum score for this item.
3098
     *
3099
     * @param int $score Maximum score - must be a decimal or an empty string
3100
     *
3101
     * @return bool True on success, false on error
3102
     */
3103
    public function set_max_score($score)
3104
    {
3105
        if (is_int($score) || '' == $score) {
3106
            $this->view_max_score = $score;
3107
3108
            return true;
3109
        }
3110
3111
        return false;
3112
    }
3113
3114
    /**
3115
     * Sets the status for this item.
3116
     *
3117
     * @param string $status Status - must be one of the values defined in $this->possible_status
3118
     *                       (this affects the status setting)
3119
     *
3120
     * @return bool True on success, false on error
3121
     */
3122
    public function set_status($status)
3123
    {
3124
        if (self::DEBUG) {
3125
            error_log('learnpathItem::set_status('.$status.')');
3126
        }
3127
3128
        $found = false;
3129
        foreach ($this->possible_status as $possible) {
3130
            if (preg_match('/^'.$possible.'$/i', $status)) {
3131
                $found = true;
3132
            }
3133
        }
3134
3135
        if ($found) {
3136
            $this->status = $status;
3137
            if (self::DEBUG) {
3138
                error_log(
3139
                    'learnpathItem::set_status() - '.
3140
                        'Updated object status of item '.$this->db_id.
3141
                    ' to '.$this->status
3142
                );
3143
            }
3144
3145
            return true;
3146
        }
3147
3148
        if (self::DEBUG) {
3149
            error_log('Setting status: '.$this->possible_status[0]);
3150
        }
3151
        $this->status = $this->possible_status[0];
3152
3153
        return false;
3154
    }
3155
3156
    /**
3157
     * Set the (indexing) terms for this learnpath item.
3158
     *
3159
     * @param string $terms Terms, as a comma-split list
3160
     *
3161
     * @return bool Always return true
3162
     */
3163
    public function set_terms($terms)
3164
    {
3165
        global $charset;
3166
        $lp_item = Database::get_course_table(TABLE_LP_ITEM);
3167
        $a_terms = preg_split('/,/', $terms);
3168
        $i_terms = preg_split('/,/', $this->get_terms());
3169
        foreach ($i_terms as $term) {
3170
            if (!in_array($term, $a_terms)) {
3171
                array_push($a_terms, $term);
3172
            }
3173
        }
3174
        $new_terms = $a_terms;
3175
        $new_terms_string = implode(',', $new_terms);
3176
3177
        // TODO: Validate csv string.
3178
        $terms = Database::escape_string(api_htmlentities($new_terms_string, ENT_QUOTES, $charset));
3179
        $sql = "UPDATE $lp_item
3180
                SET terms = '$terms'
3181
                WHERE iid=".$this->get_id();
3182
        Database::query($sql);
3183
        // Save it to search engine.
3184
        if ('true' == api_get_setting('search_enabled')) {
3185
            $di = new ChamiloIndexer();
3186
            $di->update_terms($this->get_search_did(), $new_terms, 'T');
3187
        }
3188
3189
        return true;
3190
    }
3191
3192
    /**
3193
     * Get the document ID from inside the text index database.
3194
     *
3195
     * @return int Search index database document ID
3196
     */
3197
    public function get_search_did()
3198
    {
3199
        return $this->search_did;
3200
    }
3201
3202
    /**
3203
     * Sets the item viewing time in a usable form, given that SCORM packages
3204
     * often give it as 00:00:00.0000.
3205
     *
3206
     * @param    string    Time as given by SCORM
3207
     * @param string $format
3208
     */
3209
    public function set_time($scorm_time, $format = 'scorm')
3210
    {
3211
        $debug = self::DEBUG;
3212
        if ($debug) {
3213
            error_log("learnpathItem::set_time($scorm_time, $format)");
3214
            error_log("this->type: ".$this->type);
3215
            error_log("this->current_start_time: ".$this->current_start_time);
3216
        }
3217
3218
        if ('0' == $scorm_time &&
3219
            'sco' !== $this->type &&
3220
            0 != $this->current_start_time
3221
        ) {
3222
            $myTime = time() - $this->current_start_time;
3223
            if ($myTime > 0) {
3224
                $this->update_time($myTime);
3225
                if ($debug) {
3226
                    error_log('found asset - set time to '.$myTime);
3227
                }
3228
            } else {
3229
                if ($debug) {
3230
                    error_log('Time not set');
3231
                }
3232
            }
3233
        } else {
3234
            switch ($format) {
3235
                case 'scorm':
3236
                    $res = [];
3237
                    if (preg_match(
3238
                        '/^(\d{1,4}):(\d{2}):(\d{2})(\.\d{1,4})?/',
3239
                        $scorm_time,
3240
                        $res
3241
                    )
3242
                    ) {
3243
                        $hour = $res[1];
3244
                        $min = $res[2];
3245
                        $sec = $res[3];
3246
                        // Getting total number of seconds spent.
3247
                        $totalSec = $hour * 3600 + $min * 60 + $sec;
3248
                        if ($debug) {
3249
                            error_log("totalSec : $totalSec");
3250
                            error_log("Now calling to scorm_update_time()");
3251
                        }
3252
                        $this->scorm_update_time($totalSec);
3253
                    }
3254
                    break;
3255
                case 'int':
3256
                    if ($debug) {
3257
                        error_log("scorm_time = $scorm_time");
3258
                        error_log("Now calling to scorm_update_time()");
3259
                    }
3260
                    $this->scorm_update_time($scorm_time);
3261
                    break;
3262
            }
3263
        }
3264
    }
3265
3266
    /**
3267
     * Sets the item's title.
3268
     *
3269
     * @param string $string Title
3270
     */
3271
    public function set_title($string = '')
3272
    {
3273
        if (!empty($string)) {
3274
            $this->title = $string;
3275
        }
3276
    }
3277
3278
    /**
3279
     * Sets the item's type.
3280
     *
3281
     * @param string $string Type
3282
     */
3283
    public function set_type($string = '')
3284
    {
3285
        if (!empty($string)) {
3286
            $this->type = $string;
3287
        }
3288
    }
3289
3290
    /**
3291
     * Checks if the current status is part of the list of status given.
3292
     *
3293
     * @param array $list An array of status to check for.
3294
     *                    If the current status is one of the strings, return true
3295
     *
3296
     * @return bool True if the status was one of the given strings,
3297
     *              false otherwise
3298
     */
3299
    public function status_is($list = [])
3300
    {
3301
        if (self::DEBUG > 1) {
3302
            error_log(
3303
                '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

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

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

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