Test Setup Failed
Push — master ( f71949...6c6bd7 )
by Julito
55:21
created

learnpath::update_default_view_mode()   C

Complexity

Conditions 8
Paths 14

Size

Total Lines 41
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 34
nc 14
nop 0
dl 0
loc 41
rs 5.3846
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use ChamiloSession as Session;
5
use Chamilo\CourseBundle\Entity\CLpCategory;
6
use Chamilo\CourseBundle\Component\CourseCopy\CourseBuilder;
7
use Chamilo\CourseBundle\Component\CourseCopy\CourseRestorer;
8
use Gedmo\Sortable\Entity\Repository\SortableRepository;
9
use Symfony\Component\Filesystem\Filesystem;
10
use Symfony\Component\Finder\Finder;
11
use Chamilo\CourseBundle\Entity\CLp;
12
use Chamilo\CourseBundle\Entity\CTool;
13
use Chamilo\UserBundle\Entity\User;
14
15
/**
16
 * Class learnpath
17
 * This class defines the parent attributes and methods for Chamilo learnpaths
18
 * and SCORM learnpaths. It is used by the scorm class.
19
 *
20
 * @package chamilo.learnpath
21
 * @author  Yannick Warnier <[email protected]>
22
 * @author  Julio Montoya   <[email protected]> Several improvements and fixes
23
 */
24
class learnpath
25
{
26
    public $attempt = 0; // The number for the current ID view.
27
    public $cc; // Course (code) this learnpath is located in. @todo change name for something more comprensible ...
28
    public $current; // Id of the current item the user is viewing.
29
    public $current_score; // The score of the current item.
30
    public $current_time_start; // The time the user loaded this resource (this does not mean he can see it yet).
31
    public $current_time_stop; // The time the user closed this resource.
32
    public $default_status = 'not attempted';
33
    public $encoding = 'UTF-8';
34
    public $error = '';
35
    public $extra_information = ''; // This string can be used by proprietary SCORM contents to store data about the current learnpath.
36
    public $force_commit = false; // For SCORM only - if set to true, will send a scorm LMSCommit() request on each LMSSetValue().
37
    public $index; // The index of the active learnpath_item in $ordered_items array.
38
    public $items = array();
39
    public $last; // item_id of last item viewed in the learning path.
40
    public $last_item_seen = 0; // In case we have already come in this learnpath, reuse the last item seen if authorized.
41
    public $license; // Which license this course has been given - not used yet on 20060522.
42
    public $lp_id; // DB ID for this learnpath.
43
    public $lp_view_id; // DB ID for lp_view
44
    public $maker; // Which maker has conceived the content (ENI, Articulate, ...).
45
    public $message = '';
46
    public $mode = 'embedded'; // Holds the video display mode (fullscreen or embedded).
47
    public $name; // Learnpath name (they generally have one).
48
    public $ordered_items = array(); // List of the learnpath items in the order they are to be read.
49
    public $path = ''; // Path inside the scorm directory (if scorm).
50
    public $theme; // The current theme of the learning path.
51
    public $preview_image; // The current image of the learning path.
52
    public $accumulateScormTime; // Flag to decide whether to accumulate SCORM time or not
53
54
    // Tells if all the items of the learnpath can be tried again. Defaults to "no" (=1).
55
    public $prevent_reinit = 1;
56
57
    // Describes the mode of progress bar display.
58
    public $seriousgame_mode = 0;
59
    public $progress_bar_mode = '%';
60
61
    // Percentage progress as saved in the db.
62
    public $progress_db = 0;
63
    public $proximity; // Wether the content is distant or local or unknown.
64
    public $refs_list = array(); //list of items by ref => db_id. Used only for prerequisites match.
65
    // !!!This array (refs_list) is built differently depending on the nature of the LP.
66
    // If SCORM, uses ref, if Chamilo, uses id to keep a unique value.
67
    public $type; //type of learnpath. Could be 'chamilo', 'scorm', 'scorm2004', 'aicc', ...
68
    // TODO: Check if this type variable is useful here (instead of just in the controller script).
69
    public $user_id; //ID of the user that is viewing/using the course
70
    public $update_queue = array();
71
    public $scorm_debug = 0;
72
    public $arrMenu = array(); // Array for the menu items.
73
    public $debug = 0; // Logging level.
74
    public $lp_session_id = 0;
75
    public $lp_view_session_id = 0; // The specific view might be bound to a session.
76
    public $prerequisite = 0;
77
    public $use_max_score = 1; // 1 or 0
78
    public $subscribeUsers = 0; // Subscribe users or not
79
    public $created_on = '';
80
    public $modified_on = '';
81
    public $publicated_on = '';
82
    public $expired_on = '';
83
    public $ref = null;
84
    public $course_int_id;
85
    public $course_info = array();
86
    public $categoryId;
87
88
    /**
89
     * Constructor.
90
     * Needs a database handler, a course code and a learnpath id from the database.
91
     * Also builds the list of items into $this->items.
92
     * @param   string $course Course code
93
     * @param   integer $lp_id
94
     * @param   integer $user_id
95
     * @return  mixed True on success, false on error
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
96
     */
97
    public function __construct($course, $lp_id, $user_id)
98
    {
99
        $this->encoding = api_get_system_encoding();
100 View Code Duplication
        if ($this->debug > 0) {
101
            error_log('New LP - In learnpath::__construct('.$course.','.$lp_id.','.$user_id.')', 0);
102
        }
103
        if (empty($course)) {
104
            $course = api_get_course_id();
105
        }
106
        $course_info = api_get_course_info($course);
107
        if (!empty($course_info)) {
108
            $this->cc = $course_info['code'];
109
            $this->course_info = $course_info;
110
            $course_id = $course_info['real_id'];
111
        } else {
112
            $this->error = 'Course code does not exist in database.';
113
        }
114
115
        $this->set_course_int_id($course_id);
116
        $lp_id = (int) $lp_id;
117
        // Check learnpath ID.
118
        if (empty($lp_id)) {
119
            $this->error = 'Learnpath ID is empty';
120
        } else {
121
            // TODO: Make it flexible to use any course_code (still using env course code here).
122
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
123
            $sql = "SELECT * FROM $lp_table
124
                    WHERE id = '$lp_id' AND c_id = $course_id";
125
            if ($this->debug > 2) {
126
                error_log('New LP - learnpath::__construct() '.__LINE__.' - Querying lp: '.$sql, 0);
127
            }
128
            $res = Database::query($sql);
129
            if (Database::num_rows($res) > 0) {
130
                $this->lp_id = $lp_id;
131
                $row = Database::fetch_array($res);
132
                $this->type = $row['lp_type'];
133
                $this->name = stripslashes($row['name']);
134
                $this->proximity = $row['content_local'];
135
                $this->theme = $row['theme'];
136
                $this->maker = $row['content_maker'];
137
                $this->prevent_reinit = $row['prevent_reinit'];
138
                $this->seriousgame_mode = $row['seriousgame_mode'];
139
                $this->license = $row['content_license'];
140
                $this->scorm_debug = $row['debug'];
141
                $this->js_lib = $row['js_lib'];
142
                $this->path = $row['path'];
143
                $this->preview_image = $row['preview_image'];
144
                $this->author = $row['author'];
145
                $this->hide_toc_frame = $row['hide_toc_frame'];
146
                $this->lp_session_id = $row['session_id'];
147
                $this->use_max_score = $row['use_max_score'];
148
                $this->subscribeUsers = $row['subscribe_users'];
149
                $this->created_on = $row['created_on'];
150
                $this->modified_on = $row['modified_on'];
151
                $this->ref = $row['ref'];
152
                $this->categoryId = $row['category_id'];
153
                $this->accumulateScormTime = isset($row['accumulate_scorm_time']) ? $row['accumulate_scorm_time'] : 'true';
154
155
                if (!empty($row['publicated_on'])) {
156
                    $this->publicated_on = $row['publicated_on'];
157
                }
158
159
                if (!empty($row['expired_on'])) {
160
                    $this->expired_on = $row['expired_on'];
161
                }
162 View Code Duplication
                if ($this->type == 2) {
163
                    if ($row['force_commit'] == 1) {
164
                        $this->force_commit = true;
165
                    }
166
                }
167
                $this->mode = $row['default_view_mod'];
168
169
                // Check user ID.
170
                if (empty($user_id)) {
171
                    $this->error = 'User ID is empty';
172
                } else {
173
                    $user_info = api_get_user_info($user_id);
174
                    if (!empty($user_info)) {
175
                        $this->user_id = $user_info['user_id'];
176
                    } else {
177
                        $this->error = 'User ID does not exist in database ('.$sql.')';
178
                    }
179
                }
180
181
                // End of variables checking.
182
                $session_id = api_get_session_id();
183
                //  Get the session condition for learning paths of the base + session.
184
                $session = api_get_session_condition($session_id);
185
                // Now get the latest attempt from this user on this LP, if available, otherwise create a new one.
186
                $lp_table = Database::get_course_table(TABLE_LP_VIEW);
187
188
                // Selecting by view_count descending allows to get the highest view_count first.
189
                $sql = "SELECT * FROM $lp_table
190
                        WHERE c_id = $course_id AND lp_id = '$lp_id' AND user_id = '$user_id' $session
191
                        ORDER BY view_count DESC";
192
                $res = Database::query($sql);
193
                if ($this->debug > 2) {
194
                    error_log('New LP - learnpath::__construct() '.__LINE__.' - querying lp_view: '.$sql, 0);
195
                }
196
197
                if (Database::num_rows($res) > 0) {
198
                    if ($this->debug > 2) {
199
                        error_log('New LP - learnpath::__construct() '.__LINE__.' - Found previous view', 0);
200
                    }
201
                    $row = Database::fetch_array($res);
202
                    $this->attempt = $row['view_count'];
203
                    $this->lp_view_id = $row['id'];
204
                    $this->last_item_seen = $row['last_item'];
205
                    $this->progress_db = $row['progress'];
206
                    $this->lp_view_session_id = $row['session_id'];
207
                } else if (!api_is_invitee()) {
208
                    if ($this->debug > 2) {
209
                        error_log('New LP - learnpath::__construct() '.__LINE__.' - NOT Found previous view', 0);
210
                    }
211
                    $this->attempt = 1;
212
                    $params = [
213
                        'c_id' => $course_id,
214
                        'lp_id' => $lp_id,
215
                        'user_id' => $user_id,
216
                        'view_count' => 1,
217
                        'session_id' => $session_id,
218
                        'last_item' => 0
219
                    ];
220
                    $this->lp_view_id = Database::insert($lp_table, $params);
221
                    if (!empty($this->lp_view_id)) {
222
                        $sql = "UPDATE $lp_table SET id = iid WHERE iid = ".$this->lp_view_id;
223
                        Database::query($sql);
224
                    }
225
                }
226
227
                // Initialise items.
228
                $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
229
                $sql = "SELECT * FROM $lp_item_table
230
                        WHERE c_id = $course_id AND lp_id = '".$this->lp_id."'
231
                        ORDER BY parent_item_id, display_order";
232
                $res = Database::query($sql);
233
234
                if ($this->debug > 2) {
235
                    error_log('New LP - learnpath::__construct() '.__LINE__.' - query lp items: '.$sql, 0);
236
                    error_log('-- Start while--', 0);
237
                }
238
239
                $lp_item_id_list = array();
240
                while ($row = Database::fetch_array($res)) {
241
                    $lp_item_id_list[] = $row['id'];
242
                    switch ($this->type) {
243 View Code Duplication
                        case 3: //aicc
244
                            $oItem = new aiccItem('db', $row['id'], $course_id);
245
                            if (is_object($oItem)) {
246
                                $my_item_id = $oItem->get_id();
247
                                $oItem->set_lp_view($this->lp_view_id, $course_id);
248
                                $oItem->set_prevent_reinit($this->prevent_reinit);
249
                                // Don't use reference here as the next loop will make the pointed object change.
250
                                $this->items[$my_item_id] = $oItem;
251
                                $this->refs_list[$oItem->ref] = $my_item_id;
252
                                if ($this->debug > 2) {
253
                                    error_log(
254
                                        'New LP - learnpath::__construct() - '.
255
                                        'aicc object with id '.$my_item_id.
256
                                        ' set in items[]',
257
                                        0
258
                                    );
259
                                }
260
                            }
261
                            break;
262 View Code Duplication
                        case 2:
263
                            $oItem = new scormItem('db', $row['id'], $course_id);
264
                            if (is_object($oItem)) {
265
                                $my_item_id = $oItem->get_id();
266
                                $oItem->set_lp_view($this->lp_view_id, $course_id);
267
                                $oItem->set_prevent_reinit($this->prevent_reinit);
268
                                // Don't use reference here as the next loop will make the pointed object change.
269
                                $this->items[$my_item_id] = $oItem;
270
                                $this->refs_list[$oItem->ref] = $my_item_id;
271
                                if ($this->debug > 2) {
272
                                    error_log('New LP - object with id '.$my_item_id.' set in items[]', 0);
273
                                }
274
                            }
275
                            break;
276
                        case 1:
277
                        default:
278
                            if ($this->debug > 2) {
279
                                error_log('New LP - learnpath::__construct() '.__LINE__.' - calling learnpathItem', 0);
280
                            }
281
                            $oItem = new learnpathItem($row['id'], $user_id, $course_id, $row);
282
283
                            if ($this->debug > 2) {
284
                                error_log('New LP - learnpath::__construct() '.__LINE__.' - end calling learnpathItem', 0);
285
                            }
286
                            if (is_object($oItem)) {
287
                                $my_item_id = $oItem->get_id();
288
                                //$oItem->set_lp_view($this->lp_view_id); // Moved down to when we are sure the item_view exists.
289
                                $oItem->set_prevent_reinit($this->prevent_reinit);
290
                                // Don't use reference here as the next loop will make the pointed object change.
291
                                $this->items[$my_item_id] = $oItem;
292
                                $this->refs_list[$my_item_id] = $my_item_id;
293 View Code Duplication
                                if ($this->debug > 2) {
294
                                    error_log(
295
                                        'New LP - learnpath::__construct() '.__LINE__.
296
                                        ' - object with id '.$my_item_id.' set in items[]',
297
                                        0
298
                                    );
299
                                }
300
                            }
301
                            break;
302
                    }
303
304
                    // Setting the object level with variable $this->items[$i][parent]
305
                    foreach ($this->items as $itemLPObject) {
306
                        $level = self::get_level_for_item($this->items, $itemLPObject->db_id);
307
                        $itemLPObject->level = $level;
308
                    }
309
310
                    // Setting the view in the item object.
311
                    if (is_object($this->items[$row['id']])) {
312
                        $this->items[$row['id']]->set_lp_view($this->lp_view_id, $course_id);
313
                        if ($this->items[$row['id']]->get_type() == TOOL_HOTPOTATOES) {
314
                            $this->items[$row['id']]->current_start_time = 0;
315
                            $this->items[$row['id']]->current_stop_time = 0;
316
                        }
317
                    }
318
                }
319
320
                if ($this->debug > 2) {
321
                    error_log('New LP - learnpath::__construct() '.__LINE__.' ----- end while ----', 0);
322
                }
323
324
                if (!empty($lp_item_id_list)) {
325
                    $lp_item_id_list_to_string = implode("','", $lp_item_id_list);
326
                    if (!empty($lp_item_id_list_to_string)) {
327
                        // Get last viewing vars.
328
                        $itemViewTable = Database::get_course_table(TABLE_LP_ITEM_VIEW);
329
                        // This query should only return one or zero result.
330
                        $sql = "SELECT lp_item_id, status
331
                                FROM $itemViewTable
332
                                WHERE
333
                                    c_id = $course_id AND
334
                                    lp_view_id = ".$this->lp_view_id." AND
335
                                    lp_item_id IN ('".$lp_item_id_list_to_string."')
336
                                ORDER BY view_count DESC ";
337
338
                        if ($this->debug > 2) {
339
                            error_log(
340
                                'New LP - learnpath::__construct() - Selecting item_views: '.$sql,
341
                                0
342
                            );
343
                        }
344
345
                        $status_list = array();
346
                        $res = Database::query($sql);
347
                        while ($row = Database:: fetch_array($res)) {
348
                            $status_list[$row['lp_item_id']] = $row['status'];
349
                        }
350
351
                        foreach ($lp_item_id_list as $item_id) {
352
                            if (isset($status_list[$item_id])) {
353
                                $status = $status_list[$item_id];
354
                                if (is_object($this->items[$item_id])) {
355
                                    $this->items[$item_id]->set_status($status);
356
                                    if (empty($status)) {
357
                                        $this->items[$item_id]->set_status(
358
                                            $this->default_status
359
                                        );
360
                                    }
361
                                }
362
                            } else {
363
                                if (!api_is_invitee()) {
364
                                    if (is_object($this->items[$item_id])) {
365
                                        $this->items[$item_id]->set_status(
366
                                            $this->default_status
367
                                        );
368
                                    }
369
370
                                    if (!empty($this->lp_view_id)) {
371
                                        // Add that row to the lp_item_view table so that we have something to show in the stats page.
372
                                        $params = [
373
                                            'c_id' => $course_id,
374
                                            'lp_item_id' => $course_id,
375
                                            'lp_view_id' => $this->lp_view_id,
376
                                            'view_count' => 1,
377
                                            'status' => 'not attempted',
378
                                            'start_time' => api_get_utc_datetime(),
379
                                            'total_time' => 0,
380
                                            'score' => 0
381
                                        ];
382
                                        $insertId = Database::insert($itemViewTable, $params);
383
384
                                        if ($insertId) {
385
                                            $sql = "UPDATE $itemViewTable SET id = iid
386
                                                    WHERE iid = $insertId";
387
                                            Database::query($sql);
388
                                        }
389
390
                                        $this->items[$item_id]->set_lp_view(
391
                                            $this->lp_view_id,
392
                                            $course_id
393
                                        );
394
                                    }
395
                                }
396
                            }
397
                        }
398
                    }
399
                }
400
401
402
                $this->ordered_items = self::get_flat_ordered_items_list(
403
                    $this->get_id(),
404
                    0,
405
                    $course_id
406
                );
407
                $this->max_ordered_items = 0;
408
                foreach ($this->ordered_items as $index => $dummy) {
409
                    if ($index > $this->max_ordered_items && !empty($dummy)) {
410
                        $this->max_ordered_items = $index;
411
                    }
412
                }
413
                // TODO: Define the current item better.
414
                $this->first();
415
                if ($this->debug > 2) {
416
                    error_log('New LP - learnpath::__construct() '.__LINE__.' - End of learnpath constructor for learnpath '.$this->get_id(), 0);
417
                }
418
            } else {
419
                $this->error = 'Learnpath ID does not exist in database ('.$sql.')';
420
            }
421
        }
422
    }
423
424
    /**
425
     * @return string
426
     */
427
    public function getCourseCode()
428
    {
429
        return $this->cc;
430
    }
431
432
    /**
433
     * @return int
434
     */
435
    public function get_course_int_id()
436
    {
437
        return isset($this->course_int_id) ? $this->course_int_id : api_get_course_int_id();
438
    }
439
440
    /**
441
     * @param $course_id
442
     * @return int
443
     */
444
    public function set_course_int_id($course_id)
445
    {
446
        return $this->course_int_id = (int) $course_id;
447
    }
448
449
    /**
450
     * Get the depth level of LP item
451
     * @param array $items
452
     * @param int $currentItemId
453
     * @return int
454
     */
455
    private static function get_level_for_item($items, $currentItemId)
456
    {
457
        $parentItemId = $items[$currentItemId]->parent;
458
        if ($parentItemId == 0) {
459
            return 0;
460
        } else {
461
            return self::get_level_for_item($items, $parentItemId) + 1;
462
        }
463
    }
464
465
    /**
466
     * Function rewritten based on old_add_item() from Yannick Warnier.
467
     * Due the fact that users can decide where the item should come, I had to overlook this function and
468
     * I found it better to rewrite it. Old function is still available.
469
     * Added also the possibility to add a description.
470
     *
471
     * @param int $parent
472
     * @param int $previous
473
     * @param string $type
474
     * @param int $id resource ID (ref)
475
     * @param string $title
476
     * @param string $description
477
     * @param int $prerequisites
478
     * @param int $max_time_allowed
479
     * @param int $userId
480
     *
481
     * @return int
482
     */
483
    public function add_item(
484
        $parent,
485
        $previous,
486
        $type = 'dir',
487
        $id,
488
        $title,
489
        $description,
490
        $prerequisites = 0,
491
        $max_time_allowed = 0,
492
        $userId = 0
493
    ) {
494
        $course_id = $this->course_info['real_id'];
495
        if ($this->debug > 0) {
496
            error_log('New LP - In learnpath::add_item('.$parent.','.$previous.','.$type.','.$id.','.$title.')', 0);
497
        }
498
        if (empty($course_id)) {
499
            // Sometimes Oogie doesn't catch the course info but sets $this->cc
500
            $this->course_info = api_get_course_info($this->cc);
501
            $course_id = $this->course_info['real_id'];
502
        }
503
        $userId = empty($userId) ? api_get_user_id() : $userId;
504
        $sessionId = api_get_session_id();
505
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
506
        $_course = $this->course_info;
507
        $parent = intval($parent);
508
        $previous = intval($previous);
509
        $id = intval($id);
510
        $max_time_allowed = htmlentities($max_time_allowed);
511
        if (empty($max_time_allowed)) {
512
            $max_time_allowed = 0;
513
        }
514
        $sql = "SELECT COUNT(id) AS num
515
                FROM $tbl_lp_item
516
                WHERE
517
                    c_id = $course_id AND
518
                    lp_id = ".$this->get_id()." AND
519
                    parent_item_id = " . $parent;
520
521
        $res_count = Database::query($sql);
522
        $row = Database::fetch_array($res_count);
523
        $num = $row['num'];
524
525
        if ($num > 0) {
526
            if (empty($previous)) {
527
                $sql = "SELECT id, next_item_id, display_order
528
                        FROM " . $tbl_lp_item."
529
                        WHERE
530
                            c_id = $course_id AND
531
                            lp_id = ".$this->get_id()." AND
532
                            parent_item_id = " . $parent." AND
533
                            previous_item_id = 0 OR
534
                            previous_item_id=" . $parent;
535
                $result = Database::query($sql);
536
                $row = Database::fetch_array($result);
537
                $tmp_previous = 0;
538
                $next = $row['id'];
539
                $display_order = 0;
540
            } else {
541
                $previous = (int) $previous;
542
                $sql = "SELECT id, previous_item_id, next_item_id, display_order
543
						FROM $tbl_lp_item
544
                        WHERE
545
                            c_id = $course_id AND
546
                            lp_id = ".$this->get_id()." AND
547
                            id = " . $previous;
548
549
                $result = Database::query($sql);
550
                $row = Database:: fetch_array($result);
551
552
                $tmp_previous = $row['id'];
553
                $next = $row['next_item_id'];
554
                $display_order = $row['display_order'];
555
            }
556
        } else {
557
            $tmp_previous = 0;
558
            $next = 0;
559
            $display_order = 0;
560
        }
561
562
        $id = intval($id);
563
        $typeCleaned = Database::escape_string($type);
564
        if ($type == 'quiz') {
565
            $sql = 'SELECT SUM(ponderation)
566
                    FROM ' . Database::get_course_table(TABLE_QUIZ_QUESTION).' as quiz_question
567
                    INNER JOIN  ' . Database::get_course_table(TABLE_QUIZ_TEST_QUESTION).' as quiz_rel_question
568
                    ON
569
                        quiz_question.id = quiz_rel_question.question_id AND
570
                        quiz_question.c_id = quiz_rel_question.c_id
571
                    WHERE
572
                        quiz_rel_question.exercice_id = '.$id." AND
573
                        quiz_question.c_id = $course_id AND
574
                        quiz_rel_question.c_id = $course_id ";
575
            $rsQuiz = Database::query($sql);
576
            $max_score = Database::result($rsQuiz, 0, 0);
577
578
            // Disabling the exercise if we add it inside a LP
579
            $exercise = new Exercise($course_id);
580
            $exercise->read($id);
581
            $exercise->disable();
582
            $exercise->save();
583
        } else {
584
            $max_score = 100;
585
        }
586
587
        $params = array(
588
            "c_id" => $course_id,
589
            "lp_id" => $this->get_id(),
590
            "item_type" => $typeCleaned,
591
            "ref" => '',
592
            "title" => $title,
593
            "description" => $description,
594
            "path" => $id,
595
            "max_score" => $max_score,
596
            "parent_item_id" => $parent,
597
            "previous_item_id" => $previous,
598
            "next_item_id" => intval($next),
599
            "display_order" => $display_order + 1,
600
            "prerequisite" => $prerequisites,
601
            "max_time_allowed" => $max_time_allowed,
602
            'min_score' => 0,
603
            'launch_data' => ''
604
        );
605
606
        if ($prerequisites != 0) {
607
            $params['prerequisite'] = $prerequisites;
608
        }
609
610
        $new_item_id = Database::insert($tbl_lp_item, $params);
611
612
        if ($this->debug > 2) {
613
            error_log('New LP - Inserting dir/chapter: '.$new_item_id, 0);
614
        }
615
616
        if ($new_item_id) {
617
            $sql = "UPDATE $tbl_lp_item SET id = iid WHERE iid = $new_item_id";
618
            Database::query($sql);
619
620
            $sql = "UPDATE $tbl_lp_item
621
                    SET previous_item_id = $new_item_id 
622
                    WHERE c_id = $course_id AND id = $next";
623
            Database::query($sql);
624
625
            // Update the item that should be before the new item.
626
            $sql = "UPDATE $tbl_lp_item
627
                    SET next_item_id = $new_item_id
628
                    WHERE c_id = $course_id AND id = $tmp_previous";
629
            Database::query($sql);
630
631
            // Update all the items after the new item.
632
            $sql = "UPDATE ".$tbl_lp_item."
633
                        SET display_order = display_order + 1
634
                    WHERE
635
                        c_id = $course_id AND
636
                        lp_id = ".$this->get_id()." AND
637
                        id <> " . $new_item_id." AND
638
                        parent_item_id = " . $parent." AND
639
                        display_order > " . $display_order;
640
            Database::query($sql);
641
642
            // Update the item that should come after the new item.
643
            $sql = "UPDATE ".$tbl_lp_item."
644
                    SET ref = " . $new_item_id."
645
                    WHERE c_id = $course_id AND id = ".$new_item_id;
646
            Database::query($sql);
647
648
            // Upload audio.
649
            if (!empty($_FILES['mp3']['name'])) {
650
                // Create the audio folder if it does not exist yet.
651
                $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/';
652
                if (!is_dir($filepath.'audio')) {
653
                    mkdir($filepath.'audio', api_get_permissions_for_new_directories());
654
                    $audio_id = add_document(
655
                        $_course,
656
                        '/audio',
657
                        'folder',
658
                        0,
659
                        'audio',
660
                        '',
661
                        0,
662
                        true,
663
                        null,
664
                        $sessionId,
665
                        $userId
666
                    );
667
                    api_item_property_update(
668
                        $_course,
669
                        TOOL_DOCUMENT,
670
                        $audio_id,
671
                        'FolderCreated',
672
                        $userId,
673
                        null,
674
                        null,
675
                        null,
676
                        null,
677
                        $sessionId
678
                    );
679
                    api_item_property_update(
680
                        $_course,
681
                        TOOL_DOCUMENT,
682
                        $audio_id,
683
                        'invisible',
684
                        $userId,
685
                        null,
686
                        null,
687
                        null,
688
                        null,
689
                        $sessionId
690
                    );
691
                }
692
693
                $file_path = handle_uploaded_document(
694
                    $_course,
695
                    $_FILES['mp3'],
696
                    api_get_path(SYS_COURSE_PATH).$_course['path'].'/document',
697
                    '/audio',
698
                    $userId,
699
                    '',
700
                    '',
701
                    '',
702
                    '',
703
                    false
704
                );
705
706
                // Getting the filename only.
707
                $file_components = explode('/', $file_path);
708
                $file = $file_components[count($file_components) - 1];
709
710
                // Store the mp3 file in the lp_item table.
711
                $sql = "UPDATE $tbl_lp_item SET
712
                            audio = '".Database::escape_string($file)."'
713
                        WHERE id = '" . intval($new_item_id)."'";
714
                Database::query($sql);
715
            }
716
        }
717
718
        return $new_item_id;
719
    }
720
721
    /**
722
     * Static admin function allowing addition of a learnpath to a course.
723
     * @param string    Course code
724
     * @param string    Learnpath name
725
     * @param string    Learnpath description string, if provided
726
     * @param string    Type of learnpath (default = 'guess', others = 'dokeos', 'aicc',...)
727
     * @param string    Type of files origin (default = 'zip', others = 'dir','web_dir',...)
728
     * @param string $zipname Zip file containing the learnpath or directory containing the learnpath
729
     * @param string $publicated_on
730
     * @param string $expired_on
731
     * @param int $categoryId
732
     * @param int $userId
733
     * @return integer    The new learnpath ID on success, 0 on failure
734
     */
735
    public static function add_lp(
736
        $courseCode,
737
        $name,
738
        $description = '',
739
        $learnpath = 'guess',
740
        $origin = 'zip',
741
        $zipname = '',
742
        $publicated_on = '',
743
        $expired_on = '',
744
        $categoryId = 0,
745
        $userId = 0
746
    ) {
747
        global $charset;
748
749
        if (!empty($courseCode)) {
750
            $courseInfo = api_get_course_info($courseCode);
751
            $course_id = $courseInfo['real_id'];
752
        } else {
753
            $course_id = api_get_course_int_id();
754
            $courseInfo = api_get_course_info();
755
        }
756
757
        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
758
        // Check course code exists.
759
        // Check lp_name doesn't exist, otherwise append something.
760
        $i = 0;
761
        $name = Database::escape_string($name);
762
        $categoryId = intval($categoryId);
763
764
        // Session id.
765
        $session_id = api_get_session_id();
766
        $userId = empty($userId) ? api_get_user_id() : $userId;
767
        $check_name = "SELECT * FROM $tbl_lp
768
                       WHERE c_id = $course_id AND name = '$name'";
769
770
        $res_name = Database::query($check_name);
771
772
        if (empty($publicated_on)) {
773
            $publicated_on = null;
774
        } else {
775
            $publicated_on = Database::escape_string(api_get_utc_datetime($publicated_on));
0 ignored issues
show
Bug introduced by
It seems like api_get_utc_datetime($publicated_on) targeting api_get_utc_datetime() can also be of type null or object<DateTime>; however, Database::escape_string() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
776
        }
777
778
        if (empty($expired_on)) {
779
            $expired_on = null;
780
        } else {
781
            $expired_on = Database::escape_string(api_get_utc_datetime($expired_on));
0 ignored issues
show
Bug introduced by
It seems like api_get_utc_datetime($expired_on) targeting api_get_utc_datetime() can also be of type null or object<DateTime>; however, Database::escape_string() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
782
        }
783
784
        while (Database::num_rows($res_name)) {
785
            // There is already one such name, update the current one a bit.
786
            $i++;
787
            $name = $name.' - '.$i;
788
            $check_name = "SELECT * FROM $tbl_lp WHERE c_id = $course_id AND name = '$name'";
789
            $res_name = Database::query($check_name);
790
        }
791
        // New name does not exist yet; keep it.
792
        // Escape description.
793
        // Kevin: added htmlentities().
794
        $description = Database::escape_string(api_htmlentities($description, ENT_QUOTES, $charset));
795
        $type = 1;
796
        switch ($learnpath) {
797
            case 'guess':
798
                break;
799
            case 'dokeos':
800
            case 'chamilo':
801
                $type = 1;
802
                break;
803
            case 'aicc':
804
                break;
805
        }
806
807
        switch ($origin) {
808
            case 'zip':
809
                // Check zip name string. If empty, we are currently creating a new Chamilo learnpath.
810
                break;
811
            case 'manual':
812
            default:
813
                $get_max = "SELECT MAX(display_order) FROM $tbl_lp WHERE c_id = $course_id";
814
                $res_max = Database::query($get_max);
815
                if (Database::num_rows($res_max) < 1) {
816
                    $dsp = 1;
817
                } else {
818
                    $row = Database::fetch_array($res_max);
819
                    $dsp = $row[0] + 1;
820
                }
821
822
                $params = [
823
                    'c_id' => $course_id,
824
                    'lp_type' => $type,
825
                    'name' => $name,
826
                    'description' => $description,
827
                    'path' => '',
828
                    'default_view_mod' => 'embedded',
829
                    'default_encoding' => 'UTF-8',
830
                    'display_order' => $dsp,
831
                    'content_maker' => 'Chamilo',
832
                    'content_local' => 'local',
833
                    'js_lib' => '',
834
                    'session_id' => $session_id,
835
                    'created_on' => api_get_utc_datetime(),
836
                    'modified_on'  => api_get_utc_datetime(),
837
                    'publicated_on' => $publicated_on,
838
                    'expired_on' => $expired_on,
839
                    'category_id' => $categoryId,
840
                    'force_commit' => 0,
841
                    'content_license' => '',
842
                    'debug' => 0,
843
                    'theme' => '',
844
                    'preview_image' => '',
845
                    'author' => '',
846
                    'prerequisite' => 0,
847
                    'hide_toc_frame' => 0,
848
                    'seriousgame_mode' => 0,
849
                    'autolaunch' => 0,
850
                    'max_attempts' => 0,
851
                    'subscribe_users' => 0,
852
                    'accumulate_scorm_time' => 1
853
                ];
854
855
                $id = Database::insert($tbl_lp, $params);
856
857 View Code Duplication
                if ($id > 0) {
858
                    $sql = "UPDATE $tbl_lp SET id = iid WHERE iid = $id";
859
                    Database::query($sql);
860
861
                    // Insert into item_property.
862
                    api_item_property_update(
863
                        $courseInfo,
864
                        TOOL_LEARNPATH,
865
                        $id,
866
                        'LearnpathAdded',
867
                        $userId
868
                    );
869
                    api_set_default_visibility($id, TOOL_LEARNPATH, 0, $courseInfo, $session_id, $userId);
870
                    return $id;
871
                }
872
                break;
873
        }
874
    }
875
876
    /**
877
     * Auto completes the parents of an item in case it's been completed or passed
878
     * @param int $item Optional ID of the item from which to look for parents
879
     */
880
    public function autocomplete_parents($item)
881
    {
882
        $debug = $this->debug;
883
884
        if ($debug) {
885
            error_log('Learnpath::autocomplete_parents()', 0);
886
        }
887
888
        if (empty($item)) {
889
            $item = $this->current;
890
        }
891
892
        $currentItem = $this->getItem($item);
893
        if ($currentItem) {
894
            $parent_id = $currentItem->get_parent();
895
            $parent = $this->getItem($parent_id);
896
            if ($parent) {
897
                // if $item points to an object and there is a parent.
898
                if ($debug) {
899
                    error_log(
900
                        'Autocompleting parent of item '.$item.' "'.$currentItem->get_title().'" (item '.$parent_id.' "'.$parent->get_title().'") ',
901
                        0
902
                    );
903
                }
904
905
                // New experiment including failed and browsed in completed status.
906
                //$current_status = $currentItem->get_status();
907
                //if ($currentItem->is_done() || $current_status == 'browsed' || $current_status == 'failed') {
908
                // Fixes chapter auto complete
909
                if (true) {
0 ignored issues
show
Bug introduced by
Avoid IF statements that are always true or false
Loading history...
910
                    // If the current item is completed or passes or succeeded.
911
                    $updateParentStatus = true;
912
                    if ($debug) {
913
                        error_log('Status of current item is alright', 0);
914
                    }
915
916
                    foreach ($parent->get_children() as $childItemId) {
917
                        $childItem = $this->getItem($childItemId);
918
919
                        // If children was not set try to get the info
920
                        if (empty($childItem->db_item_view_id)) {
921
                            $childItem->set_lp_view($this->lp_view_id, $this->course_int_id);
922
                        }
923
924
                        // Check all his brothers (parent's children) for completion status.
925
                        if ($childItemId != $item) {
926
                            if ($debug) {
927
                                error_log(
928
                                    'Looking at brother #'.$childItemId.' "'.$childItem->get_title().'", status is '.$childItem->get_status(),
929
                                    0
930
                                );
931
                            }
932
                            // Trying completing parents of failed and browsed items as well.
933
                            if ($childItem->status_is(
934
                                array(
935
                                    'completed',
936
                                    'passed',
937
                                    'succeeded',
938
                                    'browsed',
939
                                    'failed'
940
                                )
941
                            )
942
                            ) {
943
                                // Keep completion status to true.
944
                                continue;
945
                            } else {
946
                                if ($debug > 2) {
947
                                    error_log(
948
                                        'Found one incomplete child of parent #'.$parent_id.': child #'.$childItemId.' "'.$childItem->get_title().'", is '.$childItem->get_status().' db_item_view_id:#'.$childItem->db_item_view_id,
949
                                        0
950
                                    );
951
                                }
952
                                $updateParentStatus = false;
953
                                break;
954
                            }
955
                        }
956
                    }
957
958
                    if ($updateParentStatus) {
959
                        // If all the children were completed:
960
                        $parent->set_status('completed');
961
                        $parent->save(false, $this->prerequisites_match($parent->get_id()));
962
                        // Force the status to "completed"
963
                        //$this->update_queue[$parent->get_id()] = $parent->get_status();
964
                        $this->update_queue[$parent->get_id()] = 'completed';
965
                        if ($debug) {
966
                            error_log(
967
                                'Added parent #'.$parent->get_id().' "'.$parent->get_title().'" to update queue status: completed '.
968
                                print_r($this->update_queue, 1),
969
                                0
970
                            );
971
                        }
972
                        // Recursive call.
973
                        $this->autocomplete_parents($parent->get_id());
974
                    }
975
                }
976
            } else {
977
                if ($debug) {
978
                    error_log("Parent #$parent_id does not exists");
979
                }
980
            }
981
        } else {
982
            if ($debug) {
983
                error_log("#$item is an item that doesn't have parents");
984
            }
985
        }
986
    }
987
988
    /**
989
     * Auto saves the current results into the database for the whole learnpath
990
     * @todo: Add save operations for the learnpath itself.
991
     */
992
    public function autosave()
993
    {
994
        if ($this->debug > 0) {
995
            error_log('New LP - In learnpath::autosave()', 0);
996
        }
997
    }
998
999
    /**
1000
     * Closes the current resource
1001
     *
1002
     * Stops the timer
1003
     * Saves into the database if required
1004
     * Clears the current resource data from this object
1005
     * @return boolean    True on success, false on failure
1006
     */
1007
    public function close()
1008
    {
1009
        if ($this->debug > 0) {
1010
            error_log('New LP - In learnpath::close()', 0);
1011
        }
1012
        if (empty($this->lp_id)) {
1013
            $this->error = 'Trying to close this learnpath but no ID is set';
1014
            return false;
1015
        }
1016
        $this->current_time_stop = time();
1017
        $this->ordered_items = array();
1018
        $this->index = 0;
1019
        unset($this->lp_id);
1020
        //unset other stuff
1021
        return true;
1022
    }
1023
1024
    /**
1025
     * Static admin function allowing removal of a learnpath
1026
     * @param	array $courseInfo
1027
     * @param	integer	Learnpath ID
1028
     * @param	string	Whether to delete data or keep it (default: 'keep', others: 'remove')
1029
     * @return	boolean	True on success, false on failure (might change that to return number of elements deleted)
1030
     */
1031
    public function delete($courseInfo = null, $id = null, $delete = 'keep')
1032
    {
1033
        $course_id = api_get_course_int_id();
1034
        if (!empty($courseInfo)) {
1035
            $course_id = isset($courseInfo['real_id']) ? $courseInfo['real_id'] : $course_id;
1036
        }
1037
1038
        // TODO: Implement a way of getting this to work when the current object is not set.
1039
        // In clear: implement this in the item class as well (abstract class) and use the given ID in queries.
1040
        // If an ID is specifically given and the current LP is not the same, prevent delete.
1041
        if (!empty($id) && ($id != $this->lp_id)) {
1042
            return false;
1043
        }
1044
1045
        $lp = Database::get_course_table(TABLE_LP_MAIN);
1046
        $lp_item = Database::get_course_table(TABLE_LP_ITEM);
1047
        $lp_view = Database::get_course_table(TABLE_LP_VIEW);
1048
        $lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW);
1049
1050
        // Delete lp item id.
1051
        foreach ($this->items as $id => $dummy) {
1052
            $sql = "DELETE FROM $lp_item_view
1053
                    WHERE c_id = $course_id AND lp_item_id = '".$id."'";
1054
            Database::query($sql);
1055
        }
1056
1057
        // Proposed by Christophe (nickname: clefevre)
1058
        $sql = "DELETE FROM $lp_item WHERE c_id = ".$course_id." AND lp_id = ".$this->lp_id;
1059
        Database::query($sql);
1060
1061
        $sql = "DELETE FROM $lp_view WHERE c_id = ".$course_id." AND lp_id = ".$this->lp_id;
1062
        Database::query($sql);
1063
1064
        self::toggle_publish($this->lp_id, 'i');
1065
1066
        if ($this->type == 2 || $this->type == 3) {
1067
            // This is a scorm learning path, delete the files as well.
1068
            $sql = "SELECT path FROM $lp
1069
                    WHERE c_id = ".$course_id." AND id = ".$this->lp_id;
1070
            $res = Database::query($sql);
1071
            if (Database::num_rows($res) > 0) {
1072
                $row = Database::fetch_array($res);
1073
                $path = $row['path'];
1074
                $sql = "SELECT id FROM $lp
1075
                        WHERE c_id = ".$course_id." AND path = '$path' AND id != ".$this->lp_id;
1076
                $res = Database::query($sql);
1077
                if (Database::num_rows($res) > 0) {
1078
                    // Another learning path uses this directory, so don't delete it.
1079
                    if ($this->debug > 2) {
1080
                        error_log('New LP - In learnpath::delete(), found other LP using path '.$path.', keeping directory', 0);
1081
                    }
1082
                } else {
1083
                    // No other LP uses that directory, delete it.
1084
                    $course_rel_dir = api_get_course_path().'/scorm/'; // scorm dir web path starting from /courses
1085
                    $course_scorm_dir = api_get_path(SYS_COURSE_PATH).$course_rel_dir; // The absolute system path for this course.
1086
                    if ($delete == 'remove' && is_dir($course_scorm_dir.$path) && !empty ($course_scorm_dir)) {
1087
                        if ($this->debug > 2) {
1088
                            error_log('New LP - In learnpath::delete(), found SCORM, deleting directory: '.$course_scorm_dir.$path, 0);
1089
                        }
1090
                        // Proposed by Christophe (clefevre).
1091
                        if (strcmp(substr($path, -2), "/.") == 0) {
1092
                            $path = substr($path, 0, -1); // Remove "." at the end.
1093
                        }
1094
                        //exec('rm -rf ' . $course_scorm_dir . $path); // See Bug #5208, this is not OS-portable way.
1095
                        rmdirr($course_scorm_dir.$path);
1096
                    }
1097
                }
1098
            }
1099
        }
1100
1101
        $tbl_tool = Database::get_course_table(TABLE_TOOL_LIST);
1102
        $link = 'lp/lp_controller.php?action=view&lp_id='.$this->lp_id;
1103
        // Delete tools
1104
        $sql = "DELETE FROM $tbl_tool
1105
                WHERE c_id = ".$course_id." AND (link LIKE '$link%' AND image='scormbuilder.gif')";
1106
        Database::query($sql);
1107
1108
        $sql = "DELETE FROM $lp WHERE c_id = ".$course_id." AND id = ".$this->lp_id;
1109
        Database::query($sql);
1110
        // Updates the display order of all lps.
1111
        $this->update_display_order();
1112
1113
        api_item_property_update(
1114
            api_get_course_info(),
1115
            TOOL_LEARNPATH,
1116
            $this->lp_id,
1117
            'delete',
1118
            api_get_user_id()
1119
        );
1120
1121
        $link_info = GradebookUtils::isResourceInCourseGradebook(
1122
            api_get_course_int_id(),
1123
            4,
1124
            $id,
1125
            api_get_session_id()
1126
        );
1127
        if ($link_info !== false) {
1128
            GradebookUtils::remove_resource_from_course_gradebook($link_info['id']);
1129
        }
1130
1131
        if (api_get_setting('search_enabled') == 'true') {
1132
            require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1133
            delete_all_values_for_item($this->cc, TOOL_LEARNPATH, $this->lp_id);
1134
        }
1135
    }
1136
1137
    /**
1138
     * Removes all the children of one item - dangerous!
1139
     * @param    integer $id Element ID of which children have to be removed
1140
     * @return    integer    Total number of children removed
1141
     */
1142
    public function delete_children_items($id)
1143
    {
1144
        $course_id = $this->course_info['real_id'];
1145
        if ($this->debug > 0) {
1146
            error_log('New LP - In learnpath::delete_children_items('.$id.')', 0);
1147
        }
1148
        $num = 0;
1149
        if (empty($id) || $id != strval(intval($id))) {
1150
            return false;
1151
        }
1152
        $lp_item = Database::get_course_table(TABLE_LP_ITEM);
1153
        $sql = "SELECT * FROM $lp_item WHERE c_id = ".$course_id." AND parent_item_id = $id";
1154
        $res = Database::query($sql);
1155
        while ($row = Database::fetch_array($res)) {
1156
            $num += $this->delete_children_items($row['id']);
1157
            $sql_del = "DELETE FROM $lp_item WHERE c_id = ".$course_id." AND id = ".$row['id'];
1158
            Database::query($sql_del);
1159
            $num++;
1160
        }
1161
        return $num;
1162
    }
1163
1164
    /**
1165
     * Removes an item from the current learnpath
1166
     * @param    integer $id Elem ID (0 if first)
1167
     * @param    integer $remove Whether to remove the resource/data from the
1168
     * system or leave it (default: 'keep', others 'remove')
1169
     * @return    integer    Number of elements moved
1170
     * @todo implement resource removal
1171
     */
1172
    public function delete_item($id, $remove = 'keep')
1173
    {
1174
        $course_id = api_get_course_int_id();
1175
        if ($this->debug > 0) {
1176
            error_log('New LP - In learnpath::delete_item()', 0);
1177
        }
1178
        // TODO: Implement the resource removal.
1179
        if (empty($id) || $id != strval(intval($id))) {
1180
            return false;
1181
        }
1182
        // First select item to get previous, next, and display order.
1183
        $lp_item = Database::get_course_table(TABLE_LP_ITEM);
1184
        $sql_sel = "SELECT * FROM $lp_item WHERE c_id = ".$course_id." AND id = $id";
1185
        $res_sel = Database::query($sql_sel);
1186
        if (Database::num_rows($res_sel) < 1) {
1187
            return false;
1188
        }
1189
        $row = Database::fetch_array($res_sel);
1190
        $previous = $row['previous_item_id'];
1191
        $next = $row['next_item_id'];
1192
        $display = $row['display_order'];
1193
        $parent = $row['parent_item_id'];
1194
        $lp = $row['lp_id'];
1195
        // Delete children items.
1196
        $num = $this->delete_children_items($id);
1197
        if ($this->debug > 2) {
1198
            error_log('New LP - learnpath::delete_item() - deleted '.$num.' children of element '.$id, 0);
1199
        }
1200
        // Now delete the item.
1201
        $sql_del = "DELETE FROM $lp_item WHERE c_id = $course_id AND id = $id";
1202
        if ($this->debug > 2) {
1203
            error_log('New LP - Deleting item: '.$sql_del, 0);
1204
        }
1205
        Database::query($sql_del);
1206
        // Now update surrounding items.
1207
        $sql_upd = "UPDATE $lp_item SET next_item_id = $next
1208
                    WHERE c_id = ".$course_id." AND id = $previous";
1209
        Database::query($sql_upd);
1210
        $sql_upd = "UPDATE $lp_item SET previous_item_id = $previous
1211
                    WHERE c_id = ".$course_id." AND id = $next";
1212
        Database::query($sql_upd);
1213
        // Now update all following items with new display order.
1214
        $sql_all = "UPDATE $lp_item SET display_order = display_order-1
1215
                    WHERE c_id = ".$course_id." AND lp_id = $lp AND parent_item_id = $parent AND display_order > $display";
1216
        Database::query($sql_all);
1217
1218
        //Removing prerequisites since the item will not longer exist
1219
        $sql_all = "UPDATE $lp_item SET prerequisite = '' WHERE c_id = ".$course_id." AND prerequisite = $id";
1220
        Database::query($sql_all);
1221
1222
        // Remove from search engine if enabled.
1223
        if (api_get_setting('search_enabled') == 'true') {
1224
            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
1225
            $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d LIMIT 1';
1226
            $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp, $id);
1227
            $res = Database::query($sql);
1228
            if (Database::num_rows($res) > 0) {
1229
                $row2 = Database::fetch_array($res);
1230
                require_once api_get_path(LIBRARY_PATH).'search/ChamiloIndexer.class.php';
1231
                $di = new ChamiloIndexer();
1232
                $di->remove_document((int) $row2['search_did']);
1233
            }
1234
            $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d LIMIT 1';
1235
            $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp, $id);
1236
            Database::query($sql);
1237
        }
1238
    }
1239
1240
    /**
1241
     * Updates an item's content in place
1242
     * @param   integer $id Element ID
1243
     * @param   integer $parent Parent item ID
1244
     * @param   integer $previous Previous item ID
1245
     * @param   string  $title Item title
1246
     * @param   string  $description Item description
1247
     * @param   string  $prerequisites Prerequisites (optional)
1248
     * @param   array   $audio The array resulting of the $_FILES[mp3] element
1249
     * @param   int     $max_time_allowed
1250
     * @param   string  $url
1251
     * @return  boolean True on success, false on error
1252
     */
1253
    public function edit_item(
1254
        $id,
1255
        $parent,
1256
        $previous,
1257
        $title,
1258
        $description,
1259
        $prerequisites = '0',
1260
        $audio = array(),
1261
        $max_time_allowed = 0,
1262
        $url = ''
1263
    ) {
1264
        $course_id = api_get_course_int_id();
1265
        $_course = api_get_course_info();
1266
1267
        if ($this->debug > 0) {
1268
            error_log('New LP - In learnpath::edit_item()', 0);
1269
        }
1270
        if (empty($max_time_allowed)) {
1271
            $max_time_allowed = 0;
1272
        }
1273
        if (empty($id) || ($id != strval(intval($id))) || empty($title)) {
1274
            return false;
1275
        }
1276
1277
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
1278
        $sql_select = "SELECT * FROM ".$tbl_lp_item." WHERE c_id = ".$course_id." AND id = ".$id;
1279
        $res_select = Database::query($sql_select);
1280
        $row_select = Database::fetch_array($res_select);
1281
        $audio_update_sql = '';
1282
        if (is_array($audio) && !empty($audio['tmp_name']) && $audio['error'] === 0) {
1283
            // Create the audio folder if it does not exist yet.
1284
            $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/';
1285 View Code Duplication
            if (!is_dir($filepath.'audio')) {
1286
                mkdir($filepath.'audio', api_get_permissions_for_new_directories());
1287
                $audio_id = add_document(
1288
                    $_course,
1289
                    '/audio',
1290
                    'folder',
1291
                    0,
1292
                    'audio'
1293
                );
1294
                api_item_property_update(
1295
                    $_course,
1296
                    TOOL_DOCUMENT,
1297
                    $audio_id,
1298
                    'FolderCreated',
1299
                    api_get_user_id(),
1300
                    null,
1301
                    null,
1302
                    null,
1303
                    null,
1304
                    api_get_session_id()
1305
                );
1306
                api_item_property_update(
1307
                    $_course,
1308
                    TOOL_DOCUMENT,
1309
                    $audio_id,
1310
                    'invisible',
1311
                    api_get_user_id(),
1312
                    null,
1313
                    null,
1314
                    null,
1315
                    null,
1316
                    api_get_session_id()
1317
                );
1318
            }
1319
1320
            // Upload file in documents.
1321
            $pi = pathinfo($audio['name']);
1322
            if ($pi['extension'] == 'mp3') {
1323
                $c_det = api_get_course_info($this->cc);
1324
                $bp = api_get_path(SYS_COURSE_PATH).$c_det['path'].'/document';
1325
                $path = handle_uploaded_document(
1326
                    $c_det,
1327
                    $audio,
1328
                    $bp,
1329
                    '/audio',
1330
                    api_get_user_id(),
1331
                    0,
1332
                    null,
1333
                    0,
1334
                    'rename',
1335
                    false,
1336
                    0
1337
                );
1338
                $path = substr($path, 7);
1339
                // Update reference in lp_item - audio path is the path from inside de document/audio/ dir.
1340
                $audio_update_sql = ", audio = '".Database::escape_string($path)."' ";
1341
            }
1342
        }
1343
1344
        $same_parent = ($row_select['parent_item_id'] == $parent) ? true : false;
1345
        $same_previous = ($row_select['previous_item_id'] == $previous) ? true : false;
1346
1347
        // TODO: htmlspecialchars to be checked for encoding related problems.
1348
        if ($same_parent && $same_previous) {
1349
            // Only update title and description.
1350
            $sql = "UPDATE ".$tbl_lp_item."
1351
                    SET title = '" . Database::escape_string($title)."',
1352
                        prerequisite = '" . $prerequisites."',
1353
                        description = '" . Database::escape_string($description)."'
1354
                        " . $audio_update_sql.",
1355
                        max_time_allowed = '" . Database::escape_string($max_time_allowed)."'
1356
                    WHERE c_id = ".$course_id." AND id = ".$id;
1357
            Database::query($sql);
1358
        } else {
1359
            $old_parent = $row_select['parent_item_id'];
1360
            $old_previous = $row_select['previous_item_id'];
1361
            $old_next = $row_select['next_item_id'];
1362
            $old_order = $row_select['display_order'];
1363
            $old_prerequisite = $row_select['prerequisite'];
1364
            $old_max_time_allowed = $row_select['max_time_allowed'];
1365
1366
            /* BEGIN -- virtually remove the current item id */
1367
            /* for the next and previous item it is like the current item doesn't exist anymore */
1368
1369
            if ($old_previous != 0) {
1370
                // Next
1371
                $sql = "UPDATE ".$tbl_lp_item."
1372
                        SET next_item_id = " . $old_next."
1373
                        WHERE c_id = ".$course_id." AND id = ".$old_previous;
1374
                Database::query($sql);
1375
            }
1376
1377
            if ($old_next != 0) {
1378
                // Previous
1379
                $sql = "UPDATE ".$tbl_lp_item."
1380
                        SET previous_item_id = " . $old_previous."
1381
                        WHERE c_id = ".$course_id." AND id = ".$old_next;
1382
                Database::query($sql);
1383
            }
1384
1385
            // display_order - 1 for every item with a display_order bigger then the display_order of the current item.
1386
            $sql = "UPDATE ".$tbl_lp_item."
1387
                    SET display_order = display_order - 1
1388
                    WHERE
1389
                        c_id = ".$course_id." AND
1390
                        display_order > " . $old_order." AND
1391
                        lp_id = " . $this->lp_id." AND
1392
                        parent_item_id = " . $old_parent;
1393
            Database::query($sql);
1394
            /* END -- virtually remove the current item id */
1395
1396
            /* BEGIN -- update the current item id to his new location */
1397
            if ($previous == 0) {
1398
                // Select the data of the item that should come after the current item.
1399
                $sql = "SELECT id, display_order
1400
                        FROM " . $tbl_lp_item."
1401
                        WHERE
1402
                            c_id = ".$course_id." AND
1403
                            lp_id = " . $this->lp_id." AND
1404
                            parent_item_id = " . $parent." AND
1405
                            previous_item_id = " . $previous;
1406
                $res_select_old = Database::query($sql);
1407
                $row_select_old = Database::fetch_array($res_select_old);
1408
1409
                // If the new parent didn't have children before.
1410
                if (Database::num_rows($res_select_old) == 0) {
1411
                    $new_next = 0;
1412
                    $new_order = 1;
1413
                } else {
1414
                    $new_next = $row_select_old['id'];
1415
                    $new_order = $row_select_old['display_order'];
1416
                }
1417
            } else {
1418
                // Select the data of the item that should come before the current item.
1419
                $sql = "SELECT next_item_id, display_order
1420
                        FROM " . $tbl_lp_item."
1421
                        WHERE c_id = ".$course_id." AND id = ".$previous;
1422
                $res_select_old = Database::query($sql);
1423
                $row_select_old = Database::fetch_array($res_select_old);
1424
                $new_next = $row_select_old['next_item_id'];
1425
                $new_order = $row_select_old['display_order'] + 1;
1426
            }
1427
1428
            // TODO: htmlspecialchars to be checked for encoding related problems.
1429
            // Update the current item with the new data.
1430
            $sql = "UPDATE $tbl_lp_item
1431
                    SET
1432
                        title = '".Database::escape_string($title)."',
1433
                        description = '" . Database::escape_string($description)."',
1434
                        parent_item_id = " . $parent.",
1435
                        previous_item_id = " . $previous.",
1436
                        next_item_id = " . $new_next.",
1437
                        display_order = " . $new_order."
1438
                        " . $audio_update_sql."
1439
                    WHERE c_id = ".$course_id." AND id = ".$id;
1440
            Database::query($sql);
1441
1442
            if ($previous != 0) {
1443
                // Update the previous item's next_item_id.
1444
                $sql = "UPDATE ".$tbl_lp_item."
1445
                        SET next_item_id = " . $id."
1446
                        WHERE c_id = ".$course_id." AND id = ".$previous;
1447
                Database::query($sql);
1448
            }
1449
1450
            if ($new_next != 0) {
1451
                // Update the next item's previous_item_id.
1452
                $sql = "UPDATE ".$tbl_lp_item."
1453
                        SET previous_item_id = " . $id."
1454
                        WHERE c_id = ".$course_id." AND id = ".$new_next;
1455
                Database::query($sql);
1456
            }
1457
1458
            if ($old_prerequisite != $prerequisites) {
1459
                $sql = "UPDATE ".$tbl_lp_item."
1460
                        SET prerequisite = '" . $prerequisites."'
1461
                        WHERE c_id = ".$course_id." AND id = ".$id;
1462
                Database::query($sql);
1463
            }
1464
1465
            if ($old_max_time_allowed != $max_time_allowed) {
1466
                // update max time allowed
1467
                $sql = "UPDATE ".$tbl_lp_item."
1468
                        SET max_time_allowed = " . $max_time_allowed."
1469
                        WHERE c_id = ".$course_id." AND id = ".$id;
1470
                Database::query($sql);
1471
            }
1472
1473
            // Update all the items with the same or a bigger display_order than the current item.
1474
            $sql = "UPDATE ".$tbl_lp_item."
1475
                    SET display_order = display_order + 1
1476
                    WHERE
1477
                       c_id = ".$course_id." AND
1478
                       lp_id = " . $this->get_id()." AND
1479
                       id <> " . $id." AND
1480
                       parent_item_id = " . $parent." AND
1481
                       display_order >= " . $new_order;
1482
1483
            Database::query($sql);
1484
        }
1485
1486
        if ($row_select['item_type'] == 'link') {
1487
            $link = new Link();
1488
            $linkId = $row_select['path'];
1489
            $link->updateLink($linkId, $url);
1490
        }
1491
    }
1492
1493
    /**
1494
     * Updates an item's prereq in place
1495
     * @param    integer $id Element ID
1496
     * @param    string $prerequisite_id Prerequisite Element ID
1497
     * @param    int $mastery_score Prerequisite min score
1498
     * @param    int $max_score Prerequisite max score
1499
     *
1500
     * @return    boolean    True on success, false on error
1501
     */
1502
    public function edit_item_prereq($id, $prerequisite_id, $mastery_score = 0, $max_score = 100)
1503
    {
1504
        $course_id = api_get_course_int_id();
1505
        if ($this->debug > 0) {
1506
            error_log('New LP - In learnpath::edit_item_prereq('.$id.','.$prerequisite_id.','.$mastery_score.','.$max_score.')', 0);
1507
        }
1508
1509
        if (empty($id) || ($id != strval(intval($id))) || empty($prerequisite_id)) {
1510
            return false;
1511
        }
1512
1513
        $prerequisite_id = intval($prerequisite_id);
1514
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
1515
1516
        if (!is_numeric($mastery_score) || $mastery_score < 0) {
1517
            $mastery_score = 0;
1518
        }
1519
1520
        if (!is_numeric($max_score) || $max_score < 0) {
1521
            $max_score = 100;
1522
        }
1523
1524
        /*if ($mastery_score > $max_score) {
1525
            $max_score = $mastery_score;
1526
        }*/
1527
1528
        if (!is_numeric($prerequisite_id)) {
1529
            $prerequisite_id = 'NULL';
1530
        }
1531
1532
        $mastery_score = floatval($mastery_score);
1533
        $max_score = floatval($max_score);
1534
1535
        $sql = " UPDATE $tbl_lp_item
1536
                 SET
1537
                    prerequisite = $prerequisite_id ,
1538
                    prerequisite_min_score = $mastery_score ,
1539
                    prerequisite_max_score = $max_score
1540
                 WHERE c_id = $course_id AND id = $id";
1541
        Database::query($sql);
1542
        // TODO: Update the item object (can be ignored for now because refreshed).
1543
        return true;
1544
    }
1545
1546
    /**
1547
     * Static admin function exporting a learnpath into a zip file
1548
     * @param    string    Export type (scorm, zip, cd)
1549
     * @param    string    Course code
1550
     * @param    integer Learnpath ID
1551
     * @param    string    Zip file name
1552
     * @return   string    Zip file path (or false on error)
1553
     */
1554
    public function export_lp($type, $course, $id, $zipname)
1555
    {
1556
        if (empty($type) || empty($course) || empty($id) || empty($zipname)) {
1557
            return false;
1558
        }
1559
        $url = '';
1560
        switch ($type) {
1561
            case 'scorm':
1562
                break;
1563
            case 'zip':
1564
                break;
1565
            case 'cdrom':
1566
                break;
1567
        }
1568
        return $url;
1569
    }
1570
1571
    /**
1572
     * Gets all the chapters belonging to the same parent as the item/chapter given
1573
     * Can also be called as abstract method
1574
     * @param    integer $id Item ID
1575
     * @return    array    A list of all the "brother items" (or an empty array on failure)
1576
     */
1577 View Code Duplication
    public function getSiblingDirectories($id)
1578
    {
1579
        $course_id = api_get_course_int_id();
1580
        if ($this->debug > 0) {
1581
            error_log('New LP - In learnpath::getSiblingDirectories()', 0);
1582
        }
1583
1584
        if (empty($id) || $id != strval(intval($id))) {
1585
1586
            return array();
1587
        }
1588
1589
        $lp_item = Database::get_course_table(TABLE_LP_ITEM);
1590
        $sql_parent = "SELECT * FROM $lp_item
1591
                       WHERE c_id = ".$course_id." AND id = $id AND item_type='dir'";
1592
        $res_parent = Database::query($sql_parent);
1593
        if (Database::num_rows($res_parent) > 0) {
1594
            $row_parent = Database::fetch_array($res_parent);
1595
            $parent = $row_parent['parent_item_id'];
1596
            $sql = "SELECT * FROM $lp_item
1597
                    WHERE
1598
                        c_id = ".$course_id." AND
1599
                        parent_item_id = $parent AND
1600
                        id = $id AND
1601
                        item_type='dir'
1602
                    ORDER BY display_order";
1603
            $res_bros = Database::query($sql);
1604
1605
            $list = array();
1606
            while ($row_bro = Database::fetch_array($res_bros)) {
1607
                $list[] = $row_bro;
1608
            }
1609
1610
            return $list;
1611
        }
1612
1613
        return array();
1614
    }
1615
1616
    /**
1617
     * Gets all the items belonging to the same parent as the item given
1618
     * Can also be called as abstract method
1619
     * @param    integer $id Item ID
1620
     * @return    array    A list of all the "brother items" (or an empty array on failure)
1621
     */
1622 View Code Duplication
    public function get_brother_items($id)
1623
    {
1624
        $course_id = api_get_course_int_id();
1625
        if ($this->debug > 0) {
1626
            error_log('New LP - In learnpath::get_brother_items('.$id.')', 0);
1627
        }
1628
1629
        if (empty($id) || $id != strval(intval($id))) {
1630
            return array();
1631
        }
1632
1633
        $lp_item = Database::get_course_table(TABLE_LP_ITEM);
1634
        $sql_parent = "SELECT * FROM $lp_item WHERE c_id = $course_id AND id = $id";
1635
        $res_parent = Database::query($sql_parent);
1636
        if (Database::num_rows($res_parent) > 0) {
1637
            $row_parent = Database::fetch_array($res_parent);
1638
            $parent = $row_parent['parent_item_id'];
1639
            $sql_bros = "SELECT * FROM $lp_item WHERE c_id = ".$course_id." AND parent_item_id = $parent
1640
                         ORDER BY display_order";
1641
            $res_bros = Database::query($sql_bros);
1642
            $list = [];
1643
            while ($row_bro = Database::fetch_array($res_bros)) {
1644
                $list[] = $row_bro;
1645
            }
1646
            return $list;
1647
        }
1648
        return [];
1649
    }
1650
1651
    /**
1652
     * Get the specific prefix index terms of this learning path
1653
     * @param string $prefix
1654
     * @return  array Array of terms
1655
     */
1656
    public function get_common_index_terms_by_prefix($prefix)
1657
    {
1658
        require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1659
        $terms = get_specific_field_values_list_by_prefix(
1660
            $prefix,
1661
            $this->cc,
1662
            TOOL_LEARNPATH,
1663
            $this->lp_id
1664
        );
1665
        $prefix_terms = array();
1666
        if (!empty($terms)) {
1667
            foreach ($terms as $term) {
1668
                $prefix_terms[] = $term['value'];
1669
            }
1670
        }
1671
        return $prefix_terms;
1672
    }
1673
1674
    /**
1675
     * Gets the number of items currently completed
1676
     * @param bool $failedStatusException flag to determine the failed status is not considered progressed
1677
     * @return integer The number of items currently completed
1678
     */
1679
    public function get_complete_items_count($failedStatusException = false)
1680
    {
1681
        if ($this->debug > 0) {
1682
            error_log('New LP - In learnpath::get_complete_items_count()', 0);
1683
        }
1684
        $i = 0;
1685
        $completedStatusList = array(
1686
            'completed',
1687
            'passed',
1688
            'succeeded',
1689
            'browsed'
1690
        );
1691
1692
        if (!$failedStatusException) {
1693
            $completedStatusList[] = 'failed';
1694
        }
1695
1696
        foreach ($this->items as $id => $dummy) {
1697
            // Trying failed and browsed considered "progressed" as well.
1698
            if ($this->items[$id]->status_is($completedStatusList) &&
1699
                $this->items[$id]->get_type() != 'dir'
1700
            ) {
1701
                $i++;
1702
            }
1703
        }
1704
        return $i;
1705
    }
1706
1707
    /**
1708
     * Gets the current item ID
1709
     * @return    integer    The current learnpath item id
1710
     */
1711
    public function get_current_item_id()
1712
    {
1713
        $current = 0;
1714
        if ($this->debug > 0) {
1715
            error_log('New LP - In learnpath::get_current_item_id()', 0);
1716
        }
1717
        if (!empty($this->current)) {
1718
            $current = $this->current;
1719
        }
1720
        if ($this->debug > 2) {
1721
            error_log('New LP - In learnpath::get_current_item_id() - Returning '.$current, 0);
1722
        }
1723
        return $current;
1724
    }
1725
1726
    /**
1727
     * Force to get the first learnpath item id
1728
     * @return    integer    The current learnpath item id
1729
     */
1730
    public function get_first_item_id()
1731
    {
1732
        $current = 0;
1733
        if (is_array($this->ordered_items)) {
1734
            $current = $this->ordered_items[0];
1735
        }
1736
        return $current;
1737
    }
1738
1739
    /**
1740
     * Gets the total number of items available for viewing in this SCORM
1741
     * @return    integer    The total number of items
1742
     */
1743
    public function get_total_items_count()
1744
    {
1745
        if ($this->debug > 0) {
1746
            error_log('New LP - In learnpath::get_total_items_count()', 0);
1747
        }
1748
        return count($this->items);
1749
    }
1750
1751
    /**
1752
     * Gets the total number of items available for viewing in this SCORM but without chapters
1753
     * @return    integer    The total no-chapters number of items
1754
     */
1755
    public function getTotalItemsCountWithoutDirs()
1756
    {
1757
        if ($this->debug > 0) {
1758
            error_log('New LP - In learnpath::getTotalItemsCountWithoutDirs()', 0);
1759
        }
1760
        $total = 0;
1761
        $typeListNotToCount = self::getChapterTypes();
1762
        foreach ($this->items as $temp2) {
1763
            if (!in_array($temp2->get_type(), $typeListNotToCount)) {
1764
                $total++;
1765
            }
1766
        }
1767
        return $total;
1768
    }
1769
1770
    /**
1771
     * Gets the first element URL.
1772
     * @return    string    URL to load into the viewer
1773
     */
1774
    public function first()
1775
    {
1776
        if ($this->debug > 0) {
1777
            error_log('New LP - In learnpath::first()', 0);
1778
            error_log('$this->last_item_seen '.$this->last_item_seen);
1779
        }
1780
1781
        // Test if the last_item_seen exists and is not a dir.
1782
        if (count($this->ordered_items) == 0) {
1783
            $this->index = 0;
1784
        }
1785
1786
        if ($this->debug > 0) {
1787
            if (isset($this->items[$this->last_item_seen])) {
1788
                $status = $this->items[$this->last_item_seen]->get_status();
1789
            }
1790
            error_log('status '.$status);
1791
        }
1792
1793
        if (!empty($this->last_item_seen) &&
1794
            !empty($this->items[$this->last_item_seen]) &&
1795
            $this->items[$this->last_item_seen]->get_type() != 'dir'
1796
            //with this change (below) the LP will NOT go to the next item, it will take lp item we left
1797
            //&& !$this->items[$this->last_item_seen]->is_done()
1798
        ) {
1799
            if ($this->debug > 2) {
1800
                error_log('New LP - In learnpath::first() - Last item seen is '.$this->last_item_seen.' of type '.$this->items[$this->last_item_seen]->get_type(), 0);
1801
            }
1802
            $index = -1;
1803
            foreach ($this->ordered_items as $myindex => $item_id) {
1804
                if ($item_id == $this->last_item_seen) {
1805
                    $index = $myindex;
1806
                    break;
1807
                }
1808
            }
1809
            if ($index == -1) {
1810
                // Index hasn't changed, so item not found - panic (this shouldn't happen).
1811
                if ($this->debug > 2) {
1812
                    error_log('New LP - Last item ('.$this->last_item_seen.') was found in items but not in ordered_items, panic!', 0);
1813
                }
1814
                return false;
1815
            } else {
1816
                $this->last     = $this->last_item_seen;
1817
                $this->current  = $this->last_item_seen;
1818
                $this->index    = $index;
1819
            }
1820
        } else {
1821
            if ($this->debug > 2) {
1822
                error_log('New LP - In learnpath::first() - No last item seen', 0);
1823
            }
1824
            $index = 0;
1825
            // Loop through all ordered items and stop at the first item that is
1826
            // not a directory *and* that has not been completed yet.
1827
            while (!empty($this->ordered_items[$index]) AND
1828
                is_a($this->items[$this->ordered_items[$index]], 'learnpathItem') AND
1829
                (
1830
                    $this->items[$this->ordered_items[$index]]->get_type() == 'dir' OR
1831
                    $this->items[$this->ordered_items[$index]]->is_done() === true
1832
                ) AND $index < $this->max_ordered_items) {
1833
                $index++;
1834
            }
1835
            $this->last = $this->current;
1836
            // current is
1837
            $this->current = isset($this->ordered_items[$index]) ? $this->ordered_items[$index] : null;
1838
            $this->index = $index;
1839
            if ($this->debug > 2) {
1840
                error_log('$index '.$index);
1841
            }
1842 View Code Duplication
            if ($this->debug > 2) {
1843
                error_log('New LP - In learnpath::first() - No last item seen. New last = '.$this->last.'('.$this->ordered_items[$index].')', 0);
1844
            }
1845
        }
1846
        if ($this->debug > 2) {
1847
            error_log('New LP - In learnpath::first() - First item is '.$this->get_current_item_id());
1848
        }
1849
    }
1850
1851
    /**
1852
     * Gets the information about an item in a format usable as JavaScript to update
1853
     * the JS API by just printing this content into the <head> section of the message frame
1854
     * @param   int $item_id
1855
     * @return  string
1856
     */
1857
    public function get_js_info($item_id = 0)
1858
    {
1859
        if ($this->debug > 0) {
1860
            error_log('New LP - In learnpath::get_js_info('.$item_id.')', 0);
1861
        }
1862
1863
        $info = '';
1864
        $item_id = intval($item_id);
1865
1866
        if (!empty($item_id) && is_object($this->items[$item_id])) {
1867
            //if item is defined, return values from DB
1868
            $oItem = $this->items[$item_id];
1869
            $info .= '<script language="javascript">';
1870
            $info .= "top.set_score(".$oItem->get_score().");\n";
1871
            $info .= "top.set_max(".$oItem->get_max().");\n";
1872
            $info .= "top.set_min(".$oItem->get_min().");\n";
1873
            $info .= "top.set_lesson_status('".$oItem->get_status()."');";
1874
            $info .= "top.set_session_time('".$oItem->get_scorm_time('js')."');";
1875
            $info .= "top.set_suspend_data('".$oItem->get_suspend_data()."');";
1876
            $info .= "top.set_saved_lesson_status('".$oItem->get_status()."');";
1877
            $info .= "top.set_flag_synchronized();";
1878
            $info .= '</script>';
1879
            if ($this->debug > 2) {
1880
                error_log('New LP - in learnpath::get_js_info('.$item_id.') - returning: '.$info, 0);
1881
            }
1882
            return $info;
1883
1884
        } else {
1885
            // If item_id is empty, just update to default SCORM data.
1886
            $info .= '<script language="javascript">';
1887
            $info .= "top.set_score(".learnpathItem::get_score().");\n";
1888
            $info .= "top.set_max(".learnpathItem::get_max().");\n";
1889
            $info .= "top.set_min(".learnpathItem::get_min().");\n";
1890
            $info .= "top.set_lesson_status('".learnpathItem::get_status()."');";
1891
            $info .= "top.set_session_time('".learnpathItem::getScormTimeFromParameter('js')."');";
1892
            $info .= "top.set_suspend_data('".learnpathItem::get_suspend_data()."');";
1893
            $info .= "top.set_saved_lesson_status('".learnpathItem::get_status()."');";
1894
            $info .= "top.set_flag_synchronized();";
1895
            $info .= '</script>';
1896
            if ($this->debug > 2) {
1897
                error_log('New LP - in learnpath::get_js_info('.$item_id.') - returning: '.$info, 0);
1898
            }
1899
            return $info;
1900
        }
1901
    }
1902
1903
    /**
1904
     * Gets the js library from the database
1905
     * @return	string	The name of the javascript library to be used
1906
     */
1907
    public function get_js_lib()
1908
    {
1909
        $lib = '';
1910
        if (!empty ($this->js_lib)) {
1911
            $lib = $this->js_lib;
1912
        }
1913
        return $lib;
1914
    }
1915
1916
    /**
1917
     * Gets the learnpath database ID
1918
     * @return	integer	Learnpath ID in the lp table
1919
     */
1920
    public function get_id()
1921
    {
1922
        if (!empty ($this->lp_id)) {
1923
            return $this->lp_id;
1924
        } else {
1925
            return 0;
1926
        }
1927
    }
1928
1929
    /**
1930
     * Gets the last element URL.
1931
     * @return string URL to load into the viewer
1932
     */
1933
    public function get_last()
1934
    {
1935
        if ($this->debug > 0) {
1936
            error_log('New LP - In learnpath::get_last()', 0);
1937
        }
1938
        //This is just in case the lesson doesn't cointain a valid scheme, just to avoid "Notices"
1939
        if (count($this->ordered_items) > 0) {
1940
            $this->index = count($this->ordered_items) - 1;
1941
            return $this->ordered_items[$this->index];
1942
        }
1943
1944
        return false;
1945
    }
1946
1947
    /**
1948
     * Gets the navigation bar for the learnpath display screen
1949
     * @return	string	The HTML string to use as a navigation bar
1950
     */
1951
    public function get_navigation_bar($idBar = null, $display = null)
1952
    {
1953
        if ($this->debug > 0) {
1954
            error_log('New LP - In learnpath::get_navigation_bar()', 0);
1955
        }
1956
        if (empty($idBar)) {
1957
            $idBar = 'control-top';
1958
        }
1959
1960
        $navbar = null;
1961
        $lp_id = $this->lp_id;
1962
        $mycurrentitemid = $this->get_current_item_id();
1963
1964
        $reportingText = get_lang('Reporting');
1965
        $previousText = get_lang('ScormPrevious');
1966
        $nextText = get_lang('ScormNext');
1967
        $fullScreenText = get_lang('ScormExitFullScreen');
1968
1969
        if ($this->mode == 'fullscreen') {
1970
            $navbar = '
1971
                  <span id="'.$idBar.'" class="buttons">
1972
                    <a class="icon-toolbar" href="lp_controller.php?action=stats&'.api_get_cidreq(true).'&lp_id='.$lp_id.'" onclick="window.parent.API.save_asset();return true;" target="content_name" title="'.$reportingText.'" id="stats_link">
1973
                        <span class="fa fa-info"></span><span class="sr-only">' . $reportingText.'</span>
1974
                    </a>
1975
                    <a class="icon-toolbar" id="scorm-previous" href="#" onclick="switch_item(' . $mycurrentitemid.',\'previous\');return false;" title="'.$previousText.'">
1976
                        <span class="fa fa-chevron-left"></span><span class="sr-only">' . $previousText.'</span>
1977
                    </a>
1978
                    <a class="icon-toolbar" id="scorm-next" href="#" onclick="switch_item(' . $mycurrentitemid.',\'next\');return false;" title="'.$nextText.'">
1979
                        <span class="fa fa-chevron-right"></span><span class="sr-only">' . $nextText.'</span>
1980
                    </a>
1981
                    <a class="icon-toolbar" id="view-embedded" href="lp_controller.php?action=mode&mode=embedded" target="_top" title="'.$fullScreenText.'">
1982
                        <span class="fa fa-columns"></span><span class="sr-only">' . $fullScreenText.'</span>
1983
                    </a>
1984
                  </span>';
1985
        } else {
1986
            $navbar = '
1987
                <span id="'.$idBar.'" class="buttons text-right">
1988
                    <a class="icon-toolbar" href="lp_controller.php?action=stats&'.api_get_cidreq(true).'&lp_id='.$lp_id.'" onclick="window.parent.API.save_asset();return true;" target="content_name" title="'.$reportingText.'" id="stats_link">
1989
                        <span class="fa fa-info"></span><span class="sr-only">' . $reportingText.'</span>
1990
                    </a>
1991
                    <a class="icon-toolbar" id="scorm-previous" href="#" onclick="switch_item(' . $mycurrentitemid.',\'previous\');return false;" title="'.$previousText.'">
1992
                        <span class="fa fa-chevron-left"></span><span class="sr-only">' . $previousText.'</span>
1993
                    </a>
1994
                    <a class="icon-toolbar" id="scorm-next" href="#" onclick="switch_item(' . $mycurrentitemid.',\'next\');return false;" title="'.$nextText.'">
1995
                        <span class="fa fa-chevron-right"></span><span class="sr-only">' . $nextText.'</span>
1996
                    </a>
1997
                </span>';
1998
        }
1999
2000
        return $navbar;
2001
    }
2002
2003
    /**
2004
     * Gets the next resource in queue (url).
2005
     * @return	string	URL to load into the viewer
2006
     */
2007
    public function get_next_index()
2008
    {
2009
        if ($this->debug > 0) {
2010
            error_log('New LP - In learnpath::get_next_index()', 0);
2011
        }
2012
        // TODO
2013
        $index = $this->index;
2014
        $index++;
2015 View Code Duplication
        if ($this->debug > 2) {
2016
            error_log('New LP - Now looking at ordered_items['.($index).'] - type is '.$this->items[$this->ordered_items[$index]]->type, 0);
2017
        }
2018
        while (!empty ($this->ordered_items[$index]) AND ($this->items[$this->ordered_items[$index]]->get_type() == 'dir') AND $index < $this->max_ordered_items) {
2019
            $index++;
2020
            if ($index == $this->max_ordered_items) {
2021
                if ($this->items[$this->ordered_items[$index]]->get_type() == 'dir') {
2022
                    return $this->index;
2023
                } else {
2024
                    return $index;
2025
                }
2026
            }
2027
        }
2028
        if (empty ($this->ordered_items[$index])) {
2029
            return $this->index;
2030
        }
2031
        if ($this->debug > 2) {
2032
            error_log('New LP - index is now '.$index, 0);
2033
        }
2034
        return $index;
2035
    }
2036
2037
    /**
2038
     * Gets item_id for the next element
2039
     * @return	integer	Next item (DB) ID
2040
     */
2041
    public function get_next_item_id()
2042
    {
2043
        if ($this->debug > 0) {
2044
            error_log('New LP - In learnpath::get_next_item_id()', 0);
2045
        }
2046
        $new_index = $this->get_next_index();
2047
        if (!empty ($new_index)) {
2048
            if (isset ($this->ordered_items[$new_index])) {
2049
                if ($this->debug > 2) {
2050
                    error_log('New LP - In learnpath::get_next_index() - Returning '.$this->ordered_items[$new_index], 0);
2051
                }
2052
                return $this->ordered_items[$new_index];
2053
            }
2054
        }
2055
        if ($this->debug > 2) {
2056
            error_log('New LP - In learnpath::get_next_index() - Problem - Returning 0', 0);
2057
        }
2058
        return 0;
2059
    }
2060
2061
    /**
2062
     * Returns the package type ('scorm','aicc','scorm2004','dokeos','ppt'...)
2063
     *
2064
     * Generally, the package provided is in the form of a zip file, so the function
2065
     * has been written to test a zip file. If not a zip, the function will return the
2066
     * default return value: ''
2067
     * @param	string	the path to the file
2068
     * @param	string 	the original name of the file
2069
     * @return	string	'scorm','aicc','scorm2004','dokeos' or '' if the package cannot be recognized
2070
     */
2071
    public static function get_package_type($file_path, $file_name)
2072
    {
2073
        // Get name of the zip file without the extension.
2074
        $file_info = pathinfo($file_name);
2075
        $filename = $file_info['basename']; // Name including extension.
2076
        $extension = $file_info['extension']; // Extension only.
2077
2078
        if (!empty($_POST['ppt2lp']) && !in_array(strtolower($extension), array(
2079
                'dll',
2080
                'exe'
2081
            ))) {
2082
            return 'oogie';
2083
        }
2084
        if (!empty($_POST['woogie']) && !in_array(strtolower($extension), array(
2085
                'dll',
2086
                'exe'
2087
            ))) {
2088
            return 'woogie';
2089
        }
2090
2091
        $zipFile = new PclZip($file_path);
2092
        // Check the zip content (real size and file extension).
2093
        $zipContentArray = $zipFile->listContent();
2094
        $package_type = '';
2095
        $at_root = false;
2096
        $manifest = '';
2097
        $aicc_match_crs = 0;
2098
        $aicc_match_au = 0;
2099
        $aicc_match_des = 0;
2100
        $aicc_match_cst = 0;
2101
2102
        // The following loop should be stopped as soon as we found the right imsmanifest.xml (how to recognize it?).
2103
        if (is_array($zipContentArray) && count($zipContentArray) > 0) {
2104
            foreach ($zipContentArray as $thisContent) {
2105
                if (preg_match('~.(php.*|phtml)$~i', $thisContent['filename'])) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
2106
                    // New behaviour: Don't do anything. These files will be removed in scorm::import_package.
2107
                } elseif (stristr($thisContent['filename'], 'imsmanifest.xml') !== false) {
2108
                    $manifest = $thisContent['filename']; // Just the relative directory inside scorm/
2109
                    $package_type = 'scorm';
2110
                    break; // Exit the foreach loop.
2111
                } elseif (
2112
                    preg_match('/aicc\//i', $thisContent['filename']) ||
2113
                    in_array(strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION)), array('crs', 'au', 'des', 'cst'))
2114
                ) {
2115
                    $ext = strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION));
2116
                    switch ($ext) {
2117
                        case 'crs':
2118
                            $aicc_match_crs = 1;
2119
                            break;
2120
                        case 'au':
2121
                            $aicc_match_au = 1;
2122
                            break;
2123
                        case 'des':
2124
                            $aicc_match_des = 1;
2125
                            break;
2126
                        case 'cst':
2127
                            $aicc_match_cst = 1;
2128
                            break;
2129
                        default:
2130
                            break;
2131
                    }
2132
                    //break; // Don't exit the loop, because if we find an imsmanifest afterwards, we want it, not the AICC.
2133
                } else {
2134
                    $package_type = '';
2135
                }
2136
            }
2137
        }
2138
        if (empty($package_type) && 4 == ($aicc_match_crs + $aicc_match_au + $aicc_match_des + $aicc_match_cst)) {
2139
            // If found an aicc directory... (!= false means it cannot be false (error) or 0 (no match)).
2140
            $package_type = 'aicc';
2141
        }
2142
        return $package_type;
2143
    }
2144
2145
    /**
2146
     * Gets the previous resource in queue (url). Also initialises time values for this viewing
2147
     * @return string URL to load into the viewer
2148
     */
2149
    public function get_previous_index()
2150
    {
2151
        if ($this->debug > 0) {
2152
            error_log('New LP - In learnpath::get_previous_index()', 0);
2153
        }
2154
        $index = $this->index;
2155
        if (isset ($this->ordered_items[$index - 1])) {
2156
            $index--;
2157
            while (isset($this->ordered_items[$index]) && ($this->items[$this->ordered_items[$index]]->get_type() == 'dir')) {
2158
                $index--;
2159
                if ($index < 0) {
2160
                    return $this->index;
2161
                }
2162
            }
2163
        } else {
2164
            if ($this->debug > 2) {
2165
                error_log('New LP - get_previous_index() - there was no previous index available, reusing '.$index, 0);
2166
            }
2167
            // There is no previous item.
2168
        }
2169
        return $index;
2170
    }
2171
2172
    /**
2173
     * Gets item_id for the next element
2174
     * @return	integer	Previous item (DB) ID
2175
     */
2176
    public function get_previous_item_id()
2177
    {
2178
        if ($this->debug > 0) {
2179
            error_log('New LP - In learnpath::get_previous_item_id()', 0);
2180
        }
2181
        $new_index = $this->get_previous_index();
2182
        return $this->ordered_items[$new_index];
2183
    }
2184
2185
    /**
2186
     * Gets the progress value from the progress_db attribute
2187
     * @return	integer	Current progress value
2188
     * @deprecated This method does not seem to be used as of 20170514
2189
     */
2190
    public function get_progress()
2191
    {
2192
        if ($this->debug > 0) {
2193
            error_log('New LP - In learnpath::get_progress()', 0);
2194
        }
2195
        if (!empty ($this->progress_db)) {
2196
            return $this->progress_db;
2197
        }
2198
        return 0;
2199
    }
2200
2201
    /**
2202
     * Returns the HTML necessary to print a mediaplayer block inside a page
2203
     * @return string	The mediaplayer HTML
2204
     */
2205
    public function get_mediaplayer($lpItemId, $autostart = 'true')
2206
    {
2207
        $course_id = api_get_course_int_id();
2208
        $_course = api_get_course_info();
2209
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
2210
        $tbl_lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW);
2211
2212
        // Getting all the information about the item.
2213
        $sql = "SELECT * FROM $tbl_lp_item as lp
2214
                INNER JOIN $tbl_lp_item_view as lp_view
2215
                ON (lp.id = lp_view.lp_item_id AND lp.c_id = lp_view.c_id)
2216
                WHERE
2217
                    lp.id = '$lpItemId' AND
2218
                    lp.c_id = $course_id AND
2219
                    lp_view.c_id = $course_id";
2220
        $result = Database::query($sql);
2221
        $row = Database::fetch_assoc($result);
2222
        $output = '';
2223
2224
        if (!empty ($row['audio'])) {
2225
            $list = $_SESSION['oLP']->get_toc();
2226
            $type_quiz = false;
2227
2228
            foreach ($list as $toc) {
2229
                if ($toc['id'] == $_SESSION['oLP']->current && ($toc['type'] == 'quiz')) {
2230
                    $type_quiz = true;
2231
                }
2232
            }
2233
2234
            if ($type_quiz) {
2235
                if ($_SESSION['oLP']->prevent_reinit == 1) {
2236
                    $autostart_audio = $row['status'] === 'completed' ? 'false' : 'true';
2237
                } else {
2238
                    $autostart_audio = $autostart;
2239
                }
2240
            } else {
2241
                $autostart_audio = 'true';
2242
            }
2243
2244
            $courseInfo = api_get_course_info();
2245
            $audio = $row['audio'];
2246
2247
            $file = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/audio/'.$audio;
2248
            $url = api_get_path(WEB_COURSE_PATH).$courseInfo['path'].'/document/audio/'.$audio.'?'.api_get_cidreq();
2249
2250
            if (!file_exists($file)) {
2251
                $lpPathInfo = $_SESSION['oLP']->generate_lp_folder(api_get_course_info());
2252
                $file = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'.$lpPathInfo['dir'].$audio;
2253
                $url = api_get_path(WEB_COURSE_PATH).$_course['path'].'/document'.$lpPathInfo['dir'].$audio.'?'.api_get_cidreq();
2254
            }
2255
2256
            $player = Display::getMediaPlayer(
2257
                $file,
2258
                array(
2259
                    'id' => 'lp_audio_media_player',
2260
                    'url' => $url,
2261
                    'autoplay' => $autostart_audio,
2262
                    'width' => '100%'
2263
                )
2264
            );
2265
2266
            // The mp3 player.
2267
            $output  = '<div id="container">';
2268
            $output .= $player;
2269
            $output .= '</div>';
2270
        }
2271
2272
        return $output;
2273
    }
2274
2275
    /**
2276
     * @param int $studentId
2277
     * @param int $prerequisite
2278
     * @param array $courseInfo
2279
     * @return bool
2280
     *
2281
     */
2282
    public static function isBlockedByPrerequisite(
2283
        $studentId,
2284
        $prerequisite,
2285
        $courseInfo,
2286
        $sessionId
2287
    ) {
2288
        $isBlocked = false;
2289
2290
        if (!empty($prerequisite)) {
2291
            $progress = self::getProgress(
2292
                $prerequisite,
2293
                $studentId,
2294
                $courseInfo['real_id'],
2295
                $sessionId
2296
            );
2297
            if ($progress < 100) {
2298
                $isBlocked = true;
2299
            }
2300
        }
2301
2302
        return $isBlocked;
2303
    }
2304
2305
    /**
2306
     * Checks if the learning path is visible for student after the progress
2307
     * of its prerequisite is completed, considering the time availability and
2308
     * the LP visibility.
2309
     * @param int $lp_id
2310
     * @param int $student_id
2311
     * @param string Course code (optional)
2312
     * @param int $sessionId
2313
     * @return	bool
2314
     */
2315
    public static function is_lp_visible_for_student(
2316
        $lp_id,
2317
        $student_id,
2318
        $courseCode = null,
2319
        $sessionId = 0
2320
    ) {
2321
        $courseInfo = api_get_course_info($courseCode);
2322
        $lp_id = (int) $lp_id;
2323
        $sessionId = (int) $sessionId;
2324
2325
        if (empty($sessionId)) {
2326
            $sessionId = api_get_session_id();
2327
        }
2328
2329
        if (empty($courseInfo)) {
2330
            return false;
2331
        }
2332
2333
        $itemInfo = api_get_item_property_info(
2334
            $courseInfo['real_id'],
2335
            TOOL_LEARNPATH,
2336
            $lp_id,
2337
            $sessionId
2338
        );
2339
2340
        // If the item was deleted.
2341
        if (isset($itemInfo['visibility']) && $itemInfo['visibility'] == 2) {
2342
            return false;
2343
        }
2344
2345
        // @todo remove this query and load the row info as a parameter
2346
        $tbl_learnpath = Database::get_course_table(TABLE_LP_MAIN);
2347
        // Get current prerequisite
2348
        $sql = "SELECT id, prerequisite, subscribe_users, publicated_on, expired_on
2349
                FROM $tbl_learnpath
2350
                WHERE c_id = ".$courseInfo['real_id']." AND id = $lp_id";
2351
2352
        $rs  = Database::query($sql);
2353
        $now = time();
2354
        if (Database::num_rows($rs) > 0) {
2355
            $row = Database::fetch_array($rs, 'ASSOC');
2356
            $prerequisite = $row['prerequisite'];
2357
            $is_visible = true;
2358
2359
            $isBlocked = self::isBlockedByPrerequisite(
2360
                $student_id,
2361
                $prerequisite,
2362
                $courseInfo,
2363
                $sessionId
2364
            );
2365
2366
            if ($isBlocked) {
2367
                $is_visible = false;
2368
            }
2369
2370
            // Also check the time availability of the LP
2371
            if ($is_visible) {
2372
                // Adding visibility restrictions
2373
                if (!empty($row['publicated_on'])) {
2374
                    if ($now < api_strtotime($row['publicated_on'], 'UTC')) {
2375
                        $is_visible = false;
2376
                    }
2377
                }
2378
2379
                // Blocking empty start times see BT#2800
2380
                global $_custom;
2381
                if (isset($_custom['lps_hidden_when_no_start_date']) &&
2382
                    $_custom['lps_hidden_when_no_start_date']
2383
                ) {
2384
                    if (empty($row['publicated_on'])) {
2385
                        $is_visible = false;
2386
                    }
2387
                }
2388
2389
                if (!empty($row['expired_on'])) {
2390
                    if ($now > api_strtotime($row['expired_on'], 'UTC')) {
2391
                        $is_visible = false;
2392
                    }
2393
                }
2394
            }
2395
2396
            // Check if the subscription users/group to a LP is ON
2397
            if (isset($row['subscribe_users']) && $row['subscribe_users'] == 1) {
2398
                // Try group
2399
                $is_visible = false;
2400
2401
                // Checking only the user visibility
2402
                $userVisibility = api_get_item_visibility(
2403
                    $courseInfo,
2404
                    'learnpath',
2405
                    $row['id'],
2406
                    $sessionId,
2407
                    $student_id,
2408
                    'LearnpathSubscription'
2409
                );
2410
2411
                if ($userVisibility == 1) {
2412
                    $is_visible = true;
2413
                } else {
2414
                    $userGroups = GroupManager::getAllGroupPerUserSubscription($student_id);
2415
                    if (!empty($userGroups)) {
2416
                        foreach ($userGroups as $groupInfo) {
2417
                            $groupId = $groupInfo['iid'];
2418
                            $userVisibility = api_get_item_visibility(
2419
                                $courseInfo,
2420
                                'learnpath',
2421
                                $row['id'],
2422
                                $sessionId,
2423
                                null,
2424
                                'LearnpathSubscription',
2425
                                $groupId
2426
                            );
2427
2428
                            if ($userVisibility == 1) {
2429
                                $is_visible = true;
2430
                                break;
2431
                            }
2432
                        }
2433
                    }
2434
                }
2435
            }
2436
2437
            return $is_visible;
2438
        }
2439
2440
        return false;
2441
    }
2442
2443
    /**
2444
     * @param int $lpId
2445
     * @param int $userId
2446
     * @param int $courseId
2447
     * @param int $sessionId
2448
     * @return int
2449
     */
2450
    public static function getProgress($lpId, $userId, $courseId, $sessionId = 0)
2451
    {
2452
        $lpId = intval($lpId);
2453
        $userId = intval($userId);
2454
        $courseId = intval($courseId);
2455
        $sessionId = intval($sessionId);
2456
        $progress = 0;
2457
2458
        $sessionCondition = api_get_session_condition($sessionId);
2459
        $table = Database::get_course_table(TABLE_LP_VIEW);
2460
        $sql = "SELECT * FROM $table
2461
                WHERE
2462
                    c_id = ".$courseId." AND
2463
                    lp_id = $lpId AND
2464
                    user_id = $userId $sessionCondition";
2465
        $res = Database::query($sql);
2466
        if (Database::num_rows($res) > 0) {
2467
            $row = Database:: fetch_array($res);
2468
            $progress = $row['progress'];
2469
        }
2470
2471
        return (int) $progress;
2472
    }
2473
2474
    /**
2475
     * Displays a progress bar
2476
     * completed so far.
2477
     * @param	integer	$percentage Progress value to display
2478
     * @param	string	$text_add Text to display near the progress value
2479
     * @return	string	HTML string containing the progress bar
2480
     */
2481
    public static function get_progress_bar($percentage = -1, $text_add = '')
2482
    {
2483
        $text = $percentage.$text_add;
2484
        $output = '<div class="progress">
2485
                        <div id="progress_bar_value" class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="' .$percentage.'" aria-valuemin="0" aria-valuemax="100" style="width: '.$text.';">
2486
                        '. $text.'
2487
                        </div>
2488
                    </div>';
2489
2490
        return $output;
2491
    }
2492
2493
    /**
2494
     * @param string $mode can be '%' or 'abs'
2495
     * otherwise this value will be used $this->progress_bar_mode
2496
     * @return string
2497
     */
2498
    public function getProgressBar($mode = null)
2499
    {
2500
        list($percentage, $text_add) = $this->get_progress_bar_text($mode);
2501
        return self::get_progress_bar($percentage, $text_add);
2502
    }
2503
2504
    /**
2505
     * Gets the progress bar info to display inside the progress bar.
2506
     * Also used by scorm_api.php
2507
     * @param	string	$mode Mode of display (can be '%' or 'abs').abs means
2508
     * we display a number of completed elements per total elements
2509
     * @param	integer	$add Additional steps to fake as completed
2510
     * @return	list array Percentage or number and symbol (% or /xx)
2511
     */
2512
    public function get_progress_bar_text($mode = '', $add = 0)
2513
    {
2514
        if ($this->debug > 0) {
2515
            error_log('New LP - In learnpath::get_progress_bar_text()', 0);
2516
        }
2517
        if (empty($mode)) {
2518
            $mode = $this->progress_bar_mode;
2519
        }
2520
        $total_items = $this->getTotalItemsCountWithoutDirs();
2521
        if ($this->debug > 2) {
2522
            error_log('New LP - Total items available in this learnpath: '.$total_items, 0);
2523
        }
2524
        $completeItems = $this->get_complete_items_count();
2525
        if ($this->debug > 2) {
2526
            error_log('New LP - Items completed so far: '.$completeItems, 0);
2527
        }
2528
        if ($add != 0) {
2529
            $completeItems += $add;
2530
            if ($this->debug > 2) {
2531
                error_log('New LP - Items completed so far (+modifier): '.$completeItems, 0);
2532
            }
2533
        }
2534
        $text = '';
2535
        if ($completeItems > $total_items) {
2536
            $completeItems = $total_items;
2537
        }
2538
        $percentage = 0;
2539
        if ($mode == '%') {
2540
            if ($total_items > 0) {
2541
                $percentage = ((float) $completeItems / (float) $total_items) * 100;
2542
            } else {
2543
                $percentage = 0;
2544
            }
2545
            $percentage = number_format($percentage, 0);
2546
            $text = '%';
2547
        } elseif ($mode == 'abs') {
2548
            $percentage = $completeItems;
2549
            $text = '/'.$total_items;
2550
        }
2551
2552
        return array(
2553
            $percentage,
2554
            $text
2555
        );
2556
    }
2557
2558
    /**
2559
     * Gets the progress bar mode
2560
     * @return	string	The progress bar mode attribute
2561
     */
2562
    public function get_progress_bar_mode()
2563
    {
2564
        if ($this->debug > 0) {
2565
            error_log('New LP - In learnpath::get_progress_bar_mode()', 0);
2566
        }
2567
        if (!empty ($this->progress_bar_mode)) {
2568
            return $this->progress_bar_mode;
2569
        } else {
2570
            return '%';
2571
        }
2572
    }
2573
2574
    /**
2575
     * Gets the learnpath proximity (remote or local)
2576
     * @return	string	Learnpath proximity
2577
     */
2578
    public function get_proximity()
2579
    {
2580
        if ($this->debug > 0) {
2581
            error_log('New LP - In learnpath::get_proximity()', 0);
2582
        }
2583
        if (!empty ($this->proximity)) {
2584
            return $this->proximity;
2585
        } else {
2586
            return '';
2587
        }
2588
    }
2589
2590
    /**
2591
     * Gets the learnpath theme (remote or local)
2592
     * @return	string	Learnpath theme
2593
     */
2594
    public function get_theme()
2595
    {
2596
        if ($this->debug > 0) {
2597
            error_log('New LP - In learnpath::get_theme()', 0);
2598
        }
2599
        if (!empty ($this->theme)) {
2600
            return $this->theme;
2601
        } else {
2602
            return '';
2603
        }
2604
    }
2605
2606
    /**
2607
     * Gets the learnpath session id
2608
     * @return int
2609
     */
2610 View Code Duplication
    public function get_lp_session_id()
2611
    {
2612
        if ($this->debug > 0) {
2613
            error_log('New LP - In learnpath::get_lp_session_id()', 0);
2614
        }
2615
        if (!empty ($this->lp_session_id)) {
2616
            return (int) $this->lp_session_id;
2617
        } else {
2618
            return 0;
2619
        }
2620
    }
2621
2622
    /**
2623
     * Gets the learnpath image
2624
     * @return	string	Web URL of the LP image
2625
     */
2626
    public function get_preview_image()
2627
    {
2628
        if ($this->debug > 0) {
2629
            error_log('New LP - In learnpath::get_preview_image()', 0);
2630
        }
2631
        if (!empty($this->preview_image)) {
2632
            return $this->preview_image;
2633
        } else {
2634
            return '';
2635
        }
2636
    }
2637
2638
    /**
2639
     * @param string $size
2640
     * @param string $path_type
2641
     * @return bool|string
2642
     */
2643
    public function get_preview_image_path($size = null, $path_type = 'web')
2644
    {
2645
        $preview_image = $this->get_preview_image();
2646
        if (isset($preview_image) && !empty($preview_image)) {
2647
            $image_sys_path = api_get_path(SYS_COURSE_PATH).$this->course_info['path'].'/upload/learning_path/images/';
2648
            $image_path = api_get_path(WEB_COURSE_PATH).$this->course_info['path'].'/upload/learning_path/images/';
2649
2650
            if (isset($size)) {
2651
                $info = pathinfo($preview_image);
2652
                $image_custom_size = $info['filename'].'.'.$size.'.'.$info['extension'];
2653
2654
                if (file_exists($image_sys_path.$image_custom_size)) {
2655
                    if ($path_type == 'web') {
2656
                        return $image_path.$image_custom_size;
2657
                    } else {
2658
                        return $image_sys_path.$image_custom_size;
2659
                    }
2660
                }
2661
            } else {
2662
                if ($path_type == 'web') {
2663
                    return $image_path.$preview_image;
2664
                } else {
2665
                    return $image_sys_path.$preview_image;
2666
                }
2667
            }
2668
        }
2669
2670
        return false;
2671
    }
2672
2673
    /**
2674
     * Gets the learnpath author
2675
     * @return string	LP's author
2676
     */
2677
    public function get_author()
2678
    {
2679
        if ($this->debug > 0) {
2680
            error_log('New LP - In learnpath::get_author()', 0);
2681
        }
2682
        if (!empty ($this->author)) {
2683
            return $this->author;
2684
        } else {
2685
            return '';
2686
        }
2687
    }
2688
2689
    /**
2690
     * Gets the learnpath author
2691
     * @return	string	LP's author
2692
     */
2693
    public function get_hide_toc_frame()
2694
    {
2695
        if ($this->debug > 0) {
2696
            error_log('New LP - In learnpath::get_author()', 0);
2697
        }
2698
        if (!empty ($this->hide_toc_frame)) {
2699
            return $this->hide_toc_frame;
2700
        } else {
2701
            return '';
2702
        }
2703
    }
2704
2705
    /**
2706
     * Generate a new prerequisites string for a given item. If this item was a sco and
2707
     * its prerequisites were strings (instead of IDs), then transform those strings into
2708
     * IDs, knowing that SCORM IDs are kept in the "ref" field of the lp_item table.
2709
     * Prefix all item IDs that end-up in the prerequisites string by "ITEM_" to use the
2710
     * same rule as the scorm_export() method
2711
     * @param	integer		Item ID
2712
     * @return	string		Prerequisites string ready for the export as SCORM
2713
     */
2714
    public function get_scorm_prereq_string($item_id)
2715
    {
2716
        if ($this->debug > 0) {
2717
            error_log('New LP - In learnpath::get_scorm_prereq_string()', 0);
2718
        }
2719
        if (!is_object($this->items[$item_id])) {
2720
            return false;
2721
        }
2722
        /** @var learnpathItem $oItem */
2723
        $oItem = $this->items[$item_id];
2724
        $prereq = $oItem->get_prereq_string();
2725
2726
        if (empty($prereq)) {
2727
            return '';
2728
        }
2729
        if (preg_match('/^\d+$/', $prereq) && is_object($this->items[$prereq])) {
2730
            // If the prerequisite is a simple integer ID and this ID exists as an item ID,
2731
            // then simply return it (with the ITEM_ prefix).
2732
            //return 'ITEM_' . $prereq;
2733
            return $this->items[$prereq]->ref;
2734
        } else {
2735
            if (isset($this->refs_list[$prereq])) {
2736
                // It's a simple string item from which the ID can be found in the refs list,
2737
                // so we can transform it directly to an ID for export.
2738
                return $this->items[$this->refs_list[$prereq]]->ref;
2739
            } else if (isset($this->refs_list['ITEM_'.$prereq])) {
2740
                return $this->items[$this->refs_list['ITEM_'.$prereq]]->ref;
2741
            } else {
2742
                // The last case, if it's a complex form, then find all the IDs (SCORM strings)
2743
                // and replace them, one by one, by the internal IDs (chamilo db)
2744
                // TODO: Modify the '*' replacement to replace the multiplier in front of it
2745
                // by a space as well.
2746
                $find = array(
2747
                    '&',
2748
                    '|',
2749
                    '~',
2750
                    '=',
2751
                    '<>',
2752
                    '{',
2753
                    '}',
2754
                    '*',
2755
                    '(',
2756
                    ')'
2757
                );
2758
                $replace = array(
2759
                    ' ',
2760
                    ' ',
2761
                    ' ',
2762
                    ' ',
2763
                    ' ',
2764
                    ' ',
2765
                    ' ',
2766
                    ' ',
2767
                    ' ',
2768
                    ' '
2769
                );
2770
                $prereq_mod = str_replace($find, $replace, $prereq);
2771
                $ids = explode(' ', $prereq_mod);
2772
                foreach ($ids as $id) {
2773
                    $id = trim($id);
2774
                    if (isset ($this->refs_list[$id])) {
2775
                        $prereq = preg_replace('/[^a-zA-Z_0-9]('.$id.')[^a-zA-Z_0-9]/', 'ITEM_'.$this->refs_list[$id], $prereq);
2776
                    }
2777
                }
2778
2779
                return $prereq;
2780
            }
2781
        }
2782
    }
2783
2784
    /**
2785
     * Returns the XML DOM document's node
2786
     * @param	resource	Reference to a list of objects to search for the given ITEM_*
2787
     * @param	string		The identifier to look for
2788
     * @return	mixed		The reference to the element found with that identifier. False if not found
2789
     */
2790
    public function get_scorm_xml_node(& $children, $id)
2791
    {
2792
        for ($i = 0; $i < $children->length; $i++) {
2793
            $item_temp = $children->item($i);
2794
            if ($item_temp->nodeName == 'item') {
2795
                if ($item_temp->getAttribute('identifier') == $id) {
2796
                    return $item_temp;
2797
                }
2798
            }
2799
            $subchildren = $item_temp->childNodes;
2800
            if ($subchildren && $subchildren->length > 0) {
2801
                $val = $this->get_scorm_xml_node($subchildren, $id);
2802
                if (is_object($val)) {
2803
2804
                    return $val;
2805
                }
2806
            }
2807
        }
2808
2809
        return false;
2810
    }
2811
2812
    /**
2813
     * Gets the status list for all LP's items
2814
     * @return	array	Array of [index] => [item ID => current status]
2815
     */
2816
    public function get_items_status_list()
2817
    {
2818
        if ($this->debug > 0) {
2819
            error_log('New LP - In learnpath::get_items_status_list()', 0);
2820
        }
2821
        $list = array();
2822
        foreach ($this->ordered_items as $item_id) {
2823
            $list[] = array(
2824
                $item_id => $this->items[$item_id]->get_status()
2825
            );
2826
        }
2827
        return $list;
2828
    }
2829
2830
    /**
2831
     * Return the number of interactions for the given learnpath Item View ID.
2832
     * This method can be used as static.
2833
     * @param	integer	Item View ID
2834
     * @param   integer course id
2835
     * @return	integer	Number of interactions
2836
     */
2837 View Code Duplication
    public static function get_interactions_count_from_db($lp_iv_id, $course_id)
2838
    {
2839
        $table = Database::get_course_table(TABLE_LP_IV_INTERACTION);
2840
        $lp_iv_id = intval($lp_iv_id);
2841
        $course_id = intval($course_id);
2842
2843
        $sql = "SELECT count(*) FROM $table
2844
                WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id";
2845
        $res = Database::query($sql);
2846
        $num = 0;
2847
        if (Database::num_rows($res)) {
2848
            $row = Database::fetch_array($res);
2849
            $num = $row[0];
2850
        }
2851
        return $num;
2852
    }
2853
2854
    /**
2855
     * Return the interactions as an array for the given lp_iv_id.
2856
     * This method can be used as static.
2857
     * @param	integer	Learnpath Item View ID
2858
     * @return	array
2859
     * @todo 	Transcode labels instead of switching to HTML (which requires to know the encoding of the LP)
2860
     */
2861
    public static function get_iv_interactions_array($lp_iv_id)
2862
    {
2863
        $course_id = api_get_course_int_id();
2864
        $list = array();
2865
        $table = Database::get_course_table(TABLE_LP_IV_INTERACTION);
2866
2867
        if (empty($lp_iv_id)) {
2868
            return array();
2869
        }
2870
2871
        $sql = "SELECT * FROM $table
2872
                WHERE c_id = ".$course_id." AND lp_iv_id = $lp_iv_id
2873
                ORDER BY order_id ASC";
2874
        $res = Database::query($sql);
2875
        $num = Database::num_rows($res);
2876
        if ($num > 0) {
2877
            $list[] = array(
2878
                'order_id' => api_htmlentities(get_lang('Order'), ENT_QUOTES),
2879
                'id' => api_htmlentities(get_lang('InteractionID'), ENT_QUOTES),
2880
                'type' => api_htmlentities(get_lang('Type'), ENT_QUOTES),
2881
                'time' => api_htmlentities(get_lang('TimeFinished'), ENT_QUOTES),
2882
                'correct_responses' => api_htmlentities(get_lang('CorrectAnswers'), ENT_QUOTES),
2883
                'student_response' => api_htmlentities(get_lang('StudentResponse'), ENT_QUOTES),
2884
                'result' => api_htmlentities(get_lang('Result'), ENT_QUOTES),
2885
                'latency' => api_htmlentities(get_lang('LatencyTimeSpent'), ENT_QUOTES)
2886
            );
2887
            while ($row = Database::fetch_array($res)) {
2888
                $list[] = array(
2889
                    'order_id' => ($row['order_id'] + 1),
2890
                    'id' => urldecode($row['interaction_id']), //urldecode because they often have %2F or stuff like that
2891
                    'type' => $row['interaction_type'],
2892
                    'time' => $row['completion_time'],
2893
                    //'correct_responses' => $row['correct_responses'],
2894
                    'correct_responses' => '', // Hide correct responses from students.
2895
                    'student_response' => $row['student_response'],
2896
                    'result' => $row['result'],
2897
                    'latency' => $row['latency']
2898
                );
2899
            }
2900
        }
2901
2902
        return $list;
2903
    }
2904
2905
    /**
2906
     * Return the number of objectives for the given learnpath Item View ID.
2907
     * This method can be used as static.
2908
     * @param	integer	Item View ID
2909
     * @return	integer	Number of objectives
2910
     */
2911 View Code Duplication
    public static function get_objectives_count_from_db($lp_iv_id, $course_id)
2912
    {
2913
        $table = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
2914
        $course_id = intval($course_id);
2915
        $lp_iv_id = intval($lp_iv_id);
2916
        $sql = "SELECT count(*) FROM $table
2917
                WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id";
2918
        //@todo seems that this always returns 0
2919
        $res = Database::query($sql);
2920
        $num = 0;
2921
        if (Database::num_rows($res)) {
2922
            $row = Database::fetch_array($res);
2923
            $num = $row[0];
2924
        }
2925
2926
        return $num;
2927
    }
2928
2929
    /**
2930
     * Return the objectives as an array for the given lp_iv_id.
2931
     * This method can be used as static.
2932
     * @param	integer	Learnpath Item View ID
2933
     * @return	array
2934
     * @todo 	Translate labels
2935
     */
2936
    public static function get_iv_objectives_array($lp_iv_id = 0)
2937
    {
2938
        $course_id = api_get_course_int_id();
2939
        $table = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
2940
        $sql = "SELECT * FROM $table
2941
                WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id
2942
                ORDER BY order_id ASC";
2943
        $res = Database::query($sql);
2944
        $num = Database::num_rows($res);
2945
        $list = array();
2946
        if ($num > 0) {
2947
            $list[] = array(
2948
                'order_id' => api_htmlentities(get_lang('Order'), ENT_QUOTES),
2949
                'objective_id' => api_htmlentities(get_lang('ObjectiveID'), ENT_QUOTES),
2950
                'score_raw' => api_htmlentities(get_lang('ObjectiveRawScore'), ENT_QUOTES),
2951
                'score_max' => api_htmlentities(get_lang('ObjectiveMaxScore'), ENT_QUOTES),
2952
                'score_min' => api_htmlentities(get_lang('ObjectiveMinScore'), ENT_QUOTES),
2953
                'status' => api_htmlentities(get_lang('ObjectiveStatus'), ENT_QUOTES)
2954
            );
2955
            while ($row = Database::fetch_array($res)) {
2956
                $list[] = array(
2957
                    'order_id' => ($row['order_id'] + 1),
2958
                    'objective_id' => urldecode($row['objective_id']), // urldecode() because they often have %2F or stuff like that.
2959
                    'score_raw' => $row['score_raw'],
2960
                    'score_max' => $row['score_max'],
2961
                    'score_min' => $row['score_min'],
2962
                    'status' => $row['status']
2963
                );
2964
            }
2965
        }
2966
2967
        return $list;
2968
    }
2969
2970
    /**
2971
     * Generate and return the table of contents for this learnpath. The (flat) table returned can be
2972
     * used by get_html_toc() to be ready to display
2973
     * @return	array	TOC as a table with 4 elements per row: title, link, status and level
2974
     */
2975
    public function get_toc()
2976
    {
2977
        if ($this->debug > 0) {
2978
            error_log('learnpath::get_toc()', 0);
2979
        }
2980
        $toc = array();
2981
        foreach ($this->ordered_items as $item_id) {
2982
            if ($this->debug > 2) {
2983
                error_log('learnpath::get_toc(): getting info for item '.$item_id, 0);
2984
            }
2985
            // TODO: Change this link generation and use new function instead.
2986
            $toc[] = array(
2987
                'id' => $item_id,
2988
                'title' => $this->items[$item_id]->get_title(),
2989
                'status' => $this->items[$item_id]->get_status(),
2990
                'level' => $this->items[$item_id]->get_level(),
2991
                'type' => $this->items[$item_id]->get_type(),
2992
                'description' => $this->items[$item_id]->get_description(),
2993
                'path' => $this->items[$item_id]->get_path(),
2994
            );
2995
        }
2996
        if ($this->debug > 2) {
2997
            error_log('New LP - In learnpath::get_toc() - TOC array: '.print_r($toc, true), 0);
2998
        }
2999
        return $toc;
3000
    }
3001
3002
    /**
3003
     * Generate and return the table of contents for this learnpath. The JS
3004
     * table returned is used inside of scorm_api.php
3005
     * @return  string  A JS array vairiable construction
3006
     */
3007
    public function get_items_details_as_js($varname = 'olms.lms_item_types')
3008
    {
3009
        if ($this->debug > 0) {
3010
            error_log('New LP - In learnpath::get_items_details_as_js()', 0);
3011
        }
3012
        $toc = $varname.' = new Array();';
3013
        foreach ($this->ordered_items as $item_id) {
3014
            $toc .= $varname."['i$item_id'] = '".$this->items[$item_id]->get_type()."';";
3015
        }
3016
        if ($this->debug > 2) {
3017
            error_log('New LP - In learnpath::get_items_details_as_js() - TOC array: '.print_r($toc, true), 0);
3018
        }
3019
        return $toc;
3020
    }
3021
3022
    /**
3023
     * Gets the learning path type
3024
     * @param	boolean		Return the name? If false, return the ID. Default is false.
3025
     * @return	mixed		Type ID or name, depending on the parameter
3026
     */
3027
    public function get_type($get_name = false)
3028
    {
3029
        $res = false;
3030
        if ($this->debug > 0) {
3031
            error_log('New LP - In learnpath::get_type()', 0);
3032
        }
3033
        if (!empty($this->type)) {
3034
            if ($get_name) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
3035
                // Get it from the lp_type table in main db.
3036
            } else {
3037
                $res = $this->type;
3038
            }
3039
        }
3040
        if ($this->debug > 2) {
3041
            error_log('New LP - In learnpath::get_type() - Returning '.($res ? $res : 'false'), 0);
3042
        }
3043
        return $res;
3044
    }
3045
3046
    /**
3047
     * Gets the learning path type as static method
3048
     * @param	boolean		Return the name? If false, return the ID. Default is false.
3049
     * @return	mixed		Type ID or name, depending on the parameter
3050
     */
3051
    public static function get_type_static($lp_id = 0)
3052
    {
3053
        $course_id = api_get_course_int_id();
3054
        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
3055
        $lp_id = intval($lp_id);
3056
        $sql = "SELECT lp_type FROM $tbl_lp
3057
                WHERE c_id = $course_id AND id = '".$lp_id."'";
3058
        $res = Database::query($sql);
3059
        if ($res === false) {
3060
            return null;
3061
        }
3062
        if (Database::num_rows($res) <= 0) {
3063
            return null;
3064
        }
3065
        $row = Database::fetch_array($res);
3066
        return $row['lp_type'];
3067
    }
3068
3069
    /**
3070
     * Gets a flat list of item IDs ordered for display (level by level ordered by order_display)
3071
     * This method can be used as abstract and is recursive
3072
     * @param	integer	Learnpath ID
3073
     * @param	integer	Parent ID of the items to look for
3074
     * @return	array	Ordered list of item IDs (empty array on error)
3075
     */
3076
    public static function get_flat_ordered_items_list($lp, $parent = 0, $course_id = null)
3077
    {
3078
        if (empty($course_id)) {
3079
            $course_id = api_get_course_int_id();
3080
        } else {
3081
            $course_id = intval($course_id);
3082
        }
3083
        $list = array();
3084
3085
        if (empty($lp)) {
3086
            return $list;
3087
        }
3088
3089
        $lp = intval($lp);
3090
        $parent = intval($parent);
3091
3092
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
3093
        $sql = "SELECT id FROM $tbl_lp_item
3094
                WHERE c_id = $course_id AND lp_id = $lp AND parent_item_id = $parent
3095
                ORDER BY display_order";
3096
3097
        $res = Database::query($sql);
3098
        while ($row = Database::fetch_array($res)) {
3099
            $sublist = self::get_flat_ordered_items_list($lp, $row['id'], $course_id);
3100
            $list[] = $row['id'];
3101
            foreach ($sublist as $item) {
3102
                $list[] = $item;
3103
            }
3104
        }
3105
        return $list;
3106
    }
3107
3108
    /**
3109
     * @return array
3110
     */
3111
    public static function getChapterTypes()
3112
    {
3113
        return array(
3114
            'dir'
3115
        );
3116
    }
3117
    /**
3118
     * Uses the table generated by get_toc() and returns an HTML-formatted string ready to display
3119
     * @return	string	HTML TOC ready to display
3120
     */
3121
    public function getListArrayToc($toc_list = null)
3122
    {
3123
        if ($this->debug > 0) {
3124
            error_log('In learnpath::get_html_toc()', 0);
3125
        }
3126
        if (empty($toc_list)) {
3127
            $toc_list = $this->get_toc();
3128
        }
3129
        // Temporary variables.
3130
        $mycurrentitemid = $this->get_current_item_id();
3131
        $list = [];
3132
        $arrayList = [];
3133
        $classStatus = [
3134
            'not attempted' => 'scorm_not_attempted',
3135
            'incomplete' => 'scorm_not_attempted',
3136
            'failed' => 'scorm_failed',
3137
            'completed' => 'scorm_completed',
3138
            'passed' => 'scorm_completed',
3139
            'succeeded' => 'scorm_completed',
3140
            'browsed' => 'scorm_completed',
3141
            ];
3142
        foreach ($toc_list as $item) {
3143
3144
            $list['id'] = $item['id'];
3145
            $list['status'] = $item['status'];
3146
            $cssStatus = null;
3147
3148
            if (isset($classStatus[$item['status']])) {
3149
                $cssStatus = $classStatus[$item['status']];
3150
            }
3151
3152
            $classStyle = ' ';
3153
            $dirTypes = self::getChapterTypes();
3154
3155
            if (in_array($item['type'], $dirTypes)) {
3156
                $classStyle = 'scorm_item_section ';
3157
            }
3158
            if ($item['id'] == $this->current) {
3159
                $classStyle = 'scorm_item_normal '.$classStyle.'scorm_highlight';
3160
            } elseif (!in_array($item['type'], $dirTypes)) {
3161
                $classStyle = 'scorm_item_normal '.$classStyle.' ';
3162
            }
3163
            $title = $item['title'];
3164
            if (empty($title)) {
3165
                $title = self::rl_get_resource_name(api_get_course_id(), $this->get_id(), $item['id']);
3166
            }
3167
            $title = Security::remove_XSS($item['title']);
3168
3169
            if (empty($item['description'])) {
3170
                $list['description'] = $title;
3171
            } else {
3172
                $list['description'] = $item['description'];
3173
            }
3174
3175
            $list['class'] = $classStyle.' '.$cssStatus;
3176
            $list['level'] = $item['level'];
3177
            $list['type'] = $item['type'];
3178
3179
            if (in_array($item['type'], $dirTypes)) {
3180
                $list['css_level'] = 'level_'.$item['level'];
3181
            } else {
3182
                $list['css_level'] = 'level_'.$item['level']
3183
                    .' scorm_type_'
3184
                    .learnpath::format_scorm_type_item($item['type']);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
3185
            }
3186
3187
            if (in_array($item['type'], $dirTypes)) {
3188
                $list['title'] = stripslashes($title);
3189
            } else {
3190
                $list['title'] = stripslashes($title);
3191
                $list['url'] = $this->get_link('http', $item['id'], $toc_list);
3192
                $list['current_id'] = $mycurrentitemid;
3193
            }
3194
            $arrayList[] = $list;
3195
        }
3196
3197
        return $arrayList;
3198
    }
3199
3200
    /**
3201
     * Returns an HTML-formatted string ready to display with teacher buttons
3202
     * in LP view menu
3203
     * @return	string	HTML TOC ready to display
3204
     */
3205
    public function get_teacher_toc_buttons()
3206
    {
3207
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true, false, false);
3208
        $hide_teacher_icons_lp = api_get_configuration_value('hide_teacher_icons_lp');
3209
        $html = '';
3210
        if ($is_allowed_to_edit && $hide_teacher_icons_lp == false) {
3211
            $gradebook = '';
3212
            if (!empty($_GET['gradebook'])) {
3213
                $gradebook = Security::remove_XSS($_GET['gradebook']);
3214
            }
3215
            if ($this->get_lp_session_id() == api_get_session_id()) {
3216
                $html .= '<div id="actions_lp" class="actions_lp"><hr>';
3217
                $html .= '<div class="btn-group">';
3218
                $html .= "<a class='btn btn-sm btn-default' href='lp_controller.php?".api_get_cidreq()."&gradebook=$gradebook&action=build&lp_id=".$this->lp_id."&isStudentView=false' target='_parent'>".
3219
                    Display::returnFontAwesomeIcon('street-view').get_lang('Overview')."</a>";
3220
                $html .= "<a class='btn btn-sm btn-default' href='lp_controller.php?".api_get_cidreq()."&action=add_item&type=step&lp_id=".$this->lp_id."&isStudentView=false' target='_parent'>".
3221
                    Display::returnFontAwesomeIcon('pencil').get_lang('Edit')."</a>";
3222
                $html .= '<a class="btn btn-sm btn-default" href="lp_controller.php?'.api_get_cidreq()."&gradebook=$gradebook&action=edit&lp_id=".$this->lp_id.'&isStudentView=false">'.
3223
                    Display::returnFontAwesomeIcon('cog').get_lang('Settings').'</a>';
3224
                $html .= '</div>';
3225
                $html .= '</div>';
3226
            }
3227
        }
3228
3229
        return $html;
3230
3231
    }
3232
    /**
3233
     * Gets the learnpath maker name - generally the editor's name
3234
     * @return	string	Learnpath maker name
3235
     */
3236
    public function get_maker()
3237
    {
3238
        if ($this->debug > 0) {
3239
            error_log('New LP - In learnpath::get_maker()', 0);
3240
        }
3241
        if (!empty ($this->maker)) {
3242
            return $this->maker;
3243
        } else {
3244
            return '';
3245
        }
3246
    }
3247
3248
    /**
3249
     * Gets the learnpath name/title
3250
     * @return	string	Learnpath name/title
3251
     */
3252 View Code Duplication
    public function get_name()
3253
    {
3254
        if ($this->debug > 0) {
3255
            error_log('New LP - In learnpath::get_name()', 0);
3256
        }
3257
        if (!empty ($this->name)) {
3258
            return $this->name;
3259
        } else {
3260
            return 'N/A';
3261
        }
3262
    }
3263
3264
    /**
3265
     * Gets a link to the resource from the present location, depending on item ID.
3266
     * @param	string	$type Type of link expected
3267
     * @param	integer	$item_id Learnpath item ID
3268
     * @return	string	$provided_toc Link to the lp_item resource
3269
     */
3270
    public function get_link($type = 'http', $item_id = null, $provided_toc = false)
3271
    {
3272
        $course_id = $this->get_course_int_id();
3273
3274 View Code Duplication
        if ($this->debug > 0) {
3275
            error_log('New LP - In learnpath::get_link('.$type.','.$item_id.')', 0);
3276
        }
3277 View Code Duplication
        if (empty($item_id)) {
3278
            if ($this->debug > 2) {
3279
                error_log('New LP - In learnpath::get_link() - no item id given in learnpath::get_link(), using current: '.$this->get_current_item_id(), 0);
3280
            }
3281
            $item_id = $this->get_current_item_id();
3282
        }
3283
3284 View Code Duplication
        if (empty($item_id)) {
3285
            if ($this->debug > 2) {
3286
                error_log('New LP - In learnpath::get_link() - no current item id found in learnpath object', 0);
3287
            }
3288
            //still empty, this means there was no item_id given and we are not in an object context or
3289
            //the object property is empty, return empty link
3290
            $item_id = $this->first();
3291
            return '';
3292
        }
3293
3294
        $file = '';
3295
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3296
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
3297
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3298
        $item_id = intval($item_id);
3299
3300
        $sql = "SELECT
3301
                    l.lp_type as ltype,
3302
                    l.path as lpath,
3303
                    li.item_type as litype,
3304
                    li.path as lipath,
3305
                    li.parameters as liparams
3306
        		FROM $lp_table l
3307
                INNER JOIN $lp_item_table li
3308
                    ON (li.lp_id = l.id AND l.c_id = $course_id AND li.c_id = $course_id )
3309
        		WHERE li.id = $item_id ";
3310
        if ($this->debug > 2) {
3311
            error_log('New LP - In learnpath::get_link() - selecting item '.$sql, 0);
3312
        }
3313
        $res = Database::query($sql);
3314
        if (Database::num_rows($res) > 0) {
3315
            $row = Database::fetch_array($res);
3316
            $lp_type = $row['ltype'];
3317
            $lp_path = $row['lpath'];
3318
            $lp_item_type = $row['litype'];
3319
            $lp_item_path = $row['lipath'];
3320
            $lp_item_params = $row['liparams'];
3321
3322
            if (empty($lp_item_params) && strpos($lp_item_path, '?') !== false) {
3323
                list($lp_item_path, $lp_item_params) = explode('?', $lp_item_path);
3324
            }
3325
            $sys_course_path = api_get_path(SYS_COURSE_PATH).api_get_course_path();
3326
            if ($type == 'http') {
3327
                $course_path = api_get_path(WEB_COURSE_PATH).api_get_course_path(); //web path
3328
            } else {
3329
                $course_path = $sys_course_path; //system path
3330
            }
3331
3332
            // Fixed issue BT#1272 - If the item type is a Chamilo Item (quiz, link, etc), then change the lp type to thread it as a normal Chamilo LP not a SCO.
3333
            if (in_array($lp_item_type, array('quiz', 'document', 'final_item', 'link', 'forum', 'thread', 'student_publication'))) {
3334
                $lp_type = 1;
3335
            }
3336
3337
            if ($this->debug > 2) {
3338
                error_log('New LP - In learnpath::get_link() - $lp_type '.$lp_type, 0);
3339
                error_log('New LP - In learnpath::get_link() - $lp_item_type '.$lp_item_type, 0);
3340
            }
3341
3342
            // Now go through the specific cases to get the end of the path
3343
            // @todo Use constants instead of int values.
3344
            switch ($lp_type) {
3345
                case 1 :
3346
                    $file = self::rl_get_resource_link_for_learnpath(
3347
                        $course_id,
3348
                        $this->get_id(),
3349
                        $item_id,
3350
                        $this->get_view_id()
3351
                    );
3352
                    if ($this->debug > 0) {
3353
                        error_log('rl_get_resource_link_for_learnpath - file: '.$file, 0);
3354
                    }
3355
3356
                    switch ($lp_item_type) {
3357
                        case 'dir':
3358
                            $file = 'lp_content.php?type=dir';
3359
                            break;
3360
                        case 'link':
3361
                            if (Link::is_youtube_link($file)) {
3362
                                $src = Link::get_youtube_video_id($file);
3363
                                $file = api_get_path(WEB_CODE_PATH).'lp/embed.php?type=youtube&source='.$src;
3364
                            } elseif (Link::isVimeoLink($file)) {
3365
                                $src = Link::getVimeoLinkId($file);
3366
                                $file = api_get_path(WEB_CODE_PATH).'lp/embed.php?type=vimeo&source='.$src;
3367
                            } else {
3368
                                // If the current site is HTTPS and the link is
3369
                                // HTTP, browsers will refuse opening the link
3370
                                $urlId = api_get_current_access_url_id();
3371
                                $url = api_get_access_url($urlId, false);
3372
                                $protocol = substr($url['url'], 0, 5);
3373
                                if ($protocol === 'https') {
3374
                                    $linkProtocol = substr($file, 0, 5);
3375
                                    if ($linkProtocol === 'http:') {
3376
                                        //this is the special intervention case
3377
                                        $file = api_get_path(WEB_CODE_PATH).'lp/embed.php?type=nonhttps&source='.urlencode($file);
3378
                                    }
3379
                                }
3380
                            }
3381
                            break;
3382
                        case 'quiz':
3383
                            // Check how much attempts of a exercise exits in lp
3384
                            $lp_item_id = $this->get_current_item_id();
3385
                            $lp_view_id = $this->get_view_id();
3386
3387
                            $prevent_reinit = null;
3388
                            if (isset($this->items[$this->current])) {
3389
                                $prevent_reinit = $this->items[$this->current]->get_prevent_reinit();
3390
                            }
3391
3392
                            if (empty($provided_toc)) {
3393
                                if ($this->debug > 0) {
3394
                                    error_log('In learnpath::get_link() Loading get_toc ', 0);
3395
                                }
3396
                                $list = $this->get_toc();
3397
                            } else {
3398
                                if ($this->debug > 0) {
3399
                                    error_log('In learnpath::get_link() Loading get_toc from "cache" ', 0);
3400
                                }
3401
                                $list = $provided_toc;
3402
                            }
3403
3404
                            $type_quiz = false;
3405
3406 View Code Duplication
                            foreach ($list as $toc) {
0 ignored issues
show
Bug introduced by
The expression $list of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
3407
                                if ($toc['id'] == $lp_item_id && ($toc['type'] == 'quiz')) {
3408
                                    $type_quiz = true;
3409
                                }
3410
                            }
3411
3412
                            if ($type_quiz) {
3413
                                $lp_item_id = intval($lp_item_id);
3414
                                $lp_view_id = intval($lp_view_id);
3415
                                $sql = "SELECT count(*) FROM $lp_item_view_table
3416
                                        WHERE
3417
                                            c_id = $course_id AND
3418
                                            lp_item_id='".$lp_item_id."' AND
3419
                                            lp_view_id ='".$lp_view_id."' AND
3420
                                            status='completed'";
3421
                                $result = Database::query($sql);
3422
                                $row_count = Database:: fetch_row($result);
3423
                                $count_item_view = (int) $row_count[0];
3424
                                $not_multiple_attempt = 0;
3425
                                if ($prevent_reinit === 1 && $count_item_view > 0) {
3426
                                    $not_multiple_attempt = 1;
3427
                                }
3428
                                $file .= '&not_multiple_attempt='.$not_multiple_attempt;
3429
                            }
3430
                            break;
3431
                    }
3432
3433
                    $tmp_array = explode('/', $file);
3434
                    $document_name = $tmp_array[count($tmp_array) - 1];
3435
                    if (strpos($document_name, '_DELETED_')) {
3436
                        $file = 'blank.php?error=document_deleted';
3437
                    }
3438
3439
                    break;
3440
                case 2 :
3441
                    if ($this->debug > 2) {
3442
                        error_log('New LP - In learnpath::get_link() '.__LINE__.' - Item type: '.$lp_item_type, 0);
3443
                    }
3444
3445
                    if ($lp_item_type != 'dir') {
3446
                        // Quite complex here:
3447
                        // We want to make sure 'http://' (and similar) links can
3448
                        // be loaded as is (withouth the Chamilo path in front) but
3449
                        // some contents use this form: resource.htm?resource=http://blablabla
3450
                        // which means we have to find a protocol at the path's start, otherwise
3451
                        // it should not be considered as an external URL.
3452
3453
                        //if ($this->prerequisites_match($item_id)) {
3454
                        if (preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path) != 0) {
3455
                            if ($this->debug > 2) {
3456
                                error_log('New LP - In learnpath::get_link() '.__LINE__.' - Found match for protocol in '.$lp_item_path, 0);
3457
                            }
3458
                            // Distant url, return as is.
3459
                            $file = $lp_item_path;
3460
                        } else {
3461
                            if ($this->debug > 2) {
3462
                                error_log('New LP - In learnpath::get_link() '.__LINE__.' - No starting protocol in '.$lp_item_path, 0);
3463
                            }
3464
                            // Prevent getting untranslatable urls.
3465
                            $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path);
3466
                            $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path);
3467
                            // Prepare the path.
3468
                            $file = $course_path.'/scorm/'.$lp_path.'/'.$lp_item_path;
3469
                            // TODO: Fix this for urls with protocol header.
3470
                            $file = str_replace('//', '/', $file);
3471
                            $file = str_replace(':/', '://', $file);
3472
                            if (substr($lp_path, -1) == '/') {
3473
                                $lp_path = substr($lp_path, 0, -1);
3474
                            }
3475
3476
                            if (!is_file(realpath($sys_course_path.'/scorm/'.$lp_path.'/'.$lp_item_path))) {
3477
                                // if file not found.
3478
                                $decoded = html_entity_decode($lp_item_path);
3479
                                list ($decoded) = explode('?', $decoded);
3480
                                if (!is_file(realpath($sys_course_path.'/scorm/'.$lp_path.'/'.$decoded))) {
3481
                                    $file = self::rl_get_resource_link_for_learnpath(
3482
                                        $course_id,
3483
                                        $this->get_id(),
3484
                                        $item_id,
3485
                                        $this->get_view_id()
3486
                                    );
3487
                                    if (empty($file)) {
3488
                                        $file = 'blank.php?error=document_not_found';
3489
                                    } else {
3490
                                        $tmp_array = explode('/', $file);
3491
                                        $document_name = $tmp_array[count($tmp_array) - 1];
3492
                                        if (strpos($document_name, '_DELETED_')) {
3493
                                            $file = 'blank.php?error=document_deleted';
3494
                                        } else {
3495
                                            $file = 'blank.php?error=document_not_found';
3496
                                        }
3497
                                    }
3498
                                } else {
3499
                                    $file = $course_path.'/scorm/'.$lp_path.'/'.$decoded;
3500
                                }
3501
                            }
3502
                        }
3503
3504
                        // We want to use parameters if they were defined in the imsmanifest
3505
                        if (strpos($file, 'blank.php') === false) {
3506
                            $file .= (strstr($file, '?') === false ? '?' : '').$lp_item_params;
3507
                        }
3508
                    } else {
3509
                        $file = 'lp_content.php?type=dir';
3510
                    }
3511
                    break;
3512
                case 3 :
3513
                    if ($this->debug > 2) {
3514
                        error_log('New LP - In learnpath::get_link() '.__LINE__.' - Item type: '.$lp_item_type, 0);
3515
                    }
3516
                    // Formatting AICC HACP append URL.
3517
                    $aicc_append = '?aicc_sid='.urlencode(session_id()).'&aicc_url='.urlencode(api_get_path(WEB_CODE_PATH).'lp/aicc_hacp.php').'&';
3518
                    if (!empty($lp_item_params)) {
3519
                        $aicc_append .= $lp_item_params.'&';
3520
                    }
3521
                    if ($lp_item_type != 'dir') {
3522
                        // Quite complex here:
3523
                        // We want to make sure 'http://' (and similar) links can
3524
                        // be loaded as is (withouth the Chamilo path in front) but
3525
                        // some contents use this form: resource.htm?resource=http://blablabla
3526
                        // which means we have to find a protocol at the path's start, otherwise
3527
                        // it should not be considered as an external URL.
3528
3529
                        if (preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path) != 0) {
3530
                            if ($this->debug > 2) {
3531
                                error_log('New LP - In learnpath::get_link() '.__LINE__.' - Found match for protocol in '.$lp_item_path, 0);
3532
                            }
3533
                            // Distant url, return as is.
3534
                            $file = $lp_item_path;
3535
                            // Enabled and modified by Ivan Tcholakov, 16-OCT-2008.
3536
                            /*
3537
                            if (stristr($file,'<servername>') !== false) {
3538
                                $file = str_replace('<servername>', $course_path.'/scorm/'.$lp_path.'/', $lp_item_path);
3539
                            }
3540
                            */
3541
                            if (stripos($file, '<servername>') !== false) {
3542
                                //$file = str_replace('<servername>',$course_path.'/scorm/'.$lp_path.'/',$lp_item_path);
3543
                                $web_course_path = str_replace('https://', '', str_replace('http://', '', $course_path));
3544
                                $file = str_replace('<servername>', $web_course_path.'/scorm/'.$lp_path, $lp_item_path);
3545
                            }
3546
                            //
3547
                            $file .= $aicc_append;
3548
                        } else {
3549
                            if ($this->debug > 2) {
3550
                                error_log('New LP - In learnpath::get_link() '.__LINE__.' - No starting protocol in '.$lp_item_path, 0);
3551
                            }
3552
                            // Prevent getting untranslatable urls.
3553
                            $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path);
3554
                            $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path);
3555
                            // Prepare the path - lp_path might be unusable because it includes the "aicc" subdir name.
3556
                            $file = $course_path.'/scorm/'.$lp_path.'/'.$lp_item_path;
3557
                            // TODO: Fix this for urls with protocol header.
3558
                            $file = str_replace('//', '/', $file);
3559
                            $file = str_replace(':/', '://', $file);
3560
                            $file .= $aicc_append;
3561
                        }
3562
                    } else {
3563
                        $file = 'lp_content.php?type=dir';
3564
                    }
3565
                    break;
3566
                case 4 :
3567
                    break;
3568
                default:
3569
                    break;
3570
            }
3571
            // Replace &amp; by & because &amp; will break URL with params
3572
            $file = !empty($file) ? str_replace('&amp;', '&', $file) : '';
3573
        }
3574
        if ($this->debug > 2) {
3575
            error_log('New LP - In learnpath::get_link() - returning "'.$file.'" from get_link', 0);
3576
        }
3577
        return $file;
3578
    }
3579
3580
    /**
3581
     * Gets the latest usable view or generate a new one
3582
     * @param	integer	Optional attempt number. If none given, takes the highest from the lp_view table
3583
     * @return	integer	DB lp_view id
3584
     */
3585
    public function get_view($attempt_num = 0)
3586
    {
3587
        if ($this->debug > 0) {
3588
            error_log('New LP - In learnpath::get_view()', 0);
3589
        }
3590
        $search = '';
3591
        // Use $attempt_num to enable multi-views management (disabled so far).
3592
        if ($attempt_num != 0 AND intval(strval($attempt_num)) == $attempt_num) {
3593
            $search = 'AND view_count = '.$attempt_num;
3594
        }
3595
        // When missing $attempt_num, search for a unique lp_view record for this lp and user.
3596
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
3597
3598
        $course_id = api_get_course_int_id();
3599
        $sessionId = api_get_session_id();
3600
3601
        $sql = "SELECT id, view_count FROM $lp_view_table
3602
        		WHERE
3603
        		    c_id = ".$course_id." AND
3604
        		    lp_id = " . $this->get_id()." AND
3605
        		    user_id = " . $this->get_user_id()." AND
3606
        		    session_id = $sessionId
3607
        		    $search
3608
                ORDER BY view_count DESC";
3609
        $res = Database::query($sql);
3610
        if (Database::num_rows($res) > 0) {
3611
            $row = Database::fetch_array($res);
3612
            $this->lp_view_id = $row['id'];
3613
        } else if (!api_is_invitee()) {
3614
            // There is no database record, create one.
3615
            $sql = "INSERT INTO $lp_view_table (c_id, lp_id,user_id, view_count, session_id) VALUES
3616
            		($course_id, ".$this->get_id().",".$this->get_user_id().", 1, $sessionId)";
3617
            Database::query($sql);
3618
            $id = Database::insert_id();
3619
            $this->lp_view_id = $id;
3620
3621
            $sql = "UPDATE $lp_view_table SET id = iid WHERE iid = $id";
3622
            Database::query($sql);
3623
        }
3624
3625
        return $this->lp_view_id;
3626
    }
3627
3628
    /**
3629
     * Gets the current view id
3630
     * @return	integer	View ID (from lp_view)
3631
     */
3632
    public function get_view_id()
3633
    {
3634
        if ($this->debug > 0) {
3635
            error_log('New LP - In learnpath::get_view_id()', 0);
3636
        }
3637
        if (!empty ($this->lp_view_id)) {
3638
            return $this->lp_view_id;
3639
        } else {
3640
            return 0;
3641
        }
3642
    }
3643
3644
    /**
3645
     * Gets the update queue
3646
     * @return	array	Array containing IDs of items to be updated by JavaScript
3647
     */
3648
    public function get_update_queue()
3649
    {
3650
        if ($this->debug > 0) {
3651
            error_log('New LP - In learnpath::get_update_queue()', 0);
3652
        }
3653
        return $this->update_queue;
3654
    }
3655
3656
    /**
3657
     * Gets the user ID
3658
     * @return	integer	User ID
3659
     */
3660 View Code Duplication
    public function get_user_id()
3661
    {
3662
        if ($this->debug > 0) {
3663
            error_log('New LP - In learnpath::get_user_id()', 0);
3664
        }
3665
        if (!empty ($this->user_id)) {
3666
            return $this->user_id;
3667
        } else {
3668
            return false;
3669
        }
3670
    }
3671
3672
    /**
3673
     * Checks if any of the items has an audio element attached
3674
     * @return  bool    True or false
3675
     */
3676
    public function has_audio()
3677
    {
3678
        if ($this->debug > 1) {
3679
            error_log('New LP - In learnpath::has_audio()', 0);
3680
        }
3681
        $has = false;
3682
        foreach ($this->items as $i => $item) {
3683
            if (!empty ($this->items[$i]->audio)) {
3684
                $has = true;
3685
                break;
3686
            }
3687
        }
3688
        return $has;
3689
    }
3690
3691
    /**
3692
     * Logs a message into a file
3693
     * @param	string 	Message to log
3694
     * @return	boolean	True on success, false on error or if msg empty
3695
     */
3696
    public function log($msg)
3697
    {
3698
        if ($this->debug > 0) {
3699
            error_log('New LP - In learnpath::log()', 0);
3700
        }
3701
        // TODO
3702
        $this->error .= $msg;
3703
        return true;
3704
    }
3705
3706
    /**
3707
     * Moves an item up and down at its level
3708
     * @param	integer	Item to move up and down
3709
     * @param	string	Direction 'up' or 'down'
3710
     * @return	integer	New display order, or false on error
3711
     */
3712
    public function move_item($id, $direction)
3713
    {
3714
        $course_id = api_get_course_int_id();
3715
        if ($this->debug > 0) {
3716
            error_log('New LP - In learnpath::move_item('.$id.','.$direction.')', 0);
3717
        }
3718
        if (empty($id) || empty($direction)) {
3719
            return false;
3720
        }
3721
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
3722
        $sql_sel = "SELECT *
3723
                    FROM " . $tbl_lp_item."
3724
                    WHERE c_id = ".$course_id." AND id = ".$id;
3725
        $res_sel = Database::query($sql_sel);
3726
        // Check if elem exists.
3727
        if (Database::num_rows($res_sel) < 1) {
3728
            return false;
3729
        }
3730
        // Gather data.
3731
        $row = Database::fetch_array($res_sel);
3732
        $previous = $row['previous_item_id'];
3733
        $next = $row['next_item_id'];
3734
        $display = $row['display_order'];
3735
        $parent = $row['parent_item_id'];
3736
        $lp = $row['lp_id'];
3737
        // Update the item (switch with previous/next one).
3738
        switch ($direction) {
3739
            case 'up':
3740
                if ($this->debug > 2) {
3741
                    error_log('Movement up detected', 0);
3742
                }
3743
                if ($display > 1) {
3744
                    $sql_sel2 = "SELECT * FROM $tbl_lp_item
3745
                                 WHERE c_id = ".$course_id." AND id = $previous";
3746
3747
                    if ($this->debug > 2) {
3748
                        error_log('Selecting previous: '.$sql_sel2, 0);
3749
                    }
3750
                    $res_sel2 = Database::query($sql_sel2);
3751
                    if (Database::num_rows($res_sel2) < 1) {
3752
                        $previous_previous = 0;
3753
                    }
3754
                    // Gather data.
3755
                    $row2 = Database::fetch_array($res_sel2);
3756
                    $previous_previous = $row2['previous_item_id'];
3757
                    // Update previous_previous item (switch "next" with current).
3758 View Code Duplication
                    if ($previous_previous != 0) {
3759
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3760
                                        next_item_id = $id
3761
                                    WHERE c_id = ".$course_id." AND id = $previous_previous";
3762
                        if ($this->debug > 2) {
3763
                            error_log($sql_upd2, 0);
3764
                        }
3765
                        Database::query($sql_upd2);
3766
                    }
3767
                    // Update previous item (switch with current).
3768 View Code Duplication
                    if ($previous != 0) {
3769
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3770
                                    next_item_id = $next,
3771
                                    previous_item_id = $id,
3772
                                    display_order = display_order +1
3773
                                    WHERE c_id = ".$course_id." AND id = $previous";
3774
                        if ($this->debug > 2) {
3775
                            error_log($sql_upd2, 0);
3776
                        }
3777
                        Database::query($sql_upd2);
3778
                    }
3779
3780
                    // Update current item (switch with previous).
3781 View Code Duplication
                    if ($id != 0) {
3782
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3783
                                        next_item_id = $previous,
3784
                                        previous_item_id = $previous_previous,
3785
                                        display_order = display_order-1
3786
                                    WHERE c_id = ".$course_id." AND id = $id";
3787
                        if ($this->debug > 2) {
3788
                            error_log($sql_upd2, 0);
3789
                        }
3790
                        Database::query($sql_upd2);
3791
                    }
3792
                    // Update next item (new previous item).
3793 View Code Duplication
                    if ($next != 0) {
3794
                        $sql_upd2 = "UPDATE $tbl_lp_item SET previous_item_id = $previous
3795
                                     WHERE c_id = ".$course_id." AND id = $next";
3796
                        if ($this->debug > 2) {
3797
                            error_log($sql_upd2, 0);
3798
                        }
3799
                        Database::query($sql_upd2);
3800
                    }
3801
                    $display = $display - 1;
3802
                }
3803
                break;
3804
            case 'down':
3805
                if ($this->debug > 2) {
3806
                    error_log('Movement down detected', 0);
3807
                }
3808
                if ($next != 0) {
3809
                    $sql_sel2 = "SELECT * FROM $tbl_lp_item WHERE c_id = ".$course_id." AND id = $next";
3810
                    if ($this->debug > 2) {
3811
                        error_log('Selecting next: '.$sql_sel2, 0);
3812
                    }
3813
                    $res_sel2 = Database::query($sql_sel2);
3814
                    if (Database::num_rows($res_sel2) < 1) {
3815
                        $next_next = 0;
3816
                    }
3817
                    // Gather data.
3818
                    $row2 = Database::fetch_array($res_sel2);
3819
                    $next_next = $row2['next_item_id'];
3820
                    // Update previous item (switch with current).
3821 View Code Duplication
                    if ($previous != 0) {
3822
                        $sql_upd2 = "UPDATE $tbl_lp_item SET next_item_id = $next
3823
                                     WHERE c_id = ".$course_id." AND id = $previous";
3824
                        Database::query($sql_upd2);
3825
                    }
3826
                    // Update current item (switch with previous).
3827 View Code Duplication
                    if ($id != 0) {
3828
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3829
                                     previous_item_id = $next, next_item_id = $next_next, display_order = display_order+1
3830
                                     WHERE c_id = ".$course_id." AND id = $id";
3831
                        Database::query($sql_upd2);
3832
                    }
3833
3834
                    // Update next item (new previous item).
3835 View Code Duplication
                    if ($next != 0) {
3836
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3837
                                     previous_item_id = $previous, next_item_id = $id, display_order = display_order-1
3838
                                     WHERE c_id = ".$course_id." AND id = $next";
3839
                        Database::query($sql_upd2);
3840
                    }
3841
3842
                    // Update next_next item (switch "previous" with current).
3843 View Code Duplication
                    if ($next_next != 0) {
3844
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3845
                                     previous_item_id = $id
3846
                                     WHERE c_id = ".$course_id." AND id = $next_next";
3847
                        Database::query($sql_upd2);
3848
                    }
3849
                    $display = $display + 1;
3850
                }
3851
                break;
3852
            default:
3853
                return false;
3854
        }
3855
        return $display;
3856
    }
3857
3858
    /**
3859
     * Move a LP up (display_order)
3860
     * @param int $lp_id Learnpath ID
3861
     * @param int $categoryId
3862
     * @return bool
3863
     */
3864
    public static function move_up($lp_id, $categoryId = 0)
3865
    {
3866
        $courseId = api_get_course_int_id();
3867
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3868
3869
        $categoryCondition = '';
3870
        if (!empty($categoryId)) {
3871
            $categoryId = (int) $categoryId;
3872
            $categoryCondition = " AND category_id = $categoryId";
3873
        }
3874
        $sql = "SELECT * FROM $lp_table
3875
                WHERE c_id = $courseId
3876
                $categoryCondition
3877
                ORDER BY display_order";
3878
        $res = Database::query($sql);
3879
        if ($res === false) {
3880
            return false;
3881
        }
3882
3883
        $lps = [];
3884
        $lp_order = [];
3885
        $num = Database::num_rows($res);
3886
        // First check the order is correct, globally (might be wrong because
3887
        // of versions < 1.8.4)
3888
        if ($num > 0) {
3889
            $i = 1;
3890
            while ($row = Database::fetch_array($res)) {
3891 View Code Duplication
                if ($row['display_order'] != $i) { // If we find a gap in the order, we need to fix it.
3892
                    $sql = "UPDATE $lp_table SET display_order = $i
3893
                            WHERE c_id = $courseId AND id = ".$row['id'];
3894
                    Database::query($sql);
3895
                }
3896
                $row['display_order'] = $i;
3897
                $lps[$row['id']] = $row;
3898
                $lp_order[$i] = $row['id'];
3899
                $i++;
3900
            }
3901
        }
3902
        if ($num > 1) { // If there's only one element, no need to sort.
3903
            $order = $lps[$lp_id]['display_order'];
3904 View Code Duplication
            if ($order > 1) { // If it's the first element, no need to move up.
3905
                $sql = "UPDATE $lp_table SET display_order = $order
3906
                        WHERE c_id = $courseId AND id = ".$lp_order[$order - 1];
3907
                Database::query($sql);
3908
                $sql = "UPDATE $lp_table SET display_order = ".($order - 1)."
3909
                        WHERE c_id = $courseId AND id = ".$lp_id;
3910
                Database::query($sql);
3911
            }
3912
        }
3913
3914
        return true;
3915
    }
3916
3917
    /**
3918
     * Move a learnpath down (display_order)
3919
     * @param int $lp_id Learnpath ID
3920
     * @param int $categoryId
3921
     * @return bool
3922
     */
3923
    public static function move_down($lp_id, $categoryId = 0)
3924
    {
3925
        $courseId = api_get_course_int_id();
3926
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3927
3928
        $categoryCondition = '';
3929
        if (!empty($categoryId)) {
3930
            $categoryId = (int) $categoryId;
3931
            $categoryCondition = " AND category_id = $categoryId";
3932
        }
3933
3934
        $sql = "SELECT * FROM $lp_table
3935
                WHERE c_id = $courseId
3936
                $categoryCondition
3937
                ORDER BY display_order";
3938
        $res = Database::query($sql);
3939
        if ($res === false) {
3940
            return false;
3941
        }
3942
        $lps = [];
3943
        $lp_order = [];
3944
        $num = Database::num_rows($res);
3945
        $max = 0;
3946
        // First check the order is correct, globally (might be wrong because
3947
        // of versions < 1.8.4).
3948
        if ($num > 0) {
3949
            $i = 1;
3950
            while ($row = Database::fetch_array($res)) {
3951
                $max = $i;
3952 View Code Duplication
                if ($row['display_order'] != $i) { // If we find a gap in the order, we need to fix it.
3953
                    $need_fix = true;
3954
                    $sql_u = "UPDATE $lp_table SET display_order = $i
3955
                              WHERE c_id = ".$courseId." AND id = ".$row['id'];
3956
                    Database::query($sql_u);
3957
                }
3958
                $row['display_order'] = $i;
3959
                $lps[$row['id']] = $row;
3960
                $lp_order[$i] = $row['id'];
3961
                $i++;
3962
            }
3963
        }
3964
        if ($num > 1) { // If there's only one element, no need to sort.
3965
            $order = $lps[$lp_id]['display_order'];
3966
            if ($order < $max) { // If it's the first element, no need to move up.
3967
                $sql_u1 = "UPDATE $lp_table SET display_order = $order
3968
                           WHERE c_id = ".$courseId." AND id = ".$lp_order[$order + 1];
3969
                Database::query($sql_u1);
3970
                $sql_u2 = "UPDATE $lp_table SET display_order = ".($order + 1)."
3971
                           WHERE c_id = ".$courseId." AND id = ".$lp_id;
3972
                Database::query($sql_u2);
3973
            }
3974
        }
3975
3976
        return true;
3977
    }
3978
3979
    /**
3980
     * Updates learnpath attributes to point to the next element
3981
     * The last part is similar to set_current_item but processing the other way around
3982
     */
3983
    public function next()
3984
    {
3985
        if ($this->debug > 0) {
3986
            error_log('New LP - In learnpath::next()', 0);
3987
        }
3988
        $this->last = $this->get_current_item_id();
3989
        $this->items[$this->last]->save(false, $this->prerequisites_match($this->last));
3990
        $this->autocomplete_parents($this->last);
3991
        $new_index = $this->get_next_index();
3992
        if ($this->debug > 2) {
3993
            error_log('New LP - New index: '.$new_index, 0);
3994
        }
3995
        $this->index = $new_index;
3996
        if ($this->debug > 2) {
3997
            error_log('New LP - Now having orderedlist['.$new_index.'] = '.$this->ordered_items[$new_index], 0);
3998
        }
3999
        $this->current = $this->ordered_items[$new_index];
4000
        if ($this->debug > 2) {
4001
            error_log('New LP - new item id is '.$this->current.'-'.$this->get_current_item_id(), 0);
4002
        }
4003
    }
4004
4005
    /**
4006
     * Open a resource = initialise all local variables relative to this resource. Depending on the child
4007
     * class, this might be redefined to allow several behaviours depending on the document type.
4008
     * @param integer Resource ID
4009
     * @return boolean True on success, false otherwise
4010
     */
4011
    public function open($id)
4012
    {
4013
        if ($this->debug > 0) {
4014
            error_log('New LP - In learnpath::open()', 0);
4015
        }
4016
        // TODO:
4017
        // set the current resource attribute to this resource
4018
        // switch on element type (redefine in child class?)
4019
        // set status for this item to "opened"
4020
        // start timer
4021
        // initialise score
4022
        $this->index = 0; //or = the last item seen (see $this->last)
4023
    }
4024
4025
    /**
4026
     * Check that all prerequisites are fulfilled. Returns true and an
4027
     * empty string on success, returns false
4028
     * and the prerequisite string on error.
4029
     * This function is based on the rules for aicc_script language as
4030
     * described in the SCORM 1.2 CAM documentation page 108.
4031
     * @param	integer	$itemId Optional item ID. If none given, uses the current open item.
4032
     * @return	boolean	True if prerequisites are matched, false otherwise -
4033
     * Empty string if true returned, prerequisites string otherwise.
4034
     */
4035
    public function prerequisites_match($itemId = null)
4036
    {
4037
        $debug = $this->debug;
4038
        if ($debug > 0) {
4039
            error_log('In learnpath::prerequisites_match()', 0);
4040
        }
4041
4042
        if (empty($itemId)) {
4043
            $itemId = $this->current;
4044
        }
4045
4046
        $currentItem = $this->getItem($itemId);
4047
4048
        if ($currentItem) {
4049
            if ($this->type == 2) {
4050
                // Getting prereq from scorm
4051
                $prereq_string = $this->get_scorm_prereq_string($itemId);
4052
            } else {
4053
                $prereq_string = $currentItem->get_prereq_string();
4054
            }
4055
4056
            if (empty($prereq_string)) {
4057
                if ($debug > 0) {
4058
                    error_log('Found prereq_string is empty return true');
4059
                }
4060
                return true;
4061
            }
4062
            // Clean spaces.
4063
            $prereq_string = str_replace(' ', '', $prereq_string);
4064
            if ($debug > 0) {
4065
                error_log('Found prereq_string: '.$prereq_string, 0);
4066
            }
4067
            // Now send to the parse_prereq() function that will check this component's prerequisites.
4068
            $result = $currentItem->parse_prereq(
4069
                $prereq_string,
4070
                $this->items,
4071
                $this->refs_list,
4072
                $this->get_user_id()
4073
            );
4074
4075
            if ($result === false) {
4076
                $this->set_error_msg($currentItem->prereq_alert);
4077
            }
4078
        } else {
4079
            $result = true;
4080
            if ($debug > 1) {
4081
                error_log('$this->items['.$itemId.'] was not an object', 0);
4082
            }
4083
        }
4084
4085
        if ($debug > 1) {
4086
            error_log('End of prerequisites_match(). Error message is now '.$this->error, 0);
4087
        }
4088
        return $result;
4089
    }
4090
4091
    /**
4092
     * Updates learnpath attributes to point to the previous element
4093
     * The last part is similar to set_current_item but processing the other way around
4094
     */
4095
    public function previous()
4096
    {
4097
        if ($this->debug > 0) {
4098
            error_log('New LP - In learnpath::previous()', 0);
4099
        }
4100
        $this->last = $this->get_current_item_id();
4101
        $this->items[$this->last]->save(false, $this->prerequisites_match($this->last));
4102
        $this->autocomplete_parents($this->last);
4103
        $new_index = $this->get_previous_index();
4104
        $this->index = $new_index;
4105
        $this->current = $this->ordered_items[$new_index];
4106
    }
4107
4108
    /**
4109
     * Publishes a learnpath. This basically means show or hide the learnpath
4110
     * to normal users.
4111
     * Can be used as abstract
4112
     * @param	integer	Learnpath ID
4113
     * @param	string	New visibility
4114
     */
4115
    public static function toggle_visibility($lp_id, $set_visibility = 1)
4116
    {
4117
        $action = 'visible';
4118
        if ($set_visibility != 1) {
4119
            $action = 'invisible';
4120
            self::toggle_publish($lp_id, 'i');
4121
        }
4122
4123
        return api_item_property_update(
4124
            api_get_course_info(),
4125
            TOOL_LEARNPATH,
4126
            $lp_id,
4127
            $action,
4128
            api_get_user_id()
4129
        );
4130
    }
4131
4132
    /**
4133
     * Publishes a learnpath category.
4134
     * This basically means show or hide the learnpath category to normal users.
4135
     * @param int $id
4136
     * @param int $visibility
4137
     * @return bool
4138
     */
4139
    public static function toggleCategoryVisibility($id, $visibility = 1)
4140
    {
4141
        $action = 'visible';
4142
4143
        if ($visibility != 1) {
4144
            $action = 'invisible';
4145
4146
            $list = new LearnpathList(api_get_user_id(), null, null, null, false, $id);
4147
4148
            $learPaths = $list->get_flat_list();
4149
4150
            foreach ($learPaths as $lp) {
4151
                learnpath::toggle_visibility($lp['iid'], 0);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
4152
            }
4153
4154
            learnpath::toggleCategoryPublish($id, 0);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
4155
        }
4156
4157
        return api_item_property_update(
4158
            api_get_course_info(),
4159
            TOOL_LEARNPATH_CATEGORY,
4160
            $id,
4161
            $action,
4162
            api_get_user_id()
4163
        );
4164
    }
4165
4166
    /**
4167
     * Publishes a learnpath. This basically means show or hide the learnpath
4168
     * on the course homepage
4169
     * Can be used as abstract
4170
     * @param	integer	$lp_id Learnpath id
4171
     * @param	string	$set_visibility New visibility (v/i - visible/invisible)
4172
     * @return bool
4173
     */
4174
    public static function toggle_publish($lp_id, $set_visibility = 'v')
4175
    {
4176
        $course_id = api_get_course_int_id();
4177
        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
4178
        $lp_id = intval($lp_id);
4179
        $sql = "SELECT * FROM $tbl_lp
4180
                WHERE c_id = $course_id AND id = $lp_id";
4181
        $result = Database::query($sql);
4182
        if (Database::num_rows($result)) {
4183
            $row = Database::fetch_array($result);
4184
            $name = Database::escape_string($row['name']);
4185
            if ($set_visibility == 'i') {
4186
                $v = 0;
4187
            }
4188
            if ($set_visibility == 'v') {
4189
                $v = 1;
4190
            }
4191
4192
            $session_id = api_get_session_id();
4193
            $session_condition = api_get_session_condition($session_id);
4194
4195
            $tbl_tool = Database::get_course_table(TABLE_TOOL_LIST);
4196
            $link = 'lp/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session='.$session_id;
4197
            $oldLink = 'newscorm/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session='.$session_id;
4198
4199
            $sql = "SELECT * FROM $tbl_tool
4200
                    WHERE
4201
                        c_id = $course_id AND
4202
                        (link = '$link' OR link = '$oldLink') AND
4203
                        image = 'scormbuilder.gif' AND
4204
                        (
4205
                            link LIKE '$link%' OR
4206
                            link LIKE '$oldLink%'
4207
                        )
4208
                        $session_condition
4209
                    ";
4210
4211
            $result = Database::query($sql);
4212
            $num = Database::num_rows($result);
4213
            if ($set_visibility == 'i' && $num > 0) {
4214
                $sql = "DELETE FROM $tbl_tool
4215
                        WHERE 
4216
                            c_id = $course_id AND 
4217
                            (link = '$link' OR link = '$oldLink') AND 
4218
                            image='scormbuilder.gif' 
4219
                            $session_condition";
4220
                Database::query($sql);
4221
            } elseif ($set_visibility == 'v' && $num == 0) {
4222
                $sql = "INSERT INTO $tbl_tool (category, c_id, name, link, image, visibility, admin, address, added_tool, session_id) VALUES
4223
                        ('authoring', $course_id, '$name', '$link', 'scormbuilder.gif', '$v', '0','pastillegris.gif', 0, $session_id)";
4224
                Database::query($sql);
4225
                $insertId = Database::insert_id();
4226
                if ($insertId) {
4227
                    $sql = "UPDATE $tbl_tool SET id = iid WHERE iid = $insertId";
4228
                    Database::query($sql);
4229
                }
4230
            } elseif ($set_visibility == 'v' && $num > 0) {
4231
                $sql = "UPDATE $tbl_tool SET
4232
                            c_id = $course_id,
4233
                            name = '$name',
4234
                            link = '$link',
4235
                            image = 'scormbuilder.gif',
4236
                            visibility = '$v',
4237
                            admin = '0',
4238
                            address = 'pastillegris.gif',
4239
                            added_tool = 0,
4240
                            session_id = $session_id
4241
                        WHERE
4242
                            c_id = ".$course_id." AND
4243
                            (link = '$link' OR link = '$oldLink') AND 
4244
                            image='scormbuilder.gif' 
4245
                            $session_condition
4246
                        ";
4247
                Database::query($sql);
4248
            } else {
4249
                // Parameter and database incompatible, do nothing, exit.
4250
                return false;
4251
            }
4252
        } else {
4253
            return false;
4254
        }
4255
    }
4256
4257
    /**
4258
     * Generate the link for a learnpath category as course tool
4259
     * @param int $categoryId
4260
     * @return string
4261
     */
4262
    private static function getCategoryLinkForTool($categoryId)
4263
    {
4264
        $link = 'lp/lp_controller.php?'.api_get_cidreq().'&'
4265
            .http_build_query(
4266
                [
4267
                    'action' => 'view_category',
4268
                    'id' => $categoryId
4269
                ]
4270
            );
4271
4272
        return $link;
4273
    }
4274
4275
    /**
4276
     * Publishes a learnpath.
4277
     * Show or hide the learnpath category on the course homepage
4278
     * @param int $id
4279
     * @param int $setVisibility
4280
     * @return bool
4281
     */
4282
    public static function toggleCategoryPublish($id, $setVisibility = 1)
4283
    {
4284
        $courseId = api_get_course_int_id();
4285
        $sessionId = api_get_session_id();
4286
        $sessionCondition = api_get_session_condition($sessionId, true, false, 't.sessionId');
4287
        
4288
        $em = Database::getManager();
4289
4290
        /** @var CLpCategory $category */
4291
        $category = $em->find('ChamiloCourseBundle:CLpCategory', $id);
4292
4293
        if (!$category) {
4294
            return false;
4295
        }
4296
4297
        $link = self::getCategoryLinkForTool($id);
4298
4299
        /** @var CTool $tool */
4300
        $tool = $em
4301
            ->createQuery("
4302
                SELECT t FROM ChamiloCourseBundle:CTool t
4303
                WHERE
4304
                    t.cId = :course AND
4305
                    t.link = :link1 AND
4306
                    t.image = 'lp_category.gif' AND
4307
                    t.link LIKE :link2
4308
                    $sessionCondition
4309
            ")
4310
            ->setParameters([
4311
                'course' => (int) $courseId,
4312
                'link1' => $link,
4313
                'link2' => "$link%"
4314
            ])
4315
            ->getOneOrNullResult();
4316
4317
        if ($setVisibility == 0 && $tool) {
4318
            $em->remove($tool);
4319
            $em->flush();
4320
4321
            return true;
4322
        }
4323
4324
        if ($setVisibility == 1 && !$tool) {
4325
            $tool = new CTool();
4326
            $tool
4327
                ->setCategory('authoring')
4328
                ->setCId($courseId)
4329
                ->setName(strip_tags($category->getName()))
4330
                ->setLink($link)
4331
                ->setImage('lp_category.gif')
4332
                ->setVisibility(1)
4333
                ->setAdmin(0)
4334
                ->setAddress('pastillegris.gif')
4335
                ->setAddedTool(0)
4336
                ->setSessionId($sessionId)
4337
                ->setTarget('_self');
4338
4339
            $em->persist($tool);
4340
            $em->flush();
4341
4342
            $tool->setId($tool->getIid());
4343
4344
            $em->persist($tool);
4345
            $em->flush();
4346
4347
            return true;
4348
        }
4349
4350
        if ($setVisibility == 1 && $tool) {
4351
            $tool
4352
                ->setName(strip_tags($category->getName()))
4353
                ->setVisibility(1);
4354
4355
            $em->persist($tool);
4356
            $em->flush();
4357
4358
            return true;
4359
        }
4360
4361
        return false;
4362
    }
4363
4364
    /**
4365
     * Check if the learnpath category is visible for a user
4366
     * @param CLpCategory $category
4367
     * @param User $user
4368
     * @return bool
4369
     */
4370
    public static function categoryIsVisibleForStudent(
4371
        CLpCategory $category,
4372
        User $user
4373
    )
4374
    {
4375
        $isAllowedToEdit = api_is_allowed_to_edit(null, true);
4376
4377
        if ($isAllowedToEdit) {
4378
            return true;
4379
        }
4380
4381
        $users = $category->getUsers();
4382
4383
        if (empty($users) || !$users->count()) {
4384
            return true;
4385
        }
4386
4387
        if ($category->hasUserAdded($user)) {
4388
            return true;
4389
        }
4390
4391
        return false;
4392
    }
4393
4394
    /**
4395
     * Check if a learnpath category is published as course tool
4396
     * @param CLpCategory $category
4397
     * @param int $courseId
4398
     * @return bool
4399
     */
4400
    public static function categoryIsPusblished(
4401
        CLpCategory $category,
4402
        $courseId
4403
    )
4404
    {
4405
        $link = self::getCategoryLinkForTool($category->getId());
4406
        $em = Database::getManager();
4407
4408
        $tools = $em
4409
            ->createQuery("
4410
                SELECT t FROM ChamiloCourseBundle:CTool t
4411
                WHERE t.cId = :course AND 
4412
                    t.name = :name AND
4413
                    t.image = 'lp_category.gif' AND
4414
                    t.link LIKE :link
4415
            ")
4416
            ->setParameters([
4417
                'course' => $courseId,
4418
                'name' => strip_tags($category->getName()),
4419
                'link' => "$link%"
4420
            ])
4421
            ->getResult();
4422
4423
        /** @var CTool $tool */
4424
        $tool = current($tools);
4425
4426
        return $tool ? $tool->getVisibility() : false;
4427
    }
4428
4429
    /**
4430
     * Restart the whole learnpath. Return the URL of the first element.
4431
     * Make sure the results are saved with anoter method. This method should probably be
4432
     * redefined in children classes.
4433
     * To use a similar method  statically, use the create_new_attempt() method
4434
     * @return string URL to load in the viewer
4435
     */
4436
    public function restart()
4437
    {
4438
        if ($this->debug > 0) {
4439
            error_log('New LP - In learnpath::restart()', 0);
4440
        }
4441
        // TODO
4442
        // Call autosave method to save the current progress.
4443
        //$this->index = 0;
4444
        if (api_is_invitee()) {
4445
            return false;
4446
        }
4447
        $session_id = api_get_session_id();
4448
        $course_id = api_get_course_int_id();
4449
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
4450
        $sql = "INSERT INTO $lp_view_table (c_id, lp_id, user_id, view_count, session_id)
4451
                VALUES ($course_id, ".$this->lp_id.",".$this->get_user_id().",".($this->attempt + 1).", $session_id)";
4452
        if ($this->debug > 2) {
4453
            error_log('New LP - Inserting new lp_view for restart: '.$sql, 0);
4454
        }
4455
        Database::query($sql);
4456
        $view_id = Database::insert_id();
4457
4458
        if ($view_id) {
4459
            $sql = "UPDATE $lp_view_table SET id = iid WHERE iid = $view_id";
4460
            Database::query($sql);
4461
            $this->lp_view_id = $view_id;
4462
            $this->attempt = $this->attempt + 1;
4463
        } else {
4464
            $this->error = 'Could not insert into item_view table...';
4465
            return false;
4466
        }
4467
        $this->autocomplete_parents($this->current);
4468
        foreach ($this->items as $index => $dummy) {
4469
            $this->items[$index]->restart();
4470
            $this->items[$index]->set_lp_view($this->lp_view_id);
4471
        }
4472
        $this->first();
4473
4474
        return true;
4475
    }
4476
4477
    /**
4478
     * Saves the current item
4479
     * @return	boolean
4480
     */
4481
    public function save_current()
4482
    {
4483
        if ($this->debug > 0) {
4484
            error_log('learnpath::save_current()', 0);
4485
        }
4486
        // TODO: Do a better check on the index pointing to the right item (it is supposed to be working
4487
        // on $ordered_items[] but not sure it's always safe to use with $items[]).
4488
        if ($this->debug > 2) {
4489
            error_log('New LP - save_current() saving item '.$this->current, 0);
4490
        }
4491
        if ($this->debug > 2) {
4492
            error_log(''.print_r($this->items, true), 0);
4493
        }
4494
        if (isset($this->items[$this->current]) &&
4495
            is_object($this->items[$this->current])
4496
        ) {
4497
            $res = $this->items[$this->current]->save(false, $this->prerequisites_match($this->current));
4498
            $this->autocomplete_parents($this->current);
4499
            $status = $this->items[$this->current]->get_status();
4500
            $this->update_queue[$this->current] = $status;
4501
            return $res;
4502
        }
4503
        return false;
4504
    }
4505
4506
    /**
4507
     * Saves the given item
4508
     * @param	integer	$item_id Optional (will take from $_REQUEST if null)
4509
     * @param	boolean	$from_outside Save from url params (true) or from current attributes (false). Optional. Defaults to true
4510
     * @return	boolean
4511
     */
4512
    public function save_item($item_id = null, $from_outside = true)
4513
    {
4514
        $debug = $this->debug;
4515
        if ($debug) {
4516
            error_log('In learnpath::save_item('.$item_id.','.intval($from_outside).')', 0);
4517
        }
4518
        // TODO: Do a better check on the index pointing to the right item (it is supposed to be working
4519
        // on $ordered_items[] but not sure it's always safe to use with $items[]).
4520
        if (empty($item_id)) {
4521
            $item_id = intval($_REQUEST['id']);
4522
        }
4523
        if (empty($item_id)) {
4524
            $item_id = $this->get_current_item_id();
4525
        }
4526
        if (isset($this->items[$item_id]) && is_object($this->items[$item_id])) {
4527
            if ($debug) {
4528
                error_log('Object exists');
4529
            }
4530
4531
            // Saving the item.
4532
            $res = $this->items[$item_id]->save(
4533
                $from_outside,
4534
                $this->prerequisites_match($item_id)
4535
            );
4536
4537
            if ($debug) {
4538
                error_log('update_queue before:');
4539
                error_log(print_r($this->update_queue, 1));
4540
            }
4541
            $this->autocomplete_parents($item_id);
4542
4543
            $status = $this->items[$item_id]->get_status();
4544
            $this->update_queue[$item_id] = $status;
4545
4546
            if ($debug) {
4547
                error_log('get_status(): '.$status);
4548
                error_log('update_queue after:');
4549
                error_log(print_r($this->update_queue, 1));
4550
            }
4551
            return $res;
4552
        }
4553
        return false;
4554
    }
4555
4556
    /**
4557
     * Saves the last item seen's ID only in case
4558
     */
4559
    public function save_last()
4560
    {
4561
        $course_id = api_get_course_int_id();
4562
        if ($this->debug > 0) {
4563
            error_log('New LP - In learnpath::save_last()', 0);
4564
        }
4565
        $session_condition = api_get_session_condition(api_get_session_id(), true, false);
4566
        $table = Database::get_course_table(TABLE_LP_VIEW);
4567
4568
        if (isset($this->current) && !api_is_invitee()) {
4569
            if ($this->debug > 2) {
4570
                error_log('New LP - Saving current item ('.$this->current.') for later review', 0);
4571
            }
4572
            $sql = "UPDATE $table SET
4573
                        last_item = ".intval($this->get_current_item_id())."
4574
                    WHERE
4575
                        c_id = $course_id AND
4576
                        lp_id = ".$this->get_id()." AND
4577
                        user_id = " . $this->get_user_id()." ".$session_condition;
4578
4579
            if ($this->debug > 2) {
4580
                error_log('New LP - Saving last item seen : '.$sql, 0);
4581
            }
4582
            Database::query($sql);
4583
        }
4584
4585
        if (!api_is_invitee()) {
4586
            // Save progress.
4587
            list($progress,) = $this->get_progress_bar_text('%');
4588
            if ($progress >= 0 && $progress <= 100) {
4589
                $progress = (int) $progress;
4590
                $sql = "UPDATE $table SET
4591
                            progress = $progress
4592
                        WHERE
4593
                            c_id = ".$course_id." AND
4594
                            lp_id = " . $this->get_id()." AND
4595
                            user_id = " . $this->get_user_id()." ".$session_condition;
4596
                // Ignore errors as some tables might not have the progress field just yet.
4597
                Database::query($sql);
4598
                $this->progress_db = $progress;
4599
            }
4600
        }
4601
    }
4602
4603
    /**
4604
     * Sets the current item ID (checks if valid and authorized first)
4605
     * @param	integer	$item_id New item ID. If not given or not authorized, defaults to current
4606
     */
4607
    public function set_current_item($item_id = null)
4608
    {
4609
        if ($this->debug > 0) {
4610
            error_log('New LP - In learnpath::set_current_item('.$item_id.')', 0);
4611
        }
4612
        if (empty ($item_id)) {
4613
            if ($this->debug > 2) {
4614
                error_log('New LP - No new current item given, ignore...', 0);
4615
            }
4616
            // Do nothing.
4617
        } else {
4618
            if ($this->debug > 2) {
4619
                error_log('New LP - New current item given is '.$item_id.'...', 0);
4620
            }
4621
            if (is_numeric($item_id)) {
4622
                $item_id = intval($item_id);
4623
                // TODO: Check in database here.
4624
                $this->last = $this->current;
4625
                $this->current = $item_id;
4626
                // TODO: Update $this->index as well.
4627
                foreach ($this->ordered_items as $index => $item) {
4628
                    if ($item == $this->current) {
4629
                        $this->index = $index;
4630
                        break;
4631
                    }
4632
                }
4633 View Code Duplication
                if ($this->debug > 2) {
4634
                    error_log('New LP - set_current_item('.$item_id.') done. Index is now : '.$this->index, 0);
4635
                }
4636
            } else {
4637
                error_log('New LP - set_current_item('.$item_id.') failed. Not a numeric value: ', 0);
4638
            }
4639
        }
4640
    }
4641
4642
    /**
4643
     * Sets the encoding
4644
     * @param	string	New encoding
4645
     * @return bool
4646
     * TODO (as of Chamilo 1.8.8): Check in the future whether this method is needed.
4647
     */
4648
    public function set_encoding($enc = 'UTF-8')
4649
    {
4650
        if ($this->debug > 0) {
4651
            error_log('New LP - In learnpath::set_encoding()', 0);
4652
        }
4653
4654
        $course_id = api_get_course_int_id();
4655
        $enc = api_refine_encoding_id($enc);
4656
        if (empty($enc)) {
4657
            $enc = api_get_system_encoding();
4658
        }
4659
        if (api_is_encoding_supported($enc)) {
0 ignored issues
show
Bug introduced by
It seems like $enc defined by api_refine_encoding_id($enc) on line 4655 can also be of type array; however, api_is_encoding_supported() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
4660
            $lp = $this->get_id();
4661
            if ($lp != 0) {
4662
                $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
4663
                $sql = "UPDATE $tbl_lp SET default_encoding = '$enc' 
4664
                        WHERE c_id = ".$course_id." AND id = ".$lp;
4665
                $res = Database::query($sql);
4666
                return $res;
4667
            }
4668
        }
4669
        return false;
4670
    }
4671
4672
    /**
4673
     * Sets the JS lib setting in the database directly.
4674
     * This is the JavaScript library file this lp needs to load on startup
4675
     * @param	string	Proximity setting
4676
     * @return  boolean True on update success. False otherwise.
4677
     */
4678 View Code Duplication
    public function set_jslib($lib = '')
4679
    {
4680
        if ($this->debug > 0) {
4681
            error_log('New LP - In learnpath::set_jslib()', 0);
4682
        }
4683
        $lp = $this->get_id();
4684
        $course_id = api_get_course_int_id();
4685
4686
        if ($lp != 0) {
4687
            $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
4688
            $sql = "UPDATE $tbl_lp SET js_lib = '$lib' 
4689
                    WHERE c_id = ".$course_id." AND id = ".$lp;
4690
            $res = Database::query($sql);
4691
            return $res;
4692
        } else {
4693
            return false;
4694
        }
4695
    }
4696
4697
    /**
4698
     * Sets the name of the LP maker (publisher) (and save)
4699
     * @param	string	Optional string giving the new content_maker of this learnpath
4700
     * @return  boolean True
4701
     */
4702
    public function set_maker($name = '')
4703
    {
4704
        if ($this->debug > 0) {
4705
            error_log('New LP - In learnpath::set_maker()', 0);
4706
        }
4707
        if (empty ($name))
4708
            return false;
4709
        $this->maker = $name;
4710
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4711
        $course_id = api_get_course_int_id();
4712
        $lp_id = $this->get_id();
4713
        $sql = "UPDATE $lp_table SET
4714
                content_maker = '".Database::escape_string($this->maker)."'
4715
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4716
        if ($this->debug > 2) {
4717
            error_log('New LP - lp updated with new content_maker : '.$this->maker, 0);
4718
        }
4719
        Database::query($sql);
4720
        return true;
4721
    }
4722
4723
    /**
4724
     * Sets the name of the current learnpath (and save)
4725
     * @param	string	$name Optional string giving the new name of this learnpath
4726
     * @return  boolean True/False
4727
     */
4728
    public function set_name($name = null)
4729
    {
4730
        if ($this->debug > 0) {
4731
            error_log('New LP - In learnpath::set_name()', 0);
4732
        }
4733
        if (empty($name)) {
4734
            return false;
4735
        }
4736
        $this->name = Database::escape_string($name);
4737
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4738
        $lp_id = $this->get_id();
4739
        $course_id = $this->course_info['real_id'];
4740
        $sql = "UPDATE $lp_table SET
4741
                name = '".Database::escape_string($this->name)."'
4742
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4743
        if ($this->debug > 2) {
4744
            error_log('New LP - lp updated with new name : '.$this->name, 0);
4745
        }
4746
        $result = Database::query($sql);
4747
        // If the lp is visible on the homepage, change his name there.
4748
        if (Database::affected_rows($result)) {
4749
            $session_id = api_get_session_id();
4750
            $session_condition = api_get_session_condition($session_id);
4751
            $tbl_tool = Database::get_course_table(TABLE_TOOL_LIST);
4752
            $link = 'lp/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session='.$session_id;
4753
            $sql = "UPDATE $tbl_tool SET name = '$this->name'
4754
            	    WHERE
4755
            	        c_id = $course_id AND
4756
            	        (link='$link' AND image='scormbuilder.gif' $session_condition)";
4757
            Database::query($sql);
4758
            return true;
4759
        } else {
4760
            return false;
4761
        }
4762
    }
4763
4764
    /**
4765
     * Set index specified prefix terms for all items in this path
4766
     * @param   string  Comma-separated list of terms
4767
     * @param   char Xapian term prefix
4768
     * @return  boolean False on error, true otherwise
4769
     */
4770
    public function set_terms_by_prefix($terms_string, $prefix)
4771
    {
4772
        $course_id = api_get_course_int_id();
4773
        if (api_get_setting('search_enabled') !== 'true')
4774
            return false;
4775
4776
        if (!extension_loaded('xapian')) {
4777
            return false;
4778
        }
4779
4780
        $terms_string = trim($terms_string);
4781
        $terms = explode(',', $terms_string);
4782
        array_walk($terms, 'trim_value');
4783
4784
        $stored_terms = $this->get_common_index_terms_by_prefix($prefix);
4785
4786
        // Don't do anything if no change, verify only at DB, not the search engine.
4787 View Code Duplication
        if ((count(array_diff($terms, $stored_terms)) == 0) && (count(array_diff($stored_terms, $terms)) == 0))
4788
            return false;
4789
4790
        require_once 'xapian.php'; // TODO: Try catch every xapian use or make wrappers on API.
4791
        require_once api_get_path(LIBRARY_PATH).'search/ChamiloIndexer.class.php';
4792
        require_once api_get_path(LIBRARY_PATH).'search/xapian/XapianQuery.php';
4793
        require_once api_get_path(LIBRARY_PATH).'search/IndexableChunk.class.php';
4794
4795
        $items_table = Database::get_course_table(TABLE_LP_ITEM);
4796
        // TODO: Make query secure agains XSS : use member attr instead of post var.
4797
        $lp_id = intval($_POST['lp_id']);
4798
        $sql = "SELECT * FROM $items_table WHERE c_id = $course_id AND lp_id = $lp_id";
4799
        $result = Database::query($sql);
4800
        $di = new ChamiloIndexer();
4801
4802
        while ($lp_item = Database::fetch_array($result)) {
4803
            // Get search_did.
4804
            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
4805
            $sql = 'SELECT * FROM %s
4806
                    WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d
4807
                    LIMIT 1';
4808
            $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp_id, $lp_item['id']);
4809
4810
            //echo $sql; echo '<br>';
4811
            $res = Database::query($sql);
4812
            if (Database::num_rows($res) > 0) {
4813
                $se_ref = Database::fetch_array($res);
4814
4815
                // Compare terms.
4816
                $doc = $di->get_document($se_ref['search_did']);
4817
                $xapian_terms = xapian_get_doc_terms($doc, $prefix);
4818
                $xterms = array();
4819
                foreach ($xapian_terms as $xapian_term) {
0 ignored issues
show
Bug introduced by
The expression $xapian_terms of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
4820
                    $xterms[] = substr($xapian_term['name'], 1);
4821
                }
4822
4823
                $dterms = $terms;
4824
4825
                $missing_terms = array_diff($dterms, $xterms);
4826
                $deprecated_terms = array_diff($xterms, $dterms);
4827
4828
                // Save it to search engine.
4829
                foreach ($missing_terms as $term) {
4830
                    $doc->add_term($prefix.$term, 1);
4831
                }
4832
                foreach ($deprecated_terms as $term) {
4833
                    $doc->remove_term($prefix.$term);
4834
                }
4835
                $di->getDb()->replace_document((int) $se_ref['search_did'], $doc);
4836
                $di->getDb()->flush();
4837
            }
4838
        }
4839
        return true;
4840
    }
4841
4842
    /**
4843
     * Sets the theme of the LP (local/remote) (and save)
4844
     * @param	string	Optional string giving the new theme of this learnpath
4845
     * @return   bool    Returns true if theme name is not empty
4846
     */
4847 View Code Duplication
    public function set_theme($name = '')
4848
    {
4849
        $course_id = api_get_course_int_id();
4850
        if ($this->debug > 0) {
4851
            error_log('New LP - In learnpath::set_theme()', 0);
4852
        }
4853
        $this->theme = $name;
4854
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4855
        $lp_id = $this->get_id();
4856
        $sql = "UPDATE $lp_table SET theme = '".Database::escape_string($this->theme)."'
4857
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4858
        if ($this->debug > 2) {
4859
            error_log('New LP - lp updated with new theme : '.$this->theme, 0);
4860
        }
4861
        Database::query($sql);
4862
4863
        return true;
4864
    }
4865
4866
    /**
4867
     * Sets the image of an LP (and save)
4868
     * @param	 string	Optional string giving the new image of this learnpath
4869
     * @return bool   Returns true if theme name is not empty
4870
     */
4871 View Code Duplication
    public function set_preview_image($name = '')
4872
    {
4873
        $course_id = api_get_course_int_id();
4874
        if ($this->debug > 0) {
4875
            error_log('New LP - In learnpath::set_preview_image()', 0);
4876
        }
4877
4878
        $this->preview_image = $name;
4879
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4880
        $lp_id = $this->get_id();
4881
        $sql = "UPDATE $lp_table SET
4882
                preview_image = '".Database::escape_string($this->preview_image)."'
4883
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4884
        if ($this->debug > 2) {
4885
            error_log('New LP - lp updated with new preview image : '.$this->preview_image, 0);
4886
        }
4887
        Database::query($sql);
4888
        return true;
4889
    }
4890
4891
    /**
4892
     * Sets the author of a LP (and save)
4893
     * @param	string	Optional string giving the new author of this learnpath
4894
     * @return   bool    Returns true if author's name is not empty
4895
     */
4896 View Code Duplication
    public function set_author($name = '')
4897
    {
4898
        $course_id = api_get_course_int_id();
4899
        if ($this->debug > 0) {
4900
            error_log('New LP - In learnpath::set_author()', 0);
4901
        }
4902
        $this->author = $name;
4903
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4904
        $lp_id = $this->get_id();
4905
        $sql = "UPDATE $lp_table SET author = '".Database::escape_string($name)."'
4906
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4907
        if ($this->debug > 2) {
4908
            error_log('New LP - lp updated with new preview author : '.$this->author, 0);
4909
        }
4910
        Database::query($sql);
4911
4912
        return true;
4913
    }
4914
4915
    /**
4916
     * Sets the hide_toc_frame parameter of a LP (and save)
4917
     * @param	int	1 if frame is hidden 0 then else
4918
     * @return   bool    Returns true if author's name is not empty
4919
     */
4920
    public function set_hide_toc_frame($hide)
4921
    {
4922
        $course_id = api_get_course_int_id();
4923
        if ($this->debug > 0) {
4924
            error_log('New LP - In learnpath::set_hide_toc_frame()', 0);
4925
        }
4926
        if (intval($hide) == $hide) {
4927
            $this->hide_toc_frame = $hide;
4928
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4929
            $lp_id = $this->get_id();
4930
            $sql = "UPDATE $lp_table SET
4931
                    hide_toc_frame = '".(int) $this->hide_toc_frame."'
4932
                    WHERE c_id = ".$course_id." AND id = '$lp_id'";
4933
            if ($this->debug > 2) {
4934
                error_log('New LP - lp updated with new preview hide_toc_frame : '.$this->author, 0);
4935
            }
4936
            Database::query($sql);
4937
4938
            return true;
4939
        } else {
4940
            return false;
4941
        }
4942
    }
4943
4944
    /**
4945
     * Sets the prerequisite of a LP (and save)
4946
     * @param	int		integer giving the new prerequisite of this learnpath
4947
     * @return 	bool 	returns true if prerequisite is not empty
4948
     */
4949 View Code Duplication
    public function set_prerequisite($prerequisite)
4950
    {
4951
        $course_id = api_get_course_int_id();
4952
        if ($this->debug > 0) {
4953
            error_log('New LP - In learnpath::set_prerequisite()', 0);
4954
        }
4955
        $this->prerequisite = intval($prerequisite);
4956
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4957
        $lp_id = $this->get_id();
4958
        $sql = "UPDATE $lp_table SET prerequisite = '".$this->prerequisite."'
4959
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4960
        if ($this->debug > 2) {
4961
            error_log('New LP - lp updated with new preview requisite : '.$this->requisite, 0);
4962
        }
4963
        Database::query($sql);
4964
        return true;
4965
    }
4966
4967
    /**
4968
     * Sets the location/proximity of the LP (local/remote) (and save)
4969
     * @param	string	Optional string giving the new location of this learnpath
4970
     * @return  boolean True on success / False on error
4971
     */
4972
    public function set_proximity($name = '')
4973
    {
4974
        $course_id = api_get_course_int_id();
4975
        if ($this->debug > 0) {
4976
            error_log('New LP - In learnpath::set_proximity()', 0);
4977
        }
4978
        if (empty ($name))
4979
            return false;
4980
4981
        $this->proximity = $name;
4982
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4983
        $lp_id = $this->get_id();
4984
        $sql = "UPDATE $lp_table SET
4985
                    content_local = '".Database::escape_string($name)."'
4986
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4987
        if ($this->debug > 2) {
4988
            error_log('New LP - lp updated with new proximity : '.$this->proximity, 0);
4989
        }
4990
        Database::query($sql);
4991
        return true;
4992
    }
4993
4994
    /**
4995
     * Sets the previous item ID to a given ID. Generally, this should be set to the previous 'current' item
4996
     * @param	integer	DB ID of the item
4997
     */
4998
    public function set_previous_item($id)
4999
    {
5000
        if ($this->debug > 0) {
5001
            error_log('New LP - In learnpath::set_previous_item()', 0);
5002
        }
5003
        $this->last = $id;
5004
    }
5005
5006
    /**
5007
     * Sets use_max_score
5008
     * @param   string  $use_max_score Optional string giving the new location of this learnpath
5009
     * @return  boolean True on success / False on error
5010
     */
5011
    public function set_use_max_score($use_max_score = 1)
5012
    {
5013
        $course_id = api_get_course_int_id();
5014
        if ($this->debug > 0) {
5015
            error_log('New LP - In learnpath::set_use_max_score()', 0);
5016
        }
5017
        $use_max_score = intval($use_max_score);
5018
        $this->use_max_score = $use_max_score;
5019
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5020
        $lp_id = $this->get_id();
5021
        $sql = "UPDATE $lp_table SET
5022
                    use_max_score = '".$this->use_max_score."'
5023
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
5024
5025
        if ($this->debug > 2) {
5026
            error_log('New LP - lp updated with new use_max_score : '.$this->use_max_score, 0);
5027
        }
5028
        Database::query($sql);
5029
5030
        return true;
5031
    }
5032
5033
    /**
5034
     * Sets and saves the expired_on date
5035
     * @param   string  $expired_on Optional string giving the new author of this learnpath
5036
     * @return   bool    Returns true if author's name is not empty
5037
     */
5038 View Code Duplication
    public function set_expired_on($expired_on)
5039
    {
5040
        if ($this->debug > 0) {
5041
            error_log('New LP - In learnpath::set_expired_on()', 0);
5042
        }
5043
5044
        $em = Database::getManager();
5045
        /** @var CLp $lp */
5046
        $lp = $em
5047
            ->getRepository('ChamiloCourseBundle:CLp')
5048
            ->findOneBy(['id' => $this->get_id(), 'cId' => api_get_course_int_id()]);
5049
5050
        if (!$lp) {
5051
            return false;
5052
        }
5053
5054
        $this->expired_on = !empty($expired_on) ? api_get_utc_datetime($expired_on, false, true) : null;
0 ignored issues
show
Documentation Bug introduced by
It seems like !empty($expired_on) ? ap...on, false, true) : null can also be of type object<DateTime>. However, the property $expired_on is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
5055
5056
        $lp->setExpiredOn($this->expired_on);
0 ignored issues
show
Bug introduced by
It seems like $this->expired_on can also be of type null or string; however, Chamilo\CourseBundle\Entity\CLp::setExpiredOn() does only seem to accept object<DateTime>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
5057
5058
        $em->persist($lp);
5059
        $em->flush();
5060
5061
        if ($this->debug > 2) {
5062
            error_log('New LP - lp updated with new expired_on : '.$this->expired_on, 0);
5063
        }
5064
5065
        return true;
5066
    }
5067
5068
    /**
5069
     * Sets and saves the publicated_on date
5070
     * @param   string  $publicated_on Optional string giving the new author of this learnpath
5071
     * @return   bool    Returns true if author's name is not empty
5072
     */
5073 View Code Duplication
    public function set_publicated_on($publicated_on)
5074
    {
5075
        if ($this->debug > 0) {
5076
            error_log('New LP - In learnpath::set_expired_on()', 0);
5077
        }
5078
5079
        $em = Database::getManager();
5080
        /** @var CLp $lp */
5081
        $lp = $em
5082
            ->getRepository('ChamiloCourseBundle:CLp')
5083
            ->findOneBy(['id' => $this->get_id(), 'cId' => api_get_course_int_id()]);
5084
5085
        if (!$lp) {
5086
            return false;
5087
        }
5088
5089
        $this->publicated_on = !empty($publicated_on) ? api_get_utc_datetime($publicated_on, false, true) : null;
0 ignored issues
show
Documentation Bug introduced by
It seems like !empty($publicated_on) ?...on, false, true) : null can also be of type object<DateTime>. However, the property $publicated_on is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
5090
        $lp->setPublicatedOn($this->publicated_on);
0 ignored issues
show
Bug introduced by
It seems like $this->publicated_on can also be of type null or string; however, Chamilo\CourseBundle\Entity\CLp::setPublicatedOn() does only seem to accept object<DateTime>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
5091
        $em->persist($lp);
5092
        $em->flush();
5093
5094
        if ($this->debug > 2) {
5095
            error_log('New LP - lp updated with new publicated_on : '.$this->publicated_on, 0);
5096
        }
5097
5098
        return true;
5099
    }
5100
5101
    /**
5102
     * Sets and saves the expired_on date
5103
     * @return   bool    Returns true if author's name is not empty
5104
     */
5105 View Code Duplication
    public function set_modified_on()
5106
    {
5107
        $course_id = api_get_course_int_id();
5108
        if ($this->debug > 0) {
5109
            error_log('New LP - In learnpath::set_expired_on()', 0);
5110
        }
5111
        $this->modified_on = api_get_utc_datetime();
0 ignored issues
show
Documentation Bug introduced by
It seems like api_get_utc_datetime() can also be of type object<DateTime>. However, the property $modified_on is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
5112
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5113
        $lp_id = $this->get_id();
5114
        $sql = "UPDATE $lp_table SET modified_on = '".$this->modified_on."'
5115
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
5116
        if ($this->debug > 2) {
5117
            error_log('New LP - lp updated with new expired_on : '.$this->modified_on, 0);
5118
        }
5119
        Database::query($sql);
5120
5121
        return true;
5122
    }
5123
5124
    /**
5125
     * Sets the object's error message
5126
     * @param	string	Error message. If empty, reinits the error string
5127
     * @return 	void
5128
     */
5129
    public function set_error_msg($error = '')
5130
    {
5131
        if ($this->debug > 0) {
5132
            error_log('New LP - In learnpath::set_error_msg()', 0);
5133
        }
5134
        if (empty ($error)) {
5135
            $this->error = '';
5136
        } else {
5137
            $this->error .= $error;
5138
        }
5139
    }
5140
5141
    /**
5142
     * Launches the current item if not 'sco'
5143
     * (starts timer and make sure there is a record ready in the DB)
5144
     * @param  boolean  $allow_new_attempt Whether to allow a new attempt or not
5145
     * @return boolean
5146
     */
5147
    public function start_current_item($allow_new_attempt = false)
5148
    {
5149
        if ($this->debug > 0) {
5150
            error_log('New LP - In learnpath::start_current_item()', 0);
5151
        }
5152
        if ($this->current != 0 && is_object($this->items[$this->current])) {
5153
            $type = $this->get_type();
5154
            $item_type = $this->items[$this->current]->get_type();
5155
            if (($type == 2 && $item_type != 'sco') ||
5156
                ($type == 3 && $item_type != 'au') ||
5157
                ($type == 1 && $item_type != TOOL_QUIZ && $item_type != TOOL_HOTPOTATOES)
5158
            ) {
5159
                $this->items[$this->current]->open($allow_new_attempt);
5160
                $this->autocomplete_parents($this->current);
5161
                $prereq_check = $this->prerequisites_match($this->current);
5162
                $this->items[$this->current]->save(false, $prereq_check);
5163
                //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
5164
            }
5165
            // If sco, then it is supposed to have been updated by some other call.
5166
            if ($item_type == 'sco') {
5167
                $this->items[$this->current]->restart();
5168
            }
5169
        }
5170
        if ($this->debug > 0) {
5171
            error_log('New LP - End of learnpath::start_current_item()', 0);
5172
        }
5173
        return true;
5174
    }
5175
5176
    /**
5177
     * Stops the processing and counters for the old item (as held in $this->last)
5178
     * @return boolean  True/False
5179
     */
5180
    public function stop_previous_item()
5181
    {
5182
        if ($this->debug > 0) {
5183
            error_log('New LP - In learnpath::stop_previous_item()', 0);
5184
        }
5185
5186
        if ($this->last != 0 && $this->last != $this->current && is_object($this->items[$this->last])) {
5187
            if ($this->debug > 2) {
5188
                error_log('New LP - In learnpath::stop_previous_item() - '.$this->last.' is object', 0);
5189
            }
5190
            switch ($this->get_type()) {
5191 View Code Duplication
                case '3':
5192
                    if ($this->items[$this->last]->get_type() != 'au') {
5193
                        if ($this->debug > 2) {
5194
                            error_log('New LP - In learnpath::stop_previous_item() - '.$this->last.' in lp_type 3 is <> au', 0);
5195
                        }
5196
                        $this->items[$this->last]->close();
5197
                        //$this->autocomplete_parents($this->last);
5198
                        //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
5199
                    } else {
5200
                        if ($this->debug > 2) {
5201
                            error_log('New LP - In learnpath::stop_previous_item() - Item is an AU, saving is managed by AICC signals', 0);
5202
                        }
5203
                    }
5204
                    break;
5205 View Code Duplication
                case '2':
5206
                    if ($this->items[$this->last]->get_type() != 'sco') {
5207
                        if ($this->debug > 2) {
5208
                            error_log('New LP - In learnpath::stop_previous_item() - '.$this->last.' in lp_type 2 is <> sco', 0);
5209
                        }
5210
                        $this->items[$this->last]->close();
5211
                        //$this->autocomplete_parents($this->last);
5212
                        //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
5213
                    } else {
5214
                        if ($this->debug > 2) {
5215
                            error_log('New LP - In learnpath::stop_previous_item() - Item is a SCO, saving is managed by SCO signals', 0);
5216
                        }
5217
                    }
5218
                    break;
5219
                case '1':
5220
                default:
5221
                    if ($this->debug > 2) {
5222
                        error_log('New LP - In learnpath::stop_previous_item() - '.$this->last.' in lp_type 1 is asset', 0);
5223
                    }
5224
                    $this->items[$this->last]->close();
5225
                    break;
5226
            }
5227
        } else {
5228
            if ($this->debug > 2) {
5229
                error_log('New LP - In learnpath::stop_previous_item() - No previous element found, ignoring...', 0);
5230
            }
5231
            return false;
5232
        }
5233
        return true;
5234
    }
5235
5236
    /**
5237
     * Updates the default view mode from fullscreen to embedded and inversely
5238
     * @return	string The current default view mode ('fullscreen' or 'embedded')
5239
     */
5240
    public function update_default_view_mode()
5241
    {
5242
        $course_id = api_get_course_int_id();
5243
        if ($this->debug > 0) {
5244
            error_log('New LP - In learnpath::update_default_view_mode()', 0);
5245
        }
5246
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5247
        $sql = "SELECT * FROM $lp_table
5248
                WHERE c_id = ".$course_id." AND id = ".$this->get_id();
5249
        $res = Database::query($sql);
5250
        if (Database::num_rows($res) > 0) {
5251
            $row = Database::fetch_array($res);
5252
            $default_view_mode = $row['default_view_mod'];
5253
            $view_mode = $default_view_mode;
5254
            switch ($default_view_mode) {
5255
                case 'fullscreen': // default with popup
5256
                    $view_mode = 'embedded';
5257
                    break;
5258
                case 'embedded': // default view with left menu
5259
                    $view_mode = 'embedframe';
5260
                    break;
5261
                case 'embedframe': //folded menu
5262
                    $view_mode = 'impress';
5263
                    break;
5264
                case 'impress':
5265
                    $view_mode = 'fullscreen';
5266
                    break;
5267
            }
5268
            $sql = "UPDATE $lp_table SET default_view_mod = '$view_mode'
5269
                    WHERE c_id = ".$course_id." AND id = ".$this->get_id();
5270
            Database::query($sql);
5271
            $this->mode = $view_mode;
5272
5273
            return $view_mode;
5274
        } else {
5275
            if ($this->debug > 2) {
5276
                error_log('New LP - Problem in update_default_view() - could not find LP '.$this->get_id().' in DB', 0);
5277
            }
5278
        }
5279
        return -1;
5280
    }
5281
5282
    /**
5283
     * Updates the default behaviour about auto-commiting SCORM updates
5284
     * @return	boolean	True if auto-commit has been set to 'on', false otherwise
5285
     */
5286
    public function update_default_scorm_commit()
5287
    {
5288
        $course_id = api_get_course_int_id();
5289
        if ($this->debug > 0) {
5290
            error_log('New LP - In learnpath::update_default_scorm_commit()', 0);
5291
        }
5292
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5293
        $sql = "SELECT * FROM $lp_table
5294
                WHERE c_id = ".$course_id." AND id = ".$this->get_id();
5295
        $res = Database::query($sql);
5296
        if (Database::num_rows($res) > 0) {
5297
            $row = Database::fetch_array($res);
5298
            $force = $row['force_commit'];
5299
            if ($force == 1) {
5300
                $force = 0;
5301
                $force_return = false;
5302
            } elseif ($force == 0) {
5303
                $force = 1;
5304
                $force_return = true;
5305
            }
5306
            $sql = "UPDATE $lp_table SET force_commit = $force
5307
                    WHERE c_id = ".$course_id." AND id = ".$this->get_id();
5308
            Database::query($sql);
5309
            $this->force_commit = $force_return;
5310
5311
            return $force_return;
5312
        } else {
5313
            if ($this->debug > 2) {
5314
                error_log('New LP - Problem in update_default_scorm_commit() - could not find LP '.$this->get_id().' in DB', 0);
5315
            }
5316
        }
5317
        return -1;
5318
    }
5319
5320
    /**
5321
     * Updates the order of learning paths (goes through all of them by order and fills the gaps)
5322
     * @return	bool	True on success, false on failure
5323
     */
5324
    public function update_display_order()
5325
    {
5326
        $course_id = api_get_course_int_id();
5327
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5328
5329
        $sql = "SELECT * FROM $lp_table WHERE c_id = ".$course_id." ORDER BY display_order";
5330
        $res = Database::query($sql);
5331
        if ($res === false) {
5332
            return false;
5333
        }
5334
5335
        $num = Database::num_rows($res);
5336
        // First check the order is correct, globally (might be wrong because
5337
        // of versions < 1.8.4).
5338
        if ($num > 0) {
5339
            $i = 1;
5340
            while ($row = Database::fetch_array($res)) {
5341 View Code Duplication
                if ($row['display_order'] != $i) {
5342
                    // If we find a gap in the order, we need to fix it.
5343
                    $sql = "UPDATE $lp_table SET display_order = $i
5344
                            WHERE c_id = ".$course_id." AND id = ".$row['id'];
5345
                    Database::query($sql);
5346
                }
5347
                $i++;
5348
            }
5349
        }
5350
        return true;
5351
    }
5352
5353
    /**
5354
     * Updates the "prevent_reinit" value that enables control on reinitialising items on second view
5355
     * @return	boolean	True if prevent_reinit has been set to 'on', false otherwise (or 1 or 0 in this case)
5356
     */
5357 View Code Duplication
    public function update_reinit()
5358
    {
5359
        $course_id = api_get_course_int_id();
5360
        if ($this->debug > 0) {
5361
            error_log('New LP - In learnpath::update_reinit()', 0);
5362
        }
5363
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5364
        $sql = "SELECT * FROM $lp_table
5365
                WHERE c_id = ".$course_id." AND id = ".$this->get_id();
5366
        $res = Database::query($sql);
5367
        if (Database::num_rows($res) > 0) {
5368
            $row = Database::fetch_array($res);
5369
            $force = $row['prevent_reinit'];
5370
            if ($force == 1) {
5371
                $force = 0;
5372
            } elseif ($force == 0) {
5373
                $force = 1;
5374
            }
5375
            $sql = "UPDATE $lp_table SET prevent_reinit = $force
5376
                    WHERE c_id = ".$course_id." AND id = ".$this->get_id();
5377
            Database::query($sql);
5378
            $this->prevent_reinit = $force;
5379
            return $force;
5380
        } else {
5381
            if ($this->debug > 2) {
5382
                error_log('New LP - Problem in update_reinit() - could not find LP '.$this->get_id().' in DB', 0);
5383
            }
5384
        }
5385
        return -1;
5386
    }
5387
5388
    /**
5389
     * Determine the attempt_mode thanks to prevent_reinit and seriousgame_mode db flag
5390
     *
5391
     * @return string 'single', 'multi' or 'seriousgame'
5392
     * @author ndiechburg <[email protected]>
5393
     **/
5394
    public function get_attempt_mode()
5395
    {
5396
        //Set default value for seriousgame_mode
5397
        if (!isset($this->seriousgame_mode)) {
5398
            $this->seriousgame_mode = 0;
5399
        }
5400
        // Set default value for prevent_reinit
5401
        if (!isset($this->prevent_reinit)) {
5402
            $this->prevent_reinit = 1;
5403
        }
5404
        if ($this->seriousgame_mode == 1 && $this->prevent_reinit == 1) {
5405
            return 'seriousgame';
5406
        }
5407
        if ($this->seriousgame_mode == 0 && $this->prevent_reinit == 1) {
5408
            return 'single';
5409
        }
5410
        if ($this->seriousgame_mode == 0 && $this->prevent_reinit == 0) {
5411
            return 'multiple';
5412
        }
5413
        return 'single';
5414
    }
5415
5416
    /**
5417
     * Register the attempt mode into db thanks to flags prevent_reinit and seriousgame_mode flags
5418
     *
5419
     * @param string 'seriousgame', 'single' or 'multiple'
5420
     * @return boolean
5421
     * @author ndiechburg <[email protected]>
5422
     **/
5423
    public function set_attempt_mode($mode)
5424
    {
5425
        $course_id = api_get_course_int_id();
5426
        switch ($mode) {
5427
            case 'seriousgame':
5428
                $sg_mode = 1;
5429
                $prevent_reinit = 1;
5430
                break;
5431
            case 'single':
5432
                $sg_mode = 0;
5433
                $prevent_reinit = 1;
5434
                break;
5435
            case 'multiple':
5436
                $sg_mode = 0;
5437
                $prevent_reinit = 0;
5438
                break;
5439
            default:
5440
                $sg_mode = 0;
5441
                $prevent_reinit = 0;
5442
                break;
5443
        }
5444
        $this->prevent_reinit = $prevent_reinit;
5445
        $this->seriousgame_mode = $sg_mode;
5446
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5447
        $sql = "UPDATE $lp_table SET
5448
                prevent_reinit = $prevent_reinit ,
5449
                seriousgame_mode = $sg_mode
5450
                WHERE c_id = ".$course_id." AND id = ".$this->get_id();
5451
        $res = Database::query($sql);
5452
        if ($res) {
5453
            return true;
5454
        } else {
5455
            return false;
5456
        }
5457
    }
5458
5459
    /**
5460
     * Switch between multiple attempt, single attempt or serious_game mode (only for scorm)
5461
     *
5462
     * @return boolean
5463
     * @author ndiechburg <[email protected]>
5464
     **/
5465
    public function switch_attempt_mode()
5466
    {
5467
        if ($this->debug > 0) {
5468
            error_log('New LP - In learnpath::switch_attempt_mode()', 0);
5469
        }
5470
        $mode = $this->get_attempt_mode();
5471
        switch ($mode) {
5472
            case 'single':
5473
                $next_mode = 'multiple';
5474
                break;
5475
            case 'multiple':
5476
                $next_mode = 'seriousgame';
5477
                break;
5478
            case 'seriousgame':
5479
                $next_mode = 'single';
5480
                break;
5481
            default:
5482
                $next_mode = 'single';
5483
                break;
5484
        }
5485
        $this->set_attempt_mode($next_mode);
5486
    }
5487
5488
    /**
5489
     * Switch the lp in ktm mode. This is a special scorm mode with unique attempt
5490
     * but possibility to do again a completed item.
5491
     *
5492
     * @return boolean true if seriousgame_mode has been set to 1, false otherwise
5493
     * @author ndiechburg <[email protected]>
5494
     **/
5495 View Code Duplication
    public function set_seriousgame_mode()
5496
    {
5497
        $course_id = api_get_course_int_id();
5498
        if ($this->debug > 0) {
5499
            error_log('New LP - In learnpath::set_seriousgame_mode()', 0);
5500
        }
5501
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5502
        $sql = "SELECT * FROM $lp_table WHERE c_id = ".$course_id." AND id = ".$this->get_id();
5503
        $res = Database::query($sql);
5504
        if (Database::num_rows($res) > 0) {
5505
            $row = Database::fetch_array($res);
5506
            $force = $row['seriousgame_mode'];
5507
            if ($force == 1) {
5508
                $force = 0;
5509
            } elseif ($force == 0) {
5510
                $force = 1;
5511
            }
5512
            $sql = "UPDATE $lp_table SET seriousgame_mode = $force
5513
			        WHERE c_id = ".$course_id." AND id = ".$this->get_id();
5514
            Database::query($sql);
5515
            $this->seriousgame_mode = $force;
5516
            return $force;
5517
        } else {
5518
            if ($this->debug > 2) {
5519
                error_log('New LP - Problem in set_seriousgame_mode() - could not find LP '.$this->get_id().' in DB', 0);
5520
            }
5521
        }
5522
        return -1;
5523
    }
5524
5525
    /**
5526
     * Updates the "scorm_debug" value that shows or hide the debug window
5527
     * @return	boolean	True if scorm_debug has been set to 'on', false otherwise (or 1 or 0 in this case)
5528
     */
5529 View Code Duplication
    public function update_scorm_debug()
5530
    {
5531
        $course_id = api_get_course_int_id();
5532
        if ($this->debug > 0) {
5533
            error_log('New LP - In learnpath::update_scorm_debug()', 0);
5534
        }
5535
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
5536
        $sql = "SELECT * FROM $lp_table
5537
                WHERE c_id = ".$course_id." AND id = ".$this->get_id();
5538
        $res = Database::query($sql);
5539
        if (Database::num_rows($res) > 0) {
5540
            $row = Database::fetch_array($res);
5541
            $force = $row['debug'];
5542
            if ($force == 1) {
5543
                $force = 0;
5544
            } elseif ($force == 0) {
5545
                $force = 1;
5546
            }
5547
            $sql = "UPDATE $lp_table SET debug = $force
5548
                    WHERE c_id = ".$course_id." AND id = ".$this->get_id();
5549
            Database::query($sql);
5550
            $this->scorm_debug = $force;
5551
            return $force;
5552
        } else {
5553
            if ($this->debug > 2) {
5554
                error_log('New LP - Problem in update_scorm_debug() - could not find LP '.$this->get_id().' in DB', 0);
5555
            }
5556
        }
5557
        return -1;
5558
    }
5559
5560
    /**
5561
     * Function that makes a call to the function sort_tree_array and create_tree_array
5562
     * @author Kevin Van Den Haute
5563
     * @param  array
5564
     */
5565
    public function tree_array($array)
5566
    {
5567
        if ($this->debug > 1) {
5568
            error_log('New LP - In learnpath::tree_array()', 0);
5569
        }
5570
        $array = $this->sort_tree_array($array);
5571
        $this->create_tree_array($array);
5572
    }
5573
5574
    /**
5575
     * Creates an array with the elements of the learning path tree in it
5576
     *
5577
     * @author Kevin Van Den Haute
5578
     * @param array $array
5579
     * @param int $parent
5580
     * @param int $depth
5581
     * @param array $tmp
5582
     */
5583
    public function create_tree_array($array, $parent = 0, $depth = -1, $tmp = array())
5584
    {
5585
        if ($this->debug > 1) {
5586
            error_log('New LP - In learnpath::create_tree_array())', 0);
5587
        }
5588
5589
        if (is_array($array)) {
5590
            for ($i = 0; $i < count($array); $i++) {
5591
                if ($array[$i]['parent_item_id'] == $parent) {
5592
                    if (!in_array($array[$i]['parent_item_id'], $tmp)) {
5593
                        $tmp[] = $array[$i]['parent_item_id'];
5594
                        $depth++;
5595
                    }
5596
                    $preq = (empty($array[$i]['prerequisite']) ? '' : $array[$i]['prerequisite']);
5597
                    $audio = isset($array[$i]['audio']) ? $array[$i]['audio'] : null;
5598
                    $path = isset($array[$i]['path']) ? $array[$i]['path'] : null;
5599
5600
                    $prerequisiteMinScore = isset($array[$i]['prerequisite_min_score']) ? $array[$i]['prerequisite_min_score'] : null;
5601
                    $prerequisiteMaxScore = isset($array[$i]['prerequisite_max_score']) ? $array[$i]['prerequisite_max_score'] : null;
5602
                    $ref = isset($array[$i]['ref']) ? $array[$i]['ref'] : '';
5603
                    $this->arrMenu[] = array(
5604
                        'id' => $array[$i]['id'],
5605
                        'ref' => $ref,
5606
                        'item_type' => $array[$i]['item_type'],
5607
                        'title' => $array[$i]['title'],
5608
                        'path' => $path,
5609
                        'description' => $array[$i]['description'],
5610
                        'parent_item_id' => $array[$i]['parent_item_id'],
5611
                        'previous_item_id' => $array[$i]['previous_item_id'],
5612
                        'next_item_id' => $array[$i]['next_item_id'],
5613
                        'min_score' => $array[$i]['min_score'],
5614
                        'max_score' => $array[$i]['max_score'],
5615
                        'mastery_score' => $array[$i]['mastery_score'],
5616
                        'display_order' => $array[$i]['display_order'],
5617
                        'prerequisite' => $preq,
5618
                        'depth' => $depth,
5619
                        'audio' => $audio,
5620
                        'prerequisite_min_score' => $prerequisiteMinScore,
5621
                        'prerequisite_max_score' => $prerequisiteMaxScore
5622
                    );
5623
5624
                    $this->create_tree_array($array, $array[$i]['id'], $depth, $tmp);
5625
                }
5626
            }
5627
        }
5628
    }
5629
5630
    /**
5631
     * Sorts a multi dimensional array by parent id and display order
5632
     * @author Kevin Van Den Haute
5633
     *
5634
     * @param array $array (array with al the learning path items in it)
5635
     *
5636
     * @return array
5637
     */
5638
    public function sort_tree_array($array)
5639
    {
5640
        foreach ($array as $key => $row) {
5641
            $parent[$key] = $row['parent_item_id'];
5642
            $position[$key] = $row['display_order'];
5643
        }
5644
5645
        if (count($array) > 0) {
5646
            array_multisort($parent, SORT_ASC, $position, SORT_ASC, $array);
5647
        }
5648
5649
        return $array;
5650
    }
5651
5652
    /**
5653
     * Function that creates a html list of learning path items so that we can add audio files to them
5654
     * @author Kevin Van Den Haute
5655
     * @return string
5656
     */
5657
    public function overview()
5658
    {
5659
        if ($this->debug > 0) {
5660
            error_log('New LP - In learnpath::overview()', 0);
5661
        }
5662
5663
        $_SESSION['gradebook'] = isset($_GET['gradebook']) ? Security::remove_XSS($_GET['gradebook']) : null;
5664
        $return = '';
5665
5666
        $update_audio = isset($_GET['updateaudio']) ? $_GET['updateaudio'] : null;
5667
5668
        // we need to start a form when we want to update all the mp3 files
5669
        if ($update_audio == 'true') {
5670
            $return .= '<form action="'.api_get_self().'?'.api_get_cidreq().'&updateaudio='.Security::remove_XSS($_GET['updateaudio']).'&action='.Security::remove_XSS($_GET['action']).'&lp_id='.$_SESSION['oLP']->lp_id.'" method="post" enctype="multipart/form-data" name="updatemp3" id="updatemp3">';
5671
        }
5672
        $return .= '<div id="message"></div>';
5673
        if (count($this->items) == 0) {
5674
            $return .= Display::return_message(get_lang('YouShouldAddItemsBeforeAttachAudio'), 'normal');
5675
        } else {
5676
            $return_audio = '<table class="data_table">';
5677
            $return_audio .= '<tr>';
5678
            $return_audio .= '<th width="40%">'.get_lang('Title').'</th>';
5679
            $return_audio .= '<th>'.get_lang('Audio').'</th>';
5680
            $return_audio .= '</tr>';
5681
5682
            if ($update_audio != 'true') {
5683
                $return .= '<div class="col-md-12">';
5684
                $return .= self::return_new_tree($update_audio);
5685
                $return .= '</div>';
5686
                $return .= Display::div(
5687
                    Display::url(get_lang('Save'), '#', array('id' => 'listSubmit', 'class' => 'btn btn-primary')),
5688
                    array('style' => 'float:left; margin-top:15px;width:100%')
5689
                );
5690
            } else {
5691
                $return_audio .= self::return_new_tree($update_audio);
5692
                $return .= $return_audio.'</table>';
5693
            }
5694
5695
            // We need to close the form when we are updating the mp3 files.
5696
            if ($update_audio == 'true') {
5697
                $return .= '<div class="footer-audio">';
5698
                $return .= Display::button(
5699
                    'save_audio',
5700
                    '<em class="fa fa-file-audio-o"></em> '.get_lang('SaveAudioAndOrganization'),
5701
                    array('class' => 'btn btn-primary', 'type' => 'submit')
5702
                );
5703
                $return .= '</div>';
5704
            }
5705
        }
5706
5707
        // We need to close the form when we are updating the mp3 files.
5708
        if ($update_audio == 'true' && isset($this->arrMenu) && count($this->arrMenu) != 0) {
5709
            $return .= '</form>';
5710
        }
5711
5712
        return $return;
5713
    }
5714
5715
    /**
5716
     * @param string string $update_audio
5717
     * @param bool $drop_element_here
5718
     * @return string
5719
     */
5720
    public function return_new_tree($update_audio = 'false', $drop_element_here = false)
5721
    {
5722
        $return = '';
5723
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
5724
        $course_id = api_get_course_int_id();
5725
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
5726
5727
        $sql = "SELECT * FROM $tbl_lp_item
5728
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
5729
5730
        $result = Database::query($sql);
5731
        $arrLP = array();
5732
        while ($row = Database::fetch_array($result)) {
5733
5734
            $arrLP[] = array(
5735
                'id' => $row['id'],
5736
                'item_type' => $row['item_type'],
5737
                'title' => Security::remove_XSS($row['title']),
5738
                'path' => $row['path'],
5739
                'description' => Security::remove_XSS($row['description']),
5740
                'parent_item_id' => $row['parent_item_id'],
5741
                'previous_item_id' => $row['previous_item_id'],
5742
                'next_item_id' => $row['next_item_id'],
5743
                'max_score' => $row['max_score'],
5744
                'min_score' => $row['min_score'],
5745
                'mastery_score' => $row['mastery_score'],
5746
                'prerequisite' => $row['prerequisite'],
5747
                'display_order' => $row['display_order'],
5748
                'audio' => $row['audio'],
5749
                'prerequisite_max_score' => $row['prerequisite_max_score'],
5750
                'prerequisite_min_score' => $row['prerequisite_min_score']
5751
            );
5752
        }
5753
5754
        $this->tree_array($arrLP);
5755
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
5756
        unset($this->arrMenu);
5757
        $default_data = null;
5758
        $default_content = null;
5759
        $elements = array();
5760
        $return_audio = null;
5761
5762
        for ($i = 0; $i < count($arrLP); $i++) {
5763
            $title = $arrLP[$i]['title'];
5764
            $title_cut = cut($arrLP[$i]['title'], 25);
5765
5766
            // Link for the documents
5767
            if ($arrLP[$i]['item_type'] == 'document') {
5768
                $url = api_get_self().'?'.api_get_cidreq().'&action=view_item&mode=preview_document&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id;
5769
                $title_cut = Display::url(
5770
                    $title_cut,
5771
                    $url,
5772
                    array(
5773
                        'class' => 'ajax moved',
5774
                        'data-title' => $title_cut
5775
                    )
5776
                );
5777
            }
5778
5779
            // Detect if type is FINAL_ITEM to set path_id to SESSION
5780
            if ($arrLP[$i]['item_type'] == TOOL_LP_FINAL_ITEM) {
5781
                Session::write('pathItem', $arrLP[$i]['path']);
5782
            }
5783
5784
            if (($i % 2) == 0) {
5785
                $oddClass = 'row_odd';
5786
            } else {
5787
                $oddClass = 'row_even';
5788
            }
5789
            $return_audio .= '<tr id ="lp_item_'.$arrLP[$i]['id'].'" class="'.$oddClass.'">';
5790
            $icon_name = str_replace(' ', '', $arrLP[$i]['item_type']);
5791
5792
            if (file_exists('../img/lp_'.$icon_name.'.png')) {
5793
                $icon = Display::return_icon('lp_'.$icon_name.'.png');
5794
            } else {
5795
                if (file_exists('../img/lp_'.$icon_name.'.gif')) {
5796
                    $icon = Display::return_icon('lp_'.$icon_name.'.gif');
5797
                } else {
5798
                    if ($arrLP[$i]['item_type'] === TOOL_LP_FINAL_ITEM) {
5799
                        $icon = Display::return_icon('certificate.png');
5800
                    } else {
5801
                        $icon = Display::return_icon('folder_document.gif');
5802
                    }
5803
                }
5804
            }
5805
5806
            // The audio column.
5807
            $return_audio .= '<td align="left" style="padding-left:10px;">';
5808
            $audio = '';
5809
            if (!$update_audio || $update_audio <> 'true') {
5810
                if (empty($arrLP[$i]['audio'])) {
5811
                    $audio .= '';
5812
                }
5813
            } else {
5814
                $types = self::getChapterTypes();
5815
                if (!in_array($arrLP[$i]['item_type'], $types)) {
5816
                    $audio .= '<input type="file" name="mp3file'.$arrLP[$i]['id'].'" id="mp3file" />';
5817
                    if (!empty ($arrLP[$i]['audio'])) {
5818
                        $audio .= '<br />'.Security::remove_XSS($arrLP[$i]['audio']).'<br />
5819
                        <input type="checkbox" name="removemp3' . $arrLP[$i]['id'].'" id="checkbox'.$arrLP[$i]['id'].'" />'.get_lang('RemoveAudio');
5820
                    }
5821
                }
5822
            }
5823
5824
            $return_audio .= Display::span($icon.' '.$title).Display::tag('td', $audio, array('style'=>''));
5825
            $return_audio .= '</td>';
5826
            $move_icon = '';
5827
            $move_item_icon = '';
5828
            $edit_icon = '';
5829
            $delete_icon = '';
5830
            $audio_icon = '';
5831
            $prerequisities_icon = '';
5832
            $forumIcon = '';
5833
            $previewIcon = '';
5834
5835
            if ($is_allowed_to_edit) {
5836
                if (!$update_audio || $update_audio <> 'true') {
5837 View Code Duplication
                    if ($arrLP[$i]['item_type'] !== TOOL_LP_FINAL_ITEM) {
5838
                        $move_icon .= '<a class="moved" href="#">';
5839
                        $move_icon .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
5840
                        $move_icon .= '</a>';
5841
                    }
5842
                }
5843
5844
                // No edit for this item types
5845
                if (!in_array($arrLP[$i]['item_type'], array('sco', 'asset', 'final_item'))) {
5846
                    if ($arrLP[$i]['item_type'] != 'dir') {
5847
                        $edit_icon .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=edit_item&view=build&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'&path_item='.$arrLP[$i]['path'].'" class="btn btn-default">';
5848
                        $edit_icon .= Display::return_icon('edit.png', get_lang('LearnpathEditModule'), array(), ICON_SIZE_TINY);
5849
                        $edit_icon .= '</a>';
5850
5851
                        if (!in_array($arrLP[$i]['item_type'], ['forum', 'thread'])) {
5852
                            $forumThread = $this->items[$arrLP[$i]['id']]->getForumThread(
5853
                                $this->course_int_id,
5854
                                $this->lp_session_id
5855
                            );
5856
                            if ($forumThread) {
5857
                                $forumIconUrl = api_get_self().'?'.api_get_cidreq().'&'.http_build_query([
5858
                                    'action' => 'dissociate_forum',
5859
                                    'id' => $arrLP[$i]['id'],
5860
                                    'lp_id' => $this->lp_id
5861
                                ]);
5862
                                $forumIcon = Display::url(
5863
                                    Display::return_icon('forum.png', get_lang('DissociateForumToLPItem'), [], ICON_SIZE_TINY),
5864
                                    $forumIconUrl,
5865
                                    ['class' => 'btn btn-default lp-btn-dissociate-forum']
5866
                                );
5867
                            } else {
5868
                                $forumIconUrl = api_get_self().'?'.api_get_cidreq().'&'.http_build_query([
5869
                                    'action' => 'create_forum',
5870
                                    'id' => $arrLP[$i]['id'],
5871
                                    'lp_id' => $this->lp_id
5872
                                ]);
5873
                                $forumIcon = Display::url(
5874
                                    Display::return_icon('forum.png', get_lang('AssociateForumToLPItem'), [], ICON_SIZE_TINY),
5875
                                    $forumIconUrl,
5876
                                    ['class' => "btn btn-default lp-btn-associate-forum"]
5877
                                );
5878
                            }
5879
                        }
5880
                    } else {
5881
                        $edit_icon .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=edit_item&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'&path_item='.$arrLP[$i]['path'].'" class="btn btn-default">';
5882
                        $edit_icon .= Display::return_icon('edit.png', get_lang('LearnpathEditModule'), array(), ICON_SIZE_TINY);
5883
                        $edit_icon .= '</a>';
5884
                    }
5885
                } else {
5886
                    if ($arrLP[$i]['item_type'] == TOOL_LP_FINAL_ITEM) {
5887
                        $edit_icon .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=edit_item&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'" class="btn btn-default">';
5888
                        $edit_icon .= Display::return_icon('edit.png', get_lang('Edit'), array(), ICON_SIZE_TINY);
5889
                        $edit_icon .= '</a>';
5890
                    }
5891
                }
5892
5893
                $delete_icon .= ' <a href="'.api_get_self().'?'.api_get_cidreq().'&action=delete_item&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id.'" onclick="return confirmation(\''.addslashes($title).'\');" class="btn btn-default">';
5894
                $delete_icon .= Display::return_icon(
5895
                    'delete.png',
5896
                    get_lang('LearnpathDeleteModule'),
5897
                    [],
5898
                    ICON_SIZE_TINY
5899
                );
5900
                $delete_icon .= '</a>';
5901
5902
                $url = api_get_self().'?'.api_get_cidreq().'&view=build&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id;
5903
                $previewImage = Display::return_icon(
5904
                    'preview_view.png',
5905
                    get_lang('Preview'),
5906
                    [],
5907
                    ICON_SIZE_TINY
5908
                );
5909
5910
                switch ($arrLP[$i]['item_type']) {
5911
                    case TOOL_DOCUMENT:
5912
                    case TOOL_LP_FINAL_ITEM:
5913
                        $urlPreviewLink = api_get_self().'?'.api_get_cidreq().'&action=view_item&mode=preview_document&id='.$arrLP[$i]['id'].'&lp_id='.$this->lp_id;
5914
                        $previewIcon = Display::url(
5915
                            $previewImage,
5916
                            $urlPreviewLink,
5917
                            array(
5918
                                'target' => '_blank',
5919
                                'class' => 'btn btn-default',
5920
                                'data-title' => $arrLP[$i]['title']
5921
                            )
5922
                        );
5923
                        break;
5924
                    case TOOL_THREAD:
5925
                    case TOOL_FORUM:
5926
                    case TOOL_QUIZ:
5927
                    case TOOL_STUDENTPUBLICATION:
5928
                    case TOOL_LP_FINAL_ITEM:
5929
                    case TOOL_LINK:
5930
                        //$target = '';
5931
                        //$class = 'btn btn-default ajax';
5932
                        //if ($arrLP[$i]['item_type'] == TOOL_LINK) {
5933
                        $class = 'btn btn-default';
5934
                        $target = '_blank';
5935
                        //}
5936
5937
                        $link = self::rl_get_resource_link_for_learnpath(
5938
                            $this->course_int_id,
5939
                            $this->lp_id,
5940
                            $arrLP[$i]['id'],
5941
                            0,
5942
                            ''
5943
                        );
5944
                        $previewIcon = Display::url(
5945
                            $previewImage,
5946
                            $link,
5947
                            [
5948
                                'class' => $class,
5949
                                'data-title' => $arrLP[$i]['title'],
5950
                                'target' => $target
5951
                            ]
5952
                        );
5953
                        break;
5954
                    default:
5955
                        $previewIcon = Display::url(
5956
                            $previewImage,
5957
                            $url.'&action=view_item',
5958
                            ['class' => 'btn btn-default', 'target' => '_blank']
5959
                        );
5960
                        break;
5961
                }
5962
5963
                if ($arrLP[$i]['item_type'] != 'dir') {
5964
                    $prerequisities_icon = Display::url(
5965
                        Display::return_icon(
5966
                            'accept.png',
5967
                            get_lang('LearnpathPrerequisites'),
5968
                            array(),
5969
                            ICON_SIZE_TINY
5970
                        ),
5971
                        $url.'&action=edit_item_prereq', ['class' => 'btn btn-default']
5972
                    );
5973
                    if ($arrLP[$i]['item_type'] != 'final_item') {
5974
                        $move_item_icon = Display::url(
5975
                            Display::return_icon(
5976
                                'move.png',
5977
                                get_lang('Move'),
5978
                                array(),
5979
                                ICON_SIZE_TINY
5980
                            ),
5981
                            $url.'&action=move_item',
5982
                            ['class' => 'btn btn-default']
5983
                        );
5984
                    }
5985
                    $audio_icon = Display::url(
5986
                        Display::return_icon('audio.png', get_lang('UplUpload'), array(), ICON_SIZE_TINY),
5987
                        $url.'&action=add_audio',
5988
                        ['class' => 'btn btn-default']
5989
                    );
5990
                }
5991
            }
5992
            if ($update_audio != 'true') {
5993
                $row = $move_icon.' '.$icon.
5994
                    Display::span($title_cut).
5995
                    Display::tag(
5996
                        'div',
5997
                        "<div class=\"btn-group btn-group-xs\">$previewIcon $audio $edit_icon $forumIcon $prerequisities_icon $move_item_icon $audio_icon $delete_icon</div>",
5998
                        array('class'=>'btn-toolbar button_actions')
5999
                    );
6000
            } else {
6001
                $row = Display::span($title.$icon).Display::span($audio, array('class'=>'button_actions'));
6002
            }
6003
6004
            $parent_id = $arrLP[$i]['parent_item_id'];
6005
            $default_data[$arrLP[$i]['id']] = $row;
6006
            $default_content[$arrLP[$i]['id']] = $arrLP[$i];
6007
6008
            if (empty($parent_id)) {
6009
                $elements[$arrLP[$i]['id']]['data'] = $row;
6010
                $elements[$arrLP[$i]['id']]['type'] = $arrLP[$i]['item_type'];
6011
            } else {
6012
                $parent_arrays = array();
6013
                if ($arrLP[$i]['depth'] > 1) {
6014
                    //Getting list of parents
6015
                    for ($j = 0; $j < $arrLP[$i]['depth']; $j++) {
6016
                        foreach ($arrLP as $item) {
6017
                            if ($item['id'] == $parent_id) {
6018
                                if ($item['parent_item_id'] == 0) {
6019
                                    $parent_id = $item['id'];
6020
                                    break;
6021
                                } else {
6022
                                    $parent_id = $item['parent_item_id'];
6023
                                    if (empty($parent_arrays)) {
6024
                                        $parent_arrays[] = intval($item['id']);
6025
                                    }
6026
                                    $parent_arrays[] = $parent_id;
6027
                                    break;
6028
                                }
6029
                            }
6030
                        }
6031
                    }
6032
                }
6033
6034
                if (!empty($parent_arrays)) {
6035
                    $parent_arrays = array_reverse($parent_arrays);
6036
                    $val = '$elements';
6037
                    $x = 0;
6038
                    foreach ($parent_arrays as $item) {
6039
                        if ($x != count($parent_arrays) - 1) {
6040
                            $val .= '["'.$item.'"]["children"]';
6041
                        } else {
6042
                            $val .= '["'.$item.'"]["children"]';
6043
                        }
6044
                        $x++;
6045
                    }
6046
                    $val .= "";
6047
                    $code_str = $val."[".$arrLP[$i]['id']."][\"load_data\"] = '".$arrLP[$i]['id']."' ; ";
6048
                    eval($code_str);
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
6049
                } else {
6050
                    $elements[$parent_id]['children'][$arrLP[$i]['id']]['data'] = $row;
6051
                    $elements[$parent_id]['children'][$arrLP[$i]['id']]['type'] = $arrLP[$i]['item_type'];
6052
                }
6053
            }
6054
        }
6055
6056
        $list = '<ul id="lp_item_list">';
6057
        $tree = self::print_recursive($elements, $default_data, $default_content);
6058
6059
        if (!empty($tree)) {
6060
            $list .= $tree;
6061
        } else {
6062
            if ($drop_element_here) {
6063
                $list .= Display::return_message(get_lang("DragAndDropAnElementHere"));
6064
            }
6065
        }
6066
        $list .= '</ul>';
6067
6068
        $return .= Display::panelCollapse(
6069
            $this->name,
6070
            $list,
6071
            'scorm-list',
6072
            null,
6073
            'scorm-list-accordion',
6074
            'scorm-list-collapse'
6075
        );
6076
6077
        if ($update_audio == 'true') {
6078
            $return = $return_audio;
6079
        }
6080
6081
        return $return;
6082
    }
6083
6084
    /**
6085
     * @param array $elements
6086
     * @param array $default_data
6087
     * @param array $default_content
6088
     * @return string
6089
     */
6090
    public function print_recursive($elements, $default_data, $default_content)
6091
    {
6092
        $return = '';
6093
        foreach ($elements as $key => $item) {
6094
            if (isset($item['load_data']) || empty($item['data'])) {
6095
                $item['data'] = $default_data[$item['load_data']];
6096
                $item['type'] = $default_content[$item['load_data']]['item_type'];
6097
            }
6098
            $sub_list = '';
6099
            if (isset($item['type']) && $item['type'] == 'dir') {
6100
                $sub_list = Display::tag('li', '', array('class'=>'sub_item empty')); // empty value
6101
            }
6102
            if (empty($item['children'])) {
6103
                $sub_list = Display::tag('ul', $sub_list, array('id'=>'UL_'.$key, 'class'=>'record li_container'));
6104
                $active = null;
6105
                if (isset($_REQUEST['id']) && $key == $_REQUEST['id']) {
6106
                    $active = 'active';
6107
                }
6108
                $return .= Display::tag(
6109
                    'li',
6110
                    Display::div($item['data'], array('class'=>"item_data $active")).$sub_list,
6111
                    array('id'=>$key, 'class'=>'record li_container')
6112
                );
6113
            } else {
6114
                // Sections
6115
                if (isset($item['children'])) {
6116
                    $data = self::print_recursive($item['children'], $default_data, $default_content);
6117
                }
6118
                $sub_list = Display::tag('ul', $sub_list.$data, array('id'=>'UL_'.$key, 'class'=>'record li_container'));
6119
                $return .= Display::tag(
6120
                    'li',
6121
                    Display::div($item['data'], array('class'=>'item_data')).$sub_list,
6122
                    array('id'=>$key, 'class'=>'record li_container')
6123
                );
6124
            }
6125
        }
6126
6127
        return $return;
6128
    }
6129
6130
    /**
6131
     * This function builds the action menu
6132
     * @param bool $returnContent Optional
6133
     * @param bool $showRequirementButtons Optional. Allow show the requirements button
6134
     * @param bool $isConfigPage Optional. If is the config page, show the edit button
6135
     * @param bool $allowExpand Optional. Allow show the expand/contract button
6136
     * @return string
6137
     */
6138
    public function build_action_menu($returnContent = false, $showRequirementButtons = true, $isConfigPage = false, $allowExpand = true)
6139
    {
6140
        $gradebook = isset($_GET['gradebook']) ? Security::remove_XSS($_GET['gradebook']) : null;
6141
        $actionsLeft = '';
6142
        $actionsRight = '';
6143
6144
        $actionsLeft .= Display::url(
6145
            Display:: return_icon('preview_view.png', get_lang('Display'), '', ICON_SIZE_MEDIUM),
6146
            'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([
6147
                'gradebook' => $gradebook,
6148
                'action' => 'view',
6149
                'lp_id' => $_SESSION['oLP']->lp_id,
6150
                'isStudentView' => 'true'
6151
            ])
6152
        );
6153
        $actionsLeft .= Display::url(
6154
            Display:: return_icon('upload_audio.png', get_lang('UpdateAllAudioFragments'), '', ICON_SIZE_MEDIUM),
6155
            'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([
6156
                'action' => 'admin_view',
6157
                'lp_id' => $_SESSION['oLP']->lp_id,
6158
                'updateaudio' => 'true'
6159
            ])
6160
        );
6161
6162
        if (!$isConfigPage) {
6163
            $actionsLeft .= Display::url(
6164
                Display::return_icon('settings.png', get_lang('CourseSettings'), '', ICON_SIZE_MEDIUM),
6165
                'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([
6166
                    'action' => 'edit',
6167
                    'lp_id' => $_SESSION['oLP']->lp_id
6168
                ])
6169
            );
6170
        } else {
6171
            $actionsLeft .= Display::url(
6172
                Display::return_icon('edit.png', get_lang('Edit'), '', ICON_SIZE_MEDIUM),
6173
                'lp_controller.php?'.http_build_query([
6174
                    'action' => 'build',
6175
                    'lp_id' => $_SESSION['oLP']->lp_id
6176
                ]).'&'.api_get_cidreq()
6177
            );
6178
        }
6179
6180
        if ($allowExpand) {
6181
            $actionsLeft .= Display::url(
6182
                Display::return_icon('expand.png', get_lang('Expand'), array('id' => 'expand'), ICON_SIZE_MEDIUM).
6183
                Display::return_icon('contract.png', get_lang('Collapse'), array('id' => 'contract', 'class' => 'hide'), ICON_SIZE_MEDIUM),
6184
                '#',
6185
                ['role' => 'button', 'id' => 'hide_bar_template']
6186
            );
6187
        }
6188
6189
        if ($showRequirementButtons) {
6190
            $buttons = array(
6191
                array(
6192
                    'title' => get_lang('SetPrerequisiteForEachItem'),
6193
                    'href' => 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([
6194
                        'action' => 'set_previous_step_as_prerequisite',
6195
                        'lp_id' => $_SESSION['oLP']->lp_id
6196
                    ])
6197
                ),
6198
                array(
6199
                    'title' => get_lang('ClearAllPrerequisites'),
6200
                    'href' => 'lp_controller.php?'.api_get_cidreq().'&'.http_build_query([
6201
                        'action' => 'clear_prerequisites',
6202
                        'lp_id' => $_SESSION['oLP']->lp_id
6203
                    ])
6204
                ),
6205
            );
6206
            $actionsRight = Display::groupButtonWithDropDown(get_lang('PrerequisitesOptions'), $buttons, true);
6207
        }
6208
6209
        $toolbar = Display::toolbarAction('actions-lp-controller', array($actionsLeft, $actionsRight));
6210
6211
        if ($returnContent) {
6212
6213
            return $toolbar;
6214
        }
6215
6216
        echo $toolbar;
6217
    }
6218
6219
    /**
6220
     * Creates the default learning path folder
6221
     * @param array $course
6222
     * @param int $creatorId
6223
     *
6224
     * @return bool
6225
     */
6226
    public static function generate_learning_path_folder($course, $creatorId = 0)
6227
    {
6228
        // Creating learning_path folder
6229
        $dir = '/learning_path';
6230
        $filepath = api_get_path(SYS_COURSE_PATH).$course['path'].'/document';
6231
        $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId;
6232
6233
        $folder = false;
6234
        if (!is_dir($filepath.'/'.$dir)) {
6235
            $folderData = create_unexisting_directory(
6236
                $course,
6237
                $creatorId,
6238
                0,
6239
                0,
6240
                0,
6241
                $filepath,
6242
                $dir,
6243
                get_lang('LearningPaths'),
6244
                0
6245
            );
6246
            if (!empty($folderData)) {
6247
                $folder = true;
6248
            }
6249
        } else {
6250
            $folder = true;
6251
        }
6252
6253
        return $folder;
6254
    }
6255
6256
    /**
6257
     * @param array $course
6258
     * @param string $lp_name
6259
     * @param int $creatorId
6260
     *
6261
     * @return array
6262
     */
6263
    public function generate_lp_folder($course, $lp_name = '', $creatorId = 0)
6264
    {
6265
        $filepath = '';
6266
        $dir = '/learning_path/';
6267
6268
        if (empty($lp_name)) {
6269
            $lp_name = $this->name;
6270
        }
6271
        $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId;
6272
6273
        $folder = self::generate_learning_path_folder($course, $creatorId);
6274
6275
        // Limits title size
6276
        $title = api_substr(api_replace_dangerous_char($lp_name), 0, 80);
6277
        $dir = $dir.$title;
6278
6279
        // Creating LP folder
6280
        $documentId = null;
6281
6282
        if ($folder) {
6283
            $filepath = api_get_path(SYS_COURSE_PATH).$course['path'].'/document';
6284
            if (!is_dir($filepath.'/'.$dir)) {
6285
                $folderData = create_unexisting_directory(
6286
                    $course,
6287
                    $creatorId,
6288
                    0,
6289
                    0,
6290
                    0,
6291
                    $filepath,
6292
                    $dir,
6293
                    $lp_name
6294
                );
6295
                if (!empty($folderData)) {
6296
                    $folder = true;
6297
                }
6298
6299
                $documentId = $folderData['id'];
6300
            } else {
6301
                $folder = true;
6302
            }
6303
            $dir = $dir.'/';
6304
            if ($folder) {
6305
                $filepath = api_get_path(SYS_COURSE_PATH).$course['path'].'/document'.$dir;
6306
            }
6307
        }
6308
6309
        if (empty($documentId)) {
6310
            $dir = api_remove_trailing_slash($dir);
6311
            $documentId = DocumentManager::get_document_id($course, $dir, 0);
6312
        }
6313
6314
        $array = array(
6315
            'dir' => $dir,
6316
            'filepath' => $filepath,
6317
            'folder' => $folder,
6318
            'id' => $documentId
6319
        );
6320
6321
        return $array;
6322
    }
6323
6324
    /**
6325
     * Create a new document //still needs some finetuning
6326
     * @param array $courseInfo
6327
     * @param string $content
6328
     * @param string $title
6329
     * @param string $extension
6330
     * @param int $parentId
6331
     * @param int $creatorId creator id
6332
     *
6333
     * @return string
6334
     */
6335
    public function create_document(
6336
        $courseInfo,
6337
        $content = '',
6338
        $title = '',
6339
        $extension = 'html',
6340
        $parentId = 0,
6341
        $creatorId = 0
6342
    ) {
6343
        if (!empty($courseInfo)) {
6344
            $course_id = $courseInfo['real_id'];
6345
        } else {
6346
            $course_id = api_get_course_int_id();
6347
        }
6348
6349
       $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId;
6350
       $sessionId = api_get_session_id();
6351
6352
        // Generates folder
6353
        $result = $this->generate_lp_folder($courseInfo);
6354
        $dir = $result['dir'];
6355
6356
        if (empty($parentId) || $parentId == '/') {
6357
            $postDir = isset($_POST['dir']) ? $_POST['dir'] : $dir;
6358
            $dir = isset($_GET['dir']) ? $_GET['dir'] : $postDir; // Please, do not modify this dirname formatting.
6359
6360
            if ($parentId === '/') {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $parentId (integer) and '/' (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
6361
                $dir = '/';
6362
            }
6363
6364
            // Please, do not modify this dirname formatting.
6365
            if (strstr($dir, '..')) {
6366
                $dir = '/';
6367
            }
6368
6369
            if (!empty($dir[0]) && $dir[0] == '.') {
6370
                $dir = substr($dir, 1);
6371
            }
6372
            if (!empty($dir[0]) && $dir[0] != '/') {
6373
                $dir = '/'.$dir;
6374
            }
6375 View Code Duplication
            if (isset($dir[strlen($dir) - 1]) && $dir[strlen($dir) - 1] != '/') {
6376
                $dir .= '/';
6377
            }
6378
        } else {
6379
            $parentInfo = DocumentManager::get_document_data_by_id($parentId, $courseInfo['code']);
6380
            if (!empty($parentInfo)) {
6381
                $dir = $parentInfo['path'].'/';
6382
            }
6383
        }
6384
6385
        $filepath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/'.$dir;
6386 View Code Duplication
        if (!is_dir($filepath)) {
6387
            $dir = '/';
6388
            $filepath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/'.$dir;
6389
        }
6390
6391
        // stripslashes() before calling api_replace_dangerous_char() because $_POST['title']
6392
        // is already escaped twice when it gets here.
6393
6394
        $originalTitle = !empty($title) ? $title : $_POST['title'];
6395
        if (!empty($title)) {
6396
            $title = api_replace_dangerous_char(stripslashes($title));
6397
        } else {
6398
            $title = api_replace_dangerous_char(stripslashes($_POST['title']));
6399
        }
6400
6401
        $title = disable_dangerous_file($title);
6402
        $filename = $title;
6403
        $content = !empty($content) ? $content : $_POST['content_lp'];
6404
        $tmp_filename = $filename;
6405
6406
        $i = 0;
6407 View Code Duplication
        while (file_exists($filepath.$tmp_filename.'.'.$extension)) {
6408
            $tmp_filename = $filename.'_'.++ $i;
6409
        }
6410
6411
        $filename = $tmp_filename.'.'.$extension;
6412
        if ($extension == 'html') {
6413
            $content = stripslashes($content);
6414
            $content = str_replace(
6415
                api_get_path(WEB_COURSE_PATH),
6416
                api_get_path(REL_PATH).'courses/',
6417
                $content
6418
            );
6419
6420
            // Change the path of mp3 to absolute.
6421
6422
            // The first regexp deals with :// urls.
6423
            $content = preg_replace(
6424
                "|(flashvars=\"file=)([^:/]+)/|",
6425
                "$1".api_get_path(
6426
                    REL_COURSE_PATH
6427
                ).$courseInfo['path'].'/document/',
6428
                $content
6429
            );
6430
            // The second regexp deals with audio/ urls.
6431
            $content = preg_replace(
6432
                "|(flashvars=\"file=)([^/]+)/|",
6433
                "$1".api_get_path(
6434
                    REL_COURSE_PATH
6435
                ).$courseInfo['path'].'/document/$2/',
6436
                $content
6437
            );
6438
            // For flv player: To prevent edition problem with firefox, we have to use a strange tip (don't blame me please).
6439
            $content = str_replace(
6440
                '</body>',
6441
                '<style type="text/css">body{}</style></body>',
6442
                $content
6443
            );
6444
        }
6445
6446
        if (!file_exists($filepath.$filename)) {
6447
            if ($fp = @ fopen($filepath.$filename, 'w')) {
6448
                fputs($fp, $content);
6449
                fclose($fp);
6450
6451
                $file_size = filesize($filepath.$filename);
6452
                $save_file_path = $dir.$filename;
6453
6454
                $document_id = add_document(
6455
                    $courseInfo,
6456
                    $save_file_path,
6457
                    'file',
6458
                    $file_size,
6459
                    $tmp_filename,
6460
                    '',
6461
                    0, //readonly
6462
                    true,
6463
                    null,
6464
                    $sessionId,
6465
                    $creatorId
6466
                );
6467
6468
                if ($document_id) {
6469
                    api_item_property_update(
6470
                        $courseInfo,
6471
                        TOOL_DOCUMENT,
6472
                        $document_id,
6473
                        'DocumentAdded',
6474
                        $creatorId,
6475
                        null,
6476
                        null,
6477
                        null,
6478
                        null,
6479
                        $sessionId
6480
                    );
6481
6482
                    $new_comment = isset($_POST['comment']) ? trim($_POST['comment']) : '';
6483
                    $new_title = $originalTitle;
6484
6485
                    if ($new_comment || $new_title) {
6486
                        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
6487
                        $ct = '';
6488
                        if ($new_comment)
6489
                            $ct .= ", comment='".Database::escape_string($new_comment)."'";
6490
                        if ($new_title)
6491
                            $ct .= ", title='".Database::escape_string($new_title)."' ";
6492
6493
                        $sql = "UPDATE ".$tbl_doc." SET ".substr($ct, 1)."
6494
                               WHERE c_id = ".$course_id." AND id = ".$document_id;
6495
                        Database::query($sql);
6496
                    }
6497
                }
6498
                return $document_id;
6499
            }
6500
        }
6501
    }
6502
6503
    /**
6504
     * Edit a document based on $_POST and $_GET parameters 'dir' and 'path'
6505
     * @param 	array $_course array
6506
     * @return 	void
6507
     */
6508
    public function edit_document($_course)
6509
    {
6510
        $course_id = api_get_course_int_id();
6511
        $urlAppend = api_get_configuration_value('url_append');
6512
        // Please, do not modify this dirname formatting.
6513
        $postDir = isset($_POST['dir']) ? $_POST['dir'] : '';
6514
        $dir = isset($_GET['dir']) ? $_GET['dir'] : $postDir;
6515
6516
        if (strstr($dir, '..')) {
6517
            $dir = '/';
6518
        }
6519
6520
        if (isset($dir[0]) && $dir[0] == '.') {
6521
            $dir = substr($dir, 1);
6522
        }
6523
6524
        if (isset($dir[0]) && $dir[0] != '/') {
6525
            $dir = '/'.$dir;
6526
        }
6527
6528 View Code Duplication
        if (isset($dir[strlen($dir) - 1]) && $dir[strlen($dir) - 1] != '/') {
6529
            $dir .= '/';
6530
        }
6531
6532
        $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'.$dir;
6533
6534 View Code Duplication
        if (!is_dir($filepath)) {
6535
            $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/';
6536
        }
6537
6538
        $table_doc = Database::get_course_table(TABLE_DOCUMENT);
6539
6540
        if (isset($_POST['path']) && !empty($_POST['path'])) {
6541
            $document_id = intval($_POST['path']);
6542
            $sql = "SELECT path FROM ".$table_doc."
6543
                    WHERE c_id = $course_id AND id = ".$document_id;
6544
            $res = Database::query($sql);
6545
            $row = Database::fetch_array($res);
6546
            $content = stripslashes($_POST['content_lp']);
6547
            $file = $filepath.$row['path'];
6548
6549
            if ($fp = @ fopen($file, 'w')) {
6550
                $content = str_replace(api_get_path(WEB_COURSE_PATH), $urlAppend.api_get_path(REL_COURSE_PATH), $content);
6551
                // Change the path of mp3 to absolute.
6552
                // The first regexp deals with :// urls.
6553
                $content = preg_replace("|(flashvars=\"file=)([^:/]+)/|", "$1".api_get_path(REL_COURSE_PATH).$_course['path'].'/document/', $content);
6554
                // The second regexp deals with audio/ urls.
6555
                $content = preg_replace("|(flashvars=\"file=)([^:/]+)/|", "$1".api_get_path(REL_COURSE_PATH).$_course['path'].'/document/$2/', $content);
6556
                fputs($fp, $content);
6557
                fclose($fp);
6558
6559
                $sql = "UPDATE ".$table_doc." SET
6560
                            title='".Database::escape_string($_POST['title'])."'
6561
                        WHERE c_id = ".$course_id." AND id = ".$document_id;
6562
6563
                Database::query($sql);
6564
            }
6565
        }
6566
    }
6567
6568
    /**
6569
     * Displays the selected item, with a panel for manipulating the item
6570
     * @param int $item_id
6571
     * @param string $msg
6572
     * @return string
6573
     */
6574
    public function display_item($item_id, $msg = null, $show_actions = true)
6575
    {
6576
        $course_id = api_get_course_int_id();
6577
        $return = '';
6578
        if (is_numeric($item_id)) {
6579
            $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
6580
            $sql = "SELECT lp.* FROM $tbl_lp_item as lp
6581
                    WHERE 
6582
                        c_id = $course_id AND 
6583
                        lp.id = ".intval($item_id);
6584
            $result = Database::query($sql);
6585
            while ($row = Database::fetch_array($result, 'ASSOC')) {
6586
                $_SESSION['parent_item_id'] = $row['item_type'] == 'dir' ? $item_id : 0;
6587
6588
                // Prevents wrong parent selection for document, see Bug#1251.
6589
                if ($row['item_type'] != 'dir') {
6590
                    $_SESSION['parent_item_id'] = $row['parent_item_id'];
6591
                }
6592
6593
                if ($show_actions) {
6594
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6595
                }
6596
                $return .= '<div style="padding:10px;">';
6597
6598
                if ($msg != '') {
6599
                    $return .= $msg;
6600
                }
6601
6602
                $return .= '<h3>'.$row['title'].'</h3>';
6603
6604
                switch ($row['item_type']) {
6605
                    case TOOL_THREAD:
6606
                        $link = $this->rl_get_resource_link_for_learnpath(
6607
                            $course_id,
6608
                            $row['lp_id'],
6609
                            $item_id,
6610
                            0
6611
                        );
6612
                        $return .= Display::url(
6613
                            get_lang('GoToThread'),
6614
                            $link,
6615
                            ['class' => 'btn btn-primary']
6616
                        );
6617
                        break;
6618
                    case TOOL_FORUM:
6619
                        $return .= Display::url(
6620
                            get_lang('GoToForum'),
6621
                            api_get_path(WEB_CODE_PATH).'forum/viewforum.php?'.api_get_cidreq().'&forum='.$row['path'],
6622
                            ['class' => 'btn btn-primary']
6623
                        );
6624
                        break;
6625
                    case TOOL_QUIZ:
6626
                        if (!empty($row['path'])) {
6627
                            $exercise = new Exercise();
6628
                            $exercise->read($row['path']);
6629
                            $return .= $exercise->description.'<br />';
6630
                            $return .= Display::url(
6631
                                get_lang('GoToExercise'),
6632
                                api_get_path(WEB_CODE_PATH).'exercise/overview.php?'.api_get_cidreq().'&exerciseId='.$exercise->id,
6633
                                ['class' => 'btn btn-primary']
6634
                            );
6635
                        }
6636
                        break;
6637
                    case TOOL_LP_FINAL_ITEM:
6638
                        $return .= $this->getSavedFinalItem();
6639
                        break;
6640
                    case TOOL_DOCUMENT:
6641
                        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
6642
                        $sql_doc = "SELECT path FROM ".$tbl_doc."
6643
                                    WHERE c_id = ".$course_id." AND id = ".intval($row['path']);
6644
                        $result = Database::query($sql_doc);
6645
                        $path_file = Database::result($result, 0, 0);
6646
                        $path_parts = pathinfo($path_file);
6647
                        // TODO: Correct the following naive comparisons.
6648
                        if (in_array($path_parts['extension'], array(
6649
                            'html',
6650
                            'txt',
6651
                            'png',
6652
                            'jpg',
6653
                            'JPG',
6654
                            'jpeg',
6655
                            'JPEG',
6656
                            'gif',
6657
                            'swf',
6658
                            'pdf',
6659
                            'htm'
6660
                        ))) {
6661
                            $return .= $this->display_document($row['path'], true, true);
6662
                        }
6663
                        break;
6664
                    case TOOL_HOTPOTATOES:
6665
                        $return .= $this->display_document($row['path'], false, true);
6666
                        break;
6667
6668
                }
6669
                $return .= '</div>';
6670
            }
6671
        }
6672
6673
        return $return;
6674
    }
6675
6676
    /**
6677
     * Shows the needed forms for editing a specific item
6678
     * @param int $item_id
6679
     * @return string
6680
     */
6681
    public function display_edit_item($item_id)
6682
    {
6683
        $course_id = api_get_course_int_id();
6684
        $return = '';
6685
        if (is_numeric($item_id)) {
6686
            $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
6687
            $sql = "SELECT * FROM $tbl_lp_item
6688
                    WHERE c_id = ".$course_id." AND id = ".intval($item_id);
6689
            $res = Database::query($sql);
6690
            $row = Database::fetch_array($res);
6691
            switch ($row['item_type']) {
6692
                case 'dir':
6693
                case 'asset':
6694
                case 'sco':
6695
                    if (isset($_GET['view']) && $_GET['view'] == 'build') {
6696
                        $return .= $this->display_manipulate($item_id, $row['item_type']);
6697
                        $return .= $this->display_item_form(
6698
                            $row['item_type'],
6699
                            get_lang('EditCurrentChapter').' :',
6700
                            'edit',
6701
                            $item_id,
6702
                            $row
6703
                        );
6704
                    } else {
6705
                        $return .= $this->display_item_small_form(
6706
                            $row['item_type'],
6707
                            get_lang('EditCurrentChapter').' :',
6708
                            $row
6709
                        );
6710
                    }
6711
                    break;
6712 View Code Duplication
                case TOOL_DOCUMENT:
6713
                    $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
6714
                    $sql = "SELECT lp.*, doc.path as dir
6715
                            FROM $tbl_lp_item as lp
6716
                            LEFT JOIN $tbl_doc as doc
6717
                            ON (doc.id = lp.path AND lp.c_id = doc.c_id)
6718
                            WHERE
6719
                                lp.c_id = $course_id AND
6720
                                doc.c_id = $course_id AND
6721
                                lp.id = ".intval($item_id);
6722
                    $res_step = Database::query($sql);
6723
                    $row_step = Database::fetch_array($res_step, 'ASSOC');
6724
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6725
                    $return .= $this->display_document_form('edit', $item_id, $row_step);
6726
                    break;
6727
                case TOOL_LINK:
6728
                    $link_id = (string) $row['path'];
6729
                    if (ctype_digit($link_id)) {
6730
                        $tbl_link = Database::get_course_table(TABLE_LINK);
6731
                        $sql_select = 'SELECT url FROM '.$tbl_link.'
6732
                                       WHERE c_id = '.$course_id.' AND id = '.intval($link_id);
6733
                        $res_link = Database::query($sql_select);
6734
                        $row_link = Database::fetch_array($res_link);
6735
                        if (is_array($row_link)) {
6736
                            $row['url'] = $row_link['url'];
6737
                        }
6738
                    }
6739
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6740
                    $return .= $this->display_link_form('edit', $item_id, $row);
6741
                    break;
6742 View Code Duplication
                case TOOL_LP_FINAL_ITEM:
6743
                    Session::write('finalItem', true);
6744
                    $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
6745
                    $sql = "SELECT lp.*, doc.path as dir
6746
                            FROM $tbl_lp_item as lp
6747
                            LEFT JOIN $tbl_doc as doc
6748
                            ON (doc.id = lp.path AND lp.c_id = doc.c_id)
6749
                            WHERE
6750
                                lp.c_id = $course_id AND
6751
                                doc.c_id = $course_id AND
6752
                                lp.id = ".intval($item_id);
6753
                    $res_step = Database::query($sql);
6754
                    $row_step = Database::fetch_array($res_step, 'ASSOC');
6755
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6756
                    $return .= $this->display_document_form('edit', $item_id, $row_step);
6757
                    break;
6758 View Code Duplication
                case TOOL_QUIZ:
6759
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6760
                    $return .= $this->display_quiz_form('edit', $item_id, $row);
6761
                    break;
6762
                case TOOL_HOTPOTATOES:
6763
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6764
                    $return .= $this->display_hotpotatoes_form('edit', $item_id, $row);
6765
                    break;
6766 View Code Duplication
                case TOOL_STUDENTPUBLICATION:
6767
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6768
                    $return .= $this->display_student_publication_form('edit', $item_id, $row);
6769
                    break;
6770
                case TOOL_FORUM:
6771
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6772
                    $return .= $this->display_forum_form('edit', $item_id, $row);
6773
                    break;
6774 View Code Duplication
                case TOOL_THREAD:
6775
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6776
                    $return .= $this->display_thread_form('edit', $item_id, $row);
6777
                    break;
6778
            }
6779
        }
6780
6781
        return $return;
6782
    }
6783
6784
    /**
6785
     * Function that displays a list with al the resources that
6786
     * could be added to the learning path
6787
     * @return string
6788
     */
6789
    public function display_resources()
6790
    {
6791
        $course_code = api_get_course_id();
6792
6793
        // Get all the docs.
6794
        $documents = $this->get_documents(true);
6795
6796
        // Get all the exercises.
6797
        $exercises = $this->get_exercises();
6798
6799
        // Get all the links.
6800
        $links = $this->get_links();
6801
6802
        // Get all the student publications.
6803
        $works = $this->get_student_publications();
6804
6805
        // Get all the forums.
6806
        $forums = $this->get_forums(null, $course_code);
6807
6808
        // Get the final item form (see BT#11048) .
6809
        $finish = $this->getFinalItemForm();
6810
6811
        $headers = array(
6812
            Display::return_icon('folder_document.png', get_lang('Documents'), array(), ICON_SIZE_BIG),
6813
            Display::return_icon('quiz.png', get_lang('Quiz'), array(), ICON_SIZE_BIG),
6814
            Display::return_icon('links.png', get_lang('Links'), array(), ICON_SIZE_BIG),
6815
            Display::return_icon('works.png', get_lang('Works'), array(), ICON_SIZE_BIG),
6816
            Display::return_icon('forum.png', get_lang('Forums'), array(), ICON_SIZE_BIG),
6817
            Display::return_icon('add_learnpath_section.png', get_lang('NewChapter'), array(), ICON_SIZE_BIG),
6818
            Display::return_icon('certificate.png', get_lang('Certificate'), [], ICON_SIZE_BIG),
6819
        );
6820
6821
        echo Display::return_message(get_lang('ClickOnTheLearnerViewToSeeYourLearningPath'), 'normal');
6822
        $dir = $_SESSION['oLP']->display_item_form('dir', get_lang('EnterDataNewChapter'), 'add_item');
6823
        echo Display::tabs(
6824
            $headers,
6825
            array(
6826
                $documents,
6827
                $exercises,
6828
                $links,
6829
                $works,
6830
                $forums,
6831
                $dir,
6832
                $finish,
6833
            ),
6834
            'resource_tab'
6835
        );
6836
6837
        return true;
6838
    }
6839
6840
    /**
6841
     * Returns the extension of a document
6842
     * @param string $filename
6843
     * @return string Extension (part after the last dot)
6844
     */
6845
    public function get_extension($filename)
6846
    {
6847
        $explode = explode('.', $filename);
6848
        return $explode[count($explode) - 1];
6849
    }
6850
6851
    /**
6852
     * Displays a document by id
6853
     *
6854
     * @param int $id
6855
     * @return string
6856
     */
6857
    public function display_document($id, $show_title = false, $iframe = true, $edit_link = false)
6858
    {
6859
        $_course = api_get_course_info();
6860
        $course_id = api_get_course_int_id();
6861
        $return = '';
6862
        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
6863
        $sql_doc = "SELECT * FROM ".$tbl_doc."
6864
                    WHERE c_id = ".$course_id." AND id = ".$id;
6865
        $res_doc = Database::query($sql_doc);
6866
        $row_doc = Database::fetch_array($res_doc);
6867
6868
        // TODO: Add a path filter.
6869
        if ($iframe) {
6870
            $return .= '<iframe id="learnpath_preview_frame" frameborder="0" height="400" width="100%" scrolling="auto" src="'.api_get_path(WEB_COURSE_PATH).$_course['path'].'/document'.str_replace('%2F', '/', urlencode($row_doc['path'])).'?'.api_get_cidreq().'"></iframe>';
6871
        } else {
6872
            $return .= file_get_contents(api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/'.$row_doc['path']);
6873
        }
6874
6875
        return $return;
6876
    }
6877
6878
    /**
6879
     * Return HTML form to add/edit a quiz
6880
     * @param	string	$action Action (add/edit)
6881
     * @param	integer	$id Item ID if already exists
6882
     * @param	mixed	$extra_info Extra information (quiz ID if integer)
6883
     * @return	string	HTML form
6884
     */
6885
    public function display_quiz_form($action = 'add', $id = 0, $extra_info = '')
6886
    {
6887
        $course_id = api_get_course_int_id();
6888
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
6889
        $tbl_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
6890
6891
        if ($id != 0 && is_array($extra_info)) {
6892
            $item_title = $extra_info['title'];
6893
            $item_description = $extra_info['description'];
6894
        } elseif (is_numeric($extra_info)) {
6895
            $sql = "SELECT title, description
6896
                    FROM $tbl_quiz
6897
                    WHERE c_id = $course_id AND id = ".$extra_info;
6898
6899
            $result = Database::query($sql);
6900
            $row = Database::fetch_array($result);
6901
            $item_title = $row['title'];
6902
            $item_description = $row['description'];
6903
        } else {
6904
            $item_title = '';
6905
            $item_description = '';
6906
        }
6907
        $item_title = Security::remove_XSS($item_title);
6908
        $item_description = Security::remove_XSS($item_description);
6909
6910 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
6911
            $parent = $extra_info['parent_item_id'];
6912
        } else {
6913
            $parent = 0;
6914
        }
6915
6916
        $sql = "SELECT * FROM $tbl_lp_item 
6917
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
6918
6919
        $result = Database::query($sql);
6920
        $arrLP = array();
6921 View Code Duplication
        while ($row = Database::fetch_array($result)) {
6922
            $arrLP[] = array(
6923
                'id' => $row['id'],
6924
                'item_type' => $row['item_type'],
6925
                'title' => $row['title'],
6926
                'path' => $row['path'],
6927
                'description' => $row['description'],
6928
                'parent_item_id' => $row['parent_item_id'],
6929
                'previous_item_id' => $row['previous_item_id'],
6930
                'next_item_id' => $row['next_item_id'],
6931
                'display_order' => $row['display_order'],
6932
                'max_score' => $row['max_score'],
6933
                'min_score' => $row['min_score'],
6934
                'mastery_score' => $row['mastery_score'],
6935
                'prerequisite' => $row['prerequisite'],
6936
                'max_time_allowed' => $row['max_time_allowed']
6937
            );
6938
        }
6939
6940
        $this->tree_array($arrLP);
6941
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
6942
        unset($this->arrMenu);
6943
6944
        $form = new FormValidator(
6945
            'quiz_form',
6946
            'POST',
6947
            $this->getCurrentBuildingModeURL()
6948
        );
6949
        $defaults = [];
6950
6951
        if ($action == 'add') {
6952
            $legend = get_lang('CreateTheExercise');
6953
        } elseif ($action == 'move') {
6954
            $legend = get_lang('MoveTheCurrentExercise');
6955
        } else {
6956
            $legend = get_lang('EditCurrentExecice');
6957
        }
6958
6959 View Code Duplication
        if (isset ($_GET['edit']) && $_GET['edit'] == 'true') {
6960
            $legend .= Display::return_message(get_lang('Warning').' ! '.get_lang('WarningEditingDocument'));
6961
        }
6962
6963
        $form->addHeader($legend);
6964
6965
        if ($action != 'move') {
6966
            $form->addText('title', get_lang('Title'), true, ['id' => 'idTitle']);
6967
            $defaults['title'] = $item_title;
6968
        }
6969
6970
        // Select for Parent item, root or chapter
6971
        $selectParent = $form->addSelect(
6972
            'parent',
6973
            get_lang('Parent'),
6974
            [],
6975
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);']
6976
        );
6977
        $selectParent->addOption($this->name, 0);
6978
6979
        $arrHide = array(
6980
            $id
6981
        );
6982 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
6983
            if ($action != 'add') {
6984
                if (
6985
                    ($arrLP[$i]['item_type'] == 'dir') &&
6986
                    !in_array($arrLP[$i]['id'], $arrHide) &&
6987
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
6988
                ) {
6989
                    $selectParent->addOption(
6990
                        $arrLP[$i]['title'],
6991
                        $arrLP[$i]['id'],
6992
                        ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px']
6993
                    );
6994
6995
                    if ($parent == $arrLP[$i]['id']) {
6996
                        $selectParent->setSelected($arrLP[$i]['id']);
6997
                    }
6998
                } else {
6999
                    $arrHide[] = $arrLP[$i]['id'];
7000
                }
7001
            } else {
7002
                if ($arrLP[$i]['item_type'] == 'dir') {
7003
                    $selectParent->addOption(
7004
                        $arrLP[$i]['title'],
7005
                        $arrLP[$i]['id'], ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px']
7006
                    );
7007
7008
                    if ($parent == $arrLP[$i]['id']) {
7009
                        $selectParent->setSelected($arrLP[$i]['id']);
7010
                    }
7011
                }
7012
            }
7013
        }
7014
        if (is_array($arrLP)) {
7015
            reset($arrLP);
7016
        }
7017
7018
        $selectPrevious = $form->addSelect('previous', get_lang('Position'), [], ['id' => 'previous']);
7019
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
7020
7021 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7022
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
7023
                $selectPrevious->addOption(get_lang('After').' "'.$arrLP[$i]['title'].'"', $arrLP[$i]['id']);
7024
7025
                if (is_array($extra_info)) {
7026
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7027
                        $selectPrevious->setSelected($arrLP[$i]['id']);
7028
                    }
7029
                } elseif ($action == 'add') {
7030
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7031
                }
7032
            }
7033
        }
7034
7035
        if ($action != 'move') {
7036
            $id_prerequisite = 0;
7037
            if (is_array($arrLP)) {
7038
                foreach ($arrLP as $key => $value) {
7039
                    if ($value['id'] == $id) {
7040
                        $id_prerequisite = $value['prerequisite'];
7041
                        break;
7042
                    }
7043
                }
7044
            }
7045
            $arrHide = array();
7046
            for ($i = 0; $i < count($arrLP); $i++) {
7047
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') {
7048
                    if (is_array($extra_info)) {
7049
                        if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7050
                            $s_selected_position = $arrLP[$i]['id'];
7051
                        }
7052
                    } elseif ($action == 'add') {
7053
                        $s_selected_position = 0;
7054
                    }
7055
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7056
                }
7057
            }
7058
        }
7059
7060
        if ($action == 'add') {
7061
            $form->addButtonSave(get_lang('AddExercise'), 'submit_button');
7062
        } else {
7063
            $form->addButtonSave(get_lang('EditCurrentExecice'), 'submit_button');
7064
        }
7065
7066
        if ($action == 'move') {
7067
            $form->addHidden('title', $item_title);
7068
            $form->addHidden('description', $item_description);
7069
        }
7070
7071 View Code Duplication
        if (is_numeric($extra_info)) {
7072
            $form->addHidden('path', $extra_info);
7073
        } elseif (is_array($extra_info)) {
7074
            $form->addHidden('path', $extra_info['path']);
7075
        }
7076
7077
        $form->addHidden('type', TOOL_QUIZ);
7078
        $form->addHidden('post_time', time());
7079
        $form->setDefaults($defaults);
7080
7081
        return '<div class="sectioncomment">'.$form->returnForm().'</div>';
7082
    }
7083
7084
    /**
7085
     * Addition of Hotpotatoes tests
7086
     * @param	string	Action
7087
     * @param	integer	Internal ID of the item
7088
     * @param	mixed	Extra information - can be an array with title and description indexes
7089
     * @return  string	HTML structure to display the hotpotatoes addition formular
7090
     */
7091
    public function display_hotpotatoes_form($action = 'add', $id = 0, $extra_info = '')
7092
    {
7093
        $course_id = api_get_course_int_id();
7094
        $uploadPath = DIR_HOTPOTATOES; //defined in main_api
7095
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
7096
7097
        if ($id != 0 && is_array($extra_info)) {
7098
            $item_title = stripslashes($extra_info['title']);
7099
            $item_description = stripslashes($extra_info['description']);
7100
        } elseif (is_numeric($extra_info)) {
7101
            $TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
7102
7103
            $sql = "SELECT * FROM ".$TBL_DOCUMENT."
7104
                    WHERE
7105
                        c_id = ".$course_id." AND
7106
                        path LIKE '" . $uploadPath."/%/%htm%' AND
7107
                        id = " . (int) $extra_info."
7108
                    ORDER BY id ASC";
7109
7110
            $res_hot = Database::query($sql);
7111
            $row = Database::fetch_array($res_hot);
7112
7113
            $item_title = $row['title'];
7114
            $item_description = $row['description'];
7115
7116
            if (!empty ($row['comment'])) {
7117
                $item_title = $row['comment'];
7118
            }
7119
        } else {
7120
            $item_title = '';
7121
            $item_description = '';
7122
        }
7123
7124 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
7125
            $parent = $extra_info['parent_item_id'];
7126
        } else {
7127
            $parent = 0;
7128
        }
7129
7130
        $sql = "SELECT * FROM $tbl_lp_item
7131
                WHERE c_id = ".$course_id." AND lp_id = ".$this->lp_id;
7132
        $result = Database::query($sql);
7133
        $arrLP = array();
7134 View Code Duplication
        while ($row = Database::fetch_array($result)) {
7135
            $arrLP[] = array(
7136
                'id' => $row['id'],
7137
                'item_type' => $row['item_type'],
7138
                'title' => $row['title'],
7139
                'path' => $row['path'],
7140
                'description' => $row['description'],
7141
                'parent_item_id' => $row['parent_item_id'],
7142
                'previous_item_id' => $row['previous_item_id'],
7143
                'next_item_id' => $row['next_item_id'],
7144
                'display_order' => $row['display_order'],
7145
                'max_score' => $row['max_score'],
7146
                'min_score' => $row['min_score'],
7147
                'mastery_score' => $row['mastery_score'],
7148
                'prerequisite' => $row['prerequisite'],
7149
                'max_time_allowed' => $row['max_time_allowed']
7150
            );
7151
        }
7152
7153
        $legend = '<legend>';
7154
        if ($action == 'add') {
7155
            $legend .= get_lang('CreateTheExercise');
7156
        } elseif ($action == 'move') {
7157
            $legend .= get_lang('MoveTheCurrentExercise');
7158
        } else {
7159
            $legend .= get_lang('EditCurrentExecice');
7160
        }
7161 View Code Duplication
        if (isset ($_GET['edit']) && $_GET['edit'] == 'true') {
7162
            $legend .= Display:: return_message(
7163
                get_lang('Warning').' ! '.get_lang('WarningEditingDocument')
7164
            );
7165
        }
7166
        $legend .= '</legend>';
7167
7168
        $return = '<form method="POST">';
7169
        $return .= $legend;
7170
        $return .= '<table cellpadding="0" cellspacing="0" class="lp_form">';
7171
        $return .= '<tr>';
7172
        $return .= '<td class="label"><label for="idParent">'.get_lang('Parent').' :</label></td>';
7173
        $return .= '<td class="input">';
7174
        $return .= '<select id="idParent" name="parent" onChange="javascript: load_cbo(this.value);" size="1">';
7175
        $return .= '<option class="top" value="0">'.$this->name.'</option>';
7176
        $arrHide = array(
7177
            $id
7178
        );
7179
7180
        if (count($arrLP) > 0) {
7181
            for ($i = 0; $i < count($arrLP); $i++) {
7182
                if ($action != 'add') {
7183
                    if ($arrLP[$i]['item_type'] == 'dir' && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
7184
                        $return .= '<option '.(($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '').'style="padding-left:'.($arrLP[$i]['depth'] * 10).'px;" value="'.$arrLP[$i]['id'].'">'.$arrLP[$i]['title'].'</option>';
7185
                    } else {
7186
                        $arrHide[] = $arrLP[$i]['id'];
7187
                    }
7188
                } else {
7189
                    if ($arrLP[$i]['item_type'] == 'dir')
7190
                        $return .= '<option '.(($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '').'style="padding-left:'.($arrLP[$i]['depth'] * 10).'px;" value="'.$arrLP[$i]['id'].'">'.$arrLP[$i]['title'].'</option>';
7191
                }
7192
            }
7193
            reset($arrLP);
7194
        }
7195
7196
        $return .= '</select>';
7197
        $return .= '</td>';
7198
        $return .= '</tr>';
7199
        $return .= '<tr>';
7200
        $return .= '<td class="label"><label for="previous">'.get_lang('Position').' :</label></td>';
7201
        $return .= '<td class="input">';
7202
        $return .= '<select id="previous" name="previous" size="1">';
7203
        $return .= '<option class="top" value="0">'.get_lang('FirstPosition').'</option>';
7204
7205
        for ($i = 0; $i < count($arrLP); $i++) {
7206
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
7207
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
7208
                    $selected = 'selected="selected" ';
7209
                elseif ($action == 'add') $selected = 'selected="selected" ';
7210
                else
7211
                    $selected = '';
7212
7213
                $return .= '<option '.$selected.'value="'.$arrLP[$i]['id'].'">'.get_lang('After').' "'.$arrLP[$i]['title'].'"</option>';
7214
            }
7215
        }
7216
7217
        $return .= '</select>';
7218
        $return .= '</td>';
7219
        $return .= '</tr>';
7220
7221
        if ($action != 'move') {
7222
            $return .= '<tr>';
7223
            $return .= '<td class="label"><label for="idTitle">'.get_lang('Title').' :</label></td>';
7224
            $return .= '<td class="input"><input id="idTitle" name="title" type="text" value="'.$item_title.'" /></td>';
7225
            $return .= '</tr>';
7226
            $id_prerequisite = 0;
7227 View Code Duplication
            if (is_array($arrLP) && count($arrLP) > 0) {
7228
                foreach ($arrLP as $key => $value) {
7229
                    if ($value['id'] == $id) {
7230
                        $id_prerequisite = $value['prerequisite'];
7231
                        break;
7232
                    }
7233
                }
7234
7235
                $arrHide = array();
7236
                for ($i = 0; $i < count($arrLP); $i++) {
7237
                    if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') {
7238
                        if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
7239
                            $s_selected_position = $arrLP[$i]['id'];
7240
                        elseif ($action == 'add') $s_selected_position = 0;
7241
                        $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7242
                    }
7243
                }
7244
            }
7245
        }
7246
7247
        $return .= '<tr>';
7248
        $return .= '<td>&nbsp; </td><td><button class="save" name="submit_button" action="edit" type="submit">'.get_lang('SaveHotpotatoes').'</button></td>';
7249
        $return .= '</tr>';
7250
        $return .= '</table>';
7251
7252
        if ($action == 'move') {
7253
            $return .= '<input name="title" type="hidden" value="'.$item_title.'" />';
7254
            $return .= '<input name="description" type="hidden" value="'.$item_description.'" />';
7255
        }
7256
7257
        if (is_numeric($extra_info)) {
7258
            $return .= '<input name="path" type="hidden" value="'.$extra_info.'" />';
7259
        } elseif (is_array($extra_info)) {
7260
            $return .= '<input name="path" type="hidden" value="'.$extra_info['path'].'" />';
7261
        }
7262
        $return .= '<input name="type" type="hidden" value="'.TOOL_HOTPOTATOES.'" />';
7263
        $return .= '<input name="post_time" type="hidden" value="'.time().'" />';
7264
        $return .= '</form>';
7265
7266
        return $return;
7267
    }
7268
7269
    /**
7270
     * Return the form to display the forum edit/add option
7271
     * @param	string	Action (add/edit)
7272
     * @param	integer	ID of the lp_item if already exists
7273
     * @param	mixed	Forum ID or title
7274
     * @return	string	HTML form
7275
     */
7276
    public function display_forum_form($action = 'add', $id = 0, $extra_info = '')
7277
    {
7278
        $course_id = api_get_course_int_id();
7279
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
7280
        $tbl_forum = Database::get_course_table(TABLE_FORUM);
7281
7282
        if ($id != 0 && is_array($extra_info)) {
7283
            $item_title = stripslashes($extra_info['title']);
7284
        } elseif (is_numeric($extra_info)) {
7285
            $sql = "SELECT forum_title as title, forum_comment as comment
7286
                    FROM " . $tbl_forum."
7287
                    WHERE c_id = ".$course_id." AND forum_id = ".$extra_info;
7288
7289
            $result = Database::query($sql);
7290
            $row = Database::fetch_array($result);
7291
7292
            $item_title = $row['title'];
7293
            $item_description = $row['comment'];
7294
        } else {
7295
            $item_title = '';
7296
            $item_description = '';
7297
        }
7298
7299 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
7300
            $parent = $extra_info['parent_item_id'];
7301
        } else {
7302
            $parent = 0;
7303
        }
7304
7305
        $sql = "SELECT * FROM ".$tbl_lp_item."
7306
                WHERE
7307
                    c_id = ".$course_id." AND
7308
                    lp_id = " . $this->lp_id;
7309
        $result = Database::query($sql);
7310
        $arrLP = array();
7311 View Code Duplication
        while ($row = Database::fetch_array($result)) {
7312
            $arrLP[] = array(
7313
                'id' => $row['id'],
7314
                'item_type' => $row['item_type'],
7315
                'title' => $row['title'],
7316
                'path' => $row['path'],
7317
                'description' => $row['description'],
7318
                'parent_item_id' => $row['parent_item_id'],
7319
                'previous_item_id' => $row['previous_item_id'],
7320
                'next_item_id' => $row['next_item_id'],
7321
                'display_order' => $row['display_order'],
7322
                'max_score' => $row['max_score'],
7323
                'min_score' => $row['min_score'],
7324
                'mastery_score' => $row['mastery_score'],
7325
                'prerequisite' => $row['prerequisite']
7326
            );
7327
        }
7328
7329
        $this->tree_array($arrLP);
7330
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
7331
        unset($this->arrMenu);
7332
7333 View Code Duplication
        if ($action == 'add') {
7334
            $legend = get_lang('CreateTheForum');
7335
        } elseif ($action == 'move') {
7336
            $legend = get_lang('MoveTheCurrentForum');
7337
        } else {
7338
            $legend = get_lang('EditCurrentForum');
7339
        }
7340
7341
        $form = new FormValidator(
7342
            'forum_form',
7343
            'POST',
7344
            $this->getCurrentBuildingModeURL()
7345
        );
7346
        $defaults = [];
7347
7348
        $form->addHeader($legend);
7349
7350 View Code Duplication
        if ($action != 'move') {
7351
            $form->addText(
7352
                'title',
7353
                get_lang('Title'),
7354
                true,
7355
                ['id' => 'idTitle', 'class' => 'learnpath_item_form']
7356
            );
7357
            $defaults['title'] = $item_title;
7358
        }
7359
7360
        $selectParent = $form->addSelect(
7361
            'parent',
7362
            get_lang('Parent'),
7363
            [],
7364
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);', 'class' => 'learnpath_item_form']
7365
        );
7366
        $selectParent->addOption($this->name, 0);
7367
7368
        $arrHide = array(
7369
            $id
7370
        );
7371
7372
        //$parent_item_id = $_SESSION['parent_item_id'];
7373 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7374
            if ($action != 'add') {
7375
                if ($arrLP[$i]['item_type'] == 'dir' &&
7376
                    !in_array($arrLP[$i]['id'], $arrHide) &&
7377
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
7378
                ) {
7379
                    $selectParent->addOption(
7380
                        $arrLP[$i]['title'],
7381
                        $arrLP[$i]['id'],
7382
                        ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px']
7383
                    );
7384
7385
                    if ($parent == $arrLP[$i]['id']) {
7386
                        $selectParent->setSelected($arrLP[$i]['id']);
7387
                    }
7388
                } else {
7389
                    $arrHide[] = $arrLP[$i]['id'];
7390
                }
7391
            } else {
7392
                if ($arrLP[$i]['item_type'] == 'dir') {
7393
                    $selectParent->addOption(
7394
                        $arrLP[$i]['title'],
7395
                        $arrLP[$i]['id'],
7396
                        ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px']
7397
                    );
7398
7399
                    if ($parent == $arrLP[$i]['id']) {
7400
                        $selectParent->setSelected($arrLP[$i]['id']);
7401
                    }
7402
                }
7403
            }
7404
        }
7405
7406
        if (is_array($arrLP)) {
7407
            reset($arrLP);
7408
        }
7409
7410
        $selectPrevious = $form->addSelect(
7411
            'previous',
7412
            get_lang('Position'),
7413
            [],
7414
            ['id' => 'previous', 'class' => 'learnpath_item_form']
7415
        );
7416
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
7417
7418 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7419
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
7420
                $selectPrevious->addOption(get_lang('After').' "'.$arrLP[$i]['title'].'"', $arrLP[$i]['id']);
7421
7422
                if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7423
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7424
                } elseif ($action == 'add') {
7425
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7426
                }
7427
            }
7428
        }
7429
7430
        if ($action != 'move') {
7431
            $id_prerequisite = 0;
7432
            if (is_array($arrLP)) {
7433
                foreach ($arrLP as $key => $value) {
7434
                    if ($value['id'] == $id) {
7435
                        $id_prerequisite = $value['prerequisite'];
7436
                        break;
7437
                    }
7438
                }
7439
            }
7440
7441
            $arrHide = array();
7442
            for ($i = 0; $i < count($arrLP); $i++) {
7443
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') {
7444
                    if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id'])
7445
                        $s_selected_position = $arrLP[$i]['id'];
7446
                    elseif ($action == 'add') $s_selected_position = 0;
7447
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7448
                }
7449
            }
7450
        }
7451
7452
        if ($action == 'add') {
7453
            $form->addButtonSave(get_lang('AddForumToCourse'), 'submit_button');
7454
        } else {
7455
            $form->addButtonSave(get_lang('EditCurrentForum'), 'submit_button');
7456
        }
7457
7458
        if ($action == 'move') {
7459
            $form->addHidden('title', $item_title);
7460
            $form->addHidden('description', $item_description);
7461
        }
7462
7463 View Code Duplication
        if (is_numeric($extra_info)) {
7464
            $form->addHidden('path', $extra_info);
7465
        } elseif (is_array($extra_info)) {
7466
            $form->addHidden('path', $extra_info['path']);
7467
        }
7468
        $form->addHidden('type', TOOL_FORUM);
7469
        $form->addHidden('post_time', time());
7470
        $form->setDefaults($defaults);
7471
7472
        return '<div class="sectioncomment">'.$form->returnForm().'</div>';
7473
    }
7474
7475
    /**
7476
     * Return HTML form to add/edit forum threads
7477
     * @param	string	Action (add/edit)
7478
     * @param	integer	Item ID if already exists in learning path
7479
     * @param	mixed	Extra information (thread ID if integer)
7480
     * @return 	string	HTML form
7481
     */
7482
    public function display_thread_form($action = 'add', $id = 0, $extra_info = '')
7483
    {
7484
        $course_id = api_get_course_int_id();
7485
        if (empty($course_id)) {
7486
            return null;
7487
        }
7488
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
7489
        $tbl_forum = Database::get_course_table(TABLE_FORUM_THREAD);
7490
7491
        if ($id != 0 && is_array($extra_info)) {
7492
            $item_title = stripslashes($extra_info['title']);
7493 View Code Duplication
        } elseif (is_numeric($extra_info)) {
7494
            $sql = "SELECT thread_title as title FROM $tbl_forum
7495
                    WHERE c_id = $course_id AND thread_id = ".$extra_info;
7496
7497
            $result = Database::query($sql);
7498
            $row = Database::fetch_array($result);
7499
7500
            $item_title = $row['title'];
7501
            $item_description = '';
7502
        } else {
7503
            $item_title = '';
7504
            $item_description = '';
7505
        }
7506
7507 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
7508
            $parent = $extra_info['parent_item_id'];
7509
        } else {
7510
            $parent = 0;
7511
        }
7512
7513
        $sql = "SELECT * FROM ".$tbl_lp_item."
7514
                WHERE c_id = ".$course_id." AND lp_id = ".$this->lp_id;
7515
        $result = Database::query($sql);
7516
7517
        $arrLP = array();
7518 View Code Duplication
        while ($row = Database::fetch_array($result)) {
7519
            $arrLP[] = array(
7520
                'id' => $row['id'],
7521
                'item_type' => $row['item_type'],
7522
                'title' => $row['title'],
7523
                'path' => $row['path'],
7524
                'description' => $row['description'],
7525
                'parent_item_id' => $row['parent_item_id'],
7526
                'previous_item_id' => $row['previous_item_id'],
7527
                'next_item_id' => $row['next_item_id'],
7528
                'display_order' => $row['display_order'],
7529
                'max_score' => $row['max_score'],
7530
                'min_score' => $row['min_score'],
7531
                'mastery_score' => $row['mastery_score'],
7532
                'prerequisite' => $row['prerequisite']
7533
            );
7534
        }
7535
7536
        $this->tree_array($arrLP);
7537
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
7538
        unset($this->arrMenu);
7539
7540
        $form = new FormValidator(
7541
            'thread_form',
7542
            'POST',
7543
            $this->getCurrentBuildingModeURL()
7544
        );
7545
        $defaults = [];
7546
7547 View Code Duplication
        if ($action == 'add') {
7548
            $legend = get_lang('CreateTheForum');
7549
        } elseif ($action == 'move') {
7550
            $legend = get_lang('MoveTheCurrentForum');
7551
        } else {
7552
            $legend = get_lang('EditCurrentForum');
7553
        }
7554
7555
        $form->addHeader($legend);
7556
        $selectParent = $form->addSelect(
7557
            'parent',
7558
            get_lang('Parent'),
7559
            [],
7560
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);']
7561
        );
7562
        $selectParent->addOption($this->name, 0);
7563
7564
        $arrHide = array(
7565
            $id
7566
        );
7567
7568 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7569
            if ($action != 'add') {
7570
                if (
7571
                    ($arrLP[$i]['item_type'] == 'dir') &&
7572
                    !in_array($arrLP[$i]['id'], $arrHide) &&
7573
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
7574
                ) {
7575
                    $selectParent->addOption(
7576
                        $arrLP[$i]['title'],
7577
                        $arrLP[$i]['id'],
7578
                        ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px']
7579
                    );
7580
7581
                    if ($parent == $arrLP[$i]['id']) {
7582
                        $selectParent->setSelected($arrLP[$i]['id']);
7583
                    }
7584
                } else {
7585
                    $arrHide[] = $arrLP[$i]['id'];
7586
                }
7587
            } else {
7588
                if ($arrLP[$i]['item_type'] == 'dir') {
7589
                    $selectParent->addOption(
7590
                        $arrLP[$i]['title'],
7591
                        $arrLP[$i]['id'],
7592
                        ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px']
7593
                    );
7594
7595
                    if ($parent == $arrLP[$i]['id']) {
7596
                        $selectParent->setSelected($arrLP[$i]['id']);
7597
                    }
7598
                }
7599
            }
7600
        }
7601
7602
        if ($arrLP != null) {
7603
            reset($arrLP);
7604
        }
7605
7606
        $selectPrevious = $form->addSelect('previous', get_lang('Position'), [], ['id' => 'previous']);
7607
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
7608
7609 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7610
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
7611
                $selectPrevious->addOption(
7612
                    get_lang('After').' "'.$arrLP[$i]['title'].'"',
7613
                    $arrLP[$i]['id']
7614
                );
7615
7616
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7617
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7618
                } elseif ($action == 'add') {
7619
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7620
                }
7621
            }
7622
        }
7623
7624
        if ($action != 'move') {
7625
            $form->addText('title', get_lang('Title'), true, ['id' => 'idTitle']);
7626
            $defaults['title'] = $item_title;
7627
7628
            $id_prerequisite = 0;
7629
            if ($arrLP != null) {
7630
                foreach ($arrLP as $key => $value) {
7631
                    if ($value['id'] == $id) {
7632
                        $id_prerequisite = $value['prerequisite'];
7633
                        break;
7634
                    }
7635
                }
7636
            }
7637
7638
            $arrHide = array();
7639
            $s_selected_position = 0;
7640
7641
            for ($i = 0; $i < count($arrLP); $i++) {
7642
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') {
7643
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
7644
                        $s_selected_position = $arrLP[$i]['id'];
7645
                    elseif ($action == 'add') $s_selected_position = 0;
7646
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7647
7648
                }
7649
            }
7650
7651
            $selectPrerequisites = $form->addSelect(
7652
                'prerequisites',
7653
                get_lang('LearnpathPrerequisites'),
7654
                [],
7655
                ['id' => 'prerequisites']
7656
            );
7657
            $selectPrerequisites->addOption(get_lang('NoPrerequisites'), 0);
7658
7659
            foreach ($arrHide as $key => $value) {
7660
                $selectPrerequisites->addOption($value['value'], $key);
7661
7662
                if ($key == $s_selected_position && $action == 'add') {
7663
                    $selectPrerequisites->setSelected($key);
7664
                } elseif ($key == $id_prerequisite && $action == 'edit') {
7665
                    $selectPrerequisites->setSelected($key);
7666
                }
7667
            }
7668
        }
7669
7670
        $form->addButtonSave(get_lang('Ok'), 'submit_button');
7671
7672
        if ($action == 'move') {
7673
            $form->addHidden('title', $item_title);
7674
            $form->addHidden('description', $item_description);
7675
        }
7676
7677 View Code Duplication
        if (is_numeric($extra_info)) {
7678
            $form->addHidden('path', $extra_info);
7679
        }
7680
        elseif (is_array($extra_info)) {
7681
            $form->addHidden('path', $extra_info['path']);
7682
        }
7683
7684
        $form->addHidden('type', TOOL_THREAD);
7685
        $form->addHidden('post_time', time());
7686
        $form->setDefaults($defaults);
7687
7688
        return $form->returnForm();
7689
    }
7690
7691
    /**
7692
     * Return the HTML form to display an item (generally a dir item)
7693
     * @param	string	Item type (dir)
7694
     * @param	string	Title (optional, only when creating)
7695
     * @param	string	Action ('add'/'edit')
7696
     * @param	integer	lp_item ID
7697
     * @param	mixed	Extra info
7698
     * @return	string 	HTML form
7699
     */
7700
    public function display_item_form($item_type, $title = '', $action = 'add_item', $id = 0, $extra_info = 'new')
7701
    {
7702
        $course_id = api_get_course_int_id();
7703
        $_course = api_get_course_info();
7704
7705
        global $charset;
7706
7707
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
7708
7709
        if ($id != 0 && is_array($extra_info)) {
7710
            $item_title = $extra_info['title'];
7711
            $item_description = $extra_info['description'];
7712
            $item_path = api_get_path(WEB_COURSE_PATH).$_course['path'].'/scorm/'.$this->path.'/'.stripslashes($extra_info['path']);
7713
            $item_path_fck = '/scorm/'.$this->path.'/'.stripslashes($extra_info['path']);
7714
        } else {
7715
            $item_title = '';
7716
            $item_description = '';
7717
            $item_path_fck = '';
7718
        }
7719
7720 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
7721
            $parent = $extra_info['parent_item_id'];
7722
        } else {
7723
            $parent = 0;
7724
        }
7725
7726
        $id  = intval($id);
7727
        $sql = "SELECT * FROM ".$tbl_lp_item."
7728
                WHERE
7729
                    c_id = ".$course_id." AND
7730
                    lp_id = " . $this->lp_id." AND
7731
                    id != $id";
7732
7733
        if ($item_type == 'dir') {
7734
            $sql .= " AND parent_item_id = 0";
7735
        }
7736
7737
        $result = Database::query($sql);
7738
        $arrLP = [];
7739
7740 View Code Duplication
        while ($row = Database::fetch_array($result)) {
7741
            $arrLP[] = array(
7742
                'id' => $row['id'],
7743
                'item_type' => $row['item_type'],
7744
                'title' => $row['title'],
7745
                'path' => $row['path'],
7746
                'description' => $row['description'],
7747
                'parent_item_id' => $row['parent_item_id'],
7748
                'previous_item_id' => $row['previous_item_id'],
7749
                'next_item_id' => $row['next_item_id'],
7750
                'max_score' => $row['max_score'],
7751
                'min_score' => $row['min_score'],
7752
                'mastery_score' => $row['mastery_score'],
7753
                'prerequisite' => $row['prerequisite'],
7754
                'display_order' => $row['display_order']
7755
            );
7756
        }
7757
7758
        $this->tree_array($arrLP);
7759
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
7760
        unset($this->arrMenu);
7761
7762
        $gradebook = isset($_GET['gradebook']) ? Security::remove_XSS($_GET['gradebook']) : null;
7763
        $url = api_get_self().'?'.api_get_cidreq().'&gradeboook='.$gradebook.'&action='.$action.'&type='.$item_type.'&lp_id='.$this->lp_id;
7764
7765
        $form = new FormValidator('form', 'POST', $url);
7766
7767
        $defaults['title'] = api_html_entity_decode($item_title, ENT_QUOTES, $charset);
7768
        $defaults['description'] = $item_description;
7769
7770
        $form->addElement('header', $title);
7771
7772
        //$arrHide = array($id);
7773
        $arrHide[0]['value'] = Security::remove_XSS($this->name);
7774
        $arrHide[0]['padding'] = 20;
7775
        $charset = api_get_system_encoding();
7776
7777 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7778
            if ($action != 'add') {
7779
                if ($arrLP[$i]['item_type'] == 'dir' && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
7780
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7781
                    $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
7782
                    if ($parent == $arrLP[$i]['id']) {
7783
                        $s_selected_parent = $arrHide[$arrLP[$i]['id']];
7784
                    }
7785
                }
7786
            } else {
7787
                if ($arrLP[$i]['item_type'] == 'dir') {
7788
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7789
                    $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
7790
                    if ($parent == $arrLP[$i]['id']) {
7791
                        $s_selected_parent = $arrHide[$arrLP[$i]['id']];
7792
                    }
7793
                }
7794
            }
7795
        }
7796
7797
        if ($action != 'move') {
7798
            $form->addElement('text', 'title', get_lang('Title'));
7799
            $form->applyFilter('title', 'html_filter');
7800
            $form->addRule('title', get_lang('ThisFieldIsRequired'), 'required');
7801
        } else {
7802
            $form->addElement('hidden', 'title');
7803
        }
7804
7805
        $parent_select = $form->addElement(
7806
            'select',
7807
            'parent',
7808
            get_lang('Parent'),
7809
            '',
7810
            array(
7811
                'id' => 'idParent',
7812
                'onchange' => "javascript: load_cbo(this.value);",
7813
            )
7814
        );
7815
7816
        foreach ($arrHide as $key => $value) {
7817
            $parent_select->addOption($value['value'], $key, 'style="padding-left:'.$value['padding'].'px;"');
7818
        }
7819
        if (!empty($s_selected_parent)) {
7820
            $parent_select->setSelected($s_selected_parent);
7821
        }
7822
7823
        if (is_array($arrLP)) {
7824
            reset($arrLP);
7825
        }
7826
7827
        $arrHide = array();
7828
7829
        // POSITION
7830
        for ($i = 0; $i < count($arrLP); $i++) {
7831
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
7832
                //this is the same!
7833
                if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7834
                    $s_selected_position = $arrLP[$i]['id'];
7835
                } elseif ($action == 'add') {
7836
                    $s_selected_position = $arrLP[$i]['id'];
7837
                }
7838
7839
                $arrHide[$arrLP[$i]['id']]['value'] = get_lang('After').' "'.$arrLP[$i]['title'].'"';
7840
            }
7841
        }
7842
7843
        $position = $form->addElement(
7844
            'select',
7845
            'previous',
7846
            get_lang('Position'),
7847
            '',
7848
            array('id' => 'previous')
7849
        );
7850
        $padding = isset($value['padding']) ? $value['padding'] : 0;
7851
        $position->addOption(get_lang('FirstPosition'), 0, 'style="padding-left:'.$padding.'px;"');
7852
7853
        foreach ($arrHide as $key => $value) {
7854
            $position->addOption($value['value'].'"', $key, 'style="padding-left:'.$padding.'px;"');
7855
        }
7856
7857
        if (!empty ($s_selected_position)) {
7858
            $position->setSelected($s_selected_position);
7859
        }
7860
7861
        if (is_array($arrLP)) {
7862
            reset($arrLP);
7863
        }
7864
7865
        $form->addButtonSave(get_lang('SaveSection'), 'submit_button');
7866
7867
        //fix in order to use the tab
7868
        if ($item_type == 'dir') {
7869
            $form->addElement('hidden', 'type', 'dir');
7870
        }
7871
7872
        $extension = null;
7873
        if (!empty($item_path)) {
7874
            $extension = pathinfo($item_path, PATHINFO_EXTENSION);
7875
        }
7876
7877
        //assets can't be modified
7878
        //$item_type == 'asset' ||
7879
        if (($item_type == 'sco') && ($extension == 'html' || $extension == 'htm')) {
7880
            if ($item_type == 'sco') {
7881
                $form->addElement('html', '<script>alert("'.get_lang('WarningWhenEditingScorm').'")</script>');
7882
            }
7883
            $renderer = $form->defaultRenderer();
7884
            $renderer->setElementTemplate('<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{label}<br />{element}', 'content_lp');
7885
7886
            $relative_prefix = '';
7887
7888
            $editor_config = array(
7889
                'ToolbarSet' => 'LearningPathDocuments',
7890
                'Width' => '100%',
7891
                'Height' => '500',
7892
                'FullPage' => true,
7893
                'CreateDocumentDir' => $relative_prefix,
7894
                'CreateDocumentWebDir' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/scorm/',
7895
                'BaseHref' => api_get_path(WEB_COURSE_PATH).api_get_course_path().$item_path_fck
7896
            );
7897
7898
            $form->addElement('html_editor', 'content_lp', '', null, $editor_config);
7899
            $content_path = api_get_path(SYS_COURSE_PATH).api_get_course_path().$item_path_fck;
7900
            $defaults['content_lp'] = file_get_contents($content_path);
7901
        }
7902
7903
        $form->addElement('hidden', 'type', $item_type);
7904
        $form->addElement('hidden', 'post_time', time());
7905
        $form->setDefaults($defaults);
7906
        return $form->return_form();
0 ignored issues
show
Deprecated Code introduced by
The method FormValidator::return_form() has been deprecated with message: use returnForm()

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

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

Loading history...
7907
    }
7908
7909
    /**
7910
     * @return string
7911
     */
7912
    public function getCurrentBuildingModeURL()
7913
    {
7914
        $pathItem = isset($_GET['path_item']) ? (int) $_GET['path_item'] : '';
7915
        $action = isset($_GET['action']) ? Security::remove_XSS($_GET['action']) : '';
7916
        $id = isset($_GET['id']) ? (int) $_GET['id'] : '';
7917
        $view = isset($_GET['view']) ? Security::remove_XSS($_GET['view']) : '';
7918
7919
        $currentUrl = api_get_self().'?'.api_get_cidreq().'&action='.$action.'&lp_id='.$this->lp_id.'&path_item='.$pathItem.'&view='.$view.'&id='.$id;
7920
7921
        return $currentUrl;
7922
    }
7923
7924
    /**
7925
     * Returns the form to update or create a document
7926
     * @param	string	$action (add/edit)
7927
     * @param	integer	$id ID of the lp_item (if already exists)
7928
     * @param	mixed	$extra_info Integer if document ID, string if info ('new')
7929
     * @return	string	HTML form
7930
     */
7931
    public function display_document_form($action = 'add', $id = 0, $extra_info = 'new')
7932
    {
7933
        $course_id = api_get_course_int_id();
7934
        $_course = api_get_course_info();
7935
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
7936
        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
7937
7938
        $no_display_edit_textarea = false;
7939
        $item_description = '';
7940
        //If action==edit document
7941
        //We don't display the document form if it's not an editable document (html or txt file)
7942
        if ($action == 'edit') {
7943
            if (is_array($extra_info)) {
7944
                $path_parts = pathinfo($extra_info['dir']);
7945
                if ($path_parts['extension'] != "txt" && $path_parts['extension'] != "html") {
7946
                    $no_display_edit_textarea = true;
7947
                }
7948
            }
7949
        }
7950
        $no_display_add = false;
7951
7952
        // If action==add an existing document
7953
        // We don't display the document form if it's not an editable document (html or txt file).
7954
        if ($action == 'add') {
7955
            if (is_numeric($extra_info)) {
7956
                $sql_doc = "SELECT path FROM $tbl_doc 
7957
                            WHERE c_id = $course_id AND id = ".intval($extra_info);
7958
                $result = Database::query($sql_doc);
7959
                $path_file = Database::result($result, 0, 0);
7960
                $path_parts = pathinfo($path_file);
7961
                if ($path_parts['extension'] != 'txt' && $path_parts['extension'] != 'html') {
7962
                    $no_display_add = true;
7963
                }
7964
            }
7965
        }
7966
        if ($id != 0 && is_array($extra_info)) {
7967
            $item_title = stripslashes($extra_info['title']);
7968
            $item_description = stripslashes($extra_info['description']);
7969
            $item_terms = stripslashes($extra_info['terms']);
7970 View Code Duplication
            if (empty ($item_title)) {
7971
                $path_parts = pathinfo($extra_info['path']);
7972
                $item_title = stripslashes($path_parts['filename']);
7973
            }
7974
        } elseif (is_numeric($extra_info)) {
7975
            $sql = "SELECT path, title FROM $tbl_doc
7976
                    WHERE
7977
                        c_id = ".$course_id." AND
7978
                        id = " . intval($extra_info);
7979
            $result = Database::query($sql);
7980
            $row = Database::fetch_array($result);
7981
            $item_title = $row['title'];
7982
            $item_title = str_replace('_', ' ', $item_title);
7983 View Code Duplication
            if (empty ($item_title)) {
7984
                $path_parts = pathinfo($row['path']);
7985
                $item_title = stripslashes($path_parts['filename']);
7986
            }
7987
        } else {
7988
            $item_title = '';
7989
            $item_description = '';
7990
        }
7991
        $return = '<legend>';
7992
7993 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
7994
            $parent = $extra_info['parent_item_id'];
7995
        } else {
7996
            $parent = 0;
7997
        }
7998
7999
        $sql = "SELECT * FROM $tbl_lp_item
8000
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
8001
8002
        $result = Database::query($sql);
8003
        $arrLP = array();
8004 View Code Duplication
        while ($row = Database::fetch_array($result)) {
8005
            $arrLP[] = array(
8006
                'id' => $row['id'],
8007
                'item_type' => $row['item_type'],
8008
                'title' => $row['title'],
8009
                'path' => $row['path'],
8010
                'description' => $row['description'],
8011
                'parent_item_id' => $row['parent_item_id'],
8012
                'previous_item_id' => $row['previous_item_id'],
8013
                'next_item_id' => $row['next_item_id'],
8014
                'display_order' => $row['display_order'],
8015
                'max_score' => $row['max_score'],
8016
                'min_score' => $row['min_score'],
8017
                'mastery_score' => $row['mastery_score'],
8018
                'prerequisite' => $row['prerequisite']
8019
            );
8020
        }
8021
8022
        $this->tree_array($arrLP);
8023
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
8024
        unset($this->arrMenu);
8025
8026
        if ($action == 'add') {
8027
            $return .= get_lang('CreateTheDocument');
8028
        } elseif ($action == 'move') {
8029
            $return .= get_lang('MoveTheCurrentDocument');
8030
        } else {
8031
            $return .= get_lang('EditTheCurrentDocument');
8032
        }
8033
        $return .= '</legend>';
8034
8035
        if (isset($_GET['edit']) && $_GET['edit'] == 'true') {
8036
            $return .= Display::return_message(
8037
                '<strong>'.get_lang('Warning').' !</strong><br />'.get_lang('WarningEditingDocument'),
8038
                false
8039
            );
8040
        }
8041
        $form = new FormValidator(
8042
            'form',
8043
            'POST',
8044
            $this->getCurrentBuildingModeURL(),
8045
            '',
8046
            array('enctype' => 'multipart/form-data')
8047
        );
8048
        $defaults['title'] = Security::remove_XSS($item_title);
8049
        if (empty($item_title)) {
8050
            $defaults['title'] = Security::remove_XSS($item_title);
8051
        }
8052
        $defaults['description'] = $item_description;
8053
        $form->addElement('html', $return);
8054
8055
        if ($action != 'move') {
8056
            $data = $this->generate_lp_folder($_course);
8057
            if ($action != 'edit') {
8058
                $folders = DocumentManager::get_all_document_folders(
8059
                    $_course,
8060
                    0,
8061
                    true
8062
                );
8063
                DocumentManager::build_directory_selector(
8064
                    $folders,
8065
                    '',
8066
                    array(),
8067
                    true,
8068
                    $form,
8069
                    'directory_parent_id'
8070
                );
8071
            }
8072
8073
            if (isset($data['id'])) {
8074
                $defaults['directory_parent_id'] = $data['id'];
8075
            }
8076
8077
            $form->addElement(
8078
                'text',
8079
                'title',
8080
                get_lang('Title'),
8081
                array('id' => 'idTitle', 'class' => 'col-md-4')
8082
            );
8083
            $form->applyFilter('title', 'html_filter');
8084
        }
8085
8086
        $arrHide[0]['value'] = $this->name;
8087
        $arrHide[0]['padding'] = 20;
8088
8089 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
8090
            if ($action != 'add') {
8091
                if ($arrLP[$i]['item_type'] == 'dir' &&
8092
                    !in_array($arrLP[$i]['id'], $arrHide) &&
8093
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
8094
                ) {
8095
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8096
                    $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
8097
                    if ($parent == $arrLP[$i]['id']) {
8098
                        $s_selected_parent = $arrHide[$arrLP[$i]['id']];
8099
                    }
8100
                }
8101
            } else {
8102
                if ($arrLP[$i]['item_type'] == 'dir') {
8103
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8104
                    $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
8105
                    if ($parent == $arrLP[$i]['id']) {
8106
                        $s_selected_parent = $arrHide[$arrLP[$i]['id']];
8107
                    }
8108
                }
8109
            }
8110
        }
8111
8112
        $parent_select = $form->addSelect(
8113
            'parent',
8114
            get_lang('Parent'),
8115
            [],
8116
            [
8117
                'id' => 'idParent',
8118
                'onchange' => 'javascript: load_cbo(this.value);',
8119
            ]
8120
        );
8121
8122
        $my_count = 0;
8123
        foreach ($arrHide as $key => $value) {
8124
            if ($my_count != 0) {
8125
                // The LP name is also the first section and is not in the same charset like the other sections.
8126
                $value['value'] = Security::remove_XSS($value['value']);
8127
                $parent_select->addOption($value['value'], $key, 'style="padding-left:'.$value['padding'].'px;"');
8128
            } else {
8129
                $value['value'] = Security::remove_XSS($value['value']);
8130
                $parent_select->addOption($value['value'], $key, 'style="padding-left:'.$value['padding'].'px;"');
8131
            }
8132
            $my_count++;
8133
        }
8134
8135
        if (!empty($id)) {
8136
            $parent_select->setSelected($parent);
8137
        } else {
8138
            $parent_item_id = isset($_SESSION['parent_item_id']) ? $_SESSION['parent_item_id'] : 0;
8139
            $parent_select->setSelected($parent_item_id);
8140
        }
8141
8142
        if (is_array($arrLP)) {
8143
            reset($arrLP);
8144
        }
8145
8146
        $arrHide = array();
8147
        $s_selected_position = null;
8148
8149
        // POSITION
8150
        $lastPosition = null;
8151
8152
        for ($i = 0; $i < count($arrLP); $i++) {
8153
            if (($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) ||
8154
                $arrLP[$i]['item_type'] == TOOL_LP_FINAL_ITEM
8155
            ) {
8156
                if ((isset($extra_info['previous_item_id']) &&
8157
                    $extra_info['previous_item_id'] == $arrLP[$i]['id']) || $action == 'add'
8158
                ) {
8159
                    $s_selected_position = $arrLP[$i]['id'];
8160
                }
8161
                $arrHide[$arrLP[$i]['id']]['value'] = get_lang('After').' "'.$arrLP[$i]['title'].'"';
8162
            }
8163
            $lastPosition = $arrLP[$i]['id'];
8164
        }
8165
8166
        if (empty($s_selected_position)) {
8167
            $s_selected_position = $lastPosition;
8168
        }
8169
8170
        $position = $form->addSelect('previous', get_lang('Position'), [], ['id' => 'previous']);
8171
        $position->addOption(get_lang('FirstPosition'), 0);
8172
8173
        foreach ($arrHide as $key => $value) {
8174
            $padding = isset($value['padding']) ? $value['padding'] : 20;
8175
            $position->addOption(
8176
                $value['value'],
8177
                $key,
8178
                'style="padding-left:'.$padding.'px;"'
8179
            );
8180
        }
8181
        $position->setSelected($s_selected_position);
8182
8183
        if (is_array($arrLP)) {
8184
            reset($arrLP);
8185
        }
8186
8187
        if ($action != 'move') {
8188
            $id_prerequisite = 0;
8189
            if (is_array($arrLP)) {
8190
                foreach ($arrLP as $key => $value) {
8191
                    if ($value['id'] == $id) {
8192
                        $id_prerequisite = $value['prerequisite'];
8193
                        break;
8194
                    }
8195
                }
8196
            }
8197
8198
            $arrHide = array();
8199
            for ($i = 0; $i < count($arrLP); $i++) {
8200
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir' && $arrLP[$i]['item_type'] !== TOOL_LP_FINAL_ITEM) {
8201
                    if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id'])
8202
                        $s_selected_position = $arrLP[$i]['id'];
8203
                    elseif ($action == 'add') $s_selected_position = $arrLP[$i]['id'];
8204
8205
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8206
8207
                }
8208
            }
8209
8210
            if (!$no_display_add) {
8211
                $item_type = isset($extra_info['item_type']) ? $extra_info['item_type'] : null;
8212
                $edit = isset($_GET['edit']) ? $_GET['edit'] : null;
8213
                if ($extra_info == 'new' || $item_type == TOOL_DOCUMENT || $item_type == TOOL_LP_FINAL_ITEM || $edit == 'true') {
8214
                    if (isset ($_POST['content'])) {
8215
                        $content = stripslashes($_POST['content']);
8216
                    } elseif (is_array($extra_info)) {
8217
                        //If it's an html document or a text file
8218
                        if (!$no_display_edit_textarea) {
8219
                            $content = $this->display_document($extra_info['path'], false, false);
8220
                        }
8221
                    } elseif (is_numeric($extra_info)) {
8222
                        $content = $this->display_document(
8223
                            $extra_info,
8224
                            false,
8225
                            false
8226
                        );
8227
                    } else {
8228
                        $content = '';
8229
                    }
8230
8231
                    if (!$no_display_edit_textarea) {
8232
                        // We need to calculate here some specific settings for the online editor.
8233
                        // The calculated settings work for documents in the Documents tool
8234
                        // (on the root or in subfolders).
8235
                        // For documents in native scorm packages it is unclear whether the
8236
                        // online editor should be activated or not.
8237
8238
                        // A new document, it is in the root of the repository.
8239
                        $relative_path 	 = '';
8240
                        $relative_prefix = '';
8241
8242
                        if (is_array($extra_info) && $extra_info != 'new') {
8243
                            // The document already exists. Whe have to determine its relative path towards the repository root.
8244
                            $relative_path = explode('/', $extra_info['dir']);
8245
                            $cnt = count($relative_path) - 2;
8246
                            if ($cnt < 0) {
8247
                                $cnt = 0;
8248
                            }
8249
                            $relative_prefix = str_repeat('../', $cnt);
8250
                            $relative_path 	 = array_slice($relative_path, 1, $cnt);
8251
                            $relative_path 	 = implode('/', $relative_path);
8252
                            if (strlen($relative_path) > 0) {
8253
                                $relative_path = $relative_path.'/';
8254
                            }
8255
                        } else {
8256
                            $result = $this->generate_lp_folder($_course);
8257
                            $relative_path = api_substr($result['dir'], 1, strlen($result['dir']));
8258
                            $relative_prefix = '../../';
8259
                        }
8260
8261
                        $editor_config = array(
8262
                            'ToolbarSet' => 'LearningPathDocuments',
8263
                            'Width' => '100%',
8264
                            'Height' => '500',
8265
                            'FullPage' => true,
8266
                            'CreateDocumentDir' => $relative_prefix,
8267
                            'CreateDocumentWebDir' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/',
8268
                            'BaseHref' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/'.$relative_path
8269
                        );
8270
8271
                        if ($_GET['action'] == 'add_item') {
8272
                            $class = 'add';
8273
                            $text = get_lang('LPCreateDocument');
8274
                        } else {
8275
                            if ($_GET['action'] == 'edit_item') {
8276
                                $class = 'save';
8277
                                $text = get_lang('SaveDocument');
8278
                            }
8279
                        }
8280
8281
                        $form->addButtonSave($text, 'submit_button');
8282
                        $renderer = $form->defaultRenderer();
8283
                        $renderer->setElementTemplate('&nbsp;{label}{element}', 'content_lp');
8284
                        $form->addElement('html', '<div class="editor-lp">');
8285
                        $form->addHtmlEditor('content_lp', null, null, true, $editor_config, true);
8286
                        $form->addElement('html', '</div>');
8287
                        $defaults['content_lp'] = $content;
8288
                    }
8289
                } elseif (is_numeric($extra_info)) {
8290
                    $form->addButtonSave(get_lang('SaveDocument'), 'submit_button');
8291
8292
                    $return = $this->display_document($extra_info, true, true, true);
8293
                    $form->addElement('html', $return);
8294
                }
8295
            }
8296
        }
8297
        if (isset($extra_info['item_type']) &&
8298
            $extra_info['item_type'] == TOOL_LP_FINAL_ITEM
8299
        ) {
8300
            $parent_select->freeze();
8301
            $position->freeze();
8302
        }
8303
8304
        if ($action == 'move') {
8305
            $form->addElement('hidden', 'title', $item_title);
8306
            $form->addElement('hidden', 'description', $item_description);
8307
        }
8308
        if (is_numeric($extra_info)) {
8309
            $form->addButtonSave(get_lang('SaveDocument'), 'submit_button');
8310
            $form->addElement('hidden', 'path', $extra_info);
8311
        } elseif (is_array($extra_info)) {
8312
            $form->addButtonSave(get_lang('SaveDocument'), 'submit_button');
8313
            $form->addElement('hidden', 'path', $extra_info['path']);
8314
        }
8315
        $form->addElement('hidden', 'type', TOOL_DOCUMENT);
8316
        $form->addElement('hidden', 'post_time', time());
8317
        $form->setDefaults($defaults);
8318
8319
        return $form->returnForm();
8320
    }
8321
8322
    /**
8323
     * Return HTML form to add/edit a link item
8324
     * @param string	$action (add/edit)
8325
     * @param integer	$id Item ID if exists
8326
     * @param mixed		$extra_info
8327
     * @return	string	HTML form
8328
     */
8329
    public function display_link_form($action = 'add', $id = 0, $extra_info = '')
8330
    {
8331
        $course_id = api_get_course_int_id();
8332
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
8333
        $tbl_link = Database::get_course_table(TABLE_LINK);
8334
8335
        if ($id != 0 && is_array($extra_info)) {
8336
            $item_title = stripslashes($extra_info['title']);
8337
            $item_description = stripslashes($extra_info['description']);
8338
            $item_url = stripslashes($extra_info['url']);
8339
        } elseif (is_numeric($extra_info)) {
8340
            $extra_info = intval($extra_info);
8341
            $sql = "SELECT title, description, url FROM ".$tbl_link."
8342
                    WHERE c_id = ".$course_id." AND id = ".$extra_info;
8343
            $result = Database::query($sql);
8344
            $row = Database::fetch_array($result);
8345
            $item_title       = $row['title'];
8346
            $item_description = $row['description'];
8347
            $item_url = $row['url'];
8348
        } else {
8349
            $item_title = '';
8350
            $item_description = '';
8351
            $item_url = '';
8352
        }
8353
8354
        $form = new FormValidator(
8355
            'edit_link',
8356
            'POST',
8357
            $this->getCurrentBuildingModeURL()
8358
        );
8359
        $defaults = [];
8360 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
8361
            $parent = $extra_info['parent_item_id'];
8362
        } else {
8363
            $parent = 0;
8364
        }
8365
8366
        $sql = "SELECT * FROM ".$tbl_lp_item."
8367
                WHERE c_id = ".$course_id." AND lp_id = ".$this->lp_id;
8368
        $result = Database::query($sql);
8369
        $arrLP = array();
8370
8371 View Code Duplication
        while ($row = Database::fetch_array($result)) {
8372
            $arrLP[] = array(
8373
                'id' => $row['id'],
8374
                'item_type' => $row['item_type'],
8375
                'title' => $row['title'],
8376
                'path' => $row['path'],
8377
                'description' => $row['description'],
8378
                'parent_item_id' => $row['parent_item_id'],
8379
                'previous_item_id' => $row['previous_item_id'],
8380
                'next_item_id' => $row['next_item_id'],
8381
                'display_order' => $row['display_order'],
8382
                'max_score' => $row['max_score'],
8383
                'min_score' => $row['min_score'],
8384
                'mastery_score' => $row['mastery_score'],
8385
                'prerequisite' => $row['prerequisite']
8386
            );
8387
        }
8388
8389
        $this->tree_array($arrLP);
8390
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
8391
        unset($this->arrMenu);
8392
8393
        if ($action == 'add') {
8394
            $legend = get_lang('CreateTheLink');
8395
        } elseif ($action == 'move') {
8396
            $legend = get_lang('MoveCurrentLink');
8397
        } else {
8398
            $legend = get_lang('EditCurrentLink');
8399
        }
8400
8401
        $form->addHeader($legend);
8402
8403 View Code Duplication
        if ($action != 'move') {
8404
            $form->addText('title', get_lang('Title'), true, ['class' => 'learnpath_item_form']);
8405
            $defaults['title'] = $item_title;
8406
        }
8407
8408
        $selectParent = $form->addSelect(
8409
            'parent',
8410
            get_lang('Parent'),
8411
            [],
8412
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);', 'class' => 'learnpath_item_form']
8413
        );
8414
        $selectParent->addOption($this->name, 0);
8415
        $arrHide = array(
8416
            $id
8417
        );
8418
8419
        $parent_item_id = isset($_SESSION['parent_item_id']) ? $_SESSION['parent_item_id'] : 0;
8420
8421 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
8422
            if ($action != 'add') {
8423
                if (
8424
                    ($arrLP[$i]['item_type'] == 'dir') &&
8425
                    !in_array($arrLP[$i]['id'], $arrHide) &&
8426
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
8427
                ) {
8428
                    $selectParent->addOption(
8429
                        $arrLP[$i]['title'],
8430
                        $arrLP[$i]['id'],
8431
                        ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px;']
8432
                    );
8433
8434
                    if ($parent == $arrLP[$i]['id']) {
8435
                        $selectParent->setSelected($arrLP[$i]['id']);
8436
                    }
8437
                } else {
8438
                    $arrHide[] = $arrLP[$i]['id'];
8439
                }
8440
            } else {
8441
                if ($arrLP[$i]['item_type'] == 'dir') {
8442
                    $selectParent->addOption(
8443
                        $arrLP[$i]['title'],
8444
                        $arrLP[$i]['id'],
8445
                        ['style' => 'padding-left: '.(20 + $arrLP[$i]['depth'] * 20).'px']
8446
                    );
8447
8448
                    if ($parent_item_id == $arrLP[$i]['id']) {
8449
                        $selectParent->setSelected($arrLP[$i]['id']);
8450
                    }
8451
                }
8452
            }
8453
        }
8454
8455
        if (is_array($arrLP)) {
8456
            reset($arrLP);
8457
        }
8458
8459
        $selectPrevious = $form->addSelect(
8460
            'previous',
8461
            get_lang('Position'),
8462
            [],
8463
            ['id' => 'previous', 'class' => 'learnpath_item_form']
8464
        );
8465
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
8466
8467
        for ($i = 0; $i < count($arrLP); $i++) {
8468
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
8469
                $selectPrevious->addOption($arrLP[$i]['title'], $arrLP[$i]['id']);
8470
8471
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
8472
                    $selectPrevious->setSelected($arrLP[$i]['id']);
8473
                } elseif ($action == 'add') {
8474
                    $selectPrevious->setSelected($arrLP[$i]['id']);
8475
                }
8476
            }
8477
        }
8478
8479
        if ($action != 'move') {
8480
            $urlAttributes = ['class' => 'learnpath_item_form'];
8481
8482
            if (is_numeric($extra_info)) {
8483
                $urlAttributes['disabled'] = 'disabled';
8484
            }
8485
8486
            $form->addElement('url', 'url', get_lang('Url'), $urlAttributes);
8487
            $defaults['url'] = $item_url;
8488
8489
            $id_prerequisite = 0;
8490
            if (is_array($arrLP)) {
8491
                foreach ($arrLP as $key => $value) {
8492
                    if ($value['id'] == $id) {
8493
                        $id_prerequisite = $value['prerequisite'];
8494
                        break;
8495
                    }
8496
                }
8497
            }
8498
8499
            $arrHide = array();
8500
            for ($i = 0; $i < count($arrLP); $i++) {
8501
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') {
8502
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
8503
                        $s_selected_position = $arrLP[$i]['id'];
8504
                    elseif ($action == 'add') $s_selected_position = 0;
8505
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8506
8507
                }
8508
            }
8509
        }
8510
8511
        if ($action == 'add') {
8512
            $form->addButtonSave(get_lang('AddLinkToCourse'), 'submit_button');
8513
        } else {
8514
            $form->addButtonSave(get_lang('EditCurrentLink'), 'submit_button');
8515
        }
8516
8517
        if ($action == 'move') {
8518
            $form->addHidden('title', $item_title);
8519
            $form->addHidden('description', $item_description);
8520
        }
8521
8522 View Code Duplication
        if (is_numeric($extra_info)) {
8523
            $form->addHidden('path', $extra_info);
8524
        } elseif (is_array($extra_info)) {
8525
            $form->addHidden('path', $extra_info['path']);
8526
        }
8527
        $form->addHidden('type', TOOL_LINK);
8528
        $form->addHidden('post_time', time());
8529
8530
        $form->setDefaults($defaults);
8531
8532
        return '<div class="sectioncomment">'.$form->returnForm().'</div>';
8533
    }
8534
8535
    /**
8536
     * Return HTML form to add/edit a student publication (work)
8537
     * @param	string	Action (add/edit)
8538
     * @param	integer	Item ID if already exists
8539
     * @param	mixed	Extra info (work ID if integer)
8540
     * @return	string	HTML form
8541
     */
8542
    public function display_student_publication_form($action = 'add', $id = 0, $extra_info = '')
8543
    {
8544
        $course_id = api_get_course_int_id();
8545
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
8546
        $tbl_publication = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
8547
8548
        if ($id != 0 && is_array($extra_info)) {
8549
            $item_title = stripslashes($extra_info['title']);
8550
            $item_description = stripslashes($extra_info['description']);
8551 View Code Duplication
        } elseif (is_numeric($extra_info)) {
8552
            $extra_info = intval($extra_info);
8553
            $sql = "SELECT title, description
8554
                    FROM $tbl_publication
8555
                    WHERE c_id = $course_id AND id = ".$extra_info;
8556
8557
            $result = Database::query($sql);
8558
            $row = Database::fetch_array($result);
8559
8560
            $item_title = $row['title'];
8561
        } else {
8562
            $item_title = get_lang('Student_publication');
8563
        }
8564
8565 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
8566
            $parent = $extra_info['parent_item_id'];
8567
        } else {
8568
            $parent = 0;
8569
        }
8570
8571
        $sql = "SELECT * FROM $tbl_lp_item 
8572
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
8573
8574
        $result = Database::query($sql);
8575
        $arrLP = array();
8576 View Code Duplication
        while ($row = Database::fetch_array($result)) {
8577
            $arrLP[] = array(
8578
                'id' => $row['id'],
8579
                'item_type' => $row['item_type'],
8580
                'title' => $row['title'],
8581
                'path' => $row['path'],
8582
                'description' => $row['description'],
8583
                'parent_item_id' => $row['parent_item_id'],
8584
                'previous_item_id' => $row['previous_item_id'],
8585
                'next_item_id' => $row['next_item_id'],
8586
                'display_order' => $row['display_order'],
8587
                'max_score' => $row['max_score'],
8588
                'min_score' => $row['min_score'],
8589
                'mastery_score' => $row['mastery_score'],
8590
                'prerequisite' => $row['prerequisite']
8591
            );
8592
        }
8593
8594
        $this->tree_array($arrLP);
8595
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
8596
        unset($this->arrMenu);
8597
8598
        $form = new FormValidator('frm_student_publication', 'post', '#');
8599
8600
        if ($action == 'add') {
8601
            $form->addHeader(get_lang('Student_publication'));
8602
        } elseif ($action == 'move') {
8603
            $form->addHeader(get_lang('MoveCurrentStudentPublication'));
8604
        } else {
8605
            $form->addHeader(get_lang('EditCurrentStudentPublication'));
8606
        }
8607
8608 View Code Duplication
        if ($action != 'move') {
8609
            $form->addText('title', get_lang('Title'), true, ['class' => 'learnpath_item_form', 'id' => 'idTitle']);
8610
        }
8611
8612
        $parentSelect = $form->addSelect(
8613
            'parent',
8614
            get_lang('Parent'),
8615
            ['0' => $this->name],
8616
            [
8617
                'onchange' => 'javascript: load_cbo(this.value);',
8618
                'class' => 'learnpath_item_form',
8619
                'id' => 'idParent'
8620
            ]
8621
        );
8622
8623
        $arrHide = array($id);
8624
8625 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
8626
            if ($action != 'add') {
8627
                if (
8628
                    ($arrLP[$i]['item_type'] == 'dir') &&
8629
                    !in_array($arrLP[$i]['id'], $arrHide) &&
8630
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
8631
                ) {
8632
                    $parentSelect->addOption(
8633
                        $arrLP[$i]['title'],
8634
                        $arrLP[$i]['id'],
8635
                        ['style' => 'padding-left: '.(($arrLP[$i]['depth'] * 10) + 20).'px;']
8636
                    );
8637
8638
                    if ($parent == $arrLP[$i]['id']) {
8639
                        $parentSelect->setSelected($arrLP[$i]['id']);
8640
                    }
8641
                } else {
8642
                    $arrHide[] = $arrLP[$i]['id'];
8643
                }
8644
            } else {
8645
                if ($arrLP[$i]['item_type'] == 'dir') {
8646
                    $parentSelect->addOption(
8647
                        $arrLP[$i]['title'],
8648
                        $arrLP[$i]['id'],
8649
                        ['style' => 'padding-left: '.(($arrLP[$i]['depth'] * 10) + 20).'px;']
8650
                    );
8651
8652
                    if ($parent == $arrLP[$i]['id']) {
8653
                        $parentSelect->setSelected($arrLP[$i]['id']);
8654
                    }
8655
                }
8656
            }
8657
        }
8658
8659
        if (is_array($arrLP)) {
8660
            reset($arrLP);
8661
        }
8662
8663
        $previousSelect = $form->addSelect(
8664
            'previous',
8665
            get_lang('Position'),
8666
            ['0' => get_lang('FirstPosition')],
8667
            ['id' => 'previous', 'class' => 'learnpath_item_form']
8668
        );
8669
8670 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
8671
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
8672
                $previousSelect->addOption(
8673
                    get_lang('After').' "'.$arrLP[$i]['title'].'"',
8674
                    $arrLP[$i]['id']
8675
                );
8676
8677
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
8678
                    $previousSelect->setSelected($arrLP[$i]['id']);
8679
                } elseif ($action == 'add') {
8680
                    $previousSelect->setSelected($arrLP[$i]['id']);
8681
                }
8682
            }
8683
        }
8684
8685 View Code Duplication
        if ($action != 'move') {
8686
            $id_prerequisite = 0;
8687
            if (is_array($arrLP)) {
8688
                foreach ($arrLP as $key => $value) {
8689
                    if ($value['id'] == $id) {
8690
                        $id_prerequisite = $value['prerequisite'];
8691
                        break;
8692
                    }
8693
                }
8694
            }
8695
            $arrHide = array();
8696
            for ($i = 0; $i < count($arrLP); $i++) {
8697
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dir') {
8698
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
8699
                        $s_selected_position = $arrLP[$i]['id'];
8700
                    elseif ($action == 'add') $s_selected_position = 0;
8701
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8702
8703
                }
8704
            }
8705
        }
8706
8707
        if ($action == 'add') {
8708
            $form->addButtonCreate(get_lang('AddAssignmentToCourse'), 'submit_button');
8709
        } else {
8710
            $form->addButtonCreate(get_lang('EditCurrentStudentPublication'), 'submit_button');
8711
        }
8712
8713
        if ($action == 'move') {
8714
            $form->addHidden('title', $item_title);
8715
            $form->addHidden('description', $item_description);
8716
        }
8717
8718 View Code Duplication
        if (is_numeric($extra_info)) {
8719
            $form->addHidden('path', $extra_info);
8720
        } elseif (is_array($extra_info)) {
8721
            $form->addHidden('path', $extra_info['path']);
8722
        }
8723
8724
        $form->addHidden('type', TOOL_STUDENTPUBLICATION);
8725
        $form->addHidden('post_time', time());
8726
        $form->setDefaults(['title' => $item_title]);
8727
8728
        $return = '<div class="sectioncomment">';
8729
        $return .= $form->returnForm();
8730
        $return .= '</div>';
8731
8732
        return $return;
8733
    }
8734
8735
    /**
8736
     * Displays the menu for manipulating a step
8737
     *
8738
     * @param $item_id
8739
     * @param string $item_type
8740
     * @return string
8741
     */
8742
    public function display_manipulate($item_id, $item_type = TOOL_DOCUMENT)
8743
    {
8744
        $_course = api_get_course_info();
8745
        $course_id = api_get_course_int_id();
8746
        $course_code = api_get_course_id();
8747
        $return = '<div class="actions">';
8748
        switch ($item_type) {
8749
            case 'dir':
8750
                // Commented the message cause should not show it.
8751
                //$lang = get_lang('TitleManipulateChapter');
8752
                break;
8753
            case TOOL_LP_FINAL_ITEM:
8754
            case TOOL_DOCUMENT:
8755
                // Commented the message cause should not show it.
8756
                //$lang = get_lang('TitleManipulateDocument');
8757
                break;
8758
            case TOOL_LINK:
8759
            case 'link' :
8760
                // Commented the message cause should not show it.
8761
                //$lang = get_lang('TitleManipulateLink');
8762
                break;
8763
            case TOOL_QUIZ:
8764
                // Commented the message cause should not show it.
8765
                //$lang = get_lang('TitleManipulateQuiz');
8766
                break;
8767
            case TOOL_STUDENTPUBLICATION:
8768
                // Commented the message cause should not show it.
8769
                //$lang = get_lang('TitleManipulateStudentPublication');
8770
                break;
8771
        }
8772
8773
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
8774
        $item_id = intval($item_id);
8775
        $sql = "SELECT * FROM ".$tbl_lp_item." as lp
8776
                WHERE lp.c_id = ".$course_id." AND lp.id = ".$item_id;
8777
        $result = Database::query($sql);
8778
        $row = Database::fetch_assoc($result);
8779
8780
        $audio_player = null;
8781
        // We display an audio player if needed.
8782
        if (!empty($row['audio'])) {
8783
            $audio_player .= '<div class="lp_mediaplayer" id="container">
8784
                              <a href="http://www.macromedia.com/go/getflashplayer">Get the Flash Player</a> to see this player.
8785
                              </div>';
8786
            $audio_player .= api_get_js('js/mediaplayer/swfobject.js');
8787
            $audio_player .= '<script>
8788
                var s1 = new SWFObject("'.api_get_path(WEB_LIBRARY_JS_PATH).'mediaplayer/player.swf","ply","250","20","9","#FFFFFF");
8789
                s1.addParam("allowscriptaccess","always");
8790
                s1.addParam("flashvars","file=../..'.api_get_path(REL_COURSE_PATH).$_course['path'].'/document/audio/'.$row['audio'].'&autostart=true");
8791
                s1.write("container");
8792
            </script>';
8793
        }
8794
8795
        $url = api_get_self().'?'.api_get_cidreq().'&view=build&id='.$item_id.'&lp_id='.$this->lp_id;
8796
8797
        if ($item_type != TOOL_LP_FINAL_ITEM) {
8798
            $return .= Display::url(
8799
                Display::return_icon(
8800
                    'edit.png',
8801
                    get_lang('Edit'),
8802
                    array(),
8803
                    ICON_SIZE_SMALL
8804
                ),
8805
                $url.'&action=edit_item&path_item='.$row['path']
8806
            );
8807
8808
            $return .= Display::url(
8809
                Display::return_icon(
8810
                    'move.png',
8811
                    get_lang('Move'),
8812
                    array(),
8813
                    ICON_SIZE_SMALL
8814
                ),
8815
                $url.'&action=move_item'
8816
            );
8817
        }
8818
8819
        // Commented for now as prerequisites cannot be added to chapters.
8820
        if ($item_type != 'dir') {
8821
            $return .= Display::url(
8822
                Display::return_icon('accept.png', get_lang('LearnpathPrerequisites'), array(), ICON_SIZE_SMALL),
8823
                $url.'&action=edit_item_prereq'
8824
            );
8825
        }
8826
        $return .= Display::url(
8827
            Display::return_icon('delete.png', get_lang('Delete'), array(), ICON_SIZE_SMALL),
8828
            $url.'&action=delete_item'
8829
        );
8830
8831 View Code Duplication
        if ($item_type == TOOL_HOTPOTATOES) {
8832
            $document_data = DocumentManager::get_document_data_by_id($row['path'], $course_code);
8833
            $return .= get_lang('File').': '.$document_data['absolute_path_from_document'];
8834
        }
8835
8836 View Code Duplication
        if ($item_type == TOOL_DOCUMENT || $item_type == TOOL_LP_FINAL_ITEM) {
8837
            $document_data = DocumentManager::get_document_data_by_id($row['path'], $course_code);
8838
            $return .= get_lang('File').': '.$document_data['absolute_path_from_document'];
8839
        }
8840
8841
        $return .= '</div>';
8842
8843
        if (!empty($audio_player)) {
8844
            $return .= '<br />'.$audio_player;
8845
        }
8846
8847
        return $return;
8848
    }
8849
8850
    /**
8851
     * Creates the javascript needed for filling up the checkboxes without page reload
8852
     * @return string
8853
     */
8854
    public function get_js_dropdown_array()
8855
    {
8856
        $course_id = api_get_course_int_id();
8857
        $return = 'var child_name = new Array();'."\n";
8858
        $return .= 'var child_value = new Array();'."\n\n";
8859
        $return .= 'child_name[0] = new Array();'."\n";
8860
        $return .= 'child_value[0] = new Array();'."\n\n";
8861
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
8862
        $sql = "SELECT * FROM ".$tbl_lp_item."
8863
                WHERE c_id = ".$course_id." AND lp_id = ".$this->lp_id." AND parent_item_id = 0
8864
                ORDER BY display_order ASC";
8865
        $res_zero = Database::query($sql);
8866
        $i = 0;
8867
8868
        while ($row_zero = Database::fetch_array($res_zero)) {
8869
            if ($row_zero['item_type'] !== TOOL_LP_FINAL_ITEM) {
8870
                if ($row_zero['item_type'] == TOOL_QUIZ) {
8871
                    $row_zero['title'] = Exercise::get_formated_title_variable($row_zero['title']);
8872
                }
8873
                $js_var = json_encode(get_lang('After').' '.$row_zero['title']);
8874
                $return .= 'child_name[0]['.$i.'] = '.$js_var.' ;'."\n";
8875
                $return .= 'child_value[0]['.$i++.'] = "'.$row_zero['id'].'";'."\n";
8876
            }
8877
        }
8878
        $return .= "\n";
8879
        $sql = "SELECT * FROM ".$tbl_lp_item."
8880
                WHERE c_id = ".$course_id." AND lp_id = ".$this->lp_id;
8881
        $res = Database::query($sql);
8882
        while ($row = Database::fetch_array($res)) {
8883
            $sql_parent = "SELECT * FROM ".$tbl_lp_item."
8884
                           WHERE
8885
                                c_id = ".$course_id." AND
8886
                                parent_item_id = " . $row['id']."
8887
                           ORDER BY display_order ASC";
8888
            $res_parent = Database::query($sql_parent);
8889
            $i = 0;
8890
            $return .= 'child_name['.$row['id'].'] = new Array();'."\n";
8891
            $return .= 'child_value['.$row['id'].'] = new Array();'."\n\n";
8892
8893
            while ($row_parent = Database::fetch_array($res_parent)) {
8894
                $js_var = json_encode(get_lang('After').' '.$row_parent['title']);
8895
                $return .= 'child_name['.$row['id'].']['.$i.'] =   '.$js_var.' ;'."\n";
8896
                $return .= 'child_value['.$row['id'].']['.$i++.'] = "'.$row_parent['id'].'";'."\n";
8897
            }
8898
            $return .= "\n";
8899
        }
8900
8901
        return $return;
8902
    }
8903
8904
    /**
8905
     * Display the form to allow moving an item
8906
     * @param	integer $item_id		Item ID
8907
     * @return	string		HTML form
8908
     */
8909
    public function display_move_item($item_id)
8910
    {
8911
        $course_id = api_get_course_int_id();
8912
        $return = '';
8913
8914
        if (is_numeric($item_id)) {
8915
            $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
8916
8917
            $sql = "SELECT * FROM ".$tbl_lp_item."
8918
                    WHERE c_id = ".$course_id." AND id = ".$item_id;
8919
            $res = Database::query($sql);
8920
            $row = Database::fetch_array($res);
8921
8922
            switch ($row['item_type']) {
8923
                case 'dir':
8924
                case 'asset':
8925
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8926
                    $return .= $this->display_item_form(
8927
                        $row['item_type'],
8928
                        get_lang('MoveCurrentChapter'),
8929
                        'move',
8930
                        $item_id,
8931
                        $row
8932
                    );
8933
                    break;
8934
                case TOOL_DOCUMENT:
8935
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8936
                    $return .= $this->display_document_form('move', $item_id, $row);
8937
                    break;
8938 View Code Duplication
                case TOOL_LINK:
8939
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8940
                    $return .= $this->display_link_form('move', $item_id, $row);
8941
                    break;
8942 View Code Duplication
                case TOOL_HOTPOTATOES:
8943
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8944
                    $return .= $this->display_link_form('move', $item_id, $row);
8945
                    break;
8946 View Code Duplication
                case TOOL_QUIZ:
8947
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8948
                    $return .= $this->display_quiz_form('move', $item_id, $row);
8949
                    break;
8950 View Code Duplication
                case TOOL_STUDENTPUBLICATION:
8951
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8952
                    $return .= $this->display_student_publication_form('move', $item_id, $row);
8953
                    break;
8954
                case TOOL_FORUM:
8955
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8956
                    $return .= $this->display_forum_form('move', $item_id, $row);
8957
                    break;
8958 View Code Duplication
                case TOOL_THREAD:
8959
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8960
                    $return .= $this->display_forum_form('move', $item_id, $row);
8961
                    break;
8962
            }
8963
        }
8964
8965
        return $return;
8966
    }
8967
8968
    /**
8969
     * Displays a basic form on the overview page for changing the item title and the item description.
8970
     * @param string $item_type
8971
     * @param string $title
8972
     * @param array $data
8973
     * @return string
8974
     */
8975
    public function display_item_small_form($item_type, $title = '', $data = array())
8976
    {
8977
        $url = api_get_self().'?'.api_get_cidreq().'&action=edit_item&lp_id='.$this->lp_id;
8978
        $form = new FormValidator('small_form', 'post', $url);
8979
        $form->addElement('header', $title);
8980
        $form->addElement('text', 'title', get_lang('Title'));
8981
        $form->addButtonSave(get_lang('Save'), 'submit_button');
8982
        $form->addElement('hidden', 'id', $data['id']);
8983
        $form->addElement('hidden', 'parent', $data['parent_item_id']);
8984
        $form->addElement('hidden', 'previous', $data['previous_item_id']);
8985
        $form->setDefaults(array('title' => $data['title']));
8986
8987
        return $form->toHtml();
8988
    }
8989
8990
    /**
8991
     * Return HTML form to allow prerequisites selection
8992
     * @todo use FormValidator
8993
     * @param	integer Item ID
8994
     * @return	string	HTML form
8995
     */
8996
    public function display_item_prerequisites_form($item_id)
8997
    {
8998
        $course_id = api_get_course_int_id();
8999
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
9000
        $item_id = intval($item_id);
9001
        /* Current prerequisite */
9002
        $sql = "SELECT * FROM $tbl_lp_item
9003
                WHERE c_id = $course_id AND id = ".$item_id;
9004
        $result = Database::query($sql);
9005
        $row    = Database::fetch_array($result);
9006
        $prerequisiteId = $row['prerequisite'];
9007
        $return = '<legend>';
9008
        $return .= get_lang('AddEditPrerequisites');
9009
        $return .= '</legend>';
9010
        $return .= '<form method="POST">';
9011
        $return .= '<div class="table-responsive">';
9012
        $return .= '<table class="table table-hover">';
9013
        $return .= '<tr>';
9014
        $return .= '<th>'.get_lang('LearnpathPrerequisites').'</th>';
9015
        $return .= '<th width="140" >'.get_lang('Minimum').'</th>';
9016
        $return .= '<th width="140">'.get_lang('Maximum').'</th>';
9017
        $return .= '</tr>';
9018
9019
        // Adding the none option to the prerequisites see http://www.chamilo.org/es/node/146
9020
        $return .= '<tr>';
9021
        $return .= '<td colspan="3">';
9022
        $return .= '<div class="radio learnpath"><label for="idNone">';
9023
        $return .= '<input checked="checked" id="idNone" name="prerequisites" type="radio" />';
9024
        $return .= get_lang('None').'</label>';
9025
        $return .= '</div>';
9026
        $return .= '</tr>';
9027
9028
        $sql = "SELECT * FROM $tbl_lp_item
9029
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
9030
        $result = Database::query($sql);
9031
        $arrLP = array();
9032
9033
        $selectedMinScore = array();
9034
        $selectedMaxScore = array();
9035
        while ($row = Database::fetch_array($result)) {
9036
            if ($row['id'] == $item_id) {
9037
                $selectedMinScore[$row['prerequisite']] = $row['prerequisite_min_score'];
9038
                $selectedMaxScore[$row['prerequisite']] = $row['prerequisite_max_score'];
9039
            }
9040
            $arrLP[] = array(
9041
                'id' => $row['id'],
9042
                'item_type' => $row['item_type'],
9043
                'title' => $row['title'],
9044
                'ref' => $row['ref'],
9045
                'description' => $row['description'],
9046
                'parent_item_id' => $row['parent_item_id'],
9047
                'previous_item_id' => $row['previous_item_id'],
9048
                'next_item_id' => $row['next_item_id'],
9049
                'max_score' => $row['max_score'],
9050
                'min_score' => $row['min_score'],
9051
                'mastery_score' => $row['mastery_score'],
9052
                'prerequisite' => $row['prerequisite'],
9053
                'next_item_id' => $row['next_item_id'],
9054
                'display_order' => $row['display_order'],
9055
                'prerequisite_min_score' => $row['prerequisite_min_score'],
9056
                'prerequisite_max_score' => $row['prerequisite_max_score'],
9057
            );
9058
        }
9059
9060
        $this->tree_array($arrLP);
9061
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
9062
        unset($this->arrMenu);
9063
9064
        for ($i = 0; $i < count($arrLP); $i++) {
9065
            $item = $arrLP[$i];
9066
9067
            if ($item['id'] == $item_id) {
9068
                break;
9069
            }
9070
9071
            $selectedMaxScoreValue = isset($selectedMaxScore[$item['id']]) ? $selectedMaxScore[$item['id']] : $item['max_score'];
9072
            $selectedMinScoreValue = isset($selectedMinScore[$item['id']]) ? $selectedMinScore[$item['id']] : 0;
9073
9074
            $return .= '<tr>';
9075
            $return .= '<td '.(($item['item_type'] != TOOL_QUIZ && $item['item_type'] != TOOL_HOTPOTATOES) ? ' colspan="3"' : '').'>';
9076
            $return .= '<div style="margin-left:'.($item['depth'] * 20).'px;" class="radio learnpath">';
9077
            $return .= '<label for="id'.$item['id'].'">';
9078
            $return .= '<input'.(in_array($prerequisiteId, array($item['id'], $item['ref'])) ? ' checked="checked" ' : '').($item['item_type'] == 'dir' ? ' disabled="disabled" ' : ' ').'id="id'.$item['id'].'" name="prerequisites"  type="radio" value="'.$item['id'].'" />';
9079
9080
            $icon_name = str_replace(' ', '', $item['item_type']);
9081
9082
            if (file_exists('../img/lp_'.$icon_name.'.png')) {
9083
                $return .= Display::return_icon('lp_'.$icon_name.'.png');
9084
            } else {
9085
                if (file_exists('../img/lp_'.$icon_name.'.png')) {
9086
                    $return .= Display::return_icon('lp_'.$icon_name.'.png');
9087
                } else {
9088
                    $return .= Display::return_icon('folder_document.png');
9089
                }
9090
            }
9091
9092
            $return .= $item['title'].'</label>';
9093
            $return .= '</div>';
9094
            $return .= '</td>';
9095
9096
            if ($item['item_type'] == TOOL_QUIZ) {
9097
                // lets update max_score Quiz information depending of the Quiz Advanced properties
9098
                $tmp_obj_lp_item = new LpItem($course_id, $item['id']);
9099
                $tmp_obj_exercice = new Exercise();
9100
                $tmp_obj_exercice->read($tmp_obj_lp_item->path);
9101
                $tmp_obj_lp_item->max_score = $tmp_obj_exercice->get_max_score();
9102
9103
                $tmp_obj_lp_item->update_in_bdd();
9104
                $item['max_score'] = $tmp_obj_lp_item->max_score;
9105
9106
                $return .= '<td>';
9107
                $return .= '<input class="form-control" size="4" maxlength="3" name="min_'.$item['id'].'" type="number" min="0" step="any" max="'.$item['max_score'].'" value="'.$selectedMinScoreValue.'" />';
9108
                $return .= '</td>';
9109
                $return .= '<td>';
9110
                $return .= '<input class="form-control" size="4" maxlength="3" name="max_'.$item['id'].'" type="number" min="0" step="any" max="'.$item['max_score'].'" value="'.$selectedMaxScoreValue.'" />';
9111
                $return .= '</td>';
9112
            }
9113
9114
            if ($item['item_type'] == TOOL_HOTPOTATOES) {
9115
                $return .= '<td>';
9116
                $return .= '<center><input size="4" maxlength="3" name="min_'.$item['id'].'" type="number" min="0" step="any" max="'.$item['max_score'].'" value="'.$selectedMinScoreValue.'" /></center>';
9117
                $return .= '</td>';
9118
                $return .= '<td>';
9119
                $return .= '<center><input size="4" maxlength="3" name="max_'.$item['id'].'" type="number" min="0" step="any" max="'.$item['max_score'].'"  value="'.$selectedMaxScoreValue.'" /></center>';
9120
                $return .= '</td>';
9121
            }
9122
            $return .= '</tr>';
9123
        }
9124
        $return .= '<tr>';
9125
        $return .= '</tr>';
9126
        $return .= '</table>';
9127
        $return .= '</div>';
9128
        $return .= '<div class="form-group">';
9129
        $return .= '<button class="btn btn-primary" name="submit_button" type="submit">'.get_lang('ModifyPrerequisites').'</button>';
9130
        $return .= '</form>';
9131
9132
        return $return;
9133
    }
9134
9135
    /**
9136
     * Return HTML list to allow prerequisites selection for lp
9137
     * @param	integer Item ID
9138
     * @return	string	HTML form
9139
     */
9140
    public function display_lp_prerequisites_list()
9141
    {
9142
        $course_id = api_get_course_int_id();
9143
        $lp_id = $this->lp_id;
9144
        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
9145
9146
        // get current prerequisite
9147
        $sql = "SELECT * FROM $tbl_lp WHERE c_id = $course_id AND id = $lp_id ";
9148
        $result = Database::query($sql);
9149
        $row = Database::fetch_array($result);
9150
        $prerequisiteId = $row['prerequisite'];
9151
        $session_id = api_get_session_id();
9152
        $session_condition = api_get_session_condition($session_id, true, true);
9153
        $sql = "SELECT * FROM $tbl_lp
9154
                WHERE c_id = $course_id $session_condition
9155
                ORDER BY display_order ";
9156
        $rs = Database::query($sql);
9157
        $return = '';
9158
        $return .= '<select name="prerequisites" class="form-control">';
9159
        $return .= '<option value="0">'.get_lang('None').'</option>';
9160
        if (Database::num_rows($rs) > 0) {
9161
            while ($row = Database::fetch_array($rs)) {
9162
                if ($row['id'] == $lp_id) {
9163
                    continue;
9164
                }
9165
                $return .= '<option value="'.$row['id'].'" '.(($row['id'] == $prerequisiteId) ? ' selected ' : '').'>'.$row['name'].'</option>';
9166
            }
9167
        }
9168
        $return .= '</select>';
9169
9170
        return $return;
9171
    }
9172
9173
    /**
9174
     * Creates a list with all the documents in it
9175
     * @param bool $showInvisibleFiles
9176
     * @return string
9177
     */
9178
    public function get_documents($showInvisibleFiles = false)
9179
    {
9180
        $course_info = api_get_course_info();
9181
        $sessionId = api_get_session_id();
9182
9183
        $documentTree = DocumentManager::get_document_preview(
9184
            $course_info,
9185
            $this->lp_id,
9186
            null,
9187
            $sessionId,
9188
            true,
9189
            null,
9190
            null,
9191
            $showInvisibleFiles,
9192
            true
9193
        );
9194
9195
        $headers = array(
9196
            get_lang('Files'),
9197
            get_lang('CreateTheDocument'),
9198
            get_lang('Upload')
9199
        );
9200
9201
        $form = new FormValidator(
9202
            'form_upload',
9203
            'POST',
9204
            $this->getCurrentBuildingModeURL(),
9205
            '',
9206
            array('enctype' => 'multipart/form-data')
9207
        );
9208
9209
        $folders = DocumentManager::get_all_document_folders(
9210
            api_get_course_info(),
9211
            0,
9212
            true
9213
        );
9214
9215
        DocumentManager::build_directory_selector(
9216
            $folders,
9217
            '',
9218
            array(),
9219
            true,
9220
            $form,
9221
            'directory_parent_id'
9222
        );
9223
9224
        $group = array(
9225
            $form->createElement(
9226
                'radio',
9227
                'if_exists',
9228
                get_lang("UplWhatIfFileExists"),
9229
                get_lang('UplDoNothing'),
9230
                'nothing'
9231
            ),
9232
            $form->createElement(
9233
                'radio',
9234
                'if_exists',
9235
                null,
9236
                get_lang('UplOverwriteLong'),
9237
                'overwrite'
9238
            ),
9239
            $form->createElement(
9240
                'radio',
9241
                'if_exists',
9242
                null,
9243
                get_lang('UplRenameLong'),
9244
                'rename'
9245
            )
9246
        );
9247
        $form->addGroup($group, null, get_lang('UplWhatIfFileExists'));
9248
        /*$form->addElement('radio', 'if_exists', get_lang('UplWhatIfFileExists'), get_lang('UplDoNothing'), 'nothing');
9249
        $form->addElement('radio', 'if_exists', '', get_lang('UplOverwriteLong'), 'overwrite');
9250
        $form->addElement('radio', 'if_exists', '', get_lang('UplRenameLong'), 'rename');*/
9251
        $form->setDefaults(['if_exists' => 'rename']);
9252
9253
        // Check box options
9254
        $form->addElement(
9255
            'checkbox',
9256
            'unzip',
9257
            get_lang('Options'),
9258
            get_lang('Uncompress')
9259
        );
9260
9261
        $url = api_get_path(WEB_AJAX_PATH).'document.ajax.php?'.api_get_cidreq().'&a=upload_file&curdirpath=';
9262
        $form->addMultipleUpload($url);
9263
        $new = $this->display_document_form('add', 0);
9264
9265
        $tabs = Display::tabs($headers, array($documentTree, $new, $form->returnForm()), 'subtab');
9266
9267
        return $tabs;
9268
    }
9269
9270
    /**
9271
     * Creates a list with all the exercises (quiz) in it
9272
     * @return string
9273
     */
9274
    public function get_exercises()
9275
    {
9276
        $course_id = api_get_course_int_id();
9277
        $session_id = api_get_session_id();
9278
        $userInfo = api_get_user_info();
9279
9280
        // New for hotpotatoes.
9281
        $uploadPath = DIR_HOTPOTATOES; //defined in main_api
9282
        $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
9283
        $tbl_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
9284
        $condition_session = api_get_session_condition($session_id, true, true);
9285
        $setting = api_get_configuration_value('show_invisible_exercise_in_lp_list');
9286
9287
        $activeCondition = ' active <> -1 ';
9288
        if ($setting) {
9289
            $activeCondition = ' active = 1 ';
9290
        }
9291
9292
        $sql_quiz = "SELECT * FROM $tbl_quiz
9293
                     WHERE c_id = $course_id AND $activeCondition $condition_session
9294
                     ORDER BY title ASC";
9295
9296
        $sql_hot = "SELECT * FROM $tbl_doc
9297
                     WHERE c_id = $course_id AND path LIKE '".$uploadPath."/%/%htm%'  $condition_session
9298
                     ORDER BY id ASC";
9299
9300
        $res_quiz = Database::query($sql_quiz);
9301
        $res_hot  = Database::query($sql_hot);
9302
9303
        $return = '<ul class="lp_resource">';
9304
9305
        $return .= '<li class="lp_resource_element">';
9306
        $return .= Display::return_icon('new_exercice.png');
9307
        $return .= '<a href="'.api_get_path(WEB_CODE_PATH).'exercise/exercise_admin.php?'.api_get_cidreq().'&lp_id='.$this->lp_id.'">'.
9308
            get_lang('NewExercise').'</a>';
9309
        $return .= '</li>';
9310
9311
        $previewIcon = Display::return_icon('preview_view.png', get_lang('Preview'));
9312
        $exerciseUrl = api_get_path(WEB_CODE_PATH).'exercise/showinframes.php?'.api_get_cidreq();
9313
9314
        // Display hotpotatoes
9315
        while ($row_hot = Database::fetch_array($res_hot)) {
9316
            $link = Display::url(
9317
                $previewIcon,
9318
                $exerciseUrl.'&file='.$row_hot['path'],
9319
                ['target' => '_blank']
9320
            );
9321
            $return .= '<li class="lp_resource_element" data_id="'.$row_hot['id'].'" data_type="hotpotatoes" title="'.$row_hot['title'].'" >';
9322
            $return .= '<a class="moved" href="#">';
9323
            $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
9324
            $return .= '</a> ';
9325
            $return .= Display::return_icon('hotpotatoes_s.png');
9326
            $return .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_HOTPOTATOES.'&file='.$row_hot['id'].'&lp_id='.$this->lp_id.'">'.
9327
                ((!empty ($row_hot['comment'])) ? $row_hot['comment'] : Security::remove_XSS($row_hot['title'])).$link.'</a>';
9328
            $return .= '</li>';
9329
        }
9330
9331
        $exerciseUrl = api_get_path(WEB_CODE_PATH).'exercise/overview.php?'.api_get_cidreq();
9332
        while ($row_quiz = Database::fetch_array($res_quiz)) {
9333
            $title = strip_tags(
9334
                api_html_entity_decode($row_quiz['title'])
9335
            );
9336
9337
            $link = Display::url(
9338
                $previewIcon,
9339
                $exerciseUrl.'&exerciseId='.$row_quiz['id'],
9340
                ['target' => '_blank']
9341
            );
9342
            $return .= '<li class="lp_resource_element" data_id="'.$row_quiz['id'].'" data_type="quiz" title="'.$title.'" >';
9343
            $return .= '<a class="moved" href="#">';
9344
            $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
9345
            $return .= '</a> ';
9346
            $return .= Display::return_icon('quizz_small.gif', '', array(), ICON_SIZE_TINY);
9347
            $sessionStar = api_get_session_image($row_quiz['session_id'], $userInfo['status']);
9348
            $return .= '<a class="moved" href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_QUIZ.'&file='.$row_quiz['id'].'&lp_id='.$this->lp_id.'">'.
9349
                Security::remove_XSS(cut($title, 80)).$link.$sessionStar.
9350
                '</a>';
9351
9352
            $return .= '</li>';
9353
        }
9354
9355
        $return .= '</ul>';
9356
        return $return;
9357
    }
9358
9359
    /**
9360
     * Creates a list with all the links in it
9361
     * @return string
9362
     */
9363
    public function get_links()
9364
    {
9365
        $selfUrl = api_get_self();
9366
        $courseIdReq = api_get_cidreq();
9367
        $course = api_get_course_info();
9368
        $userInfo = api_get_user_info();
9369
9370
        $course_id = $course['real_id'];
9371
        $tbl_link = Database::get_course_table(TABLE_LINK);
9372
        $linkCategoryTable = Database::get_course_table(TABLE_LINK_CATEGORY);
9373
        $moveEverywhereIcon = Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
9374
9375
        $session_id = api_get_session_id();
9376
        $condition_session = api_get_session_condition(
9377
            $session_id,
9378
            true,
9379
            true,
9380
            "link.session_id"
9381
        );
9382
9383
        $sql = "SELECT 
9384
                    link.id as link_id,
9385
                    link.title as link_title,
9386
                    link.session_id as link_session_id,
9387
                    link.category_id as category_id,
9388
                    link_category.category_title as category_title
9389
                FROM $tbl_link as link
9390
                LEFT JOIN $linkCategoryTable as link_category
9391
                ON (link.category_id = link_category.id AND link.c_id = link_category.c_id)
9392
                WHERE link.c_id = ".$course_id." $condition_session
9393
                ORDER BY link_category.category_title ASC, link.title ASC";
9394
        $result = Database::query($sql);
9395
        $categorizedLinks = array();
9396
        $categories = array();
9397
9398 View Code Duplication
        while ($link = Database::fetch_array($result)) {
9399
            if (!$link['category_id']) {
9400
                $link['category_title'] = get_lang('Uncategorized');
9401
            }
9402
            $categories[$link['category_id']] = $link['category_title'];
9403
            $categorizedLinks[$link['category_id']][$link['link_id']] = $link;
9404
        }
9405
9406
        $linksHtmlCode =
9407
            '<script>
9408
            function toggle_tool(tool, id){
9409
                if(document.getElementById(tool+"_"+id+"_content").style.display == "none"){
9410
                    document.getElementById(tool+"_"+id+"_content").style.display = "block";
9411
                    document.getElementById(tool+"_"+id+"_opener").src = "' . Display::returnIconPath('remove.gif').'";
9412
                } else {
9413
                    document.getElementById(tool+"_"+id+"_content").style.display = "none";
9414
                    document.getElementById(tool+"_"+id+"_opener").src = "'.Display::returnIconPath('add.gif').'";
9415
                }
9416
            }
9417
        </script>
9418
9419
        <ul class="lp_resource">
9420
            <li class="lp_resource_element">
9421
                '.Display::return_icon('linksnew.gif').'
9422
                <a href="'.api_get_path(WEB_CODE_PATH).'link/link.php?'.$courseIdReq.'&action=addlink&lp_id='.$this->lp_id.'" title="'.get_lang('LinkAdd').'">'.
9423
                get_lang('LinkAdd').'
9424
                </a>
9425
            </li>';
9426
9427
        foreach ($categorizedLinks as $categoryId => $links) {
9428
            $linkNodes = null;
9429
            foreach ($links as $key => $linkInfo) {
9430
                $title = $linkInfo['link_title'];
9431
                $linkSessionId = $linkInfo['link_session_id'];
9432
9433
                $link = Display::url(
9434
                    Display::return_icon('preview_view.png', get_lang('Preview')),
9435
                    api_get_path(WEB_CODE_PATH).'link/link_goto.php?'.api_get_cidreq().'&link_id='.$key,
9436
                    ['target' => '_blank']
9437
                );
9438
9439
                if (api_get_item_visibility($course, TOOL_LINK, $key, $session_id) != 2) {
9440
                    $sessionStar = api_get_session_image($linkSessionId, $userInfo['status']);
9441
                    $linkNodes .=
9442
                        '<li class="lp_resource_element" data_id="'.$key.'" data_type="'.TOOL_LINK.'" title="'.$title.'" >
9443
                        <a class="moved" href="#">'.
9444
                            $moveEverywhereIcon.
9445
                        '</a>
9446
                        '.Display::return_icon('lp_link.png').'
9447
                        <a class="moved" href="'.$selfUrl.'?'.$courseIdReq.'&action=add_item&type='.
9448
                        TOOL_LINK.'&file='.$key.'&lp_id='.$this->lp_id.'">'.
9449
                        Security::remove_XSS($title).$sessionStar.$link.
9450
                        '</a>
9451
                    </li>';
9452
                }
9453
            }
9454
            $linksHtmlCode .=
9455
                '<li>
9456
                <a style="cursor:hand" onclick="javascript: toggle_tool(\''.TOOL_LINK.'\','.$categoryId.')" style="vertical-align:middle">
9457
                    <img src="'.Display::returnIconPath('add.gif').'" id="'.TOOL_LINK.'_'.$categoryId.'_opener"
9458
                    align="absbottom" />
9459
                </a>
9460
                <span style="vertical-align:middle">'.Security::remove_XSS($categories[$categoryId]).'</span>
9461
            </li>
9462
            <div style="display:none" id="'.TOOL_LINK.'_'.$categoryId.'_content">'.$linkNodes.'</div>';
9463
        }
9464
        $linksHtmlCode .= '</ul>';
9465
9466
        return $linksHtmlCode;
9467
    }
9468
9469
    /**
9470
     * Creates a list with all the student publications in it
9471
     * @return string
9472
     */
9473
    public function get_student_publications()
9474
    {
9475
        $return = '<ul class="lp_resource">';
9476
        $return .= '<li class="lp_resource_element">';
9477
        $return .= Display::return_icon('works_new.gif');
9478
        $return .= ' <a href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_STUDENTPUBLICATION.'&lp_id='.$this->lp_id.'">'.
9479
            get_lang('AddAssignmentPage').'</a>';
9480
        $return .= '</li>';
9481
        $sessionId = api_get_session_id();
9482
9483
        if (empty($sessionId)) {
9484
            require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
9485
            $works = getWorkListTeacher(0, 100, null, null, null);
9486
            if (!empty($works)) {
9487
                foreach ($works as $work) {
9488
                    $link = Display::url(
9489
                        Display::return_icon('preview_view.png', get_lang('Preview')),
9490
                        api_get_path(WEB_CODE_PATH).'work/work_list_all.php?'.api_get_cidreq().'&id='.$work['iid'],
9491
                        ['target' => '_blank']
9492
                    );
9493
9494
                    $return .= '<li class="lp_resource_element" data_id="'.$work['iid'].'" data_type="'.TOOL_STUDENTPUBLICATION.'" title="'.Security::remove_XSS(cut(strip_tags($work['title']), 80)).'">';
9495
                    $return .= '<a class="moved" href="#">';
9496
                    $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
9497
                    $return .= '</a> ';
9498
9499
                    $return .= Display::return_icon('works.gif');
9500
                    $return .= ' <a class="moved" href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_STUDENTPUBLICATION.'&file='.$work['iid'].'&lp_id='.$this->lp_id.'">'.
9501
                        Security::remove_XSS(cut(strip_tags($work['title']), 80)).' '.$link.'
9502
                    </a>';
9503
9504
                    $return .= '</li>';
9505
                }
9506
            }
9507
        }
9508
9509
        $return .= '</ul>';
9510
9511
        return $return;
9512
    }
9513
9514
    /**
9515
     * Creates a list with all the forums in it
9516
     * @return string
9517
     */
9518
    public function get_forums()
9519
    {
9520
        require_once '../forum/forumfunction.inc.php';
9521
        require_once '../forum/forumconfig.inc.php';
9522
9523
        $forumCategories = get_forum_categories();
9524
        $forumsInNoCategory = get_forums_in_category(0);
9525 View Code Duplication
        if (!empty($forumsInNoCategory)) {
9526
            $forumCategories = array_merge(
9527
                $forumCategories,
9528
                array(
9529
                    array(
9530
                        'cat_id' => 0,
9531
                        'session_id' => 0,
9532
                        'visibility' => 1,
9533
                        'cat_comment' => null,
9534
                    ),
9535
                )
9536
            );
9537
        }
9538
9539
        $forumList = get_forums();
9540
        $a_forums = [];
9541
        foreach ($forumCategories as $forumCategory) {
9542
            // The forums in this category.
9543
            $forumsInCategory = get_forums_in_category($forumCategory['cat_id']);
9544
            if (!empty($forumsInCategory)) {
9545
                foreach ($forumList as $forum) {
9546
                    if (isset($forum['forum_category']) && $forum['forum_category'] == $forumCategory['cat_id']) {
9547
                        $a_forums[] = $forum;
9548
                    }
9549
                }
9550
            }
9551
        }
9552
9553
        $return = '<ul class="lp_resource">';
9554
9555
        // First add link
9556
        $return .= '<li class="lp_resource_element">';
9557
        $return .= Display::return_icon('new_forum.png');
9558
        $return .= Display::url(
9559
            get_lang('CreateANewForum'),
9560
            api_get_path(WEB_CODE_PATH).'forum/index.php?'.api_get_cidreq().'&'.http_build_query([
9561
                'action' => 'add',
9562
                'content' => 'forum',
9563
                'lp_id' => $this->lp_id
9564
            ]),
9565
            ['title' => get_lang('CreateANewForum')]
9566
        );
9567
        $return .= '</li>';
9568
9569
        $return .= '<script>
9570
            function toggle_forum(forum_id) {
9571
                if (document.getElementById("forum_"+forum_id+"_content").style.display == "none") {
9572
                    document.getElementById("forum_"+forum_id+"_content").style.display = "block";
9573
                    document.getElementById("forum_"+forum_id+"_opener").src = "' . Display::returnIconPath('remove.gif').'";
9574
                } else {
9575
                    document.getElementById("forum_"+forum_id+"_content").style.display = "none";
9576
                    document.getElementById("forum_"+forum_id+"_opener").src = "' . Display::returnIconPath('add.gif').'";
9577
                }
9578
            }
9579
        </script>';
9580
9581
        foreach ($a_forums as $forum) {
9582
            if (!empty($forum['forum_id'])) {
9583
                $link = Display::url(
9584
                    Display::return_icon('preview_view.png', get_lang('Preview')),
9585
                    api_get_path(WEB_CODE_PATH).'forum/viewforum.php?'.api_get_cidreq().'&forum='.$forum['forum_id'],
9586
                    ['target' => '_blank']
9587
                );
9588
9589
                $return .= '<li class="lp_resource_element" data_id="'.$forum['forum_id'].'" data_type="'.TOOL_FORUM.'" title="'.$forum['forum_title'].'" >';
9590
                $return .= '<a class="moved" href="#">';
9591
                $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
9592
                $return .= ' </a>';
9593
                $return .= Display::return_icon('lp_forum.png', '', array(), ICON_SIZE_TINY);
9594
                $return .= '<a onclick="javascript:toggle_forum('.$forum['forum_id'].');" style="cursor:hand; vertical-align:middle">
9595
                                <img src="' . Display::returnIconPath('add.gif').'" id="forum_'.$forum['forum_id'].'_opener" align="absbottom" />
9596
                            </a>
9597
                            <a class="moved" href="' . api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_FORUM.'&forum_id='.$forum['forum_id'].'&lp_id='.$this->lp_id.'" style="vertical-align:middle">'.
9598
                    Security::remove_XSS($forum['forum_title']).' '.$link.'</a>';
9599
9600
                $return .= '</li>';
9601
9602
                $return .= '<div style="display:none" id="forum_'.$forum['forum_id'].'_content">';
9603
                $a_threads = get_threads($forum['forum_id']);
9604
                if (is_array($a_threads)) {
9605
                    foreach ($a_threads as $thread) {
9606
                        $link = Display::url(
9607
                            Display::return_icon('preview_view.png', get_lang('Preview')),
9608
                            api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.api_get_cidreq().'&forum='.$forum['forum_id'].'&thread='.$thread['thread_id'],
9609
                            ['target' => '_blank']
9610
                        );
9611
9612
                        $return .= '<li class="lp_resource_element" data_id="'.$thread['thread_id'].'" data_type="'.TOOL_THREAD.'" title="'.$thread['thread_title'].'" >';
9613
                        $return .= '&nbsp;<a class="moved" href="#">';
9614
                        $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
9615
                        $return .= ' </a>';
9616
                        $return .= Display::return_icon('forumthread.png', get_lang('Thread'), array(), ICON_SIZE_TINY);
9617
                        $return .= '<a class="moved" href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type='.TOOL_THREAD.'&thread_id='.$thread['thread_id'].'&lp_id='.$this->lp_id.'">'.
9618
                            Security::remove_XSS($thread['thread_title']).' '.$link.'</a>';
9619
                        $return .= '</li>';
9620
                    }
9621
                }
9622
                $return .= '</div>';
9623
            }
9624
        }
9625
        $return .= '</ul>';
9626
9627
        return $return;
9628
    }
9629
9630
    /**
9631
     * // TODO: The output encoding should be equal to the system encoding.
9632
     *
9633
     * Exports the learning path as a SCORM package. This is the main function that
9634
     * gathers the content, transforms it, writes the imsmanifest.xml file, zips the
9635
     * whole thing and returns the zip.
9636
     *
9637
     * This method needs to be called in PHP5, as it will fail with non-adequate
9638
     * XML package (like the ones for PHP4), and it is *not* a static method, so
9639
     * you need to call it on a learnpath object.
9640
     * @TODO The method might be redefined later on in the scorm class itself to avoid
9641
     * creating a SCORM structure if there is one already. However, if the initial SCORM
9642
     * path has been modified, it should use the generic method here below.
9643
     * @TODO link this function with the export_lp() function in the same class
9644
     * @param	string	Optional name of zip file. If none, title of learnpath is
9645
     * 					domesticated and trailed with ".zip"
9646
     * @return	string	Returns the zip package string, or null if error
9647
     */
9648
    public function scorm_export()
9649
    {
9650
        $_course = api_get_course_info();
9651
        $course_id = $_course['real_id'];
9652
9653
        // Remove memory and time limits as much as possible as this might be a long process...
9654
        if (function_exists('ini_set')) {
9655
            api_set_memory_limit('256M');
9656
            ini_set('max_execution_time', 600);
9657
        }
9658
9659
        // Create the zip handler (this will remain available throughout the method).
9660
        $archive_path = api_get_path(SYS_ARCHIVE_PATH);
9661
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
9662
        $temp_dir_short = uniqid();
9663
        $temp_zip_dir = $archive_path.'/'.$temp_dir_short;
9664
        $temp_zip_file = $temp_zip_dir.'/'.md5(time()).'.zip';
9665
        $zip_folder = new PclZip($temp_zip_file);
9666
        $current_course_path = api_get_path(SYS_COURSE_PATH).api_get_course_path();
9667
        $root_path = $main_path = api_get_path(SYS_PATH);
9668
        $files_cleanup = array();
9669
9670
        // Place to temporarily stash the zip file.
9671
        // create the temp dir if it doesn't exist
9672
        // or do a cleanup before creating the zip file.
9673
        if (!is_dir($temp_zip_dir)) {
9674
            mkdir($temp_zip_dir, api_get_permissions_for_new_directories());
9675
        } else {
9676
            // Cleanup: Check the temp dir for old files and delete them.
9677
            $handle = opendir($temp_zip_dir);
9678 View Code Duplication
            while (false !== ($file = readdir($handle))) {
9679
                if ($file != '.' && $file != '..') {
9680
                    unlink("$temp_zip_dir/$file");
9681
                }
9682
            }
9683
            closedir($handle);
9684
        }
9685
        $zip_files = $zip_files_abs = $zip_files_dist = array();
9686
        if (is_dir($current_course_path.'/scorm/'.$this->path) &&
9687
            is_file($current_course_path.'/scorm/'.$this->path.'/imsmanifest.xml')
9688
        ) {
9689
            // Remove the possible . at the end of the path.
9690
            $dest_path_to_lp = substr($this->path, -1) == '.' ? substr($this->path, 0, -1) : $this->path;
9691
            $dest_path_to_scorm_folder = str_replace('//', '/', $temp_zip_dir.'/scorm/'.$dest_path_to_lp);
9692
            mkdir($dest_path_to_scorm_folder, api_get_permissions_for_new_directories(), true);
9693
            copyr(
9694
                $current_course_path.'/scorm/'.$this->path,
9695
                $dest_path_to_scorm_folder,
9696
                array('imsmanifest'),
9697
                $zip_files
9698
            );
9699
        }
9700
9701
        // Build a dummy imsmanifest structure.
9702
        // Do not add to the zip yet (we still need it).
9703
        // This structure is developed following regulations for SCORM 1.2 packaging in the SCORM 1.2 Content
9704
        // Aggregation Model official document, section "2.3 Content Packaging".
9705
        // We are going to build a UTF-8 encoded manifest. Later we will recode it to the desired (and supported) encoding.
9706
        $xmldoc = new DOMDocument('1.0');
9707
        $root = $xmldoc->createElement('manifest');
9708
        $root->setAttribute('identifier', 'SingleCourseManifest');
9709
        $root->setAttribute('version', '1.1');
9710
        $root->setAttribute('xmlns', 'http://www.imsproject.org/xsd/imscp_rootv1p1p2');
9711
        $root->setAttribute('xmlns:adlcp', 'http://www.adlnet.org/xsd/adlcp_rootv1p2');
9712
        $root->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
9713
        $root->setAttribute('xsi:schemaLocation', 'http://www.imsproject.org/xsd/imscp_rootv1p1p2 imscp_rootv1p1p2.xsd http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 imsmd_rootv1p2p1.xsd http://www.adlnet.org/xsd/adlcp_rootv1p2 adlcp_rootv1p2.xsd');
9714
        // Build mandatory sub-root container elements.
9715
        $metadata = $xmldoc->createElement('metadata');
9716
        $md_schema = $xmldoc->createElement('schema', 'ADL SCORM');
9717
        $metadata->appendChild($md_schema);
9718
        $md_schemaversion = $xmldoc->createElement('schemaversion', '1.2');
9719
        $metadata->appendChild($md_schemaversion);
9720
        $root->appendChild($metadata);
9721
9722
        $organizations = $xmldoc->createElement('organizations');
9723
        $resources = $xmldoc->createElement('resources');
9724
9725
        // Build the only organization we will use in building our learnpaths.
9726
        $organizations->setAttribute('default', 'chamilo_scorm_export');
9727
        $organization = $xmldoc->createElement('organization');
9728
        $organization->setAttribute('identifier', 'chamilo_scorm_export');
9729
        // To set the title of the SCORM entity (=organization), we take the name given
9730
        // in Chamilo and convert it to HTML entities using the Chamilo charset (not the
9731
        // learning path charset) as it is the encoding that defines how it is stored
9732
        // in the database. Then we convert it to HTML entities again as the "&" character
9733
        // alone is not authorized in XML (must be &amp;).
9734
        // The title is then decoded twice when extracting (see scorm::parse_manifest).
9735
        $org_title = $xmldoc->createElement('title', api_utf8_encode($this->get_name()));
9736
        $organization->appendChild($org_title);
9737
9738
        $folder_name = 'document';
9739
9740
        // Removes the learning_path/scorm_folder path when exporting see #4841
9741
        $path_to_remove = null;
9742
        $result = $this->generate_lp_folder($_course);
9743
9744
        if (isset($result['dir']) && strpos($result['dir'], 'learning_path')) {
9745
            $path_to_remove = 'document'.$result['dir'];
9746
            $path_to_replace = $folder_name.'/';
9747
        }
9748
9749
        // Fixes chamilo scorm exports
9750
        if ($this->ref === 'chamilo_scorm_export') {
9751
            $path_to_remove = 'scorm/'.$this->path.'/document/';
9752
        }
9753
9754
        // For each element, add it to the imsmanifest structure, then add it to the zip.
9755
        // Always call the learnpathItem->scorm_export() method to change it to the SCORM format.
9756
        $link_updates = array();
9757
        $links_to_create = array();
9758
        //foreach ($this->items as $index => $item) {
9759
        foreach ($this->ordered_items as $index => $itemId) {
9760
            $item = $this->items[$itemId];
9761
            if (!in_array($item->type, array(TOOL_QUIZ, TOOL_FORUM, TOOL_THREAD, TOOL_LINK, TOOL_STUDENTPUBLICATION))) {
9762
                // Get included documents from this item.
9763
                if ($item->type === 'sco') {
9764
                    $inc_docs = $item->get_resources_from_source(
9765
                        null,
9766
                        api_get_path(SYS_COURSE_PATH).api_get_course_path().'/'.'scorm/'.$this->path.'/'.$item->get_path()
9767
                    );
9768
                } else {
9769
                    $inc_docs = $item->get_resources_from_source();
9770
                }
9771
                // Give a child element <item> to the <organization> element.
9772
                $my_item_id = $item->get_id();
9773
                $my_item = $xmldoc->createElement('item');
9774
                $my_item->setAttribute('identifier', 'ITEM_'.$my_item_id);
9775
                $my_item->setAttribute('identifierref', 'RESOURCE_'.$my_item_id);
9776
                $my_item->setAttribute('isvisible', 'true');
9777
                // Give a child element <title> to the <item> element.
9778
                $my_title = $xmldoc->createElement('title', htmlspecialchars(api_utf8_encode($item->get_title()), ENT_QUOTES, 'UTF-8'));
9779
                $my_item->appendChild($my_title);
9780
                // Give a child element <adlcp:prerequisites> to the <item> element.
9781
                $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $this->get_scorm_prereq_string($my_item_id));
9782
                $my_prereqs->setAttribute('type', 'aicc_script');
9783
                $my_item->appendChild($my_prereqs);
9784
                // Give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported.
9785
                //$xmldoc->createElement('adlcp:maxtimeallowed','');
9786
                // Give a child element <adlcp:timelimitaction> to the <item> element - not yet supported.
9787
                //$xmldoc->createElement('adlcp:timelimitaction','');
9788
                // Give a child element <adlcp:datafromlms> to the <item> element - not yet supported.
9789
                //$xmldoc->createElement('adlcp:datafromlms','');
9790
                // Give a child element <adlcp:masteryscore> to the <item> element.
9791
                $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score());
9792
                $my_item->appendChild($my_masteryscore);
9793
9794
                // Attach this item to the organization element or hits parent if there is one.
9795
                if (!empty($item->parent) && $item->parent != 0) {
9796
                    $children = $organization->childNodes;
9797
                    $possible_parent = $this->get_scorm_xml_node($children, 'ITEM_'.$item->parent);
9798
                    if (is_object($possible_parent)) {
9799
                        $possible_parent->appendChild($my_item);
9800 View Code Duplication
                    } else {
9801
                        if ($this->debug > 0) { error_log('Parent ITEM_'.$item->parent.' of item ITEM_'.$my_item_id.' not found'); }
9802
                    }
9803
                } else {
9804
                    if ($this->debug > 0) { error_log('No parent'); }
9805
                    $organization->appendChild($my_item);
9806
                }
9807
9808
                // Get the path of the file(s) from the course directory root.
9809
                $my_file_path = $item->get_file_path('scorm/'.$this->path.'/');
9810
9811
                if (!empty($path_to_remove)) {
9812
                    //From docs
9813
                    $my_xml_file_path = str_replace($path_to_remove, $path_to_replace, $my_file_path);
9814
9815
                    //From quiz
9816
                    if ($this->ref === 'chamilo_scorm_export') {
9817
                        $path_to_remove = 'scorm/'.$this->path.'/';
9818
                        $my_xml_file_path = str_replace($path_to_remove, '', $my_file_path);
9819
                    }
9820
                } else {
9821
                    $my_xml_file_path = $my_file_path;
9822
                }
9823
9824
                $my_sub_dir = dirname($my_file_path);
9825
                $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
9826
                $my_xml_sub_dir = $my_sub_dir;
9827
                // Give a <resource> child to the <resources> element
9828
                $my_resource = $xmldoc->createElement('resource');
9829
                $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
9830
                $my_resource->setAttribute('type', 'webcontent');
9831
                $my_resource->setAttribute('href', $my_xml_file_path);
9832
                // adlcp:scormtype can be either 'sco' or 'asset'.
9833
                if ($item->type === 'sco') {
9834
                    $my_resource->setAttribute('adlcp:scormtype', 'sco');
9835
                } else {
9836
                    $my_resource->setAttribute('adlcp:scormtype', 'asset');
9837
                }
9838
                // xml:base is the base directory to find the files declared in this resource.
9839
                $my_resource->setAttribute('xml:base', '');
9840
                // Give a <file> child to the <resource> element.
9841
                $my_file = $xmldoc->createElement('file');
9842
                $my_file->setAttribute('href', $my_xml_file_path);
9843
                $my_resource->appendChild($my_file);
9844
9845
                // Dependency to other files - not yet supported.
9846
                $i = 1;
9847
                if ($inc_docs)
9848
                foreach ($inc_docs as $doc_info) {
9849
                    if (count($doc_info) < 1 || empty($doc_info[0])) {
9850
                        continue;
9851
                    }
9852
                    $my_dep = $xmldoc->createElement('resource');
9853
                    $res_id = 'RESOURCE_'.$item->get_id().'_'.$i;
9854
                    $my_dep->setAttribute('identifier', $res_id);
9855
                    $my_dep->setAttribute('type', 'webcontent');
9856
                    $my_dep->setAttribute('adlcp:scormtype', 'asset');
9857
                    $my_dep_file = $xmldoc->createElement('file');
9858
                    // Check type of URL.
9859
                    if ($doc_info[1] == 'remote') {
9860
                        // Remote file. Save url as is.
9861
                        $my_dep_file->setAttribute('href', $doc_info[0]);
9862
                        $my_dep->setAttribute('xml:base', '');
9863
                    } elseif ($doc_info[1] === 'local') {
9864
                        switch ($doc_info[2]) {
9865
                            case 'url': // Local URL - save path as url for now, don't zip file.
9866
                                $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]);
9867
                                $current_dir = dirname($abs_path);
9868
                                $current_dir = str_replace('\\', '/', $current_dir);
9869
                                $file_path = realpath($abs_path);
9870
                                $file_path = str_replace('\\', '/', $file_path);
9871
                                $my_dep_file->setAttribute('href', $file_path);
9872
                                $my_dep->setAttribute('xml:base', '');
9873
                                if (strstr($file_path, $main_path) !== false) {
9874
                                    // The calculated real path is really inside Chamilo's root path.
9875
                                    // Reduce file path to what's under the DocumentRoot.
9876
                                    $file_path = substr($file_path, strlen($root_path) - 1);
9877
                                    //echo $file_path;echo '<br /><br />';
9878
                                    //error_log(__LINE__.'Reduced url path: '.$file_path, 0);
9879
                                    $zip_files_abs[] = $file_path;
9880
                                    $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9881
                                    $my_dep_file->setAttribute('href', $file_path);
9882
                                    $my_dep->setAttribute('xml:base', '');
9883
                                } elseif (empty($file_path)) {
9884
                                    /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH), api_get_path(REL_PATH)));
9885
                                    if (strpos($document_root, -1) == '/') {
9886
                                        $document_root = substr(0, -1, $document_root);
9887
                                    }*/
9888
                                    $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path;
9889
                                    $file_path = str_replace('//', '/', $file_path);
9890
                                    if (file_exists($file_path)) {
9891
                                        $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
9892
                                        $zip_files[] = $my_sub_dir.'/'.$file_path;
9893
                                        $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9894
                                        $my_dep_file->setAttribute('href', $file_path);
9895
                                        $my_dep->setAttribute('xml:base', '');
9896
                                    }
9897
                                }
9898
                                break;
9899
                            case 'abs': // Absolute path from DocumentRoot. Save file and leave path as is in the zip.
9900
                                $my_dep_file->setAttribute('href', $doc_info[0]);
9901
                                $my_dep->setAttribute('xml:base', '');
9902
9903
                                // The next lines fix a bug when using the "subdir" mode of Chamilo, whereas
9904
                                // an image path would be constructed as /var/www/subdir/subdir/img/foo.bar
9905
                                $abs_img_path_without_subdir = $doc_info[0];
9906
                                $relp = api_get_path(REL_PATH); // The url-append config param.
9907
                                $pos = strpos($abs_img_path_without_subdir, $relp);
9908
                                if ($pos === 0) {
9909
                                    $abs_img_path_without_subdir = '/'.substr($abs_img_path_without_subdir, strlen($relp));
9910
                                }
9911
9912
                                //$file_path = realpath(api_get_path(SYS_PATH).$abs_img_path_without_subdir);
9913
                                $file_path = realpath(api_get_path(SYS_APP_PATH).$abs_img_path_without_subdir);
9914
9915
                                $file_path = str_replace('\\', '/', $file_path);
9916
                                $file_path = str_replace('//', '/', $file_path);
9917
9918
                                // Prepare the current directory path (until just under 'document') with a trailing slash.
9919
                                $cur_path = substr($current_course_path, -1) == '/' ? $current_course_path : $current_course_path.'/';
9920
                                // Check if the current document is in that path.
9921
                                if (strstr($file_path, $cur_path) !== false) {
9922
                                    // The document is in that path, now get the relative path
9923
                                    // to the containing document.
9924
                                    $orig_file_path = dirname($cur_path.$my_file_path).'/';
9925
                                    $orig_file_path = str_replace('\\', '/', $orig_file_path);
9926
                                    $relative_path = '';
9927
                                    if (strstr($file_path, $cur_path) !== false) {
9928
                                        //$relative_path = substr($file_path, strlen($orig_file_path));
9929
                                        $relative_path = str_replace($cur_path, '', $file_path);
9930
                                        $file_path = substr($file_path, strlen($cur_path));
9931
                                    } else {
9932
                                        // This case is still a problem as it's difficult to calculate a relative path easily
9933
                                        // might still generate wrong links.
9934
                                        //$file_path = substr($file_path,strlen($cur_path));
9935
                                        // Calculate the directory path to the current file (without trailing slash).
9936
                                        $my_relative_path = dirname($file_path);
9937
                                        $my_relative_path = str_replace('\\', '/', $my_relative_path);
9938
                                        $my_relative_file = basename($file_path);
9939
                                        // Calculate the directory path to the containing file (without trailing slash).
9940
                                        $my_orig_file_path = substr($orig_file_path, 0, -1);
9941
                                        $dotdot = '';
9942
                                        $subdir = '';
9943
                                        while (strstr($my_relative_path, $my_orig_file_path) === false && (strlen($my_orig_file_path) > 1) && (strlen($my_relative_path) > 1)) {
9944
                                            $my_relative_path2 = dirname($my_relative_path);
9945
                                            $my_relative_path2 = str_replace('\\', '/', $my_relative_path2);
9946
                                            $my_orig_file_path = dirname($my_orig_file_path);
9947
                                            $my_orig_file_path = str_replace('\\', '/', $my_orig_file_path);
9948
                                            $subdir = substr($my_relative_path, strlen($my_relative_path2) + 1).'/'.$subdir;
9949
                                            $dotdot += '../';
9950
                                            $my_relative_path = $my_relative_path2;
9951
                                        }
9952
                                        $relative_path = $dotdot.$subdir.$my_relative_file;
9953
                                    }
9954
                                    // Put the current document in the zip (this array is the array
9955
                                    // that will manage documents already in the course folder - relative).
9956
                                    $zip_files[] = $file_path;
9957
                                    // Update the links to the current document in the containing document (make them relative).
9958
                                    $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $relative_path);
9959
                                    $my_dep_file->setAttribute('href', $file_path);
9960
                                    $my_dep->setAttribute('xml:base', '');
9961
                                } elseif (strstr($file_path, $main_path) !== false) {
9962
                                    // The calculated real path is really inside Chamilo's root path.
9963
                                    // Reduce file path to what's under the DocumentRoot.
9964
                                    $file_path = substr($file_path, strlen($root_path));
9965
                                    //echo $file_path;echo '<br /><br />';
9966
                                    //error_log('Reduced path: '.$file_path, 0);
9967
                                    $zip_files_abs[] = $file_path;
9968
                                    $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9969
                                    $my_dep_file->setAttribute('href', 'document/'.$file_path);
9970
                                    $my_dep->setAttribute('xml:base', '');
9971
                                } elseif (empty($file_path)) {
9972
                                    /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH), api_get_path(REL_PATH)));
9973
                                    if(strpos($document_root,-1) == '/') {
9974
                                        $document_root = substr(0, -1, $document_root);
9975
                                    }*/
9976
                                    $file_path = $_SERVER['DOCUMENT_ROOT'].$doc_info[0];
9977
                                    $file_path = str_replace('//', '/', $file_path);
9978
9979
                                    $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]);
9980
                                    $current_dir = dirname($abs_path);
9981
                                    $current_dir = str_replace('\\', '/', $current_dir);
9982
9983
                                    if (file_exists($file_path)) {
9984
                                        $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
9985
                                        $zip_files[] = $my_sub_dir.'/'.$file_path;
9986
                                        $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9987
                                        $my_dep_file->setAttribute('href', 'document/'.$file_path);
9988
                                        $my_dep->setAttribute('xml:base', '');
9989
                                    }
9990
                                }
9991
                                break;
9992
                            case 'rel':
9993
                                // Path relative to the current document.
9994
                                // Save xml:base as current document's directory and save file in zip as subdir.file_path
9995
                                if (substr($doc_info[0], 0, 2) == '..') {
9996
                                    // Relative path going up.
9997
                                    $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
9998
                                    $current_dir = str_replace('\\', '/', $current_dir);
9999
                                    $file_path = realpath($current_dir.$doc_info[0]);
10000
                                    $file_path = str_replace('\\', '/', $file_path);
10001
10002
                                    //error_log($file_path.' <-> '.$main_path,0);
10003
                                    if (strstr($file_path, $main_path) !== false) {
10004
                                        // The calculated real path is really inside Chamilo's root path.
10005
                                        // Reduce file path to what's under the DocumentRoot.
10006
                                        $file_path = substr($file_path, strlen($root_path));
10007
                                        //error_log('Reduced path: '.$file_path, 0);
10008
                                        $zip_files_abs[] = $file_path;
10009
                                        $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
10010
                                        $my_dep_file->setAttribute('href', 'document/'.$file_path);
10011
                                        $my_dep->setAttribute('xml:base', '');
10012
                                    }
10013 View Code Duplication
                                } else {
10014
                                    $zip_files[] = $my_sub_dir.'/'.$doc_info[0];
10015
                                    $my_dep_file->setAttribute('href', $doc_info[0]);
10016
                                    $my_dep->setAttribute('xml:base', $my_xml_sub_dir);
10017
                                }
10018
                                break;
10019
                            default:
10020
                                $my_dep_file->setAttribute('href', $doc_info[0]);
10021
                                $my_dep->setAttribute('xml:base', '');
10022
                                break;
10023
                        }
10024
                    }
10025
                    $my_dep->appendChild($my_dep_file);
10026
                    $resources->appendChild($my_dep);
10027
                    $dependency = $xmldoc->createElement('dependency');
10028
                    $dependency->setAttribute('identifierref', $res_id);
10029
                    $my_resource->appendChild($dependency);
10030
                    $i++;
10031
                }
10032
                $resources->appendChild($my_resource);
10033
                $zip_files[] = $my_file_path;
10034
            } else {
10035
                // If the item is a quiz or a link or whatever non-exportable, we include a step indicating it.
10036
                switch ($item->type) {
10037
                    case TOOL_LINK:
10038
                        $my_item = $xmldoc->createElement('item');
10039
                        $my_item->setAttribute('identifier', 'ITEM_'.$item->get_id());
10040
                        $my_item->setAttribute('identifierref', 'RESOURCE_'.$item->get_id());
10041
                        $my_item->setAttribute('isvisible', 'true');
10042
                        // Give a child element <title> to the <item> element.
10043
                        $my_title = $xmldoc->createElement('title', htmlspecialchars(api_utf8_encode($item->get_title()), ENT_QUOTES, 'UTF-8'));
10044
                        $my_item->appendChild($my_title);
10045
                        // Give a child element <adlcp:prerequisites> to the <item> element.
10046
                        $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $item->get_prereq_string());
10047
                        $my_prereqs->setAttribute('type', 'aicc_script');
10048
                        $my_item->appendChild($my_prereqs);
10049
                        // Give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported.
10050
                        //$xmldoc->createElement('adlcp:maxtimeallowed', '');
10051
                        // Give a child element <adlcp:timelimitaction> to the <item> element - not yet supported.
10052
                        //$xmldoc->createElement('adlcp:timelimitaction', '');
10053
                        // Give a child element <adlcp:datafromlms> to the <item> element - not yet supported.
10054
                        //$xmldoc->createElement('adlcp:datafromlms', '');
10055
                        // Give a child element <adlcp:masteryscore> to the <item> element.
10056
                        $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score());
10057
                        $my_item->appendChild($my_masteryscore);
10058
10059
                        // Attach this item to the organization element or its parent if there is one.
10060
                        if (!empty($item->parent) && $item->parent != 0) {
10061
                            $children = $organization->childNodes;
10062
                            for ($i = 0; $i < $children->length; $i++) {
10063
                                $item_temp = $children->item($i);
10064
                                if ($item_temp -> nodeName == 'item') {
10065
                                    if ($item_temp->getAttribute('identifier') == 'ITEM_'.$item->parent) {
10066
                                        $item_temp -> appendChild($my_item);
10067
                                    }
10068
                                }
10069
                            }
10070
                        } else {
10071
                            $organization->appendChild($my_item);
10072
                        }
10073
10074
                        $my_file_path = 'link_'.$item->get_id().'.html';
10075
                        $sql = 'SELECT url, title FROM '.Database::get_course_table(TABLE_LINK).'
10076
                                WHERE c_id = '.$course_id.' AND id='.$item->path;
10077
                        $rs = Database::query($sql);
10078
                        if ($link = Database::fetch_array($rs)) {
10079
                            $url = $link['url'];
10080
                            $title = stripslashes($link['title']);
10081
                            $links_to_create[$my_file_path] = array('title' => $title, 'url' => $url);
10082
                            $my_xml_file_path = $my_file_path;
10083
                            $my_sub_dir = dirname($my_file_path);
10084
                            $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
10085
                            $my_xml_sub_dir = $my_sub_dir;
10086
                            // Give a <resource> child to the <resources> element.
10087
                            $my_resource = $xmldoc->createElement('resource');
10088
                            $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
10089
                            $my_resource->setAttribute('type', 'webcontent');
10090
                            $my_resource->setAttribute('href', $my_xml_file_path);
10091
                            // adlcp:scormtype can be either 'sco' or 'asset'.
10092
                            $my_resource->setAttribute('adlcp:scormtype', 'asset');
10093
                            // xml:base is the base directory to find the files declared in this resource.
10094
                            $my_resource->setAttribute('xml:base', '');
10095
                            // give a <file> child to the <resource> element.
10096
                            $my_file = $xmldoc->createElement('file');
10097
                            $my_file->setAttribute('href', $my_xml_file_path);
10098
                            $my_resource->appendChild($my_file);
10099
                            $resources->appendChild($my_resource);
10100
                        }
10101
                        break;
10102
                    case TOOL_QUIZ:
10103
                        $exe_id = $item->path; // Should be using ref when everything will be cleaned up in this regard.
10104
                        $exe = new Exercise();
10105
                        $exe->read($exe_id);
10106
                        $my_item = $xmldoc->createElement('item');
10107
                        $my_item->setAttribute('identifier', 'ITEM_'.$item->get_id());
10108
                        $my_item->setAttribute('identifierref', 'RESOURCE_'.$item->get_id());
10109
                        $my_item->setAttribute('isvisible', 'true');
10110
                        // Give a child element <title> to the <item> element.
10111
                        $my_title = $xmldoc->createElement('title', htmlspecialchars(api_utf8_encode($item->get_title()), ENT_QUOTES, 'UTF-8'));
10112
                        $my_item->appendChild($my_title);
10113
                        $my_max_score = $xmldoc->createElement('max_score', $item->get_max());
10114
                        //$my_item->appendChild($my_max_score);
10115
                        // Give a child element <adlcp:prerequisites> to the <item> element.
10116
                        $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $item->get_prereq_string());
10117
                        $my_prereqs->setAttribute('type', 'aicc_script');
10118
                        $my_item->appendChild($my_prereqs);
10119
                        // Give a child element <adlcp:masteryscore> to the <item> element.
10120
                        $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score());
10121
                        $my_item->appendChild($my_masteryscore);
10122
10123
                        // Attach this item to the organization element or hits parent if there is one.
10124
10125
                        if (!empty($item->parent) && $item->parent != 0) {
10126
                            $children = $organization->childNodes;
10127
                            /*for ($i = 0; $i < $children->length; $i++) {
10128
                                $item_temp = $children->item($i);
10129
                                if ($exe_id == 81) {
10130
                                error_log($item_temp->nodeName );
10131
                                    error_log($item_temp->getAttribute('identifier'));
10132
                                }
10133
                                if ($item_temp->nodeName == 'item') {
10134
                                    if ($item_temp->getAttribute('identifier') == 'ITEM_'.$item->parent) {
10135
                                        $item_temp->appendChild($my_item);
10136
                                    }
10137
                                }
10138
                            }*/
10139
                            $possible_parent = $this->get_scorm_xml_node($children, 'ITEM_'.$item->parent);
10140
                            if ($possible_parent) {
10141
                                if ($possible_parent->getAttribute('identifier') == 'ITEM_'.$item->parent) {
10142
                                    $possible_parent->appendChild($my_item);
10143
                                }
10144
                            }
10145
                        } else {
10146
                            $organization->appendChild($my_item);
10147
                        }
10148
10149
                        // Get the path of the file(s) from the course directory root
10150
                        //$my_file_path = $item->get_file_path('scorm/'.$this->path.'/');
10151
                        $my_file_path = 'quiz_'.$item->get_id().'.html';
10152
                        // Write the contents of the exported exercise into a (big) html file
10153
                        // to later pack it into the exported SCORM. The file will be removed afterwards.
10154
                        $contents = ScormSection::export_exercise_to_scorm($exe, true);
10155
10156
                        $tmp_file_path = $archive_path.$temp_dir_short.'/'.$my_file_path;
10157
                        $res = file_put_contents($tmp_file_path, $contents);
10158
                        if ($res === false) {
10159
                            error_log('Could not write into file '.$tmp_file_path.' '.__FILE__.' '.__LINE__, 0);
10160
                        }
10161
                        $files_cleanup[] = $tmp_file_path;
10162
                        $my_xml_file_path = $my_file_path;
10163
                        $my_sub_dir = dirname($my_file_path);
10164
                        $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
10165
                        $my_xml_sub_dir = $my_sub_dir;
10166
                        // Give a <resource> child to the <resources> element.
10167
                        $my_resource = $xmldoc->createElement('resource');
10168
                        $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
10169
                        $my_resource->setAttribute('type', 'webcontent');
10170
                        $my_resource->setAttribute('href', $my_xml_file_path);
10171
10172
                        // adlcp:scormtype can be either 'sco' or 'asset'.
10173
                        $my_resource->setAttribute('adlcp:scormtype', 'sco');
10174
                        // xml:base is the base directory to find the files declared in this resource.
10175
                        $my_resource->setAttribute('xml:base', '');
10176
                        // Give a <file> child to the <resource> element.
10177
                        $my_file = $xmldoc->createElement('file');
10178
                        $my_file->setAttribute('href', $my_xml_file_path);
10179
                        $my_resource->appendChild($my_file);
10180
10181
                        // Get included docs.
10182
                        $inc_docs = $item->get_resources_from_source(null, $tmp_file_path);
10183
                        // Dependency to other files - not yet supported.
10184
                        $i = 1;
10185
                        foreach ($inc_docs as $doc_info) {
10186
                            if (count($doc_info) < 1 || empty($doc_info[0])) { continue; }
10187
                            $my_dep = $xmldoc->createElement('resource');
10188
                            $res_id = 'RESOURCE_'.$item->get_id().'_'.$i;
10189
                            $my_dep->setAttribute('identifier', $res_id);
10190
                            $my_dep->setAttribute('type', 'webcontent');
10191
                            $my_dep->setAttribute('adlcp:scormtype', 'asset');
10192
                            $my_dep_file = $xmldoc->createElement('file');
10193
                            // Check type of URL.
10194
                            if ($doc_info[1] == 'remote') {
10195
                                // Remote file. Save url as is.
10196
                                $my_dep_file->setAttribute('href', $doc_info[0]);
10197
                                $my_dep->setAttribute('xml:base', '');
10198
                            } elseif ($doc_info[1] == 'local') {
10199
                                switch ($doc_info[2]) {
10200
                                    case 'url': // Local URL - save path as url for now, don't zip file.
10201
                                        // Save file but as local file (retrieve from URL).
10202
                                        $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]);
10203
                                        $current_dir = dirname($abs_path);
10204
                                        $current_dir = str_replace('\\', '/', $current_dir);
10205
                                        $file_path = realpath($abs_path);
10206
                                        $file_path = str_replace('\\', '/', $file_path);
10207
                                        $my_dep_file->setAttribute('href', 'document/'.$file_path);
10208
                                        $my_dep->setAttribute('xml:base', '');
10209 View Code Duplication
                                        if (strstr($file_path, $main_path) !== false) {
10210
                                            // The calculated real path is really inside the chamilo root path.
10211
                                            // Reduce file path to what's under the DocumentRoot.
10212
                                            $file_path = substr($file_path, strlen($root_path));
10213
                                            $zip_files_abs[] = $file_path;
10214
                                            $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => 'document/'.$file_path);
10215
                                            $my_dep_file->setAttribute('href', 'document/'.$file_path);
10216
                                            $my_dep->setAttribute('xml:base', '');
10217
                                        } elseif (empty($file_path)) {
10218
                                            /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH),api_get_path(REL_PATH)));
10219
                                            if (strpos($document_root,-1) == '/') {
10220
                                                $document_root = substr(0, -1, $document_root);
10221
                                            }*/
10222
                                            $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path;
10223
                                            $file_path = str_replace('//', '/', $file_path);
10224
                                            if (file_exists($file_path)) {
10225
                                                $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
10226
                                                $zip_files[] = $my_sub_dir.'/'.$file_path;
10227
                                                $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => 'document/'.$file_path);
10228
                                                $my_dep_file->setAttribute('href', 'document/'.$file_path);
10229
                                                $my_dep->setAttribute('xml:base', '');
10230
                                            }
10231
                                        }
10232
                                        break;
10233
                                    case 'abs': // Absolute path from DocumentRoot. Save file and leave path as is in the zip.
10234
                                        $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
10235
                                        $current_dir = str_replace('\\', '/', $current_dir);
10236
                                        $file_path = realpath($doc_info[0]);
10237
                                        $file_path = str_replace('\\', '/', $file_path);
10238
                                        $my_dep_file->setAttribute('href', $file_path);
10239
                                        $my_dep->setAttribute('xml:base', '');
10240
10241 View Code Duplication
                                        if (strstr($file_path, $main_path) !== false) {
10242
                                            // The calculated real path is really inside the chamilo root path.
10243
                                            // Reduce file path to what's under the DocumentRoot.
10244
                                            $file_path = substr($file_path, strlen($root_path));
10245
                                            //echo $file_path;echo '<br /><br />';
10246
                                            //error_log('Reduced path: '.$file_path, 0);
10247
                                            $zip_files_abs[] = $file_path;
10248
                                            $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
10249
                                            $my_dep_file->setAttribute('href', 'document/'.$file_path);
10250
                                            $my_dep->setAttribute('xml:base', '');
10251
                                        } elseif (empty($file_path)) {
10252
                                            /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH), api_get_path(REL_PATH)));
10253
                                            if (strpos($document_root,-1) == '/') {
10254
                                                $document_root = substr(0, -1, $document_root);
10255
                                            }*/
10256
                                            $file_path = $_SERVER['DOCUMENT_ROOT'].$doc_info[0];
10257
                                            $file_path = str_replace('//', '/', $file_path);
10258
                                            if (file_exists($file_path)) {
10259
                                                $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
10260
                                                $zip_files[] = $my_sub_dir.'/'.$file_path;
10261
                                                $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
10262
                                                $my_dep_file->setAttribute('href', 'document/'.$file_path);
10263
                                                $my_dep->setAttribute('xml:base', '');
10264
                                            }
10265
                                        }
10266
                                        break;
10267
                                    case 'rel': // Path relative to the current document. Save xml:base as current document's directory and save file in zip as subdir.file_path
10268
                                        if (substr($doc_info[0], 0, 2) == '..') {
10269
                                            // Relative path going up.
10270
                                            $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
10271
                                            $current_dir = str_replace('\\', '/', $current_dir);
10272
                                            $file_path = realpath($current_dir.$doc_info[0]);
10273
                                            $file_path = str_replace('\\', '/', $file_path);
10274
                                            //error_log($file_path.' <-> '.$main_path, 0);
10275
                                            if (strstr($file_path, $main_path) !== false) {
10276
                                                // The calculated real path is really inside Chamilo's root path.
10277
                                                // Reduce file path to what's under the DocumentRoot.
10278
10279
                                                $file_path = substr($file_path, strlen($root_path));
10280
                                                $file_path_dest = $file_path;
10281
10282
                                                // File path is courses/CHAMILO/document/....
10283
                                                $info_file_path = explode('/', $file_path);
10284
                                                if ($info_file_path[0] == 'courses') { // Add character "/" in file path.
10285
                                                    $file_path_dest = 'document/'.$file_path;
10286
                                                }
10287
10288
                                                //error_log('Reduced path: '.$file_path, 0);
10289
                                                $zip_files_abs[] = $file_path;
10290
10291
                                                $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path_dest);
10292
                                                $my_dep_file->setAttribute('href', 'document/'.$file_path);
10293
                                                $my_dep->setAttribute('xml:base', '');
10294
                                            }
10295 View Code Duplication
                                        } else {
10296
                                            $zip_files[] = $my_sub_dir.'/'.$doc_info[0];
10297
                                            $my_dep_file->setAttribute('href', $doc_info[0]);
10298
                                            $my_dep->setAttribute('xml:base', $my_xml_sub_dir);
10299
                                        }
10300
                                        break;
10301
                                    default:
10302
                                        $my_dep_file->setAttribute('href', $doc_info[0]); // ../../courses/
10303
                                        $my_dep->setAttribute('xml:base', '');
10304
                                        break;
10305
                                }
10306
                            }
10307
                            $my_dep->appendChild($my_dep_file);
10308
                            $resources->appendChild($my_dep);
10309
                            $dependency = $xmldoc->createElement('dependency');
10310
                            $dependency->setAttribute('identifierref', $res_id);
10311
                            $my_resource->appendChild($dependency);
10312
                            $i++;
10313
                        }
10314
                        $resources->appendChild($my_resource);
10315
                        $zip_files[] = $my_file_path;
10316
                        break;
10317
                    default:
10318
                        // Get the path of the file(s) from the course directory root
10319
                        $my_file_path = 'non_exportable.html';
10320
                        //$my_xml_file_path = api_htmlentities(api_utf8_encode($my_file_path), ENT_COMPAT, 'UTF-8');
10321
                        $my_xml_file_path = $my_file_path;
10322
                        $my_sub_dir = dirname($my_file_path);
10323
                        $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
10324
                        //$my_xml_sub_dir = api_htmlentities(api_utf8_encode($my_sub_dir), ENT_COMPAT, 'UTF-8');
10325
                        $my_xml_sub_dir = $my_sub_dir;
10326
                        // Give a <resource> child to the <resources> element.
10327
                        $my_resource = $xmldoc->createElement('resource');
10328
                        $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
10329
                        $my_resource->setAttribute('type', 'webcontent');
10330
                        $my_resource->setAttribute('href', $folder_name.'/'.$my_xml_file_path);
10331
                        // adlcp:scormtype can be either 'sco' or 'asset'.
10332
                        $my_resource->setAttribute('adlcp:scormtype', 'asset');
10333
                        // xml:base is the base directory to find the files declared in this resource.
10334
                        $my_resource->setAttribute('xml:base', '');
10335
                        // Give a <file> child to the <resource> element.
10336
                        $my_file = $xmldoc->createElement('file');
10337
                        $my_file->setAttribute('href', 'document/'.$my_xml_file_path);
10338
                        $my_resource->appendChild($my_file);
10339
                        $resources->appendChild($my_resource);
10340
10341
                        break;
10342
                }
10343
            }
10344
        }
10345
        $organizations->appendChild($organization);
10346
        $root->appendChild($organizations);
10347
        $root->appendChild($resources);
10348
        $xmldoc->appendChild($root);
10349
10350
        $copyAll = api_get_configuration_value('add_all_files_in_lp_export');
10351
10352
        // TODO: Add a readme file here, with a short description and a link to the Reload player
10353
        // then add the file to the zip, then destroy the file (this is done automatically).
10354
        // http://www.reload.ac.uk/scormplayer.html - once done, don't forget to close FS#138
10355
10356
        foreach ($zip_files as $file_path) {
10357
            if (empty($file_path)) {
10358
                continue;
10359
            }
10360
10361
            $filePath = $sys_course_path.$_course['path'].'/'.$file_path;
10362
            $dest_file = $archive_path.$temp_dir_short.'/'.$file_path;
10363
10364
            if (!empty($path_to_remove) && !empty($path_to_replace)) {
10365
                $dest_file = str_replace($path_to_remove, $path_to_replace, $dest_file);
10366
            }
10367
            $this->create_path($dest_file);
10368
            @copy($filePath, $dest_file);
10369
10370
            // Check if the file needs a link update.
10371
            if (in_array($file_path, array_keys($link_updates))) {
10372
                $string = file_get_contents($dest_file);
10373
                unlink($dest_file);
10374
                foreach ($link_updates[$file_path] as $old_new) {
10375
                    // This is an ugly hack that allows .flv files to be found by the flv player that
10376
                    // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs
10377
                    // to find the flv to play in document/main/, so we replace main/ in the flv path by
10378
                    // ../../.. to return from inc/lib/flv_player to the document/main path.
10379
                    if (substr($old_new['dest'], -3) == 'flv' && substr($old_new['dest'], 0, 5) == 'main/') {
10380
                        $old_new['dest'] = str_replace('main/', '../../../', $old_new['dest']);
10381 View Code Duplication
                    } elseif (substr($old_new['dest'], -3) == 'flv' && substr($old_new['dest'], 0, 6) == 'video/') {
10382
                        $old_new['dest'] = str_replace('video/', '../../../../video/', $old_new['dest']);
10383
                    }
10384
                    //Fix to avoid problems with default_course_document
10385
                    if (strpos("main/default_course_document", $old_new['dest'] === false)) {
10386
                        //$newDestination = str_replace('document/', $mult.'document/', $old_new['dest']);
10387
                        $newDestination = $old_new['dest'];
10388
                    } else {
10389
                        $newDestination = str_replace('document/', '', $old_new['dest']);
10390
                    }
10391
                    $string = str_replace($old_new['orig'], $newDestination, $string);
10392
10393
                    // Add files inside the HTMLs
10394
                    $new_path = str_replace(api_get_path(REL_COURSE_PATH), '', $old_new['orig']);
10395
                    $destinationFile = $archive_path.$temp_dir_short.'/'.$old_new['dest'];
10396
                    if (file_exists($sys_course_path.$new_path)) {
10397
                        copy($sys_course_path.$new_path, $destinationFile);
10398
                    }
10399
                }
10400
                file_put_contents($dest_file, $string);
10401
            }
10402
10403
            if (file_exists($filePath) && $copyAll) {
10404
                $extension = $this->get_extension($filePath);
10405
                if (in_array($extension, ['html', 'html'])) {
10406
                    $containerOrigin = dirname($filePath);
10407
                    $containerDestination = dirname($dest_file);
10408
10409
                    $finder = new Finder();
10410
                    $finder->files()->in($containerOrigin)
10411
                        ->notName('*_DELETED_*')
10412
                        ->exclude('share_folder')
10413
                        ->exclude('chat_files')
10414
                        ->exclude('certificates')
10415
                    ;
10416
10417
                    if (is_dir($containerOrigin) && is_dir($containerDestination)) {
10418
                        $fs = new Filesystem();
10419
                        $fs->mirror(
10420
                            $containerOrigin,
10421
                            $containerDestination,
10422
                            $finder
10423
                        );
10424
                    }
10425
                }
10426
            }
10427
        }
10428
10429
        foreach ($zip_files_abs as $file_path) {
10430
            if (empty($file_path)) {
10431
                continue;
10432
            }
10433
            if (!is_file($main_path.$file_path) || !is_readable($main_path.$file_path)) {
10434
                continue;
10435
            }
10436
10437
            $dest_file = $archive_path.$temp_dir_short.'/document/'.$file_path;
10438
            $this->create_path($dest_file);
10439
            copy($main_path.$file_path, $dest_file);
10440
            // Check if the file needs a link update.
10441
            if (in_array($file_path, array_keys($link_updates))) {
10442
                $string = file_get_contents($dest_file);
10443
                unlink($dest_file);
10444
                foreach ($link_updates[$file_path] as $old_new) {
10445
                    // This is an ugly hack that allows .flv files to be found by the flv player that
10446
                    // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs
10447
                    // to find the flv to play in document/main/, so we replace main/ in the flv path by
10448
                    // ../../.. to return from inc/lib/flv_player to the document/main path.
10449 View Code Duplication
                    if (substr($old_new['dest'], -3) == 'flv' && substr($old_new['dest'], 0, 5) == 'main/') {
10450
                        $old_new['dest'] = str_replace('main/', '../../../', $old_new['dest']);
10451
                    }
10452
                    $string = str_replace($old_new['orig'], $old_new['dest'], $string);
10453
                }
10454
                file_put_contents($dest_file, $string);
10455
            }
10456
        }
10457
10458
        if (is_array($links_to_create)) {
10459
            foreach ($links_to_create as $file => $link) {
10460
                $file_content = '<!DOCTYPE html><head>
10461
                                <meta charset="'.api_get_language_isocode().'" />
10462
                                <title>'.$link['title'].'</title>
10463
                                </head>
10464
                                <body dir="'.api_get_text_direction().'">
10465
                                <div style="text-align:center">
10466
                                <a href="'.$link['url'].'">'.$link['title'].'</a></div>
10467
                                </body>
10468
                                </html>';
10469
                file_put_contents($archive_path.$temp_dir_short.'/'.$file, $file_content);
10470
            }
10471
        }
10472
10473
        // Add non exportable message explanation.
10474
        $lang_not_exportable = get_lang('ThisItemIsNotExportable');
10475
        $file_content = '<!DOCTYPE html><head>
10476
                        <meta charset="'.api_get_language_isocode().'" />
10477
                        <title>'.$lang_not_exportable.'</title>
10478
                        <meta http-equiv="Content-Type" content="text/html; charset='.api_get_system_encoding().'" />
10479
                        </head>
10480
                        <body dir="'.api_get_text_direction().'">';
10481
        $file_content .=
10482
            <<<EOD
10483
                    <style>
10484
            .error-message {
10485
                font-family: arial, verdana, helvetica, sans-serif;
10486
                border-width: 1px;
10487
                border-style: solid;
10488
                left: 50%;
10489
                margin: 10px auto;
10490
                min-height: 30px;
10491
                padding: 5px;
10492
                right: 50%;
10493
                width: 500px;
10494
                background-color: #FFD1D1;
10495
                border-color: #FF0000;
10496
                color: #000;
10497
            }
10498
        </style>
10499
    <body>
10500
        <div class="error-message">
10501
            $lang_not_exportable
10502
        </div>
10503
    </body>
10504
</html>
10505
EOD;
10506
        if (!is_dir($archive_path.$temp_dir_short.'/document')) {
10507
            @mkdir($archive_path.$temp_dir_short.'/document', api_get_permissions_for_new_directories());
10508
        }
10509
        file_put_contents($archive_path.$temp_dir_short.'/document/non_exportable.html', $file_content);
10510
10511
        // Add the extra files that go along with a SCORM package.
10512
        $main_code_path = api_get_path(SYS_CODE_PATH).'lp/packaging/';
10513
10514
        $fs = new Filesystem();
10515
        $fs->mirror($main_code_path, $archive_path.$temp_dir_short);
10516
10517
        // Finalize the imsmanifest structure, add to the zip, then return the zip.
10518
        $manifest = @$xmldoc->saveXML();
10519
        $manifest = api_utf8_decode_xml($manifest); // The manifest gets the system encoding now.
10520
        file_put_contents($archive_path.'/'.$temp_dir_short.'/imsmanifest.xml', $manifest);
10521
        $zip_folder->add(
10522
            $archive_path.'/'.$temp_dir_short,
10523
            PCLZIP_OPT_REMOVE_PATH,
10524
            $archive_path.'/'.$temp_dir_short.'/'
10525
        );
10526
10527
        // Clean possible temporary files.
10528
        foreach ($files_cleanup as $file) {
10529
            $res = unlink($file);
10530
            if ($res === false) {
10531
                error_log(
10532
                    'Could not delete temp file '.$file.' '.__FILE__.' '.__LINE__,
10533
                    0
10534
                );
10535
            }
10536
        }
10537
        $name = api_replace_dangerous_char($this->get_name()).'.zip';
10538
        DocumentManager::file_send_for_download($temp_zip_file, true, $name);
10539
    }
10540
10541
    /**
10542
     * @param int $lp_id
10543
     * @return bool
10544
     */
10545
    public function scorm_export_to_pdf($lp_id)
10546
    {
10547
        $lp_id = intval($lp_id);
10548
        $files_to_export = array();
10549
        $course_data = api_get_course_info($this->cc);
10550
        if (!empty($course_data)) {
10551
            $scorm_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/scorm/'.$this->path;
10552
10553
            $list = self::get_flat_ordered_items_list($lp_id);
10554
            if (!empty($list)) {
10555
                foreach ($list as $item_id) {
10556
                    $item = $this->items[$item_id];
10557
                    switch ($item->type) {
10558
                        case 'document':
10559
                            //Getting documents from a LP with chamilo documents
10560
                            $file_data = DocumentManager::get_document_data_by_id($item->path, $this->cc);
10561
                            // Try loading document from the base course.
10562
                            if (empty($file_data) && !empty($sessionId)) {
10563
                                $file_data = DocumentManager::get_document_data_by_id(
10564
                                    $item->path,
10565
                                    $this->cc,
10566
                                    false,
10567
                                    0
10568
                                );
10569
                            }
10570
                            $file_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/document'.$file_data['path'];
10571
                            if (file_exists($file_path)) {
10572
                                $files_to_export[] = array('title'=>$item->get_title(), 'path'=>$file_path);
10573
                            }
10574
                            break;
10575
                        case 'asset': //commes from a scorm package generated by chamilo
10576
                        case 'sco':
10577
                            $file_path = $scorm_path.'/'.$item->path;
10578
                            if (file_exists($file_path)) {
10579
                                $files_to_export[] = array('title'=>$item->get_title(), 'path' => $file_path);
10580
                            }
10581
                            break;
10582
                        case 'dir':
10583
                            $files_to_export[] = array('title'=> $item->get_title(), 'path'=>null);
10584
                            break;
10585
                    }
10586
                }
10587
            }
10588
            $pdf = new PDF();
10589
            $result = $pdf->html_to_pdf($files_to_export, $this->name, $this->cc, true);
10590
            return $result;
10591
        }
10592
10593
        return false;
10594
    }
10595
10596
    /**
10597
     * Temp function to be moved in main_api or the best place around for this.
10598
     * Creates a file path if it doesn't exist
10599
     * @param string $path
10600
     */
10601
    public function create_path($path)
10602
    {
10603
        $path_bits = explode('/', dirname($path));
10604
10605
        // IS_WINDOWS_OS has been defined in main_api.lib.php
10606
        $path_built = IS_WINDOWS_OS ? '' : '/';
10607
10608
        foreach ($path_bits as $bit) {
10609
            if (!empty ($bit)) {
10610
                $new_path = $path_built.$bit;
10611
                if (is_dir($new_path)) {
10612
                    $path_built = $new_path.'/';
10613
                } else {
10614
                    mkdir($new_path, api_get_permissions_for_new_directories());
10615
                    $path_built = $new_path.'/';
10616
                }
10617
            }
10618
        }
10619
    }
10620
10621
    /**
10622
     * Delete the image relative to this learning path. No parameter. Only works on instanciated object.
10623
     * @return	boolean	The results of the unlink function, or false if there was no image to start with
10624
     */
10625
    public function delete_lp_image()
10626
    {
10627
        $img = $this->get_preview_image();
10628
        if ($img != '') {
10629
            $del_file = $this->get_preview_image_path(null, 'sys');
10630
            if (isset($del_file) && file_exists($del_file)) {
10631
                $del_file_2 = $this->get_preview_image_path(64, 'sys');
10632
                if (file_exists($del_file_2)) {
10633
                    unlink($del_file_2);
10634
                }
10635
                $this->set_preview_image('');
10636
                return @unlink($del_file);
10637
            }
10638
        }
10639
        return false;
10640
    }
10641
10642
    /**
10643
     * Uploads an author image to the upload/learning_path/images directory
10644
     * @param	array	The image array, coming from the $_FILES superglobal
10645
     * @return	boolean	True on success, false on error
10646
     */
10647
    public function upload_image($image_array)
10648
    {
10649
        if (!empty ($image_array['name'])) {
10650
            $upload_ok = process_uploaded_file($image_array);
10651
            $has_attachment = true;
10652
        }
10653
10654
        if ($upload_ok) {
10655
            if ($has_attachment) {
10656
                $courseDir = api_get_course_path().'/upload/learning_path/images';
10657
                $sys_course_path = api_get_path(SYS_COURSE_PATH);
10658
                $updir = $sys_course_path.$courseDir;
10659
                // Try to add an extension to the file if it hasn't one.
10660
                $new_file_name = add_ext_on_mime(stripslashes($image_array['name']), $image_array['type']);
10661
10662
                if (filter_extension($new_file_name)) {
10663
                    $file_extension = explode('.', $image_array['name']);
10664
                    $file_extension = strtolower($file_extension[sizeof($file_extension) - 1]);
10665
                    $filename = uniqid('');
10666
                    $new_file_name = $filename.'.'.$file_extension;
10667
                    $new_path = $updir.'/'.$new_file_name;
10668
10669
                    // Resize the image.
10670
                    $temp = new Image($image_array['tmp_name']);
10671
                    $temp->resize(104);
10672
                    $result = $temp->send_image($new_path);
10673
10674
                    // Storing the image filename.
10675
                    if ($result) {
10676
                        $this->set_preview_image($new_file_name);
10677
10678
                        //Resize to 64px to use on course homepage
10679
                        $temp->resize(64);
10680
                        $temp->send_image($updir.'/'.$filename.'.64.'.$file_extension);
10681
                        return true;
10682
                    }
10683
                }
10684
            }
10685
        }
10686
10687
        return false;
10688
    }
10689
10690
    /**
10691
     * @param int $lp_id
10692
     * @param string $status
10693
     */
10694
    public function set_autolaunch($lp_id, $status)
10695
    {
10696
        $course_id = api_get_course_int_id();
10697
        $lp_id   = intval($lp_id);
10698
        $status  = intval($status);
10699
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
10700
10701
        // Setting everything to autolaunch = 0
10702
        $attributes['autolaunch'] = 0;
10703
        $where = array('session_id = ? AND c_id = ? '=> array(api_get_session_id(), $course_id));
10704
        Database::update($lp_table, $attributes, $where);
10705
        if ($status == 1) {
10706
            //Setting my lp_id to autolaunch = 1
10707
            $attributes['autolaunch'] = 1;
10708
            $where = array('id = ? AND session_id = ? AND c_id = ?'=> array($lp_id, api_get_session_id(), $course_id));
10709
            Database::update($lp_table, $attributes, $where);
10710
        }
10711
    }
10712
10713
    /**
10714
     * Gets previous_item_id for the next element of the lp_item table
10715
     * @author Isaac flores paz
10716
     * @return	integer	Previous item ID
10717
     */
10718
    public function select_previous_item_id()
10719
    {
10720
        $course_id = api_get_course_int_id();
10721
        if ($this->debug > 0) {
10722
            error_log('New LP - In learnpath::select_previous_item_id()', 0);
10723
        }
10724
        $table_lp_item = Database::get_course_table(TABLE_LP_ITEM);
10725
10726
        // Get the max order of the items
10727
        $sql = "SELECT max(display_order) AS display_order FROM $table_lp_item
10728
                WHERE c_id = $course_id AND lp_id = '".$this->lp_id."'";
10729
        $rs_max_order = Database::query($sql);
10730
        $row_max_order = Database::fetch_object($rs_max_order);
10731
        $max_order = $row_max_order->display_order;
10732
        // Get the previous item ID
10733
        $sql = "SELECT id as previous FROM $table_lp_item
10734
                WHERE 
10735
                    c_id = $course_id AND 
10736
                    lp_id = '".$this->lp_id."' AND 
10737
                    display_order = '".$max_order."' ";
10738
        $rs_max = Database::query($sql);
10739
        $row_max = Database::fetch_object($rs_max);
10740
10741
        // Return the previous item ID
10742
        return $row_max->previous;
10743
    }
10744
10745
    /**
10746
     * Copies an LP
10747
     */
10748
    public function copy()
10749
    {
10750
        // Course builder
10751
        $cb = new CourseBuilder();
10752
10753
        //Setting tools that will be copied
10754
        $cb->set_tools_to_build(array('learnpaths'));
10755
10756
        //Setting elements that will be copied
10757
        $cb->set_tools_specific_id_list(
10758
            array('learnpaths' => array($this->lp_id))
10759
        );
10760
10761
        $course = $cb->build();
10762
10763
        //Course restorer
10764
        $course_restorer = new CourseRestorer($course);
10765
        $course_restorer->set_add_text_in_items(true);
10766
        $course_restorer->set_tool_copy_settings(
10767
            array('learnpaths' => array('reset_dates' => true))
10768
        );
10769
        $course_restorer->restore(
10770
            api_get_course_id(),
10771
            api_get_session_id(),
10772
            false,
10773
            false
10774
        );
10775
    }
10776
10777
    /**
10778
     * Verify document size
10779
     * @param string $s
10780
     * @return bool
10781
     */
10782
    public static function verify_document_size($s)
10783
    {
10784
        $post_max = ini_get('post_max_size');
10785 View Code Duplication
        if (substr($post_max, -1, 1) == 'M') {
10786
            $post_max = intval(substr($post_max, 0, -1)) * 1024 * 1024;
10787
        } elseif (substr($post_max, -1, 1) == 'G') {
10788
            $post_max = intval(substr($post_max, 0, -1)) * 1024 * 1024 * 1024;
10789
        }
10790
        $upl_max = ini_get('upload_max_filesize');
10791 View Code Duplication
        if (substr($upl_max, -1, 1) == 'M') {
10792
            $upl_max = intval(substr($upl_max, 0, -1)) * 1024 * 1024;
10793
        } elseif (substr($upl_max, -1, 1) == 'G') {
10794
            $upl_max = intval(substr($upl_max, 0, -1)) * 1024 * 1024 * 1024;
10795
        }
10796
        $documents_total_space = DocumentManager::documents_total_space();
10797
        $course_max_space = DocumentManager::get_course_quota();
10798
        $total_size = filesize($s) + $documents_total_space;
10799
        if (filesize($s) > $post_max || filesize($s) > $upl_max || $total_size > $course_max_space) {
10800
            return true;
10801
        } else {
10802
            return false;
10803
        }
10804
    }
10805
10806
    /**
10807
     * Clear LP prerequisites
10808
     */
10809
    public function clear_prerequisites()
10810
    {
10811
        $course_id = $this->get_course_int_id();
10812
        if ($this->debug > 0) {
10813
            error_log('New LP - In learnpath::clear_prerequisites()', 0);
10814
        }
10815
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
10816
        $lp_id = $this->get_id();
10817
        //Cleaning prerequisites
10818
        $sql = "UPDATE $tbl_lp_item SET prerequisite = ''
10819
                WHERE c_id = ".$course_id." AND lp_id = '$lp_id'";
10820
        Database::query($sql);
10821
10822
        //Cleaning mastery score for exercises
10823
        $sql = "UPDATE $tbl_lp_item SET mastery_score = ''
10824
                WHERE c_id = ".$course_id." AND lp_id = '$lp_id' AND item_type = 'quiz'";
10825
        Database::query($sql);
10826
    }
10827
10828
    public function set_previous_step_as_prerequisite_for_all_items()
10829
    {
10830
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
10831
        $course_id = $this->get_course_int_id();
10832
        $lp_id = $this->get_id();
10833
10834
        if (!empty($this->items)) {
10835
            $previous_item_id = null;
10836
            $previous_item_max = 0;
10837
            $previous_item_type = null;
10838
            $last_item_not_dir = null;
10839
            $last_item_not_dir_type = null;
10840
            $last_item_not_dir_max = null;
10841
            foreach ($this->items as $item) {
10842
                // if there was a previous item... (otherwise jump to set it)
10843
                if (!empty($previous_item_id)) {
10844
                    $current_item_id = $item->get_id(); //save current id
10845
                    if ($item->get_type() != 'dir') {
10846
                        // Current item is not a folder, so it qualifies to get a prerequisites
10847
                        if ($last_item_not_dir_type == 'quiz') {
10848
                            // if previous is quiz, mark its max score as default score to be achieved
10849
                            $sql = "UPDATE $tbl_lp_item SET mastery_score = '$last_item_not_dir_max'
10850
                                    WHERE c_id = ".$course_id." AND lp_id = '$lp_id' AND id = '$last_item_not_dir'";
10851
                            Database::query($sql);
10852
                        }
10853
                        // now simply update the prerequisite to set it to the last non-chapter item
10854
                        $sql = "UPDATE $tbl_lp_item SET prerequisite = '$last_item_not_dir'
10855
                                WHERE c_id = ".$course_id." AND lp_id = '$lp_id' AND id = '$current_item_id'";
10856
                        Database::query($sql);
10857
                        // record item as 'non-chapter' reference
10858
                        $last_item_not_dir = $item->get_id();
10859
                        $last_item_not_dir_type = $item->get_type();
10860
                        $last_item_not_dir_max = $item->get_max();
10861
                    }
10862
                } else {
10863
                    if ($item->get_type() != 'dir') {
10864
                        // Current item is not a folder (but it is the first item) so record as last "non-chapter" item
10865
                        $last_item_not_dir = $item->get_id();
10866
                        $last_item_not_dir_type = $item->get_type();
10867
                        $last_item_not_dir_max = $item->get_max();
10868
                    }
10869
                }
10870
                // Saving the item as "previous item" for the next loop
10871
                $previous_item_id = $item->get_id();
10872
                $previous_item_max = $item->get_max();
10873
                $previous_item_type = $item->get_type();
10874
            }
10875
        }
10876
    }
10877
10878
    /**
10879
     * @param array $params
10880
     */
10881
    public static function createCategory($params)
10882
    {
10883
        $em = Database::getManager();
10884
        $item = new CLpCategory();
10885
        $item->setName($params['name']);
10886
        $item->setCId($params['c_id']);
10887
        $em->persist($item);
10888
        $em->flush();
10889
    }
10890
    /**
10891
     * @param array $params
10892
     */
10893
    public static function updateCategory($params)
10894
    {
10895
        $em = Database::getManager();
10896
        /** @var CLpCategory $item */
10897
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $params['id']);
10898
        if ($item) {
10899
            $item->setName($params['name']);
10900
            $em->merge($item);
10901
            $em->flush();
10902
        }
10903
    }
10904
10905
    /**
10906
     * @param int $id
10907
     */
10908 View Code Duplication
    public static function moveUpCategory($id)
10909
    {
10910
        $em = Database::getManager();
10911
        /** @var CLpCategory $item */
10912
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
10913
        if ($item) {
10914
            $position = $item->getPosition() - 1;
10915
            $item->setPosition($position);
10916
            $em->persist($item);
10917
            $em->flush();
10918
        }
10919
    }
10920
10921
    /**
10922
     * @param int $id
10923
     */
10924 View Code Duplication
    public static function moveDownCategory($id)
10925
    {
10926
        $em = Database::getManager();
10927
        /** @var CLpCategory $item */
10928
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
10929
        if ($item) {
10930
            $position = $item->getPosition() + 1;
10931
            $item->setPosition($position);
10932
            $em->persist($item);
10933
            $em->flush();
10934
        }
10935
    }
10936
10937
    /**
10938
     * @param int $courseId
10939
     * @return int|mixed
10940
     */
10941
    public static function getCountCategories($courseId)
10942
    {
10943
        if (empty($course_id)) {
0 ignored issues
show
Bug introduced by
The variable $course_id seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
10944
            return 0;
10945
        }
10946
        $em = Database::getManager();
10947
        $query = $em->createQuery('SELECT COUNT(u.id) FROM ChamiloCourseBundle:CLpCategory u WHERE u.cId = :id');
10948
        $query->setParameter('id', $courseId);
10949
10950
        return $query->getSingleScalarResult();
10951
    }
10952
10953
    /**
10954
     * @param int $courseId
10955
     *
10956
     * @return mixed
10957
     */
10958 View Code Duplication
    public static function getCategories($courseId)
10959
    {
10960
        $em = Database::getManager();
10961
        //Default behaviour
10962
        /*$items = $em->getRepository('ChamiloCourseBundle:CLpCategory')->findBy(
10963
            array('cId' => $course_id),
10964
            array('name' => 'ASC')
10965
        );*/
10966
10967
        // Using doctrine extensions
10968
        /** @var SortableRepository $repo */
10969
        $repo = $em->getRepository('ChamiloCourseBundle:CLpCategory');
10970
        $items = $repo
10971
            ->getBySortableGroupsQuery(['cId' => $courseId])
10972
            ->getResult();
10973
10974
        return $items;
10975
    }
10976
10977
    /**
10978
     * @param int $id
10979
     *
10980
     * @return CLpCategory
10981
     */
10982
    public static function getCategory($id)
10983
    {
10984
        $em = Database::getManager();
10985
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
10986
10987
        return $item;
10988
    }
10989
10990
    /**
10991
     * @param int $courseId
10992
     * @return array
10993
     */
10994 View Code Duplication
    public static function getCategoryByCourse($courseId)
10995
    {
10996
        $em = Database::getManager();
10997
        $items = $em->getRepository('ChamiloCourseBundle:CLpCategory')->findBy(
10998
            array('cId' => $courseId)
10999
        );
11000
11001
        return $items;
11002
    }
11003
11004
    /**
11005
     * @param int $id
11006
     *
11007
     * @return mixed
11008
     */
11009
    public static function deleteCategory($id)
11010
    {
11011
        $em = Database::getManager();
11012
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
11013
        if ($item) {
11014
            $courseId = $item->getCId();
11015
            $query = $em->createQuery('SELECT u FROM ChamiloCourseBundle:CLp u WHERE u.cId = :id AND u.categoryId = :catId');
11016
            $query->setParameter('id', $courseId);
11017
            $query->setParameter('catId', $item->getId());
11018
            $lps = $query->getResult();
11019
11020
            // Setting category = 0.
11021
            if ($lps) {
11022
                foreach ($lps as $lpItem) {
11023
                    $lpItem->setCategoryId(0);
11024
                }
11025
            }
11026
11027
            // Removing category.
11028
            $em->remove($item);
11029
            $em->flush();
11030
        }
11031
    }
11032
11033
    /**
11034
     * @param int $courseId
11035
     * @param bool $addSelectOption
11036
     *
11037
     * @return mixed
11038
     */
11039
    public static function getCategoryFromCourseIntoSelect($courseId, $addSelectOption = false)
11040
    {
11041
        $items = self::getCategoryByCourse($courseId);
11042
        $cats = array();
11043
        if ($addSelectOption) {
11044
            $cats = array(get_lang('SelectACategory'));
11045
        }
11046
11047
        if (!empty($items)) {
11048
            foreach ($items as $cat) {
11049
                $cats[$cat->getId()] = $cat->getName();
11050
            }
11051
        }
11052
11053
        return $cats;
11054
    }
11055
11056
    /**
11057
     * Return the scorm item type object with spaces replaced with _
11058
     * The return result is use to build a css classname like scorm_type_$return
11059
     * @param $in_type
11060
     * @return mixed
11061
     */
11062
    private static function format_scorm_type_item($in_type)
11063
    {
11064
        return str_replace(' ', '_', $in_type);
11065
    }
11066
11067
    /**
11068
     * @param string $courseCode
11069
     * @param int $lp_id
11070
     * @param int $user_id
11071
     *
11072
     * @return learnpath
11073
     */
11074
    public static function getLpFromSession($courseCode, $lp_id, $user_id)
11075
    {
11076
        $learnPath = null;
11077
        $lpObject = Session::read('lpobject');
11078
        if ($lpObject !== null) {
11079
            $learnPath = unserialize($lpObject);
11080
        }
11081
11082
        if (!is_object($learnPath)) {
11083
            $learnPath = new learnpath($courseCode, $lp_id, $user_id);
11084
        }
11085
11086
        return $learnPath;
11087
    }
11088
11089
    /**
11090
     * @param int $itemId
11091
     * @return learnpathItem|false
11092
     */
11093
    public function getItem($itemId)
11094
    {
11095
        if (isset($this->items[$itemId]) && is_object($this->items[$itemId])) {
11096
            return $this->items[$itemId];
11097
        }
11098
11099
        return false;
11100
    }
11101
11102
    /**
11103
     * @return int
11104
     */
11105
    public function getCategoryId()
11106
    {
11107
        return $this->categoryId;
11108
    }
11109
11110
    /**
11111
     * @param int $categoryId
11112
     * @return bool
11113
     */
11114
    public function setCategoryId($categoryId)
11115
    {
11116
        $this->categoryId = intval($categoryId);
11117
        $courseId = api_get_course_int_id();
11118
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
11119
        $lp_id = $this->get_id();
11120
        $sql = "UPDATE $lp_table SET category_id = ".$this->categoryId."
11121
                WHERE c_id = $courseId AND id = $lp_id";
11122
        Database::query($sql);
11123
11124
        return true;
11125
    }
11126
11127
    /**
11128
     * Get whether this is a learning path with the possibility to subscribe
11129
     * users or not
11130
     * @return int
11131
     */
11132
    public function getSubscribeUsers()
11133
    {
11134
        return $this->subscribeUsers;
11135
    }
11136
11137
    /**
11138
     * Set whether this is a learning path with the possibility to subscribe
11139
     * users or not
11140
     * @param int $subscribeUsers (0 = false, 1 = true)
0 ignored issues
show
Bug introduced by
There is no parameter named $subscribeUsers. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
11141
     * @return bool
11142
     */
11143 View Code Duplication
    public function setSubscribeUsers($value)
11144
    {
11145
        if ($this->debug > 0) {
11146
            error_log('New LP - In learnpath::set_subscribe_users()', 0);
11147
        }
11148
        $this->subscribeUsers = intval($value); ;
11149
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
11150
        $lp_id = $this->get_id();
11151
        $sql = "UPDATE $lp_table SET subscribe_users = ".$this->subscribeUsers."
11152
                WHERE c_id = ".$this->course_int_id." AND id = $lp_id";
11153
        Database::query($sql);
11154
11155
        return true;
11156
    }
11157
11158
    /**
11159
     * Calculate the count of stars for a user in this LP
11160
     * This calculation is based on the following rules:
11161
     * - the student gets one star when he gets to 50% of the learning path
11162
     * - the student gets a second star when the average score of all tests inside the learning path >= 50%
11163
     * - the student gets a third star when the average score of all tests inside the learning path >= 80%
11164
     * - the student gets the final star when the score for the *last* test is >= 80%
11165
     * @param int $sessionId Optional. The session ID
11166
     * @return int The count of stars
11167
     */
11168
    public function getCalculateStars($sessionId = 0)
11169
    {
11170
        $stars = 0;
11171
        $progress = self::getProgress($this->lp_id, $this->user_id, $this->course_int_id, $sessionId);
11172
11173
        if ($progress >= 50) {
11174
            $stars++;
11175
        }
11176
11177
        // Calculate stars chapters evaluation
11178
        $exercisesItems = $this->getExercisesItems();
11179
11180
        if (!empty($exercisesItems)) {
11181
            $totalResult = 0;
11182
11183
            foreach ($exercisesItems as $exerciseItem) {
11184
                $exerciseResultInfo = Event::getExerciseResultsByUser(
11185
                    $this->user_id,
11186
                    $exerciseItem->path,
11187
                    $this->course_int_id,
11188
                    $sessionId,
11189
                    $this->lp_id,
11190
                    $exerciseItem->db_id
11191
                );
11192
11193
                $exerciseResultInfo = end($exerciseResultInfo);
11194
11195
                if (!$exerciseResultInfo) {
11196
                    continue;
11197
                }
11198
11199
                if (!empty($exerciseResultInfo['exe_weighting'])) {
11200
                    $exerciseResult = $exerciseResultInfo['exe_result'] * 100 / $exerciseResultInfo['exe_weighting'];
11201
                } else {
11202
                    $exerciseResult = 0;
11203
                }
11204
                $totalResult += $exerciseResult;
11205
            }
11206
11207
            $totalExerciseAverage = $totalResult / (count($exercisesItems) > 0 ? count($exercisesItems) : 1);
11208
11209
            if ($totalExerciseAverage >= 50) {
11210
                $stars++;
11211
            }
11212
11213
            if ($totalExerciseAverage >= 80) {
11214
                $stars++;
11215
            }
11216
        }
11217
11218
        // Calculate star for final evaluation
11219
        $finalEvaluationItem = $this->getFinalEvaluationItem();
11220
11221
        if (!empty($finalEvaluationItem)) {
11222
            $evaluationResultInfo = Event::getExerciseResultsByUser(
11223
                $this->user_id,
11224
                $finalEvaluationItem->path,
11225
                $this->course_int_id,
11226
                $sessionId,
11227
                $this->lp_id,
11228
                $finalEvaluationItem->db_id
11229
            );
11230
11231
            $evaluationResultInfo = end($evaluationResultInfo);
11232
11233
            if ($evaluationResultInfo) {
11234
                $evaluationResult = $evaluationResultInfo['exe_result'] * 100 / $evaluationResultInfo['exe_weighting'];
11235
11236
                if ($evaluationResult >= 80) {
11237
                    $stars++;
11238
                }
11239
            }
11240
        }
11241
11242
        return $stars;
11243
    }
11244
11245
    /**
11246
     * Get the items of exercise type
11247
     * @return array The items. Otherwise return false
11248
     */
11249 View Code Duplication
    public function getExercisesItems()
11250
    {
11251
        $exercises = [];
11252
        foreach ($this->items as $item) {
11253
            if ($item->type != 'quiz') {
11254
                continue;
11255
            }
11256
            $exercises[] = $item;
11257
        }
11258
11259
        array_pop($exercises);
11260
11261
        return $exercises;
11262
    }
11263
11264
    /**
11265
     * Get the item of exercise type (evaluation type)
11266
     * @return array The final evaluation. Otherwise return false
11267
     */
11268 View Code Duplication
    public function getFinalEvaluationItem()
11269
    {
11270
        $exercises = [];
11271
        foreach ($this->items as $item) {
11272
            if ($item->type != 'quiz') {
11273
                continue;
11274
            }
11275
11276
            $exercises[] = $item;
11277
        }
11278
11279
        return array_pop($exercises);
11280
    }
11281
11282
    /**
11283
     * Calculate the total points achieved for the current user in this learning path
11284
     * @param int $sessionId Optional. The session Id
11285
     * @return int
11286
     */
11287
    public function getCalculateScore($sessionId = 0)
11288
    {
11289
        // Calculate stars chapters evaluation
11290
        $exercisesItems = $this->getExercisesItems();
11291
        $finalEvaluationItem = $this->getFinalEvaluationItem();
11292
        $totalExercisesResult = 0;
11293
        $totalEvaluationResult = 0;
11294
11295
        if ($exercisesItems !== false) {
11296
            foreach ($exercisesItems as $exerciseItem) {
11297
                $exerciseResultInfo = Event::getExerciseResultsByUser(
11298
                    $this->user_id,
11299
                    $exerciseItem->path,
11300
                    $this->course_int_id,
11301
                    $sessionId,
11302
                    $this->lp_id,
11303
                    $exerciseItem->db_id
11304
                );
11305
11306
                $exerciseResultInfo = end($exerciseResultInfo);
11307
11308
                if (!$exerciseResultInfo) {
11309
                    continue;
11310
                }
11311
11312
                $totalExercisesResult += $exerciseResultInfo['exe_result'];
11313
            }
11314
        }
11315
11316
        if (!empty($finalEvaluationItem)) {
11317
            $evaluationResultInfo = Event::getExerciseResultsByUser(
11318
                $this->user_id,
11319
                $finalEvaluationItem->path,
11320
                $this->course_int_id,
11321
                $sessionId,
11322
                $this->lp_id,
11323
                $finalEvaluationItem->db_id
11324
            );
11325
11326
            $evaluationResultInfo = end($evaluationResultInfo);
11327
11328
            if ($evaluationResultInfo) {
11329
                $totalEvaluationResult += $evaluationResultInfo['exe_result'];
11330
            }
11331
        }
11332
11333
        return $totalExercisesResult + $totalEvaluationResult;
11334
    }
11335
11336
    /**
11337
     * Check if URL is not allowed to be show in a iframe
11338
     * @param string $src
11339
     *
11340
     * @return string
11341
     */
11342
    public function fixBlockedLinks($src)
11343
    {
11344
        $urlInfo = parse_url($src);
11345
        $platformProtocol = 'https';
11346
        if (strpos(api_get_path(WEB_CODE_PATH), 'https') === false) {
11347
            $platformProtocol = 'http';
11348
        }
11349
11350
        $protocolFixApplied = false;
11351
        //Scheme validation to avoid "Notices" when the lesson doesn't contain a valid scheme
11352
        $scheme = isset($urlInfo['scheme']) ? $urlInfo['scheme'] : null;
11353
        if ($platformProtocol != $scheme) {
11354
            Session::write('x_frame_source', $src);
11355
            $src = 'blank.php?error=x_frames_options';
11356
            $protocolFixApplied = true;
11357
        }
11358
11359
        if ($protocolFixApplied == false) {
11360
            if (strpos($src, api_get_path(WEB_CODE_PATH)) === false) {
11361
                // Check X-Frame-Options
11362
                $ch = curl_init();
11363
11364
                $options = array(
11365
                    CURLOPT_URL => $src,
11366
                    CURLOPT_RETURNTRANSFER => true,
11367
                    CURLOPT_HEADER => true,
11368
                    CURLOPT_FOLLOWLOCATION => true,
11369
                    CURLOPT_ENCODING => "",
11370
                    CURLOPT_AUTOREFERER => true,
11371
                    CURLOPT_CONNECTTIMEOUT => 120,
11372
                    CURLOPT_TIMEOUT => 120,
11373
                    CURLOPT_MAXREDIRS => 10,
11374
                );
11375
                curl_setopt_array($ch, $options);
11376
                $response = curl_exec($ch);
11377
                $httpCode = curl_getinfo($ch);
11378
                $headers = substr($response, 0, $httpCode['header_size']);
11379
11380
                $error = false;
11381
                if (stripos($headers, 'X-Frame-Options: DENY') > -1
11382
                    //|| stripos($headers, 'X-Frame-Options: SAMEORIGIN') > -1
11383
                ) {
11384
                    $error = true;
11385
                }
11386
11387
                if ($error) {
11388
                    Session::write('x_frame_source', $src);
11389
                    $src = 'blank.php?error=x_frames_options';
11390
                }
11391
            }
11392
        }
11393
11394
        return $src;
11395
    }
11396
11397
    /**
11398
     * Check if this LP has a created forum in the basis course
11399
     * @return boolean
11400
     */
11401
    public function lpHasForum()
11402
    {
11403
        $forumTable = Database::get_course_table(TABLE_FORUM);
11404
        $itemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
11405
11406
        $fakeFrom = "
11407
            $forumTable f
11408
            INNER JOIN $itemProperty ip
11409
            ON (f.forum_id = ip.ref AND f.c_id = ip.c_id)
11410
        ";
11411
11412
        $resultData = Database::select(
11413
            'COUNT(f.iid) AS qty',
11414
            $fakeFrom,
11415
            [
11416
                'where' => [
11417
                    'ip.visibility != ? AND ' => 2,
11418
                    'ip.tool = ? AND ' => TOOL_FORUM,
11419
                    'f.c_id = ? AND ' => intval($this->course_int_id),
11420
                    'f.lp_id = ?' => intval($this->lp_id)
11421
                ]
11422
            ],
11423
            'first'
11424
        );
11425
11426
        return $resultData['qty'] > 0;
11427
    }
11428
11429
    /**
11430
     * Get the forum for this learning path
11431
     * @param int $sessionId
11432
     * @return boolean
11433
     */
11434
    public function getForum($sessionId = 0)
11435
    {
11436
        $forumTable = Database::get_course_table(TABLE_FORUM);
11437
        $itemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
11438
11439
        $fakeFrom = "$forumTable f
11440
            INNER JOIN $itemProperty ip ";
11441
11442
        if ($this->lp_session_id == 0) {
11443
            $fakeFrom .= "
11444
                ON (
11445
                    f.forum_id = ip.ref AND f.c_id = ip.c_id AND (
11446
                        f.session_id = ip.session_id OR ip.session_id IS NULL
11447
                    )
11448
                )
11449
            ";
11450
        } else {
11451
            $fakeFrom .= "
11452
                ON (
11453
                    f.forum_id = ip.ref AND f.c_id = ip.c_id AND f.session_id = ip.session_id
11454
                )
11455
            ";
11456
        }
11457
11458
        $resultData = Database::select(
11459
            'f.*',
11460
            $fakeFrom,
11461
            [
11462
                'where' => [
11463
                    'ip.visibility != ? AND ' => 2,
11464
                    'ip.tool = ? AND ' => TOOL_FORUM,
11465
                    'f.session_id = ? AND ' => $sessionId,
11466
                    'f.c_id = ? AND ' => intval($this->course_int_id),
11467
                    'f.lp_id = ?' => intval($this->lp_id)
11468
                ]
11469
            ],
11470
            'first'
11471
        );
11472
11473
        if (empty($resultData)) {
11474
            return false;
11475
        }
11476
11477
        return $resultData;
11478
    }
11479
11480
    /**
11481
     * Create a forum for this learning path
11482
     * @param type $forumCategoryId
11483
     * @return int The forum ID if was created. Otherwise return false
11484
     */
11485
    public function createForum($forumCategoryId)
11486
    {
11487
        require_once api_get_path(SYS_CODE_PATH).'/forum/forumfunction.inc.php';
11488
11489
        $forumId = store_forum(
11490
            [
11491
                'lp_id' => $this->lp_id,
11492
                'forum_title' => $this->name,
11493
                'forum_comment' => null,
11494
                'forum_category' => intval($forumCategoryId),
11495
                'students_can_edit_group' => ['students_can_edit' => 0],
11496
                'allow_new_threads_group' => ['allow_new_threads' => 0],
11497
                'default_view_type_group' => ['default_view_type' => 'flat'],
11498
                'group_forum' => 0,
11499
                'public_private_group_forum_group' => ['public_private_group_forum' => 'public']
11500
            ],
11501
            [],
11502
            true
11503
        );
11504
11505
        return $forumId;
11506
    }
11507
11508
    /**
11509
     * Check and obtain the lp final item if exist
11510
     *
11511
     * @return learnpathItem
11512
     */
11513
    private function getFinalItem()
11514
    {
11515
        if (empty($this->items)) {
11516
            return null;
11517
        }
11518
11519
        foreach ($this->items as $item) {
11520
            if ($item->type !== 'final_item') {
11521
                continue;
11522
            }
11523
11524
            return $item;
11525
        }
11526
    }
11527
11528
    /**
11529
     * Get the LP Final Item Template
11530
     *
11531
     * @return string
11532
     */
11533
    private function getFinalItemTemplate()
11534
    {
11535
        return file_get_contents(api_get_path(SYS_CODE_PATH).'lp/final_item_template/template.html');
11536
    }
11537
11538
    /**
11539
     * Get the LP Final Item Url
11540
     *
11541
     * @return string
11542
     */
11543
    private function getSavedFinalItem()
11544
    {
11545
        $finalItem = $this->getFinalItem();
11546
        $doc = DocumentManager::get_document_data_by_id($finalItem->path, $this->cc);
11547
        if ($doc && file_exists($doc['absolute_path'])) {
11548
            return file_get_contents($doc['absolute_path']);
11549
        }
11550
11551
        return '';
11552
    }
11553
11554
    /**
11555
     * Get the LP Final Item form
11556
     *
11557
     * @return string
11558
     */
11559
    public function getFinalItemForm()
11560
    {
11561
        $finalItem = $this->getFinalItem();
11562
        $title = '';
11563
11564
        if ($finalItem) {
11565
            $title = $finalItem->get_title();
11566
            $buttonText = get_lang('Save');
11567
            $content = $this->getSavedFinalItem();
11568
        } else {
11569
            $buttonText = get_lang('LPCreateDocument');
11570
            $content = $this->getFinalItemTemplate();
11571
        }
11572
11573
        $courseInfo = api_get_course_info();
11574
        $result = $this->generate_lp_folder($courseInfo);
11575
        $relative_path = api_substr($result['dir'], 1, strlen($result['dir']));
11576
        $relative_prefix = '../../';
11577
11578
        $editorConfig = [
11579
            'ToolbarSet' => 'LearningPathDocuments',
11580
            'Width' => '100%',
11581
            'Height' => '500',
11582
            'FullPage' => true,
11583
            'CreateDocumentDir' => $relative_prefix,
11584
            'CreateDocumentWebDir' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/',
11585
            'BaseHref' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/'.$relative_path
11586
        ];
11587
11588
        $url = api_get_self().'?'.api_get_cidreq().'&'.http_build_query([
11589
            'type' => 'document',
11590
            'lp_id' => $this->lp_id
11591
        ]);
11592
11593
        $form = new FormValidator('final_item', 'POST', $url);
11594
        $form->addText('title', get_lang('Title'));
11595
        $form->addButtonSave($buttonText);
11596
        $form->addHtml(
11597
            Display::return_message(
11598
                'Variables :</br></br> <b>((certificate))</b> </br> <b>((skill))</b>',
11599
                'normal',
11600
                false
11601
            )
11602
        );
11603
11604
        $renderer = $form->defaultRenderer();
11605
        $renderer->setElementTemplate('&nbsp;{label}{element}', 'content_lp_certificate');
11606
11607
        $form->addHtmlEditor('content_lp_certificate', null, true, false, $editorConfig, true);
11608
        $form->addHidden('action', 'add_final_item');
11609
        $form->addHidden('path', Session::read('pathItem'));
11610
        $form->addHidden('previous', $this->get_last());
11611
        $form->setDefaults(
11612
            ['title' => $title, 'content_lp_certificate' => $content]
11613
        );
11614
11615
        if ($form->validate()) {
11616
            $values = $form->exportValues();
11617
            $lastItemId = $this->get_last();
11618
11619
            if (!$finalItem) {
11620
                $documentId = $this->create_document(
11621
                    $this->course_info,
11622
                    $values['content_lp_certificate'],
11623
                    $values['title']
11624
                );
11625
                $this->add_item(
11626
                    0,
11627
                    $lastItemId,
11628
                    'final_item',
11629
                    $documentId,
11630
                    $values['title'],
11631
                    ''
11632
                );
11633
11634
                Display::addFlash(
11635
                    Display::return_message(get_lang('Added'))
11636
                );
11637
            } else {
11638
                $this->edit_document($this->course_info);
11639
            }
11640
        }
11641
11642
        return $form->returnForm();
11643
    }
11644
11645
    /**
11646
     * Check if the current lp item is first, both, last or none from lp list
11647
     *
11648
     * @param int $currentItemId
11649
     * @return string
11650
     */
11651
    public function isFirstOrLastItem($currentItemId)
11652
    {
11653
        if ($this->debug > 0) {
11654
            error_log('New LP - In learnpath::isFirstOrLastItem('.$currentItemId.')', 0);
11655
        }
11656
11657
        $lpItemId = [];
11658
        $typeListNotToVerify = self::getChapterTypes();
11659
11660
	    // Using get_toc() function instead $this->items because returns the correct order of the items
11661
        foreach ($this->get_toc() as $item) {
11662
            if (!in_array($item['type'], $typeListNotToVerify)) {
11663
                $lpItemId[] = $item['id'];
11664
            }
11665
        }
11666
11667
        $lastLpItemIndex = count($lpItemId) - 1;
11668
        $position = array_search($currentItemId, $lpItemId);
11669
11670
        switch ($position) {
11671
            case 0:
11672
                if (!$lastLpItemIndex) {
11673
                    $answer = 'both';
11674
                    break;
11675
                }
11676
11677
                $answer = 'first';
11678
                break;
11679
            case $lastLpItemIndex:
11680
                $answer = 'last';
11681
                break;
11682
            default:
11683
                $answer = 'none';
11684
        }
11685
11686
        return $answer;
11687
    }
11688
11689
    /**
11690
     * Get whether this is a learning path with the accumulated SCORM time or not
11691
     * @return int
11692
     */
11693
    public function getAccumulateScormTime()
11694
    {
11695
        return $this->accumulateScormTime;
11696
    }
11697
11698
    /**
11699
     * Set whether this is a learning path with the accumulated SCORM time or not
11700
     * @param int $value (0 = false, 1 = true)
11701
     * @return bool Always returns true
11702
     */
11703 View Code Duplication
    public function setAccumulateScormTime($value)
11704
    {
11705
        if ($this->debug > 0) {
11706
            error_log('New LP - In learnpath::setAccumulateScormTime()', 0);
11707
        }
11708
        $this->accumulateScormTime = intval($value);
11709
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
11710
        $lp_id = $this->get_id();
11711
        $sql = "UPDATE $lp_table SET accumulate_scorm_time = ".$this->accumulateScormTime."
11712
                WHERE c_id = ".$this->course_int_id." AND id = $lp_id";
11713
        Database::query($sql);
11714
11715
        return true;
11716
    }
11717
11718
    /**
11719
     * Returns an HTML-formatted link to a resource, to incorporate directly into
11720
     * the new learning path tool.
11721
     *
11722
     * The function is a big switch on tool type.
11723
     * In each case, we query the corresponding table for information and build the link
11724
     * with that information.
11725
     * @author Yannick Warnier <[email protected]> - rebranding based on
11726
     * previous work (display_addedresource_link_in_learnpath())
11727
     * @param int	$course_id Course code
11728
     * @param int $learningPathId The learning path ID (in lp table)
11729
     * @param int $id_in_path the unique index in the items table
11730
     * @param int $lpViewId
11731
     * @param string $origin
11732
     * @return string
11733
     */
11734
    public static function rl_get_resource_link_for_learnpath(
11735
        $course_id,
11736
        $learningPathId,
11737
        $id_in_path,
11738
        $lpViewId,
11739
        $origin = 'learnpath'
11740
    ) {
11741
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
11742
        $course_info = api_get_course_info_by_id($course_id);
11743
        $course_id = $course_info['real_id'];
11744
        $course_code = $course_info['code'];
11745
        $session_id = api_get_session_id();
11746
        $learningPathId = intval($learningPathId);
11747
        $id_in_path = intval($id_in_path);
11748
        $lpViewId = intval($lpViewId);
11749
        $em = Database::getManager();
11750
        $sql = "SELECT * FROM $tbl_lp_item
11751
                 WHERE
11752
                    c_id = $course_id AND
11753
                    lp_id = $learningPathId AND
11754
                    id = $id_in_path
11755
                ";
11756
        $res_item = Database::query($sql);
11757
        if (Database::num_rows($res_item) < 1) {
11758
            return -1;
11759
        }
11760
        $row_item = Database::fetch_array($res_item, 'ASSOC');
11761
11762
        $item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
11763
        // Get the lp_item_view with the highest view_count.
11764
        $sql = "SELECT * FROM $item_view_table
11765
                WHERE
11766
                    c_id = $course_id AND
11767
                    lp_item_id = ".$row_item['id']." AND
11768
                    lp_view_id = $lpViewId
11769
                ORDER BY view_count DESC";
11770
        $learnpathItemViewResult = Database::query($sql);
11771
        $learnpathItemViewData = Database::fetch_array($learnpathItemViewResult, 'ASSOC');
11772
        $learnpathItemViewId = 0;
11773
        if (isset($learnpathItemViewData)) {
11774
            $learnpathItemViewId = $learnpathItemViewData['id'];
11775
        }
11776
11777
        $type = strtolower($row_item['item_type']);
11778
        $id = (strcmp($row_item['path'], '') == 0) ? '0' : $row_item['path'];
11779
        $main_dir_path = api_get_path(WEB_CODE_PATH);
11780
        $main_course_path = api_get_path(WEB_COURSE_PATH).$course_info['directory'].'/';
11781
        $link = '';
11782
        $extraParams = "origin=$origin&cidReq=$course_code&session_id=$session_id&id_session=$session_id";
11783
        switch ($type) {
11784
            case 'dir':
11785
                $link .= $main_dir_path.'lp/blank.php';
11786
                break;
11787
            case TOOL_CALENDAR_EVENT:
11788
                $link .= $main_dir_path.'calendar/agenda.php?agenda_id='.$id.'&'.$extraParams;
11789
                break;
11790
            case TOOL_ANNOUNCEMENT:
11791
                $link .= $main_dir_path.'announcements/announcements.php?ann_id='.$id.'&'.$extraParams;
11792
                break;
11793
            case TOOL_LINK:
11794
                $link .= $main_dir_path.'link/link_goto.php?link_id='.$id.'&'.$extraParams;
11795
                break;
11796
            case TOOL_QUIZ:
11797
                if (!empty($id)) {
11798
                    $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
11799
                    $sql = "SELECT * FROM $TBL_EXERCICES WHERE c_id = $course_id AND id=$id";
11800
                    $result = Database::query($sql);
11801
                    $myrow = Database::fetch_array($result);
11802
                    if ($row_item['title'] != '') {
11803
                        $myrow['title'] = $row_item['title'];
11804
                    }
11805
                    $link .= $main_dir_path.'exercise/overview.php?lp_init=1&learnpath_item_view_id='.$learnpathItemViewId.'&learnpath_id='.$learningPathId.'&learnpath_item_id='.$id_in_path.'&exerciseId='.$id.'&'.$extraParams;
11806
                }
11807
                break;
11808
            case TOOL_HOTPOTATOES: //lowercase because of strtolower above
11809
                $TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
11810
                $result = Database::query("SELECT * FROM ".$TBL_DOCUMENT." WHERE c_id = $course_id AND id=$id");
11811
                $myrow = Database::fetch_array($result);
11812
                $path = $myrow['path'];
11813
                $link .= $main_dir_path.'exercise/showinframes.php?file='.$path.'&cid='.$course_code.'&uid='.api_get_user_id().'&learnpath_id='.$learningPathId.'&learnpath_item_id='.$id_in_path.'&lp_view_id='.$lpViewId.'&'.$extraParams;
11814
                break;
11815
            case TOOL_FORUM:
11816
                $link .= $main_dir_path.'forum/viewforum.php?forum='.$id.'&lp=true&'.$extraParams;
11817
                break;
11818
            case TOOL_THREAD:  //forum post
11819
                $tbl_topics = Database::get_course_table(TABLE_FORUM_THREAD);
11820 View Code Duplication
                if (!empty($id)) {
11821
                    $sql = "SELECT * FROM $tbl_topics WHERE c_id = $course_id AND thread_id=$id";
11822
                    $result = Database::query($sql);
11823
                    $myrow = Database::fetch_array($result);
11824
                    $link .= $main_dir_path.'forum/viewthread.php?thread='.$id.'&forum='.$myrow['forum_id'].'&lp=true&'.$extraParams;
11825
                }
11826
                break;
11827
            case TOOL_POST:
11828
                $tbl_post = Database::get_course_table(TABLE_FORUM_POST);
11829
                $result = Database::query("SELECT * FROM $tbl_post WHERE c_id = $course_id AND post_id=$id");
11830
                $myrow = Database::fetch_array($result);
11831
                $link .= $main_dir_path.'forum/viewthread.php?post='.$id.'&thread='.$myrow['thread_id'].'&forum='.$myrow['forum_id'].'&lp=true&'.$extraParams;
11832
                break;
11833
            case TOOL_DOCUMENT:
11834
                $document = $em
11835
                    ->getRepository('ChamiloCourseBundle:CDocument')
11836
                    ->findOneBy(['cId' => $course_id, 'id' => $id]);
11837
11838
                if (!$document) {
11839
                    break;
11840
                }
11841
11842
                $documentPathInfo = pathinfo($document->getPath());
11843
                $jplayer_supported_files = ['mp4', 'ogv', 'flv', 'm4v'];
11844
                $extension = isset($documentPathInfo['extension']) ? $documentPathInfo['extension'] : '';
11845
                $showDirectUrl = !in_array($extension, $jplayer_supported_files);
11846
11847
                if ($showDirectUrl) {
11848
                    $link = $main_course_path.'document'.$document->getPath().'?'.$extraParams;
11849
                } else {
11850
                    $link = api_get_path(WEB_CODE_PATH).'document/showinframes.php?id='.$id.'&'.$extraParams;
11851
                }
11852
11853
                $openmethod = 2;
11854
                $officedoc = false;
11855
                Session::write('openmethod', $openmethod);
11856
                Session::write('officedoc', $officedoc);
11857
                break;
11858
            case TOOL_LP_FINAL_ITEM:
11859
                $link .= api_get_path(WEB_CODE_PATH).'lp/lp_final_item.php?&id='.$id.'&lp_id='.$learningPathId.'&'.$extraParams;
11860
                break;
11861
            case 'assignments':
11862
                $link .= $main_dir_path.'work/work.php?'.$extraParams;
11863
                break;
11864
            case TOOL_DROPBOX:
11865
                $link .= $main_dir_path.'dropbox/index.php?'.$extraParams;
11866
                break;
11867
            case 'introduction_text': //DEPRECATED
11868
                $link .= '';
11869
                break;
11870
            case TOOL_COURSE_DESCRIPTION:
11871
                $link .= $main_dir_path.'course_description?'.$extraParams;
11872
                break;
11873
            case TOOL_GROUP:
11874
                $link .= $main_dir_path.'group/group.php?'.$extraParams;
11875
                break;
11876
            case TOOL_USER:
11877
                $link .= $main_dir_path.'user/user.php?'.$extraParams;
11878
                break;
11879
            case TOOL_STUDENTPUBLICATION:
11880
                if (!empty($row_item['path'])) {
11881
                    $link .= $main_dir_path.'work/work_list.php?id='.$row_item['path'].'&'.$extraParams;
11882
                    break;
11883
                }
11884
                $link .= $main_dir_path.'work/work.php?'.api_get_cidreq().'&id='.$row_item['path'].'&'.$extraParams;
11885
                break;
11886
        } //end switch
11887
11888
        return $link;
11889
    }
11890
11891
    /**
11892
     * Gets the name of a resource (generally used in learnpath when no name is provided)
11893
     *
11894
     * @author Yannick Warnier <[email protected]>
11895
     * @param string    Course code
11896
     * @param string    The tool type (using constants declared in main_api.lib.php)
11897
     * @param integer    The resource ID
11898
     * @return string
11899
     */
11900
    public static function rl_get_resource_name($course_code, $learningPathId, $id_in_path)
11901
    {
11902
        $_course = api_get_course_info($course_code);
11903
        $course_id = $_course['real_id'];
11904
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
11905
        $learningPathId = intval($learningPathId);
11906
        $id_in_path = intval($id_in_path);
11907
11908
        $sql_item = "SELECT item_type, title, ref FROM $tbl_lp_item
11909
                     WHERE c_id = $course_id AND lp_id = $learningPathId AND id = $id_in_path";
11910
        $res_item = Database::query($sql_item);
11911
11912
        if (Database::num_rows($res_item) < 1) {
11913
            return '';
11914
        }
11915
        $row_item = Database::fetch_array($res_item);
11916
        $type = strtolower($row_item['item_type']);
11917
        $id = $row_item['ref'];
11918
        $output = '';
11919
11920
        switch ($type) {
11921
            case TOOL_CALENDAR_EVENT:
11922
                $TABLEAGENDA = Database::get_course_table(TABLE_AGENDA);
11923
                $result = Database::query("SELECT * FROM $TABLEAGENDA WHERE c_id = $course_id AND id=$id");
11924
                $myrow = Database::fetch_array($result);
11925
                $output = $myrow['title'];
11926
                break;
11927
            case TOOL_ANNOUNCEMENT:
11928
                $tbl_announcement = Database::get_course_table(TABLE_ANNOUNCEMENT);
11929
                $result = Database::query("SELECT * FROM $tbl_announcement WHERE c_id = $course_id AND id=$id");
11930
                $myrow = Database::fetch_array($result);
11931
                $output = $myrow['title'];
11932
                break;
11933
            case TOOL_LINK:
11934
                // Doesn't take $target into account.
11935
                $TABLETOOLLINK = Database::get_course_table(TABLE_LINK);
11936
                $result = Database::query("SELECT * FROM $TABLETOOLLINK WHERE c_id = $course_id AND id=$id");
11937
                $myrow = Database::fetch_array($result);
11938
                $output = $myrow['title'];
11939
                break;
11940
            case TOOL_QUIZ:
11941
                $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
11942
                $result = Database::query("SELECT * FROM $TBL_EXERCICES WHERE c_id = $course_id AND id=$id");
11943
                $myrow = Database::fetch_array($result);
11944
                $output = $myrow['title'];
11945
                break;
11946
            case TOOL_FORUM:
11947
                $TBL_FORUMS = Database::get_course_table(TABLE_FORUM);
11948
                $result = Database::query("SELECT * FROM $TBL_FORUMS WHERE c_id = $course_id AND forum_id=$id");
11949
                $myrow = Database::fetch_array($result);
11950
                $output = $myrow['forum_name'];
11951
                break;
11952 View Code Duplication
            case TOOL_THREAD:  //=topics
11953
                $tbl_post = Database::get_course_table(TABLE_FORUM_POST);
11954
                // Grabbing the title of the post.
11955
                $sql_title = "SELECT * FROM $tbl_post WHERE c_id = $course_id AND post_id=".$id;
11956
                $result_title = Database::query($sql_title);
11957
                $myrow_title = Database::fetch_array($result_title);
11958
                $output = $myrow_title['post_title'];
11959
                break;
11960 View Code Duplication
            case TOOL_POST:
11961
                $tbl_post = Database::get_course_table(TABLE_FORUM_POST);
11962
                //$tbl_post_text = Database::get_course_table(FORUM_POST_TEXT_TABLE);
11963
                $sql = "SELECT * FROM $tbl_post p WHERE c_id = $course_id AND p.post_id = $id";
11964
                $result = Database::query($sql);
11965
                $post = Database::fetch_array($result);
11966
                $output = $post['post_title'];
11967
                break;
11968 View Code Duplication
            case 'dir':
11969
                $title = $row_item['title'];
11970
                if (!empty($title)) {
11971
                    $output = $title;
11972
                } else {
11973
                    $output = '-';
11974
                }
11975
                break;
11976 View Code Duplication
            case TOOL_DOCUMENT:
11977
                $title = $row_item['title'];
11978
                if (!empty($title)) {
11979
                    $output = $title;
11980
                } else {
11981
                    $output = '-';
11982
                }
11983
                break;
11984
            case 'hotpotatoes':
11985
                $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
11986
                $result = Database::query("SELECT * FROM $tbl_doc WHERE c_id = $course_id AND id = $id");
11987
                $myrow = Database::fetch_array($result);
11988
                $pathname = explode('/', $myrow['path']); // Making a correct name for the link.
11989
                $last = count($pathname) - 1; // Making a correct name for the link.
11990
                $filename = $pathname[$last]; // Making a correct name for the link.
11991
                $ext = explode('.', $filename);
11992
                $ext = strtolower($ext[sizeof($ext) - 1]);
11993
                $myrow['path'] = rawurlencode($myrow['path']);
11994
                $output = $filename;
11995
                break;
11996
        }
11997
        return stripslashes($output);
11998
    }
11999
}
12000
12001
if (!function_exists('trim_value')) {
12002
    function trim_value(& $value) {
0 ignored issues
show
Best Practice introduced by
The function trim_value() has been defined more than once; this definition is ignored, only the first definition in main/inc/lib/search/ChamiloIndexer.class.php (L99-102) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
12003
        $value = trim($value);
12004
    }
12005
}
12006