Completed
Push — 1.10.x ( ea3352...dbf7ee )
by Julito
30:30
created

main/newscorm/learnpath.class.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CourseBundle\Entity\CLpCategory;
5
use ChamiloSession as Session;
6
use Symfony\Component\Filesystem\Filesystem;
7
use Symfony\Component\Finder\Finder;
8
9
/**
10
 * Class learnpath
11
 * This class defines the parent attributes and methods for Chamilo learnpaths
12
 * and SCORM learnpaths. It is used by the scorm class.
13
 *
14
 * @package chamilo.learnpath
15
 * @author	Yannick Warnier <[email protected]>
16
 * @author	Julio Montoya   <[email protected]> Several improvements and fixes
17
 */
18
class learnpath
19
{
20
    public $attempt = 0; // The number for the current ID view.
21
    public $cc; // Course (code) this learnpath is located in. @todo change name for something more comprensible ...
22
    public $current; // Id of the current item the user is viewing.
23
    public $current_score; // The score of the current item.
24
    public $current_time_start; // The time the user loaded this resource (this does not mean he can see it yet).
25
    public $current_time_stop; // The time the user closed this resource.
26
    public $default_status = 'not attempted';
27
    public $encoding = 'UTF-8';
28
    public $error = '';
29
    public $extra_information = ''; // This string can be used by proprietary SCORM contents to store data about the current learnpath.
30
    public $force_commit = false; // For SCORM only - if set to true, will send a scorm LMSCommit() request on each LMSSetValue().
31
    public $index; // The index of the active learnpath_item in $ordered_items array.
32
    public $items = array();
33
    public $last; // item_id of last item viewed in the learning path.
34
    public $last_item_seen = 0; // In case we have already come in this learnpath, reuse the last item seen if authorized.
35
    public $license; // Which license this course has been given - not used yet on 20060522.
36
    public $lp_id; // DB ID for this learnpath.
37
    public $lp_view_id; // DB ID for lp_view
38
    public $log_file; // File where to log learnpath API msg.
39
    public $maker; // Which maker has conceived the content (ENI, Articulate, ...).
40
    public $message = '';
41
    public $mode = 'embedded'; // Holds the video display mode (fullscreen or embedded).
42
    public $name; // Learnpath name (they generally have one).
43
    public $ordered_items = array(); // List of the learnpath items in the order they are to be read.
44
    public $path = ''; // Path inside the scorm directory (if scorm).
45
    public $theme; // The current theme of the learning path.
46
    public $preview_image; // The current image of the learning path.
47
48
    // Tells if all the items of the learnpath can be tried again. Defaults to "no" (=1).
49
    public $prevent_reinit = 1;
50
51
    // Describes the mode of progress bar display.
52
    public $seriousgame_mode = 0;
53
    public $progress_bar_mode = '%';
54
55
    // Percentage progress as saved in the db.
56
    public $progress_db = '0';
57
    public $proximity; // Wether the content is distant or local or unknown.
58
    public $refs_list = array (); //list of items by ref => db_id. Used only for prerequisites match.
59
    // !!!This array (refs_list) is built differently depending on the nature of the LP.
60
    // If SCORM, uses ref, if Chamilo, uses id to keep a unique value.
61
    public $type; //type of learnpath. Could be 'dokeos', 'scorm', 'scorm2004', 'aicc', ...
62
    // TODO: Check if this type variable is useful here (instead of just in the controller script).
63
    public $user_id; //ID of the user that is viewing/using the course
64
    public $update_queue = array();
65
    public $scorm_debug = 0;
66
    public $arrMenu = array(); // Array for the menu items.
67
    public $debug = 0; // Logging level.
68
    public $lp_session_id = 0;
69
    public $lp_view_session_id = 0; // The specific view might be bound to a session.
70
    public $prerequisite = 0;
71
    public $use_max_score = 1; // 1 or 0
72
    public $subscribeUsers = 0; // Subscribe users or not
73
    public $created_on      = '';
74
    public $modified_on     = '';
75
    public $publicated_on   = '';
76
    public $expired_on      = '';
77
    public $ref = null;
78
    public $course_int_id;
79
    public $course_info = array();
80
    public $categoryId;
81
82
    /**
83
     * Constructor.
84
     * Needs a database handler, a course code and a learnpath id from the database.
85
     * Also builds the list of items into $this->items.
86
     * @param	string	$course Course code
87
     * @param	integer	$lp_id
88
     * @param	integer	$user_id
89
     * @return mixed True on success, false on error
90
     */
91
    public function __construct($course, $lp_id, $user_id)
92
    {
93
        $this->encoding = api_get_system_encoding();
94
        if ($this->debug > 0) {
95
            error_log('New LP - In learnpath::__construct('.$course.','.$lp_id.','.$user_id.')', 0);
96
        }
97
        if (empty($course)) {
98
            $this->error = 'Course code is empty';
99
            return false;
100
        } else {
101
            $course_info = api_get_course_info($course);
102
            if (!empty($course_info)) {
103
                $this->cc = $course_info['code'];
104
                $this->course_info = $course_info;
105
                $course_id = $course_info['real_id'];
106
            } else {
107
                $this->error = 'Course code does not exist in database.';
108
                return false;
109
            }
110
        }
111
112
        $this->set_course_int_id($course_id);
113
114
        // Check learnpath ID.
115
        if (empty($lp_id)) {
116
            $this->error = 'Learnpath ID is empty';
117
            return false;
118
        } else {
119
            // TODO: Make it flexible to use any course_code (still using env course code here).
120
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
121
            $lp_id = intval($lp_id);
122
            $sql = "SELECT * FROM $lp_table
123
                    WHERE id = '$lp_id' AND c_id = $course_id";
124
            if ($this->debug > 2) {
125
                error_log('New LP - learnpath::__construct() '.__LINE__.' - Querying lp: '.$sql, 0);
126
            }
127
            $res = Database::query($sql);
128
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
154
                if ($row['publicated_on'] != '0000-00-00 00:00:00') {
155
                    $this->publicated_on = $row['publicated_on'];
156
                }
157
158
                if ($row['expired_on'] != '0000-00-00 00:00:00') {
159
                    $this->expired_on  = $row['expired_on'];
160
                }
161 View Code Duplication
                if ($this->type == 2) {
162
                    if ($row['force_commit'] == 1) {
163
                        $this->force_commit = true;
164
                    }
165
                }
166
                $this->mode = $row['default_view_mod'];
167
            } else {
168
                $this->error = 'Learnpath ID does not exist in database ('.$sql.')';
169
170
                return false;
171
            }
172
        }
173
174
        // Check user ID.
175
        if (empty($user_id)) {
176
            $this->error = 'User ID is empty';
177
178
            return false;
179
        } else {
180
            $user_info = api_get_user_info($user_id);
181
            if (!empty($user_info)) {
182
                $this->user_id = $user_info['user_id'];
183
            } else {
184
                $this->error = 'User ID does not exist in database ('.$sql.')';
185
186
                return false;
187
            }
188
        }
189
190
        // End of variables checking.
191
        $session_id = api_get_session_id();
192
        //  Get the session condition for learning paths of the base + session.
193
        $session = api_get_session_condition($session_id);
194
        // Now get the latest attempt from this user on this LP, if available, otherwise create a new one.
195
        $lp_table = Database::get_course_table(TABLE_LP_VIEW);
196
197
        // Selecting by view_count descending allows to get the highest view_count first.
198
        $sql = "SELECT * FROM $lp_table
199
                WHERE c_id = $course_id AND lp_id = '$lp_id' AND user_id = '$user_id' $session
200
                ORDER BY view_count DESC";
201
        $res = Database::query($sql);
202
        if ($this->debug > 2) {
203
            error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - querying lp_view: ' . $sql, 0);
204
        }
205
206
        if (Database :: num_rows($res) > 0) {
207
            if ($this->debug > 2) {
208
                error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - Found previous view', 0);
209
            }
210
            $row = Database :: fetch_array($res);
211
            $this->attempt = $row['view_count'];
212
            $this->lp_view_id = $row['id'];
213
            $this->last_item_seen = $row['last_item'];
214
            $this->progress_db = $row['progress'];
215
            $this->lp_view_session_id = $row['session_id'];
216
        } else if (!api_is_invitee()) {
217
            if ($this->debug > 2) {
218
                error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - NOT Found previous view', 0);
219
            }
220
            $this->attempt = 1;
221
            $params = [
222
                'c_id' => $course_id,
223
                'lp_id' => $lp_id,
224
                'user_id' => $user_id,
225
                'view_count' => 1,
226
                'session_id' => $session_id,
227
                'last_item' => 0
228
            ];
229
            Database::insert($lp_table, $params);
230
            $this->lp_view_id = Database::insert_id();
231
232
            if ($this->debug > 2) {
233
                error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - inserting new lp_view: ' . $sql, 0);
234
            }
235
236
            $sql = "UPDATE $lp_table SET id = iid WHERE iid = ".$this->lp_view_id;
237
            Database::query($sql);
238
        }
239
240
        // Initialise items.
241
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
242
        $sql = "SELECT * FROM $lp_item_table
243
                WHERE c_id = $course_id AND lp_id = '".$this->lp_id."'
244
                ORDER BY parent_item_id, display_order";
245
        $res = Database::query($sql);
246
247
        if ($this->debug > 2) {
248
            error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - query lp items: ' . $sql, 0);
249
            error_log('-- Start while--', 0);
250
        }
251
252
        $lp_item_id_list = array();
253
254
        while ($row = Database::fetch_array($res)) {
255
            $lp_item_id_list[] = $row['id'];
256
            switch ($this->type) {
257 View Code Duplication
                case 3: //aicc
258
                    $oItem = new aiccItem('db', $row['id'], $course_id);
259
                    if (is_object($oItem)) {
260
                        $my_item_id = $oItem->get_id();
261
                        $oItem->set_lp_view($this->lp_view_id, $course_id);
262
                        $oItem->set_prevent_reinit($this->prevent_reinit);
263
                        // Don't use reference here as the next loop will make the pointed object change.
264
                        $this->items[$my_item_id] = $oItem;
265
                        $this->refs_list[$oItem->ref] = $my_item_id;
266
                        if ($this->debug > 2) {
267
                            error_log(
268
                                'New LP - learnpath::__construct() - ' .
269
                                'aicc object with id ' . $my_item_id .
270
                                ' set in items[]',
271
                                0
272
                            );
273
                        }
274
                    }
275
                    break;
276 View Code Duplication
                case 2:
277
                    $oItem = new scormItem('db', $row['id'], $course_id);
278
                    if (is_object($oItem)) {
279
                        $my_item_id = $oItem->get_id();
280
                        $oItem->set_lp_view($this->lp_view_id, $course_id);
281
                        $oItem->set_prevent_reinit($this->prevent_reinit);
282
                        // Don't use reference here as the next loop will make the pointed object change.
283
284
                        $this->items[$my_item_id] = $oItem;
285
286
                        $this->refs_list[$oItem->ref] = $my_item_id;
287
                        if ($this->debug > 2) {
288
                            error_log('New LP - object with id ' . $my_item_id . ' set in items[]', 0);
289
                        }
290
                    }
291
                    break;
292
                case 1:
293
                default:
294
                    if ($this->debug > 2) {
295
                        error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - calling learnpathItem', 0);
296
                    }
297
                    $oItem = new learnpathItem($row['id'], $user_id, $course_id, $row);
298
299
                    if ($this->debug > 2) {
300
                        error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - end calling learnpathItem', 0);
301
                    }
302
                    if (is_object($oItem)) {
303
                        $my_item_id = $oItem->get_id();
304
                        //$oItem->set_lp_view($this->lp_view_id); // Moved down to when we are sure the item_view exists.
305
                        $oItem->set_prevent_reinit($this->prevent_reinit);
306
                        // Don't use reference here as the next loop will make the pointed object change.
307
                        $this->items[$my_item_id] = $oItem;
308
                        $this->refs_list[$my_item_id] = $my_item_id;
309 View Code Duplication
                        if ($this->debug > 2) {
310
                            error_log(
311
                                'New LP - learnpath::__construct() ' . __LINE__ .
312
                                ' - object with id ' . $my_item_id . ' set in items[]',
313
                                0);
314
                        }
315
                    }
316
                    break;
317
            }
318
319
            // Setting the object level with variable $this->items[$i][parent]
320
            foreach ($this->items as $itemLPObject) {
321
                $level = learnpath::get_level_for_item($this->items, $itemLPObject->db_id);
322
                $itemLPObject->level = $level;
323
            }
324
325
            // Setting the view in the item object.
326
            if (is_object($this->items[$row['id']])) {
327
                $this->items[$row['id']]->set_lp_view($this->lp_view_id, $course_id);
328
                if ($this->items[$row['id']]->get_type() == TOOL_HOTPOTATOES) {
329
                    $this->items[$row['id']]->current_start_time = 0;
330
                    $this->items[$row['id']]->current_stop_time	= 0;
331
                }
332
            }
333
        }
334
335
        if ($this->debug > 2) {
336
            error_log('New LP - learnpath::__construct() ' . __LINE__ . ' ----- end while ----', 0);
337
        }
338
339
        if (!empty($lp_item_id_list)) {
340
            $lp_item_id_list_to_string = implode("','", $lp_item_id_list);
341
            if (!empty($lp_item_id_list_to_string)) {
342
                // Get last viewing vars.
343
                $lp_item_view_table = Database:: get_course_table(
344
                    TABLE_LP_ITEM_VIEW
345
                );
346
                // This query should only return one or zero result.
347
                $sql = "SELECT lp_item_id, status
348
                        FROM $lp_item_view_table
349
                        WHERE
350
                            c_id = $course_id AND
351
                            lp_view_id = ".$this->lp_view_id." AND
352
                            lp_item_id IN ('".$lp_item_id_list_to_string."')
353
                        ORDER BY view_count DESC ";
354
355
                if ($this->debug > 2) {
356
                    error_log(
357
                        'New LP - learnpath::__construct() - Selecting item_views: '.$sql,
358
                        0
359
                    );
360
                }
361
362
                $status_list = array();
363
                $res = Database::query($sql);
364
                while ($row = Database:: fetch_array($res)) {
365
                    $status_list[$row['lp_item_id']] = $row['status'];
366
                }
367
368
                foreach ($lp_item_id_list as $item_id) {
369
                    if (isset($status_list[$item_id])) {
370
                        $status = $status_list[$item_id];
371
                        if (is_object($this->items[$item_id])) {
372
                            $this->items[$item_id]->set_status($status);
373
                            if (empty($status)) {
374
                                $this->items[$item_id]->set_status(
375
                                    $this->default_status
376
                                );
377
                            }
378
                        }
379
                    } else {
380
                        if (!api_is_invitee()) {
381
                            if (is_object($this->items[$item_id])) {
382
                                $this->items[$item_id]->set_status(
383
                                    $this->default_status
384
                                );
385
                            }
386
387
                            // Add that row to the lp_item_view table so that we have something to show in the stats page.
388
                            $sql = "INSERT INTO $lp_item_view_table (c_id, lp_item_id, lp_view_id, view_count, status, start_time, total_time, score)
389
                                    VALUES ($course_id, ".$item_id.",".$this->lp_view_id.", 1, 'not attempted', '".time()."', 0, 0)";
390
391
                            if ($this->debug > 2) {
392
                                error_log(
393
                                    'New LP - learnpath::__construct() '.__LINE__.' - Inserting blank item_view : '.$sql,
394
                                    0
395
                                );
396
                            }
397
                            $this->items[$item_id]->set_lp_view(
398
                                $this->lp_view_id,
399
                                $course_id
400
                            );
401
402
                            Database::query($sql);
403
                            $insertId = Database::insert_id();
404
405
                            $sql = "UPDATE $lp_item_view_table SET id = iid
406
                                    WHERE iid = $insertId";
407
                            Database::query($sql);
408
                        }
409
                    }
410
                }
411
            }
412
        }
413
414
        $this->ordered_items = self::get_flat_ordered_items_list(
415
            $this->get_id(),
416
            0,
417
            $course_id
418
        );
419
        $this->max_ordered_items = 0;
420
        foreach ($this->ordered_items as $index => $dummy) {
421
            if ($index > $this->max_ordered_items && !empty($dummy)) {
422
                $this->max_ordered_items = $index;
423
            }
424
        }
425
        // TODO: Define the current item better.
426
        $this->first();
427
        if ($this->debug > 2) {
428
            error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - End of learnpath constructor for learnpath ' . $this->get_id(), 0);
429
        }
430
        return true;
431
    }
432
433
    /**
434
     * @return string
435
     */
436
    public function getCourseCode()
437
    {
438
        return $this->cc;
439
    }
440
441
    /**
442
     * @return int
443
     */
444
    public function get_course_int_id()
445
    {
446
        return isset($this->course_int_id) ? $this->course_int_id : api_get_course_int_id();
447
    }
448
449
    /**
450
     * @param $course_id
451
     * @return int
452
     */
453
    public function set_course_int_id($course_id)
454
    {
455
        return $this->course_int_id = intval($course_id);
456
    }
457
458
    /**
459
     * Get the depth level of LP item
460
     * @param $in_tab_items
461
     * @param $in_current_item_id
462
     * @return int
463
     */
464
    private static function get_level_for_item($in_tab_items, $in_current_item_id)
465
    {
466
        $parent_item_id = $in_tab_items[$in_current_item_id]->parent;
467
        if ($parent_item_id == 0) {
468
            return 0;
469
        } else {
470
            return learnpath::get_level_for_item($in_tab_items, $parent_item_id) + 1;
471
        }
472
    }
473
474
    /**
475
     * Function rewritten based on old_add_item() from Yannick Warnier.
476
     * Due the fact that users can decide where the item should come, I had to overlook this function and
477
     * I found it better to rewrite it. Old function is still available.
478
     * Added also the possibility to add a description.
479
     *
480
     * @param int $parent
481
     * @param int $previous
482
     * @param string $type
483
     * @param int  resource ID (ref)
484
     * @param string $title
485
     * @param string $description
486
     * @param int $prerequisites
487
     * @param int $max_time_allowed
488
     * @param int $userId
489
     *
490
     * @return int
491
     */
492
    public function add_item(
493
        $parent,
494
        $previous,
495
        $type = 'dokeos_chapter',
496
        $id,
497
        $title,
498
        $description,
499
        $prerequisites = 0,
500
        $max_time_allowed = 0,
501
        $userId = 0
502
    ) {
503
        $course_id = $this->course_info['real_id'];
504
        if ($this->debug > 0) {
505
            error_log('New LP - In learnpath::add_item(' . $parent . ',' . $previous . ',' . $type . ',' . $id . ',' . $title . ')', 0);
506
        }
507
        if (empty($course_id)) {
508
            // Sometimes Oogie doesn't catch the course info but sets $this->cc
509
            $this->course_info = api_get_course_info($this->cc);
510
            $course_id = $this->course_info['real_id'];
511
        }
512
        $userId = empty($userId) ? api_get_user_id() : $userId;
513
        $sessionId = api_get_session_id();
514
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
515
        $_course = $this->course_info;
516
        $parent = intval($parent);
517
        $previous = intval($previous);
518
        $id = intval($id);
519
        $max_time_allowed = htmlentities($max_time_allowed);
520
        if (empty($max_time_allowed)) {
521
            $max_time_allowed = 0;
522
        }
523
        $sql = "SELECT COUNT(id) AS num
524
                FROM $tbl_lp_item
525
                WHERE
526
                    c_id = $course_id AND
527
                    lp_id = " . $this->get_id() . " AND
528
                    parent_item_id = " . $parent;
529
530
        $res_count = Database::query($sql);
531
        $row = Database :: fetch_array($res_count);
532
        $num = $row['num'];
533
534
        if ($num > 0) {
535
            if ($previous == 0) {
536
                $sql = "SELECT id, next_item_id, display_order
537
                        FROM " . $tbl_lp_item . "
538
                        WHERE
539
                            c_id = $course_id AND
540
                            lp_id = " . $this->get_id() . " AND
541
                            parent_item_id = " . $parent . " AND
542
                            previous_item_id = 0 OR
543
                            previous_item_id=" . $parent;
544
                $result = Database::query($sql);
545
                $row = Database :: fetch_array($result);
546
547
                $tmp_previous = 0;
548
                $next = $row['id'];
549
                $display_order = 0;
550
            } else {
551
                $previous = (int) $previous;
552
                $sql = "SELECT id, previous_item_id, next_item_id, display_order
553
						FROM $tbl_lp_item
554
                        WHERE
555
                            c_id = $course_id AND
556
                            lp_id = " . $this->get_id() . " AND
557
                            id = " . $previous;
558
559
                $result = Database::query($sql);
560
                $row = Database:: fetch_array($result);
561
562
                $tmp_previous = $row['id'];
563
                $next = $row['next_item_id'];
564
                $display_order = $row['display_order'];
565
            }
566
        } else {
567
            $tmp_previous = 0;
568
            $next = 0;
569
            $display_order = 0;
570
        }
571
572
        $id = intval($id);
573
        $typeCleaned = Database::escape_string($type);
574
        if ($type == 'quiz') {
575
            $sql = 'SELECT SUM(ponderation)
576
                    FROM ' . Database :: get_course_table(TABLE_QUIZ_QUESTION) . ' as quiz_question
577
                    INNER JOIN  ' . Database :: get_course_table(TABLE_QUIZ_TEST_QUESTION) . ' as quiz_rel_question
578
                    ON
579
                        quiz_question.id = quiz_rel_question.question_id AND
580
                        quiz_question.c_id = quiz_rel_question.c_id
581
                    WHERE
582
                        quiz_rel_question.exercice_id = '.$id." AND
583
                        quiz_question.c_id = $course_id AND
584
                        quiz_rel_question.c_id = $course_id ";
585
            $rsQuiz = Database::query($sql);
586
            $max_score = Database :: result($rsQuiz, 0, 0);
587
588
            // Disabling the exercise if we add it inside a LP
589
            $exercise = new Exercise($course_id);
590
            $exercise->read($id);
591
            $exercise->disable();
592
            $exercise->save();
593
        } else {
594
            $max_score = 100;
595
        }
596
597
        $params = array(
598
            "c_id" => $course_id,
599
            "lp_id" => $this->get_id(),
600
            "item_type" => $typeCleaned,
601
            "ref" => '',
602
            "title" => $title,
603
            "description" => $description,
604
            "path" => $id,
605
            "max_score" => $max_score,
606
            "parent_item_id" => $parent,
607
            "previous_item_id" => $previous,
608
            "next_item_id" => intval($next),
609
            "display_order" => $display_order + 1,
610
            "prerequisite" => $prerequisites,
611
            "max_time_allowed" => $max_time_allowed,
612
            'min_score' => 0,
613
            'launch_data' => '',
614
        );
615
616
        if ($prerequisites != 0) {
617
            $params['prerequisite'] = $prerequisites;
618
        }
619
620
        $new_item_id = Database::insert($tbl_lp_item, $params);
621
622
        if ($this->debug > 2) {
623
            error_log('New LP - Inserting chapter: ' . $new_item_id, 0);
624
        }
625
626
        if ($new_item_id) {
627
628
            $sql = "UPDATE $tbl_lp_item SET id = iid WHERE iid = $new_item_id";
629
            Database::query($sql);
630
631
            // Update the item that should come after the new item.
632
            $sql = " UPDATE $tbl_lp_item SET
633
                        previous_item_id =  $new_item_id,
634
                        next_item_id = $new_item_id,
635
                        id = $new_item_id
636
                     WHERE iid = $new_item_id";
637
            Database::query($sql);
638
639
            // Update all the items after the new item.
640
            $sql = "UPDATE " . $tbl_lp_item . "
641
                        SET display_order = display_order + 1
642
                    WHERE
643
                        c_id = $course_id AND
644
                        lp_id = " . $this->get_id() . " AND
645
                        id <> " . $new_item_id . " AND
646
                        parent_item_id = " . $parent . " AND
647
                        display_order > " . $display_order;
648
            Database::query($sql);
649
650
            // Update the item that should come after the new item.
651
            $sql = "UPDATE " . $tbl_lp_item . "
652
                    SET ref = " . $new_item_id . "
653
                    WHERE c_id = $course_id AND id = " . $new_item_id;
654
            Database::query($sql);
655
656
            // Upload audio.
657
            if (!empty($_FILES['mp3']['name'])) {
658
                // Create the audio folder if it does not exist yet.
659
                $filepath = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document/';
660
                if (!is_dir($filepath . 'audio')) {
661
                    mkdir($filepath . 'audio', api_get_permissions_for_new_directories());
662
                    $audio_id = add_document(
663
                        $_course,
664
                        '/audio',
665
                        'folder',
666
                        0,
667
                        'audio',
668
                        '',
669
                        0,
670
                        true,
671
                        null,
672
                        $sessionId,
673
                        $userId
674
                    );
675
                    api_item_property_update(
676
                        $_course,
677
                        TOOL_DOCUMENT,
678
                        $audio_id,
679
                        'FolderCreated',
680
                        $userId,
681
                        null,
682
                        null,
683
                        null,
684
                        null,
685
                        $sessionId
686
                    );
687
                    api_item_property_update(
688
                        $_course,
689
                        TOOL_DOCUMENT,
690
                        $audio_id,
691
                        'invisible',
692
                        $userId,
693
                        null,
694
                        null,
695
                        null,
696
                        null,
697
                        $sessionId
698
                    );
699
                }
700
701
                $file_path = handle_uploaded_document(
702
                    $_course,
703
                    $_FILES['mp3'],
704
                    api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document',
705
                    '/audio',
706
                    $userId,
707
                    '',
708
                    '',
709
                    '',
710
                    '',
711
                    false
712
                );
713
714
                // Getting the filename only.
715
                $file_components = explode('/', $file_path);
716
                $file = $file_components[count($file_components) - 1];
717
718
                // Store the mp3 file in the lp_item table.
719
                $sql = "UPDATE $tbl_lp_item SET
720
                            audio = '" . Database::escape_string($file) . "'
721
                        WHERE id = '" . intval($new_item_id) . "'";
722
                Database::query($sql);
723
            }
724
        }
725
726
        return $new_item_id;
727
    }
728
729
    /**
730
     * Static admin function allowing addition of a learnpath to a course.
731
     * @param	string	Course code
732
     * @param	string	Learnpath name
733
     * @param	string	Learnpath description string, if provided
734
     * @param	string	Type of learnpath (default = 'guess', others = 'dokeos', 'aicc',...)
735
     * @param	string	Type of files origin (default = 'zip', others = 'dir','web_dir',...)
736
     * @param	string	Zip file containing the learnpath or directory containing the learnpath
737
     * @return	integer	The new learnpath ID on success, 0 on failure
738
     */
739
    public static function add_lp(
740
        $courseCode,
741
        $name,
742
        $description = '',
743
        $learnpath = 'guess',
744
        $origin = 'zip',
745
        $zipname = '',
746
        $publicated_on = '',
747
        $expired_on = '',
748
        $categoryId = 0,
749
        $userId = 0
750
    ) {
751
        global $charset;
752
753
        if (!empty($courseCode)) {
754
            $courseInfo = api_get_course_info($courseCode);
755
            $course_id = $courseInfo['real_id'];
756
        } else {
757
            $course_id = api_get_course_int_id();
758
            $courseInfo = api_get_course_info();
759
        }
760
761
        $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
762
        // Check course code exists.
763
        // Check lp_name doesn't exist, otherwise append something.
764
        $i = 0;
765
        $name = Database::escape_string($name);
766
        $categoryId = intval($categoryId);
767
768
        // Session id.
769
        $session_id = api_get_session_id();
770
771
        $userId = empty($userId) ? api_get_user_id() : $userId;
772
773
        $check_name = "SELECT * FROM $tbl_lp
774
                       WHERE c_id = $course_id AND name = '$name'";
775
776
        $res_name = Database::query($check_name);
777
778
        if ($publicated_on == '0000-00-00 00:00:00' || empty($publicated_on)) {
779
            //by default the publication date is the same that the creation date
780
            //The behaviour above was changed due BT#2800
781
            global $_custom;
782
            if (isset($_custom['lps_hidden_when_no_start_date']) && $_custom['lps_hidden_when_no_start_date']) {
783
                $publicated_on = '';
784
            } else {
785
                $publicated_on = api_get_utc_datetime();
786
            }
787
        } else {
788
            $publicated_on = Database::escape_string(api_get_utc_datetime($publicated_on));
789
        }
790
791
        if ($expired_on == '0000-00-00 00:00:00' || empty($expired_on)) {
792
            $expired_on = '';
793
        } else {
794
            $expired_on = Database::escape_string(api_get_utc_datetime($expired_on));
795
        }
796
797
        while (Database :: num_rows($res_name)) {
798
            // There is already one such name, update the current one a bit.
799
            $i++;
800
            $name = $name . ' - ' . $i;
801
            $check_name = "SELECT * FROM $tbl_lp WHERE c_id = $course_id AND name = '$name'";
802
            $res_name = Database::query($check_name);
803
        }
804
        // New name does not exist yet; keep it.
805
        // Escape description.
806
        // Kevin: added htmlentities().
807
        $description = Database::escape_string(api_htmlentities($description, ENT_QUOTES, $charset));
808
        $type = 1;
809
        switch ($learnpath) {
810
            case 'guess':
811
                break;
812
            case 'dokeos':
813
            case 'chamilo':
814
                $type = 1;
815
                break;
816
            case 'aicc':
817
                break;
818
        }
819
820
        switch ($origin) {
821
            case 'zip':
822
                // Check zip name string. If empty, we are currently creating a new Chamilo learnpath.
823
                break;
824
            case 'manual':
825
            default:
826
                $get_max = "SELECT MAX(display_order) FROM $tbl_lp WHERE c_id = $course_id";
827
                $res_max = Database::query($get_max);
828
                if (Database :: num_rows($res_max) < 1) {
829
                    $dsp = 1;
830
                } else {
831
                    $row = Database :: fetch_array($res_max);
832
                    $dsp = $row[0] + 1;
833
                }
834
835
                $params = [
836
                    'c_id' => $course_id,
837
                    'lp_type' => $type,
838
                    'name' => $name,
839
                    'description' => $description,
840
                    'path' => '',
841
                    'default_view_mod' => 'embedded',
842
                    'default_encoding' => 'UTF-8',
843
                    'display_order' => $dsp,
844
                    'content_maker' => 'Chamilo',
845
                    'content_local' => 'local',
846
                    'js_lib' => '',
847
                    'session_id' => $session_id,
848
                    'created_on' => api_get_utc_datetime(),
849
                    'modified_on'  => api_get_utc_datetime(),
850
                    'publicated_on' => $publicated_on,
851
                    'expired_on' => $expired_on,
852
                    'category_id' => $categoryId,
853
                    'force_commit' => 0,
854
                    'content_license' => '',
855
                    'debug' => 0,
856
                    'theme' => '',
857
                    'preview_image' => '',
858
                    'author' => '',
859
                    'prerequisite' => 0,
860
                    'hide_toc_frame' => 0,
861
                    'seriousgame_mode' => 0,
862
                    'autolaunch' => 0,
863
                    'max_attempts' => 0,
864
                    'subscribe_users' => 0
865
                ];
866
867
                $id = Database::insert($tbl_lp, $params);
868
869 View Code Duplication
                if ($id > 0) {
870
                    $sql = "UPDATE $tbl_lp SET id = iid WHERE iid = $id";
871
                    Database::query($sql);
872
873
                    // Insert into item_property.
874
                    api_item_property_update(
875
                        $courseInfo,
876
                        TOOL_LEARNPATH,
877
                        $id,
878
                        'LearnpathAdded',
879
                        $userId
880
                    );
881
                    api_set_default_visibility($id, TOOL_LEARNPATH, 0, $courseInfo, $session_id, $userId);
882
                    return $id;
883
                }
884
                break;
885
        }
886
    }
887
888
    /**
889
     * Auto completes the parents of an item in case it's been completed or passed
890
     * @param	integer	$item Optional ID of the item from which to look for parents
891
     */
892
    public function autocomplete_parents($item)
893
    {
894
        $debug = $this->debug;
895
896
        if ($debug) {
897
            error_log('Learnpath::autocomplete_parents()', 0);
898
        }
899
900
        if (empty($item)) {
901
            $item = $this->current;
902
        }
903
904
        $currentItem = $this->getItem($item);
905
        if ($currentItem) {
906
            $parent_id = $currentItem->get_parent();
907
            $parent = $this->getItem($parent_id);
908
            if ($parent) {
909
                // if $item points to an object and there is a parent.
910
                if ($debug) {
911
                    error_log(
912
                        'Autocompleting parent of item ' . $item . ' "'.$currentItem->get_title().'" (item ' . $parent_id . ' "'.$parent->get_title().'") ',
913
                        0
914
                    );
915
                }
916
917
                // New experiment including failed and browsed in completed status.
918
                //$current_status = $currentItem->get_status();
919
                //if ($currentItem->is_done() || $current_status == 'browsed' || $current_status == 'failed') {
920
                // Fixes chapter auto complete
921
                if (true) {
922
                    // If the current item is completed or passes or succeeded.
923
                    $updateParentStatus = true;
924
                    if ($debug) {
925
                        error_log('Status of current item is alright', 0);
926
                    }
927
928
                    foreach ($parent->get_children() as $childItemId) {
929
                        $childItem = $this->getItem($childItemId);
930
931
                        // If children was not set try to get the info
932
                        if (empty($childItem->db_item_view_id)) {
933
                            $childItem->set_lp_view($this->lp_view_id, $this->course_int_id);
934
                        }
935
936
                        // Check all his brothers (parent's children) for completion status.
937
                        if ($childItemId != $item) {
938
                            if ($debug) {
939
                                error_log(
940
                                    'Looking at brother #'.$childItemId . ' "' . $childItem->get_title() . '", status is ' . $childItem->get_status(),
941
                                    0
942
                                );
943
                            }
944
                            // Trying completing parents of failed and browsed items as well.
945
                            if ($childItem->status_is(
946
                                array(
947
                                    'completed',
948
                                    'passed',
949
                                    'succeeded',
950
                                    'browsed',
951
                                    'failed'
952
                                )
953
                            )
954
                            ) {
955
                                // Keep completion status to true.
956
                                continue;
957
                            } else {
958
                                if ($debug > 2) {
959
                                    error_log(
960
                                        '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,
961
                                        0
962
                                    );
963
                                }
964
                                $updateParentStatus = false;
965
                                break;
966
                            }
967
                        }
968
                    }
969
970
                    if ($updateParentStatus) {
971
                        // If all the children were completed:
972
                        $parent->set_status('completed');
973
                        $parent->save(false, $this->prerequisites_match($parent->get_id()));
974
                        // Force the status to "completed"
975
                        //$this->update_queue[$parent->get_id()] = $parent->get_status();
976
                        $this->update_queue[$parent->get_id()] = 'completed';
977
                        if ($debug) {
978
                            error_log(
979
                                'Added parent #'.$parent->get_id().' "'.$parent->get_title().'" to update queue status: completed '.
980
                                print_r($this->update_queue, 1),
981
                                0
982
                            );
983
                        }
984
                        // Recursive call.
985
                        $this->autocomplete_parents($parent->get_id());
986
                    }
987
                }
988
            } else {
989
                if ($debug) {
990
                    error_log("Parent #$parent_id does not exists");
991
                }
992
            }
993
        } else {
994
            if ($debug) {
995
                error_log("#$item is an item that doesn't have parents");
996
            }
997
        }
998
    }
999
1000
    /**
1001
     * Auto saves the current results into the database for the whole learnpath
1002
     */
1003
    public function autosave()
1004
    {
1005
        if ($this->debug > 0) {
1006
            error_log('New LP - In learnpath::autosave()', 0);
1007
        }
1008
        // TODO: Add save operations for the learnpath itself.
1009
    }
1010
1011
    /**
1012
     * Closes the current resource
1013
     *
1014
     * Stops the timer
1015
     * Saves into the database if required
1016
     * Clears the current resource data from this object
1017
     * @return	boolean	True on success, false on failure
1018
     */
1019
    public function close()
1020
    {
1021
        if ($this->debug > 0) {
1022
            error_log('New LP - In learnpath::close()', 0);
1023
        }
1024
        if (empty ($this->lp_id)) {
1025
            $this->error = 'Trying to close this learnpath but no ID is set';
1026
            return false;
1027
        }
1028
        $this->current_time_stop = time();
1029
        $this->ordered_items = array();
1030
        $this->index = 0;
1031
        unset ($this->lp_id);
1032
        //unset other stuff
1033
        return true;
1034
    }
1035
1036
    /**
1037
     * Static admin function allowing removal of a learnpath
1038
     * @param	array $courseInfo
1039
     * @param	integer	Learnpath ID
1040
     * @param	string	Whether to delete data or keep it (default: 'keep', others: 'remove')
1041
     * @return	boolean	True on success, false on failure (might change that to return number of elements deleted)
1042
     */
1043
    public function delete($courseInfo = null, $id = null, $delete = 'keep')
1044
    {
1045
        $course_id = api_get_course_int_id();
1046
        if (!empty($courseInfo)) {
1047
            $course_id = isset($courseInfo['real_id']) ? $courseInfo['real_id'] : $course_id;
1048
        }
1049
1050
        // TODO: Implement a way of getting this to work when the current object is not set.
1051
        // In clear: implement this in the item class as well (abstract class) and use the given ID in queries.
1052
        // If an ID is specifically given and the current LP is not the same, prevent delete.
1053
        if (!empty ($id) && ($id != $this->lp_id)) {
1054
            return false;
1055
        }
1056
1057
        $lp = Database:: get_course_table(TABLE_LP_MAIN);
1058
        $lp_item = Database:: get_course_table(TABLE_LP_ITEM);
1059
        $lp_view = Database:: get_course_table(TABLE_LP_VIEW);
1060
        $lp_item_view = Database:: get_course_table(TABLE_LP_ITEM_VIEW);
1061
1062
        // Delete lp item id.
1063
        foreach ($this->items as $id => $dummy) {
1064
            $sql = "DELETE FROM $lp_item_view
1065
                    WHERE c_id = $course_id AND lp_item_id = '" . $id . "'";
1066
            Database::query($sql);
1067
        }
1068
1069
        // Proposed by Christophe (nickname: clefevre)
1070
        $sql = "DELETE FROM $lp_item WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
1071
        Database::query($sql);
1072
1073
        $sql = "DELETE FROM $lp_view WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
1074
        Database::query($sql);
1075
1076
        self::toggle_publish($this->lp_id, 'i');
1077
1078
        if ($this->type == 2 || $this->type == 3) {
1079
            // This is a scorm learning path, delete the files as well.
1080
            $sql = "SELECT path FROM $lp
1081
                    WHERE c_id = ".$course_id." AND id = " . $this->lp_id;
1082
            $res = Database::query($sql);
1083
            if (Database :: num_rows($res) > 0) {
1084
                $row = Database :: fetch_array($res);
1085
                $path = $row['path'];
1086
                $sql = "SELECT id FROM $lp
1087
                        WHERE c_id = ".$course_id." AND path = '$path' AND id != " . $this->lp_id;
1088
                $res = Database::query($sql);
1089
                if (Database :: num_rows($res) > 0) { // Another learning path uses this directory, so don't delete it.
1090
                    if ($this->debug > 2) {
1091
                        error_log('New LP - In learnpath::delete(), found other LP using path ' . $path . ', keeping directory', 0);
1092
                    }
1093
                } else {
1094
                    // No other LP uses that directory, delete it.
1095
                    $course_rel_dir = api_get_course_path() . '/scorm/'; // scorm dir web path starting from /courses
1096
                    $course_scorm_dir = api_get_path(SYS_COURSE_PATH) . $course_rel_dir; // The absolute system path for this course.
1097
                    if ($delete == 'remove' && is_dir($course_scorm_dir . $path) and !empty ($course_scorm_dir)) {
1098
                        if ($this->debug > 2) {
1099
                            error_log('New LP - In learnpath::delete(), found SCORM, deleting directory: ' . $course_scorm_dir . $path, 0);
1100
                        }
1101
                        // Proposed by Christophe (clefevre).
1102
                        if (strcmp(substr($path, -2), "/.") == 0) {
1103
                            $path = substr($path, 0, -1); // Remove "." at the end.
1104
                        }
1105
                        //exec('rm -rf ' . $course_scorm_dir . $path); // See Bug #5208, this is not OS-portable way.
1106
                        rmdirr($course_scorm_dir . $path);
1107
                    }
1108
                }
1109
            }
1110
        }
1111
1112
        $tbl_tool = Database :: get_course_table(TABLE_TOOL_LIST);
1113
        $link = 'newscorm/lp_controller.php?action=view&lp_id='.$this->lp_id;
1114
        // Delete tools
1115
        $sql = "DELETE FROM $tbl_tool
1116
                WHERE c_id = ".$course_id." AND (link LIKE '$link%' AND image='scormbuilder.gif')";
1117
        Database::query($sql);
1118
1119
        $sql = "DELETE FROM $lp WHERE c_id = ".$course_id." AND id = " . $this->lp_id;
1120
        Database::query($sql);
1121
        // Updates the display order of all lps.
1122
        $this->update_display_order();
1123
1124
        api_item_property_update(
1125
            api_get_course_info(),
1126
            TOOL_LEARNPATH,
1127
            $this->lp_id,
1128
            'delete',
1129
            api_get_user_id()
1130
        );
1131
1132
        $link_info = GradebookUtils::is_resource_in_course_gradebook(api_get_course_id(), 4 , $id, api_get_session_id());
1133
        if ($link_info !== false) {
1134
            GradebookUtils::remove_resource_from_course_gradebook($link_info['id']);
1135
        }
1136
1137
        if (api_get_setting('search_enabled') == 'true') {
1138
            require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1139
            delete_all_values_for_item($this->cc, TOOL_LEARNPATH, $this->lp_id);
1140
        }
1141
    }
1142
1143
    /**
1144
     * Removes all the children of one item - dangerous!
1145
     * @param	integer	$id Element ID of which children have to be removed
1146
     * @return	integer	Total number of children removed
1147
     */
1148
    public function delete_children_items($id)
1149
    {
1150
        $course_id = $this->course_info['real_id'];
1151
        if ($this->debug > 0) {
1152
            error_log('New LP - In learnpath::delete_children_items(' . $id . ')', 0);
1153
        }
1154
        $num = 0;
1155
        if (empty ($id) || $id != strval(intval($id))) {
1156
            return false;
1157
        }
1158
        $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
1159
        $sql = "SELECT * FROM $lp_item WHERE c_id = ".$course_id." AND parent_item_id = $id";
1160
        $res = Database::query($sql);
1161
        while ($row = Database :: fetch_array($res)) {
1162
            $num += $this->delete_children_items($row['id']);
1163
            $sql_del = "DELETE FROM $lp_item WHERE c_id = ".$course_id." AND id = " . $row['id'];
1164
            Database::query($sql_del);
1165
            $num++;
1166
        }
1167
        return $num;
1168
    }
1169
1170
    /**
1171
     * Removes an item from the current learnpath
1172
     * @param	integer	$id Elem ID (0 if first)
1173
     * @param	integer	$remove Whether to remove the resource/data from the
1174
     * system or leave it (default: 'keep', others 'remove')
1175
     * @return	integer	Number of elements moved
1176
     * @todo implement resource removal
1177
     */
1178
    public function delete_item($id, $remove = 'keep')
1179
    {
1180
        $course_id = api_get_course_int_id();
1181
        if ($this->debug > 0) {
1182
            error_log('New LP - In learnpath::delete_item()', 0);
1183
        }
1184
        // TODO: Implement the resource removal.
1185
        if (empty ($id) || $id != strval(intval($id))) {
1186
            return false;
1187
        }
1188
        // First select item to get previous, next, and display order.
1189
        $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
1190
        $sql_sel = "SELECT * FROM $lp_item WHERE c_id = ".$course_id." AND id = $id";
1191
        $res_sel = Database::query($sql_sel);
1192
        if (Database :: num_rows($res_sel) < 1) {
1193
            return false;
1194
        }
1195
        $row = Database :: fetch_array($res_sel);
1196
        $previous = $row['previous_item_id'];
1197
        $next = $row['next_item_id'];
1198
        $display = $row['display_order'];
1199
        $parent = $row['parent_item_id'];
1200
        $lp = $row['lp_id'];
1201
        // Delete children items.
1202
        $num = $this->delete_children_items($id);
1203
        if ($this->debug > 2) {
1204
            error_log('New LP - learnpath::delete_item() - deleted ' . $num . ' children of element ' . $id, 0);
1205
        }
1206
        // Now delete the item.
1207
        $sql_del = "DELETE FROM $lp_item WHERE c_id = $course_id AND id = $id";
1208
        if ($this->debug > 2) {
1209
            error_log('New LP - Deleting item: ' . $sql_del, 0);
1210
        }
1211
        Database::query($sql_del);
1212
        // Now update surrounding items.
1213
        $sql_upd = "UPDATE $lp_item SET next_item_id = $next
1214
                    WHERE c_id = ".$course_id." AND id = $previous";
1215
        Database::query($sql_upd);
1216
        $sql_upd = "UPDATE $lp_item SET previous_item_id = $previous
1217
                    WHERE c_id = ".$course_id." AND id = $next";
1218
        Database::query($sql_upd);
1219
        // Now update all following items with new display order.
1220
        $sql_all = "UPDATE $lp_item SET display_order = display_order-1
1221
                    WHERE c_id = ".$course_id." AND lp_id = $lp AND parent_item_id = $parent AND display_order > $display";
1222
        Database::query($sql_all);
1223
1224
        //Removing prerequisites since the item will not longer exist
1225
        $sql_all = "UPDATE $lp_item SET prerequisite = '' WHERE c_id = ".$course_id." AND prerequisite = $id";
1226
        Database::query($sql_all);
1227
1228
        // Remove from search engine if enabled.
1229
        if (api_get_setting('search_enabled') == 'true') {
1230
            $tbl_se_ref = Database :: get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
1231
            $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';
1232
            $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp, $id);
1233
            $res = Database::query($sql);
1234
            if (Database :: num_rows($res) > 0) {
1235
                $row2 = Database :: fetch_array($res);
1236
                require_once api_get_path(LIBRARY_PATH).'search/ChamiloIndexer.class.php';
1237
                $di = new ChamiloIndexer();
1238
                $di->remove_document((int) $row2['search_did']);
1239
            }
1240
            $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';
1241
            $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp, $id);
1242
            Database::query($sql);
1243
        }
1244
    }
1245
1246
    /**
1247
     * Updates an item's content in place
1248
     * @param   integer $id Element ID
1249
     * @param   integer $parent Parent item ID
1250
     * @param   integer $previous Previous item ID
1251
     * @param   string  $title Item title
1252
     * @param   string  $description Item description
1253
     * @param   string  $prerequisites Prerequisites (optional)
1254
     * @param   array   $audio The array resulting of the $_FILES[mp3] element
1255
     * @param   int     $max_time_allowed
1256
     * @param   string  $url
1257
     * @return  boolean True on success, false on error
1258
     */
1259
    public function edit_item(
1260
        $id,
1261
        $parent,
1262
        $previous,
1263
        $title,
1264
        $description,
1265
        $prerequisites = '0',
1266
        $audio = array(),
1267
        $max_time_allowed = 0,
1268
        $url = ''
1269
    ) {
1270
        $course_id = api_get_course_int_id();
1271
        $_course = api_get_course_info();
1272
1273
        if ($this->debug > 0) {
1274
            error_log('New LP - In learnpath::edit_item()', 0);
1275
        }
1276
        if (empty ($max_time_allowed)) {
1277
            $max_time_allowed = 0;
1278
        }
1279
        if (empty ($id) || ($id != strval(intval($id))) || empty ($title)) {
1280
            return false;
1281
        }
1282
1283
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
1284
        $sql_select = "SELECT * FROM " . $tbl_lp_item . " WHERE c_id = ".$course_id." AND id = " . $id;
1285
        $res_select = Database::query($sql_select);
1286
        $row_select = Database :: fetch_array($res_select);
1287
        $audio_update_sql = '';
1288
        if (is_array($audio) && !empty ($audio['tmp_name']) && $audio['error'] === 0) {
1289
            // Create the audio folder if it does not exist yet.
1290
            $filepath = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document/';
1291 View Code Duplication
            if (!is_dir($filepath . 'audio')) {
1292
                mkdir($filepath . 'audio', api_get_permissions_for_new_directories());
1293
                $audio_id = add_document(
1294
                    $_course,
1295
                    '/audio',
1296
                    'folder',
1297
                    0,
1298
                    'audio'
1299
                );
1300
                api_item_property_update(
1301
                    $_course,
1302
                    TOOL_DOCUMENT,
1303
                    $audio_id,
1304
                    'FolderCreated',
1305
                    api_get_user_id(),
1306
                    null,
1307
                    null,
1308
                    null,
1309
                    null,
1310
                    api_get_session_id()
1311
                );
1312
                api_item_property_update(
1313
                    $_course,
1314
                    TOOL_DOCUMENT,
1315
                    $audio_id,
1316
                    'invisible',
1317
                    api_get_user_id(),
1318
                    null,
1319
                    null,
1320
                    null,
1321
                    null,
1322
                    api_get_session_id()
1323
                );
1324
            }
1325
1326
            // Upload file in documents.
1327
            $pi = pathinfo($audio['name']);
1328
            if ($pi['extension'] == 'mp3') {
1329
                $c_det = api_get_course_info($this->cc);
1330
                $bp = api_get_path(SYS_COURSE_PATH) . $c_det['path'] . '/document';
1331
                $path = handle_uploaded_document($c_det, $audio, $bp, '/audio', api_get_user_id(), 0, null, 0, 'rename', false, 0);
1332
                $path = substr($path, 7);
1333
                // Update reference in lp_item - audio path is the path from inside de document/audio/ dir.
1334
                $audio_update_sql = ", audio = '" . Database::escape_string($path) . "' ";
1335
            }
1336
        }
1337
1338
        $same_parent = ($row_select['parent_item_id'] == $parent) ? true : false;
1339
        $same_previous = ($row_select['previous_item_id'] == $previous) ? true : false;
1340
1341
        // TODO: htmlspecialchars to be checked for encoding related problems.
1342
        if ($same_parent && $same_previous) {
1343
            // Only update title and description.
1344
            $sql = "UPDATE " . $tbl_lp_item . "
1345
                    SET title = '" . Database::escape_string($title) . "',
1346
                        prerequisite = '" . $prerequisites . "',
1347
                        description = '" . Database::escape_string($description) . "'
1348
                        " . $audio_update_sql . ",
1349
                        max_time_allowed = '" . Database::escape_string($max_time_allowed) . "'
1350
                    WHERE c_id = ".$course_id." AND id = " . $id;
1351
            Database::query($sql);
1352
        } else {
1353
            $old_parent = $row_select['parent_item_id'];
1354
            $old_previous = $row_select['previous_item_id'];
1355
            $old_next = $row_select['next_item_id'];
1356
            $old_order = $row_select['display_order'];
1357
            $old_prerequisite = $row_select['prerequisite'];
1358
            $old_max_time_allowed = $row_select['max_time_allowed'];
1359
1360
            /* BEGIN -- virtually remove the current item id */
1361
            /* for the next and previous item it is like the current item doesn't exist anymore */
1362
1363
            if ($old_previous != 0) {
1364
                // Next
1365
                $sql = "UPDATE " . $tbl_lp_item . "
1366
                        SET next_item_id = " . $old_next . "
1367
                        WHERE c_id = ".$course_id." AND id = " . $old_previous;
1368
                Database::query($sql);
1369
            }
1370
1371
            if ($old_next != 0) {
1372
                // Previous
1373
                $sql = "UPDATE " . $tbl_lp_item . "
1374
                        SET previous_item_id = " . $old_previous . "
1375
                        WHERE c_id = ".$course_id." AND id = " . $old_next;
1376
                Database::query($sql);
1377
            }
1378
1379
            // display_order - 1 for every item with a display_order bigger then the display_order of the current item.
1380
            $sql = "UPDATE " . $tbl_lp_item . "
1381
                    SET display_order = display_order - 1
1382
                    WHERE
1383
                        c_id = ".$course_id." AND
1384
                        display_order > " . $old_order . " AND
1385
                        lp_id = " . $this->lp_id . " AND
1386
                        parent_item_id = " . $old_parent;
1387
            Database::query($sql);
1388
            /* END -- virtually remove the current item id */
1389
1390
            /* BEGIN -- update the current item id to his new location */
1391
1392
            if ($previous == 0) {
1393
                // Select the data of the item that should come after the current item.
1394
                $sql = "SELECT id, display_order
1395
                        FROM " . $tbl_lp_item . "
1396
                        WHERE
1397
                            c_id = ".$course_id." AND
1398
                            lp_id = " . $this->lp_id . " AND
1399
                            parent_item_id = " . $parent . " AND
1400
                            previous_item_id = " . $previous;
1401
                $res_select_old = Database::query($sql);
1402
                $row_select_old = Database::fetch_array($res_select_old);
1403
1404
                // If the new parent didn't have children before.
1405
                if (Database :: num_rows($res_select_old) == 0) {
1406
                    $new_next = 0;
1407
                    $new_order = 1;
1408
                } else {
1409
                    $new_next = $row_select_old['id'];
1410
                    $new_order = $row_select_old['display_order'];
1411
                }
1412
            } else {
1413
                // Select the data of the item that should come before the current item.
1414
                $sql = "SELECT next_item_id, display_order
1415
                        FROM " . $tbl_lp_item . "
1416
                        WHERE c_id = ".$course_id." AND id = " . $previous;
1417
                $res_select_old = Database::query($sql);
1418
                $row_select_old = Database :: fetch_array($res_select_old);
1419
                $new_next = $row_select_old['next_item_id'];
1420
                $new_order = $row_select_old['display_order'] + 1;
1421
            }
1422
1423
            // TODO: htmlspecialchars to be checked for encoding related problems.
1424
            // Update the current item with the new data.
1425
            $sql = "UPDATE " . $tbl_lp_item . "
1426
                    SET
1427
                        title = '" . Database::escape_string($title) . "',
1428
                        description = '" . Database::escape_string($description) . "',
1429
                        parent_item_id = " . $parent . ",
1430
                        previous_item_id = " . $previous . ",
1431
                        next_item_id = " . $new_next . ",
1432
                        display_order = " . $new_order . "
1433
                        " . $audio_update_sql . "
1434
                    WHERE c_id = ".$course_id." AND id = " . $id;
1435
            Database::query($sql);
1436
1437
            if ($previous != 0) {
1438
                // Update the previous item's next_item_id.
1439
                $sql = "UPDATE " . $tbl_lp_item . "
1440
                        SET next_item_id = " . $id . "
1441
                        WHERE c_id = ".$course_id." AND id = " . $previous;
1442
                Database::query($sql);
1443
            }
1444
1445
            if ($new_next != 0) {
1446
                // Update the next item's previous_item_id.
1447
                $sql = "UPDATE " . $tbl_lp_item . "
1448
                        SET previous_item_id = " . $id . "
1449
                        WHERE c_id = ".$course_id." AND id = " . $new_next;
1450
                Database::query($sql);
1451
            }
1452
1453
            if ($old_prerequisite != $prerequisites) {
1454
                $sql = "UPDATE " . $tbl_lp_item . "
1455
                        SET prerequisite = '" . $prerequisites . "'
1456
                        WHERE c_id = ".$course_id." AND id = " . $id;
1457
                Database::query($sql);
1458
            }
1459
1460
            if ($old_max_time_allowed != $max_time_allowed) {
1461
                // update max time allowed
1462
                $sql = "UPDATE " . $tbl_lp_item . "
1463
                        SET max_time_allowed = " . $max_time_allowed . "
1464
                        WHERE c_id = ".$course_id." AND id = " . $id;
1465
                Database::query($sql);
1466
            }
1467
1468
            // Update all the items with the same or a bigger display_order than the current item.
1469
            $sql = "UPDATE " . $tbl_lp_item . "
1470
                    SET display_order = display_order + 1
1471
                    WHERE
1472
                       c_id = ".$course_id." AND
1473
                       lp_id = " . $this->get_id() . " AND
1474
                       id <> " . $id . " AND
1475
                       parent_item_id = " . $parent . " AND
1476
                       display_order >= " . $new_order;
1477
1478
            Database::query($sql);
1479
        }
1480
1481
        if ($row_select['item_type'] == 'link') {
1482
            $link = new Link();
1483
            $linkId = $row_select['path'];
1484
            $link->updateLink($linkId, $url);
1485
        }
1486
    }
1487
1488
    /**
1489
     * Updates an item's prereq in place
1490
     * @param	integer	$id Element ID
1491
     * @param	string	$prerequisite_id Prerequisite Element ID
1492
     * @param	int 	$mastery_score Prerequisite min score
1493
     * @param	int 	$max_score Prerequisite max score
1494
     *
1495
     * @return	boolean	True on success, false on error
1496
     */
1497
    public function edit_item_prereq($id, $prerequisite_id, $mastery_score = 0, $max_score = 100)
1498
    {
1499
        $course_id = api_get_course_int_id();
1500
        if ($this->debug > 0) {
1501
            error_log('New LP - In learnpath::edit_item_prereq(' . $id . ',' . $prerequisite_id . ',' . $mastery_score . ',' . $max_score . ')', 0);
1502
        }
1503
1504
        if (empty($id) || ($id != strval(intval($id))) || empty ($prerequisite_id)) {
1505
            return false;
1506
        }
1507
1508
        $prerequisite_id = intval($prerequisite_id);
1509
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
1510
1511
        if (!is_numeric($mastery_score) || $mastery_score < 0) {
1512
            $mastery_score = 0;
1513
        }
1514
1515
        if (!is_numeric($max_score) || $max_score < 0) {
1516
            $max_score = 100;
1517
        }
1518
1519
        /*if ($mastery_score > $max_score) {
1520
            $max_score = $mastery_score;
1521
        }*/
1522
1523
        if (!is_numeric($prerequisite_id)) {
1524
            $prerequisite_id = 'NULL';
1525
        }
1526
1527
        $mastery_score = floatval($mastery_score);
1528
        $max_score = floatval($max_score);
1529
1530
        $sql = " UPDATE $tbl_lp_item
1531
                 SET
1532
                    prerequisite = $prerequisite_id ,
1533
                    prerequisite_min_score = $mastery_score ,
1534
                    prerequisite_max_score = $max_score
1535
                 WHERE c_id = $course_id AND id = $id";
1536
        Database::query($sql);
1537
1538
        if ($prerequisite_id != 'NULL' && $prerequisite_id != '') {
1539
            // Will this be enough to ensure unicity?
1540
            /*$sql = " UPDATE $tbl_lp_item
1541
                     SET mastery_score = $mastery_score
1542
                     WHERE c_id = $course_id AND ref = '$prerequisite_id'";
1543
1544
            Database::query($sql);*/
1545
        }
1546
        // TODO: Update the item object (can be ignored for now because refreshed).
1547
        return true;
1548
    }
1549
1550
    /**
1551
     * Escapes a string with the available database escape function
1552
     * @param	string	String to escape
1553
     * @return	string	String escaped
1554
     * @deprecated use  Database::escape_string
1555
     */
1556
    public function escape_string($string)
1557
    {
1558
        return Database::escape_string($string);
1559
    }
1560
1561
    /**
1562
     * Static admin function exporting a learnpath into a zip file
1563
     * @param	string	Export type (scorm, zip, cd)
1564
     * @param	string	Course code
1565
     * @param	integer Learnpath ID
1566
     * @param	string	Zip file name
1567
     * @return	string	Zip file path (or false on error)
1568
     */
1569
    public function export_lp($type, $course, $id, $zipname)
1570
    {
1571
        if (empty($type) || empty($course) || empty($id) || empty($zipname)) {
1572
            return false;
1573
        }
1574
        $url = '';
1575
        switch ($type) {
1576
            case 'scorm':
1577
                break;
1578
            case 'zip':
1579
                break;
1580
            case 'cdrom':
1581
                break;
1582
        }
1583
        return $url;
1584
    }
1585
1586
    /**
1587
     * Gets all the chapters belonging to the same parent as the item/chapter given
1588
     * Can also be called as abstract method
1589
     * @param	integer	Item ID
1590
     * @return	array	A list of all the "brother items" (or an empty array on failure)
1591
     */
1592 View Code Duplication
    public function get_brother_chapters($id)
1593
    {
1594
        $course_id = api_get_course_int_id();
1595
        if ($this->debug > 0) {
1596
            error_log('New LP - In learnpath::get_brother_chapters()', 0);
1597
        }
1598
1599
        if (empty($id) || $id != strval(intval($id))) {
1600
1601
            return array ();
1602
        }
1603
1604
        $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
1605
        $sql_parent = "SELECT * FROM $lp_item
1606
                       WHERE c_id = ".$course_id." AND id = $id AND item_type='dokeos_chapter'";
1607
        $res_parent = Database::query($sql_parent);
1608
        if (Database :: num_rows($res_parent) > 0) {
1609
            $row_parent = Database :: fetch_array($res_parent);
1610
            $parent = $row_parent['parent_item_id'];
1611
            $sql = "SELECT * FROM $lp_item
1612
                    WHERE
1613
                        c_id = ".$course_id." AND
1614
                        parent_item_id = $parent AND
1615
                        id = $id AND
1616
                        item_type='dokeos_chapter'
1617
                    ORDER BY display_order";
1618
            $res_bros = Database::query($sql);
1619
1620
            $list = array();
1621
            while ($row_bro = Database :: fetch_array($res_bros)) {
1622
                $list[] = $row_bro;
1623
            }
1624
1625
            return $list;
1626
        }
1627
1628
        return array();
1629
    }
1630
1631
    /**
1632
     * Gets all the items belonging to the same parent as the item given
1633
     * Can also be called as abstract method
1634
     * @param	integer	Item ID
1635
     * @return	array	A list of all the "brother items" (or an empty array on failure)
1636
     */
1637 View Code Duplication
    public function get_brother_items($id)
1638
    {
1639
        $course_id = api_get_course_int_id();
1640
        if ($this->debug > 0) {
1641
            error_log('New LP - In learnpath::get_brother_items(' . $id . ')', 0);
1642
        }
1643
1644
        if (empty ($id) || $id != strval(intval($id))) {
1645
            return array ();
1646
        }
1647
1648
        $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
1649
        $sql_parent = "SELECT * FROM $lp_item WHERE c_id = $course_id AND id = $id";
1650
        $res_parent = Database::query($sql_parent);
1651
        if (Database :: num_rows($res_parent) > 0) {
1652
            $row_parent = Database :: fetch_array($res_parent);
1653
            $parent = $row_parent['parent_item_id'];
1654
            $sql_bros = "SELECT * FROM $lp_item WHERE c_id = ".$course_id." AND parent_item_id = $parent
1655
                         ORDER BY display_order";
1656
            $res_bros = Database::query($sql_bros);
1657
            $list = array ();
1658
            while ($row_bro = Database :: fetch_array($res_bros)) {
1659
                $list[] = $row_bro;
1660
            }
1661
            return $list;
1662
        }
1663
        return array ();
1664
    }
1665
1666
    /**
1667
     * Get the specific prefix index terms of this learning path
1668
     * @param string $prefix
1669
     * @return  array Array of terms
1670
     */
1671
    public function get_common_index_terms_by_prefix($prefix)
1672
    {
1673
        require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1674
        $terms = get_specific_field_values_list_by_prefix(
1675
            $prefix,
1676
            $this->cc,
1677
            TOOL_LEARNPATH,
1678
            $this->lp_id
1679
        );
1680
        $prefix_terms = array();
1681
        if (!empty($terms)) {
1682
            foreach ($terms as $term) {
1683
                $prefix_terms[] = $term['value'];
1684
            }
1685
        }
1686
        return $prefix_terms;
1687
    }
1688
1689
    /**
1690
     * Gets the number of items currently completed
1691
     * @param bool $failedStatusException flag to determine the failed status is not considered progressed
1692
     * @return integer The number of items currently completed
1693
     */
1694
    public function get_complete_items_count($failedStatusException = false)
1695
    {
1696
        if ($this->debug > 0) {
1697
            error_log('New LP - In learnpath::get_complete_items_count()', 0);
1698
        }
1699
        $i = 0;
1700
        $completedStatusList = array(
1701
            'completed',
1702
            'passed',
1703
            'succeeded',
1704
            'browsed'
1705
        );
1706
1707
        if (!$failedStatusException) {
1708
            $completedStatusList[] = 'failed';
1709
        }
1710
1711
        foreach ($this->items as $id => $dummy) {
1712
            // Trying failed and browsed considered "progressed" as well.
1713
            if ($this->items[$id]->status_is($completedStatusList) &&
1714
                $this->items[$id]->get_type() != 'dokeos_chapter' &&
1715
                $this->items[$id]->get_type() != 'dir'
1716
            ) {
1717
                $i++;
1718
            }
1719
        }
1720
        return $i;
1721
    }
1722
1723
    /**
1724
     * Gets the current item ID
1725
     * @return	integer	The current learnpath item id
1726
     */
1727
    public function get_current_item_id()
1728
    {
1729
        $current = 0;
1730
        if ($this->debug > 0) {
1731
            error_log('New LP - In learnpath::get_current_item_id()', 0);
1732
        }
1733
        if (!empty($this->current)) {
1734
            $current = $this->current;
1735
        }
1736
        if ($this->debug > 2) {
1737
            error_log('New LP - In learnpath::get_current_item_id() - Returning ' . $current, 0);
1738
        }
1739
        return $current;
1740
    }
1741
1742
    /**
1743
     * Force to get the first learnpath item id
1744
     * @return	integer	The current learnpath item id
1745
     */
1746
    public function get_first_item_id()
1747
    {
1748
        $current = 0;
1749
        if (is_array($this->ordered_items)) {
1750
            $current = $this->ordered_items[0];
1751
        }
1752
        return $current;
1753
    }
1754
1755
    /**
1756
     * Gets the total number of items available for viewing in this SCORM
1757
     * @return	integer	The total number of items
1758
     */
1759
    public function get_total_items_count()
1760
    {
1761
        if ($this->debug > 0) {
1762
            error_log('New LP - In learnpath::get_total_items_count()', 0);
1763
        }
1764
        return count($this->items);
1765
    }
1766
1767
    /**
1768
     * Gets the total number of items available for viewing in this SCORM but without chapters
1769
     * @return	integer	The total no-chapters number of items
1770
     */
1771
    public function get_total_items_count_without_chapters()
1772
    {
1773
        if ($this->debug > 0) {
1774
            error_log('New LP - In learnpath::get_total_items_count_without_chapters()', 0);
1775
        }
1776
        $total = 0;
1777
        $typeListNotToCount = self::getChapterTypes();
1778
        foreach ($this->items as $temp2) {
1779
            if (!in_array($temp2->get_type(), $typeListNotToCount)) {
1780
                $total++;
1781
            }
1782
        }
1783
        return $total;
1784
    }
1785
1786
    /**
1787
     * Gets the first element URL.
1788
     * @return	string	URL to load into the viewer
1789
     */
1790
    public function first()
1791
    {
1792
        if ($this->debug > 0) {
1793
            error_log('New LP - In learnpath::first()', 0);
1794
            error_log('$this->last_item_seen '.$this->last_item_seen);
1795
        }
1796
1797
        // Test if the last_item_seen exists and is not a dir.
1798
        if (count($this->ordered_items) == 0) {
1799
            $this->index = 0;
1800
        }
1801
1802
        if ($this->debug > 0) {
1803
            if (isset($this->items[$this->last_item_seen])) {
1804
                $status = $this->items[$this->last_item_seen]->get_status();
1805
            }
1806
            error_log('status '.$status);
1807
        }
1808
1809
        if (!empty($this->last_item_seen) &&
1810
            !empty($this->items[$this->last_item_seen]) &&
1811
            $this->items[$this->last_item_seen]->get_type() != 'dir' &&
1812
            $this->items[$this->last_item_seen]->get_type() != 'dokeos_chapter'
1813
            //with this change (below) the LP will NOT go to the next item, it will take lp item we left
1814
            //&& !$this->items[$this->last_item_seen]->is_done()
1815
        ) {
1816
1817
            if ($this->debug > 2) {
1818
                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);
1819
            }
1820
            $index = -1;
1821
            foreach ($this->ordered_items as $myindex => $item_id) {
1822
                if ($item_id == $this->last_item_seen) {
1823
                    $index = $myindex;
1824
                    break;
1825
                }
1826
            }
1827
            if ($index == -1) {
1828
                // Index hasn't changed, so item not found - panic (this shouldn't happen).
1829
                if ($this->debug > 2) {
1830
                    error_log('New LP - Last item (' . $this->last_item_seen . ') was found in items but not in ordered_items, panic!', 0);
1831
                }
1832
                return false;
1833
            } else {
1834
                $this->last     = $this->last_item_seen;
1835
                $this->current  = $this->last_item_seen;
1836
                $this->index    = $index;
1837
            }
1838
        } else {
1839
            if ($this->debug > 2) {
1840
                error_log('New LP - In learnpath::first() - No last item seen', 0);
1841
            }
1842
            $index = 0;
1843
            // Loop through all ordered items and stop at the first item that is
1844
            // not a directory *and* that has not been completed yet.
1845
            while ( !empty($this->ordered_items[$index]) AND
1846
                is_a($this->items[$this->ordered_items[$index]], 'learnpathItem') AND
1847
                (
1848
                    $this->items[$this->ordered_items[$index]]->get_type() == 'dir' OR
1849
                    $this->items[$this->ordered_items[$index]]->get_type() == 'dokeos_chapter' OR
1850
                    $this->items[$this->ordered_items[$index]]->is_done() === true
1851
                ) AND $index < $this->max_ordered_items) {
1852
                $index++;
1853
            }
1854
            $this->last     = $this->current;
1855
            // current is
1856
            $this->current  = isset($this->ordered_items[$index]) ? $this->ordered_items[$index] : null;
1857
            $this->index    = $index;
1858
            if ($this->debug > 2) {
1859
                error_log('$index ' . $index);
1860
            }
1861 View Code Duplication
            if ($this->debug > 2) {
1862
                error_log('New LP - In learnpath::first() - No last item seen. New last = ' . $this->last . '(' . $this->ordered_items[$index] . ')', 0);
1863
            }
1864
        }
1865
        if ($this->debug > 2) {
1866
            error_log('New LP - In learnpath::first() - First item is ' . $this->get_current_item_id());
1867
        }
1868
    }
1869
1870
    /**
1871
     * Gets the information about an item in a format usable as JavaScript to update
1872
     * the JS API by just printing this content into the <head> section of the message frame
1873
     * @param	int $item_id
1874
     * @return	string
1875
     */
1876
    public function get_js_info($item_id = '')
1877
    {
1878
        if ($this->debug > 0) {
1879
            error_log('New LP - In learnpath::get_js_info(' . $item_id . ')', 0);
1880
        }
1881
1882
        $info = '';
1883
        $item_id = intval($item_id);
1884
1885
        if (!empty($item_id) && is_object($this->items[$item_id])) {
1886
            //if item is defined, return values from DB
1887
            $oItem = $this->items[$item_id];
1888
            $info .= '<script language="javascript">';
1889
            $info .= "top.set_score(" . $oItem->get_score() . ");\n";
1890
            $info .= "top.set_max(" . $oItem->get_max() . ");\n";
1891
            $info .= "top.set_min(" . $oItem->get_min() . ");\n";
1892
            $info .= "top.set_lesson_status('" . $oItem->get_status() . "');";
1893
            $info .= "top.set_session_time('" . $oItem->get_scorm_time('js') . "');";
1894
            $info .= "top.set_suspend_data('" . $oItem->get_suspend_data() . "');";
1895
            $info .= "top.set_saved_lesson_status('" . $oItem->get_status() . "');";
1896
            $info .= "top.set_flag_synchronized();";
1897
            $info .= '</script>';
1898
            if ($this->debug > 2) {
1899
                error_log('New LP - in learnpath::get_js_info(' . $item_id . ') - returning: ' . $info, 0);
1900
            }
1901
            return $info;
1902
1903
        } else {
1904
1905
            // If item_id is empty, just update to default SCORM data.
1906
            $info .= '<script language="javascript">';
1907
            $info .= "top.set_score(" . learnpathItem :: get_score() . ");\n";
1908
            $info .= "top.set_max(" . learnpathItem :: get_max() . ");\n";
1909
            $info .= "top.set_min(" . learnpathItem :: get_min() . ");\n";
1910
            $info .= "top.set_lesson_status('" . learnpathItem :: get_status() . "');";
1911
            $info .= "top.set_session_time('" . learnpathItem :: getScormTimeFromParameter('js') . "');";
1912
            $info .= "top.set_suspend_data('" . learnpathItem :: get_suspend_data() . "');";
1913
            $info .= "top.set_saved_lesson_status('" . learnpathItem :: get_status() . "');";
1914
            $info .= "top.set_flag_synchronized();";
1915
            $info .= '</script>';
1916
            if ($this->debug > 2) {
1917
                error_log('New LP - in learnpath::get_js_info(' . $item_id . ') - returning: ' . $info, 0);
1918
            }
1919
            return $info;
1920
        }
1921
    }
1922
1923
    /**
1924
     * Gets the js library from the database
1925
     * @return	string	The name of the javascript library to be used
1926
     */
1927
    public function get_js_lib()
1928
    {
1929
        $lib = '';
1930
        if (!empty ($this->js_lib)) {
1931
            $lib = $this->js_lib;
1932
        }
1933
        return $lib;
1934
    }
1935
1936
    /**
1937
     * Gets the learnpath database ID
1938
     * @return	integer	Learnpath ID in the lp table
1939
     */
1940
    public function get_id()
1941
    {
1942
        if (!empty ($this->lp_id)) {
1943
            return $this->lp_id;
1944
        } else {
1945
            return 0;
1946
        }
1947
    }
1948
1949
    /**
1950
     * Gets the last element URL.
1951
     * @return string URL to load into the viewer
1952
     */
1953
    public function get_last()
1954
    {
1955
        if ($this->debug > 0) {
1956
            error_log('New LP - In learnpath::get_last()', 0);
1957
        }
1958
        //This is just in case the lesson doesn't cointain a valid scheme, just to avoid "Notices"
1959
        if (count($this->ordered_items) > 0) {
1960
            $this->index = count($this->ordered_items) - 1;
1961
            return $this->ordered_items[$this->index];
1962
        }
1963
1964
        return false;
1965
    }
1966
1967
    /**
1968
     * Gets the navigation bar for the learnpath display screen
1969
     * @return	string	The HTML string to use as a navigation bar
1970
     */
1971
    public function get_navigation_bar($idBar = null, $display = null) {
1972
        if ($this->debug > 0) {
1973
            error_log('New LP - In learnpath::get_navigation_bar()', 0);
1974
        }
1975
        if(empty($idBar)){
1976
            $idBar='control-top';
1977
        }
1978
        /* if(empty($display)){
1979
            $display='display:block';
1980
        } */
1981
        $navbar = null;
1982
        $lp_id = $this->lp_id;
1983
        $mycurrentitemid = $this->get_current_item_id();
1984
1985
        if ($this->mode == 'fullscreen') {
1986
            $navbar = '
1987
                  <span id="'.$idBar.'" class="buttons">
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="stats" id="stats_link">
1989
                        <span class="fa fa-info"></span><span class="sr-only">' . get_lang('Reporting') . '</span>
1990
                    </a>
1991
                    <a class="icon-toolbar" id="scorm-previous" href="#" onclick="switch_item(' . $mycurrentitemid . ',\'previous\');return false;" title="previous">
1992
                        <span class="fa fa-chevron-left"></span><span class="sr-only">' . get_lang('ScormPrevious') . '</span>
1993
                    </a>
1994
                    <a class="icon-toolbar" id="scorm-next" href="#" onclick="switch_item(' . $mycurrentitemid . ',\'next\');return false;" title="next">
1995
                        <span class="fa fa-chevron-right"></span><span class="sr-only">' . get_lang('ScormNext') . '</span>
1996
                    </a>
1997
                    <a class="icon-toolbar" id="view-embedded" href="lp_controller.php?action=mode&mode=embedded" target="_top" title="embedded mode">
1998
                        <span class="fa fa-columns"></span><span class="sr-only">' . get_lang('ScormExitFullScreen') . '</span>
1999
                    </a>
2000
                  </span>';
2001
2002
        } else {
2003
            $navbar = '
2004
                <span id="'.$idBar.'" class="buttons text-right">
2005
                    <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="stats" id="stats_link">
2006
                        <span class="fa fa-info"></span><span class="sr-only">' . get_lang('Reporting') . '</span>
2007
                    </a>
2008
                    <a class="icon-toolbar" id="scorm-previous" href="#" onclick="switch_item(' . $mycurrentitemid . ',\'previous\');return false;" title="previous">
2009
                        <span class="fa fa-chevron-left"></span><span class="sr-only">' . get_lang('ScormPrevious') . '</span>
2010
                    </a>
2011
                    <a class="icon-toolbar" id="scorm-next" href="#" onclick="switch_item(' . $mycurrentitemid . ',\'next\');return false;" title="next">
2012
                        <span class="fa fa-chevron-right"></span><span class="sr-only">' . get_lang('ScormNext') . '</span>
2013
                    </a>
2014
                </span>';
2015
        }
2016
2017
        return $navbar;
2018
    }
2019
2020
    /**
2021
     * Gets the next resource in queue (url).
2022
     * @return	string	URL to load into the viewer
2023
     */
2024
    public function get_next_index()
2025
    {
2026
        if ($this->debug > 0) {
2027
            error_log('New LP - In learnpath::get_next_index()', 0);
2028
        }
2029
        // TODO
2030
        $index = $this->index;
2031
        $index++;
2032 View Code Duplication
        if ($this->debug > 2) {
2033
            error_log('New LP - Now looking at ordered_items[' . ($index) . '] - type is ' . $this->items[$this->ordered_items[$index]]->type, 0);
2034
        }
2035
        while (!empty ($this->ordered_items[$index]) AND ($this->items[$this->ordered_items[$index]]->get_type() == 'dir' || $this->items[$this->ordered_items[$index]]->get_type() == 'dokeos_chapter') AND $index < $this->max_ordered_items) {
2036
            $index++;
2037
            if ($index == $this->max_ordered_items){
2038
                if ($this->items[$this->ordered_items[$index]]->get_type() == 'dir' || $this->items[$this->ordered_items[$index]]->get_type() == 'dokeos_chapter') {
2039
                    return $this->index;
2040
                } else {
2041
                    return $index;
2042
                }
2043
            }
2044
        }
2045
        if (empty ($this->ordered_items[$index])) {
2046
            return $this->index;
2047
        }
2048
        if ($this->debug > 2) {
2049
            error_log('New LP - index is now ' . $index, 0);
2050
        }
2051
        return $index;
2052
    }
2053
2054
    /**
2055
     * Gets item_id for the next element
2056
     * @return	integer	Next item (DB) ID
2057
     */
2058
    public function get_next_item_id()
2059
    {
2060
        if ($this->debug > 0) {
2061
            error_log('New LP - In learnpath::get_next_item_id()', 0);
2062
        }
2063
        $new_index = $this->get_next_index();
2064
        if (!empty ($new_index)) {
2065
            if (isset ($this->ordered_items[$new_index])) {
2066
                if ($this->debug > 2) {
2067
                    error_log('New LP - In learnpath::get_next_index() - Returning ' . $this->ordered_items[$new_index], 0);
2068
                }
2069
                return $this->ordered_items[$new_index];
2070
            }
2071
        }
2072
        if ($this->debug > 2) {
2073
            error_log('New LP - In learnpath::get_next_index() - Problem - Returning 0', 0);
2074
        }
2075
        return 0;
2076
    }
2077
2078
    /**
2079
     * Returns the package type ('scorm','aicc','scorm2004','dokeos','ppt'...)
2080
     *
2081
     * Generally, the package provided is in the form of a zip file, so the function
2082
     * has been written to test a zip file. If not a zip, the function will return the
2083
     * default return value: ''
2084
     * @param	string	the path to the file
2085
     * @param	string 	the original name of the file
2086
     * @return	string	'scorm','aicc','scorm2004','dokeos' or '' if the package cannot be recognized
2087
     */
2088
    public static function get_package_type($file_path, $file_name)
2089
    {
2090
        // Get name of the zip file without the extension.
2091
        $file_info = pathinfo($file_name);
2092
        $filename = $file_info['basename']; // Name including extension.
2093
        $extension = $file_info['extension']; // Extension only.
2094
2095
        if (!empty($_POST['ppt2lp']) && !in_array(strtolower($extension), array(
2096
                'dll',
2097
                'exe'
2098
            ))) {
2099
            return 'oogie';
2100
        }
2101
        if (!empty($_POST['woogie']) && !in_array(strtolower($extension), array(
2102
                'dll',
2103
                'exe'
2104
            ))) {
2105
            return 'woogie';
2106
        }
2107
2108
        // Filename without its extension.
2109
        $file_base_name = str_replace('.' . $extension, '', $filename);
2110
2111
        $zipFile = new PclZip($file_path);
2112
        // Check the zip content (real size and file extension).
2113
        $zipContentArray = $zipFile->listContent();
2114
        $package_type = '';
2115
        $at_root = false;
2116
        $manifest = '';
2117
        $aicc_match_crs = 0;
2118
        $aicc_match_au = 0;
2119
        $aicc_match_des = 0;
2120
        $aicc_match_cst = 0;
2121
2122
        // The following loop should be stopped as soon as we found the right imsmanifest.xml (how to recognize it?).
2123
        if (is_array($zipContentArray) && count($zipContentArray) > 0) {
2124
            foreach ($zipContentArray as $thisContent) {
2125
                if (preg_match('~.(php.*|phtml)$~i', $thisContent['filename'])) {
2126
                    // New behaviour: Don't do anything. These files will be removed in scorm::import_package.
2127
                } elseif (stristr($thisContent['filename'], 'imsmanifest.xml') !== false) {
2128
                    $manifest = $thisContent['filename']; // Just the relative directory inside scorm/
2129
                    $package_type = 'scorm';
2130
                    break; // Exit the foreach loop.
2131
                } elseif (
2132
                    preg_match('/aicc\//i', $thisContent['filename']) ||
2133
                    in_array(strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION)), array( 'crs','au','des','cst'))
2134
                ) {
2135
                    $ext = strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION));
2136
                    switch ($ext) {
2137
                        case 'crs':
2138
                            $aicc_match_crs = 1;
2139
                            break;
2140
                        case 'au':
2141
                            $aicc_match_au = 1;
2142
                            break;
2143
                        case 'des':
2144
                            $aicc_match_des = 1;
2145
                            break;
2146
                        case 'cst':
2147
                            $aicc_match_cst = 1;
2148
                            break;
2149
                        default:
2150
                            break;
2151
                    }
2152
                    //break; // Don't exit the loop, because if we find an imsmanifest afterwards, we want it, not the AICC.
2153
                } else {
2154
                    $package_type = '';
2155
                }
2156
            }
2157
        }
2158
        if (empty($package_type) && 4 == ($aicc_match_crs + $aicc_match_au + $aicc_match_des + $aicc_match_cst)) {
2159
            // If found an aicc directory... (!= false means it cannot be false (error) or 0 (no match)).
2160
            $package_type = 'aicc';
2161
        }
2162
        return $package_type;
2163
    }
2164
2165
    /**
2166
     * Gets the previous resource in queue (url). Also initialises time values for this viewing
2167
     * @return string URL to load into the viewer
2168
     */
2169
    public function get_previous_index()
2170
    {
2171
        if ($this->debug > 0) {
2172
            error_log('New LP - In learnpath::get_previous_index()', 0);
2173
        }
2174
        $index = $this->index;
2175
        if (isset ($this->ordered_items[$index -1])) {
2176
            $index--;
2177
            while (isset($this->ordered_items[$index]) && ($this->items[$this->ordered_items[$index]]->get_type() == 'dir' || $this->items[$this->ordered_items[$index]]->get_type() == 'dokeos_chapter')) {
2178
                $index--;
2179
                if ($index < 0) {
2180
                    return $this->index;
2181
                }
2182
            }
2183
        } else {
2184
            if ($this->debug > 2) {
2185
                error_log('New LP - get_previous_index() - there was no previous index available, reusing ' . $index, 0);
2186
            }
2187
            // There is no previous item.
2188
        }
2189
        return $index;
2190
    }
2191
2192
    /**
2193
     * Gets item_id for the next element
2194
     * @return	integer	Previous item (DB) ID
2195
     */
2196
    public function get_previous_item_id()
2197
    {
2198
        if ($this->debug > 0) {
2199
            error_log('New LP - In learnpath::get_previous_item_id()', 0);
2200
        }
2201
        $new_index = $this->get_previous_index();
2202
        return $this->ordered_items[$new_index];
2203
    }
2204
2205
    /**
2206
     * Gets the progress value from the progress_db attribute
2207
     * @return	integer	Current progress value
2208
     */
2209
    public function get_progress()
2210
    {
2211
        if ($this->debug > 0) {
2212
            error_log('New LP - In learnpath::get_progress()', 0);
2213
        }
2214
        if (!empty ($this->progress_db)) {
2215
            return $this->progress_db;
2216
        }
2217
        return 0;
2218
    }
2219
2220
    /**
2221
     * Returns the HTML necessary to print a mediaplayer block inside a page
2222
     * @return string	The mediaplayer HTML
2223
     */
2224
    public function get_mediaplayer($autostart = 'true')
2225
    {
2226
        $course_id = api_get_course_int_id();
2227
        $_course = api_get_course_info();
2228
        $tbl_lp_item 		= Database :: get_course_table(TABLE_LP_ITEM);
2229
        $tbl_lp_item_view 	= Database :: get_course_table(TABLE_LP_ITEM_VIEW);
2230
2231
        // Getting all the information about the item.
2232
        $sql = "SELECT * FROM ".$tbl_lp_item." as lp
2233
                INNER JOIN ".$tbl_lp_item_view." as lp_view
2234
                ON lp.id = lp_view.lp_item_id
2235
                WHERE
2236
                    lp.id = '".$_SESSION['oLP']->current."' AND
2237
                    lp.c_id = $course_id AND
2238
                    lp_view.c_id = $course_id";
2239
        $result = Database::query($sql);
2240
        $row 	= Database::fetch_assoc($result);
2241
        $output = '';
2242
2243
        if (!empty ($row['audio'])) {
2244
2245
            $list = $_SESSION['oLP']->get_toc();
2246
            $type_quiz = false;
2247
2248
            foreach($list as $toc) {
2249
                if ($toc['id'] == $_SESSION['oLP']->current && ($toc['type']=='quiz') ) {
2250
                    $type_quiz = true;
2251
                }
2252
            }
2253
2254
            if ($type_quiz) {
2255
                if ($_SESSION['oLP']->prevent_reinit == 1) {
2256
                    $row['status'] === 'completed' ? $autostart_audio = 'false' : $autostart_audio = 'true';
2257
                } else {
2258
                    $autostart_audio = $autostart;
2259
                }
2260
            } else {
2261
                $autostart_audio = 'true';
2262
            }
2263
2264
            $courseInfo = api_get_course_info();
2265
2266
            $audio = $row['audio'];
2267
2268
            $file = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/audio/'.$audio;
2269
            $url = api_get_path(WEB_COURSE_PATH).$courseInfo['path'].'/document/audio/'.$audio.'?'.api_get_cidreq();
2270
2271
            if (!file_exists($file)) {
2272
                $lpPathInfo = $_SESSION['oLP']->generate_lp_folder(api_get_course_info());
2273
                $file = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'.$lpPathInfo['dir'].$audio;
2274
                $url = api_get_path(WEB_COURSE_PATH).$_course['path'].'/document'.$lpPathInfo['dir'].$audio.'?'.api_get_cidreq();
2275
            }
2276
2277
            $player = Display::getMediaPlayer(
2278
                $file,
2279
                array(
2280
                    'id' => 'lp_audio_media_player',
2281
                    'url' => $url,
2282
                    'autoplay' => $autostart_audio,
2283
                    'width' => '100%'
2284
                )
2285
            );
2286
2287
            // The mp3 player.
2288
            $output  = '<div id="container">';
2289
            $output .= $player;
2290
            $output .= '</div>';
2291
        }
2292
2293
        return $output;
2294
    }
2295
2296
    /**
2297
     * Checks if the learning path is visible for student after the progress
2298
     * of its prerequisite is completed, considering the time availability and
2299
     * the LP visibility.
2300
     * @param int $lp_id
2301
     * @param int $student_id
2302
     * @param string Course code (optional)
2303
     * @param int $sessionId
2304
     * @return	bool
2305
     */
2306
    public static function is_lp_visible_for_student(
2307
        $lp_id,
2308
        $student_id,
2309
        $courseCode = null,
2310
        $sessionId = null
2311
    ) {
2312
        $lp_id = (int)$lp_id;
2313
        $courseInfo = api_get_course_info($courseCode);
2314
        $sessionId = intval($sessionId);
2315
2316
        if (empty($sessionId)) {
2317
            $sessionId = api_get_session_id();
2318
        }
2319
2320
        $tbl_learnpath = Database::get_course_table(TABLE_LP_MAIN);
2321
        // Get current prerequisite
2322
        $sql = "SELECT id, prerequisite, subscribe_users, publicated_on, expired_on
2323
                FROM $tbl_learnpath
2324
                WHERE c_id = ".$courseInfo['real_id']." AND id = $lp_id";
2325
2326
        $itemInfo = api_get_item_property_info(
2327
            $courseInfo['real_id'],
2328
            TOOL_LEARNPATH,
2329
            $lp_id,
2330
            $sessionId
2331
        );
2332
2333
        // If the item was deleted.
2334
        if (isset($itemInfo['visibility']) && $itemInfo['visibility'] == 2) {
2335
            return false;
2336
        }
2337
2338
        $rs  = Database::query($sql);
2339
        $now = time();
2340
        if (Database::num_rows($rs) > 0) {
2341
            $row = Database::fetch_array($rs, 'ASSOC');
2342
2343
            $prerequisite = $row['prerequisite'];
2344
            $is_visible = true;
2345
2346
            if (!empty($prerequisite)) {
2347
                $progress = self::getProgress(
2348
                    $prerequisite,
2349
                    $student_id,
2350
                    $courseInfo['real_id'],
2351
                    $sessionId
2352
                );
2353
                $progress = intval($progress);
2354
                if ($progress < 100) {
2355
                    $is_visible = false;
2356
                }
2357
            }
2358
2359
            // Also check the time availability of the LP
2360
            if ($is_visible) {
2361
                // Adding visibility restrictions
2362 View Code Duplication
                if (!empty($row['publicated_on']) &&
2363
                    $row['publicated_on'] != '0000-00-00 00:00:00'
2364
                ) {
2365
                    if ($now < api_strtotime($row['publicated_on'], 'UTC')) {
2366
                        //api_not_allowed();
2367
                        $is_visible = false;
2368
                    }
2369
                }
2370
2371
                // Blocking empty start times see BT#2800
2372
                global $_custom;
2373
                if (isset($_custom['lps_hidden_when_no_start_date']) &&
2374
                    $_custom['lps_hidden_when_no_start_date']
2375
                ) {
2376
                    if (empty($row['publicated_on']) || $row['publicated_on'] == '0000-00-00 00:00:00') {
2377
                        //api_not_allowed();
2378
                        $is_visible = false;
2379
                    }
2380
                }
2381
2382 View Code Duplication
                if (!empty($row['expired_on']) && $row['expired_on'] != '0000-00-00 00:00:00') {
2383
                    if ($now > api_strtotime($row['expired_on'], 'UTC')) {
2384
                        //api_not_allowed();
2385
                        $is_visible = false;
2386
                    }
2387
                }
2388
            }
2389
2390
            // Check if the subscription users/group to a LP is ON
2391
            if (isset($row['subscribe_users']) && $row['subscribe_users'] == 1) {
2392
                // Try group
2393
                $is_visible = false;
2394
2395
                // Checking only the user visibility
2396
                $userVisibility = api_get_item_visibility(
2397
                    $courseInfo,
2398
                    'learnpath',
2399
                    $row['id'],
2400
                    $sessionId,
2401
                    $student_id,
2402
                    'LearnpathSubscription'
2403
                );
2404
2405
                if ($userVisibility == 1) {
2406
                    $is_visible = true;
2407
                } else {
2408
                    $userGroups = GroupManager::getAllGroupPerUserSubscription($student_id);
2409
                    if (!empty($userGroups)) {
2410
                        foreach ($userGroups as $groupInfo) {
2411
                            $groupId = $groupInfo['iid'];
2412
2413
                            $userVisibility = api_get_item_visibility(
2414
                                $courseInfo,
2415
                                'learnpath',
2416
                                $row['id'],
2417
                                $sessionId,
2418
                                null,
2419
                                'LearnpathSubscription',
2420
                                $groupId
2421
                            );
2422
2423
                            if ($userVisibility == 1) {
2424
                                $is_visible = true;
2425
                                break;
2426
                            }
2427
                        }
2428
                    }
2429
                }
2430
            }
2431
2432
            return $is_visible;
2433
        }
2434
2435
        return false;
2436
    }
2437
2438
    /**
2439
     * @param int $lpId
2440
     * @param int $userId
2441
     * @param int $courseId
2442
     * @param int $sessionId
2443
     * @return int
2444
     */
2445
    public static function getProgress($lpId, $userId, $courseId, $sessionId = 0)
2446
    {
2447
        $lpId = intval($lpId);
2448
        $userId = intval($userId);
2449
        $courseId = intval($courseId);
2450
        $sessionId = intval($sessionId);
2451
        $progress = 0;
2452
2453
        $sessionCondition = api_get_session_condition($sessionId);
2454
        $table = Database :: get_course_table(TABLE_LP_VIEW);
2455
        $sql = "SELECT * FROM $table
2456
                WHERE
2457
                    c_id = ".$courseId." AND
2458
                    lp_id = $lpId AND
2459
                    user_id = $userId $sessionCondition";
2460
        $res = Database::query($sql);
2461
        if (Database :: num_rows($res) > 0) {
2462
            $row = Database:: fetch_array($res);
2463
            $progress = $row['progress'];
2464
        }
2465
        return $progress;
2466
2467
    }
2468
2469
    /**
2470
     * Displays a progress bar
2471
     * completed so far.
2472
     * @param	integer	$percentage Progress value to display
2473
     * @param	string	$text_add Text to display near the progress value
2474
     * @return	string	HTML string containing the progress bar
2475
     */
2476
    public static function get_progress_bar($percentage = -1, $text_add = '')
2477
    {
2478
        $text = $percentage . $text_add;
2479
        $output = '<div class="progress">
2480
                        <div id="progress_bar_value" class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="' .$percentage. '" aria-valuemin="0" aria-valuemax="100" style="width: '.$text.';">
2481
                        '. $text .'
2482
                        </div>
2483
                    </div>';
2484
2485
        return $output;
2486
    }
2487
2488
    /**
2489
     * @param string $mode can be '%' or 'abs'
2490
     * otherwise this value will be used $this->progress_bar_mode
2491
     * @return string
2492
     */
2493
    public function getProgressBar($mode = null)
2494
    {
2495
        list($percentage, $text_add) = $this->get_progress_bar_text($mode);
2496
        return self::get_progress_bar($percentage, $text_add);
2497
    }
2498
2499
    /**
2500
     * Gets the progress bar info to display inside the progress bar.
2501
     * Also used by scorm_api.php
2502
     * @param	string	$mode Mode of display (can be '%' or 'abs').abs means
2503
     * we display a number of completed elements per total elements
2504
     * @param	integer	$add Additional steps to fake as completed
2505
     * @return	list array Percentage or number and symbol (% or /xx)
2506
     */
2507
    public function get_progress_bar_text($mode = '', $add = 0)
2508
    {
2509
        if ($this->debug > 0) {
2510
            error_log('New LP - In learnpath::get_progress_bar_text()', 0);
2511
        }
2512
        if (empty($mode)) {
2513
            $mode = $this->progress_bar_mode;
2514
        }
2515
        $total_items = $this->get_total_items_count_without_chapters();
2516
        if ($this->debug > 2) {
2517
            error_log('New LP - Total items available in this learnpath: ' . $total_items, 0);
2518
        }
2519
        $completeItems = $this->get_complete_items_count();
2520
        if ($this->debug > 2) {
2521
            error_log('New LP - Items completed so far: ' . $completeItems, 0);
2522
        }
2523
        if ($add != 0) {
2524
            $completeItems += $add;
2525
            if ($this->debug > 2) {
2526
                error_log('New LP - Items completed so far (+modifier): ' . $completeItems, 0);
2527
            }
2528
        }
2529
        $text = '';
2530
        if ($completeItems > $total_items) {
2531
            $completeItems = $total_items;
2532
        }
2533
        $percentage = 0;
2534
        if ($mode == '%') {
2535
            if ($total_items > 0) {
2536
                $percentage = ((float) $completeItems / (float) $total_items) * 100;
2537
            } else {
2538
                $percentage = 0;
2539
            }
2540
            $percentage = number_format($percentage, 0);
2541
            $text = '%';
2542
        } elseif ($mode == 'abs') {
2543
            $percentage = $completeItems;
2544
            $text = '/' . $total_items;
2545
        }
2546
2547
        return array(
2548
            $percentage,
2549
            $text
2550
        );
2551
    }
2552
2553
    /**
2554
     * Gets the progress bar mode
2555
     * @return	string	The progress bar mode attribute
2556
     */
2557
    public function get_progress_bar_mode()
2558
    {
2559
        if ($this->debug > 0) {
2560
            error_log('New LP - In learnpath::get_progress_bar_mode()', 0);
2561
        }
2562
        if (!empty ($this->progress_bar_mode)) {
2563
            return $this->progress_bar_mode;
2564
        } else {
2565
            return '%';
2566
        }
2567
    }
2568
2569
    /**
2570
     * Gets the learnpath proximity (remote or local)
2571
     * @return	string	Learnpath proximity
2572
     */
2573
    public function get_proximity()
2574
    {
2575
        if ($this->debug > 0) {
2576
            error_log('New LP - In learnpath::get_proximity()', 0);
2577
        }
2578
        if (!empty ($this->proximity)) {
2579
            return $this->proximity;
2580
        } else {
2581
            return '';
2582
        }
2583
    }
2584
2585
    /**
2586
     * Gets the learnpath theme (remote or local)
2587
     * @return	string	Learnpath theme
2588
     */
2589
    public function get_theme()
2590
    {
2591
        if ($this->debug > 0) {
2592
            error_log('New LP - In learnpath::get_theme()', 0);
2593
        }
2594
        if (!empty ($this->theme)) {
2595
            return $this->theme;
2596
        } else {
2597
            return '';
2598
        }
2599
    }
2600
2601
    /**
2602
     * Gets the learnpath session id
2603
     * @return	string	Learnpath theme
2604
     */
2605
    public function get_lp_session_id()
2606
    {
2607
        if ($this->debug > 0) {
2608
            error_log('New LP - In learnpath::get_lp_session_id()', 0);
2609
        }
2610
        if (!empty ($this->lp_session_id)) {
2611
            return $this->lp_session_id;
2612
        } else {
2613
            return 0;
2614
        }
2615
    }
2616
2617
    /**
2618
     * Gets the learnpath image
2619
     * @return	string	Web URL of the LP image
2620
     */
2621
    public function get_preview_image()
2622
    {
2623
        if ($this->debug > 0) {
2624
            error_log('New LP - In learnpath::get_preview_image()', 0);
2625
        }
2626
        if (!empty($this->preview_image)) {
2627
            return $this->preview_image;
2628
        } else {
2629
            return '';
2630
        }
2631
    }
2632
2633
    /**
2634
     * @param string $size
2635
     * @param string $path_type
2636
     * @return bool|string
2637
     */
2638
    public function get_preview_image_path($size = null, $path_type = 'web')
2639
    {
2640
        $preview_image = $this->get_preview_image();
2641
        if (isset($preview_image) && !empty($preview_image)) {
2642
            $image_sys_path = api_get_path(SYS_COURSE_PATH).$this->course_info['path'].'/upload/learning_path/images/';
2643
            $image_path = api_get_path(WEB_COURSE_PATH).$this->course_info['path'].'/upload/learning_path/images/';
2644
2645
            if (isset($size)) {
2646
                $info = pathinfo($preview_image);
2647
                $image_custom_size = $info['filename'].'.'.$size.'.'.$info['extension'];
2648
                if (file_exists($image_sys_path.$image_custom_size)) {
2649
                    if ($path_type == 'web') {
2650
                        return $image_path.$image_custom_size;
2651
                    } else {
2652
                        return $image_sys_path.$image_custom_size;
2653
                    }
2654
                }
2655
            } else {
2656
                if ($path_type == 'web') {
2657
                    return $image_path.$preview_image;
2658
                } else {
2659
                    return $image_sys_path.$preview_image;
2660
                }
2661
            }
2662
        }
2663
2664
        return false;
2665
    }
2666
2667
    /**
2668
     * Gets the learnpath author
2669
     * @return string	LP's author
2670
     */
2671
    public function get_author()
2672
    {
2673
        if ($this->debug > 0) {
2674
            error_log('New LP - In learnpath::get_author()', 0);
2675
        }
2676
        if (!empty ($this->author)) {
2677
            return $this->author;
2678
        } else {
2679
            return '';
2680
        }
2681
    }
2682
2683
    /**
2684
     * Gets the learnpath author
2685
     * @return	string	LP's author
2686
     */
2687
    public function get_hide_toc_frame()
2688
    {
2689
        if ($this->debug > 0) {
2690
            error_log('New LP - In learnpath::get_author()', 0);
2691
        }
2692
        if (!empty ($this->hide_toc_frame)) {
2693
            return $this->hide_toc_frame;
2694
        } else {
2695
            return '';
2696
        }
2697
    }
2698
2699
    /**
2700
     * Generate a new prerequisites string for a given item. If this item was a sco and
2701
     * its prerequisites were strings (instead of IDs), then transform those strings into
2702
     * IDs, knowing that SCORM IDs are kept in the "ref" field of the lp_item table.
2703
     * Prefix all item IDs that end-up in the prerequisites string by "ITEM_" to use the
2704
     * same rule as the scorm_export() method
2705
     * @param	integer		Item ID
2706
     * @return	string		Prerequisites string ready for the export as SCORM
2707
     */
2708
    public function get_scorm_prereq_string($item_id)
2709
    {
2710
        if ($this->debug > 0) {
2711
            error_log('New LP - In learnpath::get_scorm_prereq_string()', 0);
2712
        }
2713
        if (!is_object($this->items[$item_id])) {
2714
            return false;
2715
        }
2716
        /** @var learnpathItem $oItem */
2717
        $oItem = $this->items[$item_id];
2718
        $prereq = $oItem->get_prereq_string();
2719
2720
        if (empty($prereq)) {
2721
            return '';
2722
        }
2723
        if (preg_match('/^\d+$/', $prereq) && is_object($this->items[$prereq])) {
2724
            // If the prerequisite is a simple integer ID and this ID exists as an item ID,
2725
            // then simply return it (with the ITEM_ prefix).
2726
            //return 'ITEM_' . $prereq;
2727
            return $this->items[$prereq]->ref;
2728
        } else {
2729
            if (isset($this->refs_list[$prereq])) {
2730
                // It's a simple string item from which the ID can be found in the refs list,
2731
                // so we can transform it directly to an ID for export.
2732
                return $this->items[$this->refs_list[$prereq]]->ref;
2733
            } else if (isset($this->refs_list['ITEM_'.$prereq])) {
2734
                return $this->items[$this->refs_list['ITEM_'.$prereq]]->ref;
2735
            } else {
2736
                // The last case, if it's a complex form, then find all the IDs (SCORM strings)
2737
                // and replace them, one by one, by the internal IDs (chamilo db)
2738
                // TODO: Modify the '*' replacement to replace the multiplier in front of it
2739
                // by a space as well.
2740
                $find = array (
2741
                    '&',
2742
                    '|',
2743
                    '~',
2744
                    '=',
2745
                    '<>',
2746
                    '{',
2747
                    '}',
2748
                    '*',
2749
                    '(',
2750
                    ')'
2751
                );
2752
                $replace = array (
2753
                    ' ',
2754
                    ' ',
2755
                    ' ',
2756
                    ' ',
2757
                    ' ',
2758
                    ' ',
2759
                    ' ',
2760
                    ' ',
2761
                    ' ',
2762
                    ' '
2763
                );
2764
                $prereq_mod = str_replace($find, $replace, $prereq);
2765
                $ids = explode(' ', $prereq_mod);
2766
                foreach ($ids as $id) {
2767
                    $id = trim($id);
2768
                    if (isset ($this->refs_list[$id])) {
2769
                        $prereq = preg_replace('/[^a-zA-Z_0-9](' . $id . ')[^a-zA-Z_0-9]/', 'ITEM_' . $this->refs_list[$id], $prereq);
2770
                    }
2771
                }
2772
2773
                return $prereq;
2774
            }
2775
        }
2776
    }
2777
2778
    /**
2779
     * Returns the XML DOM document's node
2780
     * @param	resource	Reference to a list of objects to search for the given ITEM_*
2781
     * @param	string		The identifier to look for
2782
     * @return	mixed		The reference to the element found with that identifier. False if not found
2783
     */
2784
    public function get_scorm_xml_node(& $children, $id)
2785
    {
2786
        for ($i = 0; $i < $children->length; $i++) {
2787
            $item_temp = $children->item($i);
2788
            if ($item_temp->nodeName == 'item') {
2789
                if ($item_temp->getAttribute('identifier') == $id) {
2790
                    return $item_temp;
2791
                }
2792
            }
2793
            $subchildren = $item_temp->childNodes;
2794
            if ($subchildren && $subchildren->length > 0) {
2795
                $val = $this->get_scorm_xml_node($subchildren, $id);
2796
                if (is_object($val)) {
2797
2798
                    return $val;
2799
                }
2800
            }
2801
        }
2802
2803
        return false;
2804
    }
2805
2806
    /**
2807
     * Returns a usable array of stats related to the current learnpath and user
2808
     * @return array	Well-formatted array containing status for the current learnpath
2809
     */
2810
    public function get_stats()
2811
    {
2812
        if ($this->debug > 0) {
2813
            error_log('New LP - In learnpath::get_stats()', 0);
2814
        }
2815
    }
2816
2817
    /**
2818
     * Static method. Can be re-implemented by children. Gives an array of statistics for
2819
     * the given course (for all learnpaths and all users)
2820
     * @param	string	Course code
2821
     * @return array	Well-formatted array containing status for the course's learnpaths
2822
     */
2823
    public function get_stats_course($course)
2824
    {
2825
        //if ($this->debug > 0) { error_log('New LP - In learnpath::get_stats_course()', 0); }
2826
        // TODO
2827
    }
2828
2829
    /**
2830
     * Static method. Can be re-implemented by children. Gives an array of statistics for
2831
     * the given course and learnpath (for all users)
2832
     * @param	string	Course code
2833
     * @param	integer	Learnpath ID
2834
     * @return array	Well-formatted array containing status for the specified learnpath
2835
     */
2836
    public function get_stats_lp($course, $lp)
2837
    {
2838
        //if ($this->debug > 0) { error_log('New LP - In learnpath::get_stats_lp()', 0); }
2839
        // TODO
2840
    }
2841
2842
    /**
2843
     * Static method. Can be re-implemented by children. Gives an array of statistics for
2844
     * the given course, learnpath and user.
2845
     * @param	string	Course code
2846
     * @param	integer	Learnpath ID
2847
     * @param	integer	User ID
2848
     * @return array	Well-formatted array containing status for the specified learnpath and user
2849
     */
2850
    public function get_stats_lp_user($course, $lp, $user)
2851
    {
2852
        //if ($this->debug > 0) { error_log('New LP - In learnpath::get_stats_lp_user()', 0); }
2853
        // TODO
2854
    }
2855
2856
    /**
2857
     * Static method. Can be re-implemented by children. Gives an array of statistics for
2858
     * the given course and learnpath (for all users)
2859
     * @param	string	Course code
2860
     * @param	integer	User ID
2861
     * @return array	Well-formatted array containing status for the user's learnpaths
2862
     */
2863
    public function get_stats_user($course, $user) {
2864
        //if ($this->debug > 0) { error_log('New LP - In learnpath::get_stats_user()', 0); }
2865
        // TODO
2866
    }
2867
2868
    /**
2869
     * Gets the status list for all LP's items
2870
     * @return	array	Array of [index] => [item ID => current status]
2871
     */
2872
    public function get_items_status_list()
2873
    {
2874
        if ($this->debug > 0) {
2875
            error_log('New LP - In learnpath::get_items_status_list()', 0);
2876
        }
2877
        $list = array ();
2878
        foreach ($this->ordered_items as $item_id) {
2879
            $list[] = array (
2880
                $item_id => $this->items[$item_id]->get_status()
2881
            );
2882
        }
2883
        return $list;
2884
    }
2885
2886
    /**
2887
     * Return the number of interactions for the given learnpath Item View ID.
2888
     * This method can be used as static.
2889
     * @param	integer	Item View ID
2890
     * @param   integer course id
2891
     * @return	integer	Number of interactions
2892
     */
2893 View Code Duplication
    public static function get_interactions_count_from_db($lp_iv_id, $course_id)
2894
    {
2895
        $table = Database :: get_course_table(TABLE_LP_IV_INTERACTION);
2896
        $lp_iv_id = intval($lp_iv_id);
2897
        $course_id = intval($course_id);
2898
2899
        $sql = "SELECT count(*) FROM $table
2900
                WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id";
2901
        $res = Database::query($sql);
2902
        $num = 0;
2903
        if (Database::num_rows($res)) {
2904
            $row = Database::fetch_array($res);
2905
            $num = $row[0];
2906
        }
2907
        return $num;
2908
    }
2909
2910
    /**
2911
     * Return the interactions as an array for the given lp_iv_id.
2912
     * This method can be used as static.
2913
     * @param	integer	Learnpath Item View ID
2914
     * @return	array
2915
     * @todo 	Transcode labels instead of switching to HTML (which requires to know the encoding of the LP)
2916
     */
2917
    public static function get_iv_interactions_array($lp_iv_id)
2918
    {
2919
        $course_id = api_get_course_int_id();
2920
        $list = array();
2921
        $table = Database :: get_course_table(TABLE_LP_IV_INTERACTION);
2922
2923
        if (empty($lp_iv_id)) {
2924
            return array();
2925
        }
2926
2927
        $sql = "SELECT * FROM $table
2928
                WHERE c_id = ".$course_id." AND lp_iv_id = $lp_iv_id
2929
                ORDER BY order_id ASC";
2930
        $res = Database::query($sql);
2931
        $num = Database :: num_rows($res);
2932
        if ($num > 0) {
2933
            $list[] = array (
2934
                'order_id' => api_htmlentities(get_lang('Order'), ENT_QUOTES),
2935
                'id' => api_htmlentities(get_lang('InteractionID'), ENT_QUOTES),
2936
                'type' => api_htmlentities(get_lang('Type'), ENT_QUOTES),
2937
                'time' => api_htmlentities(get_lang('TimeFinished'), ENT_QUOTES),
2938
                'correct_responses' => api_htmlentities(get_lang('CorrectAnswers'), ENT_QUOTES),
2939
                'student_response' => api_htmlentities(get_lang('StudentResponse'), ENT_QUOTES),
2940
                'result' => api_htmlentities(get_lang('Result'), ENT_QUOTES),
2941
                'latency' => api_htmlentities(get_lang('LatencyTimeSpent'), ENT_QUOTES)
2942
            );
2943
            while ($row = Database :: fetch_array($res)) {
2944
                $list[] = array (
2945
                    'order_id' => ($row['order_id'] + 1),
2946
                    'id' => urldecode($row['interaction_id']), //urldecode because they often have %2F or stuff like that
2947
                    'type' => $row['interaction_type'],
2948
                    'time' => $row['completion_time'],
2949
                    //'correct_responses' => $row['correct_responses'],
2950
                    'correct_responses' => '', // Hide correct responses from students.
2951
                    'student_response' => $row['student_response'],
2952
                    'result' => $row['result'],
2953
                    'latency' => $row['latency']
2954
                );
2955
            }
2956
        }
2957
2958
        return $list;
2959
    }
2960
2961
    /**
2962
     * Return the number of objectives for the given learnpath Item View ID.
2963
     * This method can be used as static.
2964
     * @param	integer	Item View ID
2965
     * @return	integer	Number of objectives
2966
     */
2967 View Code Duplication
    public static function get_objectives_count_from_db($lp_iv_id, $course_id)
2968
    {
2969
        $table = Database :: get_course_table(TABLE_LP_IV_OBJECTIVE);
2970
        $course_id = intval($course_id);
2971
        $lp_iv_id = intval($lp_iv_id);
2972
        $sql = "SELECT count(*) FROM $table
2973
                WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id";
2974
        //@todo seems that this always returns 0
2975
        $res = Database::query($sql);
2976
        $num = 0;
2977
        if (Database::num_rows($res)) {
2978
            $row = Database :: fetch_array($res);
2979
            $num = $row[0];
2980
        }
2981
2982
        return $num;
2983
    }
2984
2985
    /**
2986
     * Return the objectives as an array for the given lp_iv_id.
2987
     * This method can be used as static.
2988
     * @param	integer	Learnpath Item View ID
2989
     * @return	array
2990
     * @todo 	Translate labels
2991
     */
2992
    public static function get_iv_objectives_array($lp_iv_id = 0)
2993
    {
2994
        $course_id = api_get_course_int_id();
2995
        $table = Database :: get_course_table(TABLE_LP_IV_OBJECTIVE);
2996
        $sql = "SELECT * FROM $table
2997
                WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id
2998
                ORDER BY order_id ASC";
2999
        $res = Database::query($sql);
3000
        $num = Database :: num_rows($res);
3001
        $list = array();
3002
        if ($num > 0) {
3003
            $list[] = array(
3004
                'order_id' => api_htmlentities(get_lang('Order'), ENT_QUOTES),
3005
                'objective_id' => api_htmlentities(get_lang('ObjectiveID'), ENT_QUOTES),
3006
                'score_raw' => api_htmlentities(get_lang('ObjectiveRawScore'), ENT_QUOTES),
3007
                'score_max' => api_htmlentities(get_lang('ObjectiveMaxScore'), ENT_QUOTES),
3008
                'score_min' => api_htmlentities(get_lang('ObjectiveMinScore'), ENT_QUOTES),
3009
                'status' => api_htmlentities(get_lang('ObjectiveStatus'), ENT_QUOTES)
3010
            );
3011
            while ($row = Database :: fetch_array($res)) {
3012
                $list[] = array (
3013
                    'order_id' => ($row['order_id'] + 1),
3014
                    'objective_id' => urldecode($row['objective_id']), // urldecode() because they often have %2F or stuff like that.
3015
                    'score_raw' => $row['score_raw'],
3016
                    'score_max' => $row['score_max'],
3017
                    'score_min' => $row['score_min'],
3018
                    'status' => $row['status']
3019
                );
3020
            }
3021
        }
3022
3023
        return $list;
3024
    }
3025
3026
    /**
3027
     * Generate and return the table of contents for this learnpath. The (flat) table returned can be
3028
     * used by get_html_toc() to be ready to display
3029
     * @return	array	TOC as a table with 4 elements per row: title, link, status and level
3030
     */
3031
    public function get_toc()
3032
    {
3033
        if ($this->debug > 0) {
3034
            error_log('learnpath::get_toc()', 0);
3035
        }
3036
        $toc = array();
3037
        foreach ($this->ordered_items as $item_id) {
3038
            if ($this->debug > 2) {
3039
                error_log('learnpath::get_toc(): getting info for item ' . $item_id, 0);
3040
            }
3041
            // TODO: Change this link generation and use new function instead.
3042
            $toc[] = array (
3043
                'id'            => $item_id,
3044
                'title'         => $this->items[$item_id]->get_title(),
3045
                'status'        => $this->items[$item_id]->get_status(),
3046
                'level'         => $this->items[$item_id]->get_level(),
3047
                'type'          => $this->items[$item_id]->get_type(),
3048
                'description'   => $this->items[$item_id]->get_description(),
3049
                'path'          => $this->items[$item_id]->get_path(),
3050
            );
3051
        }
3052
        if ($this->debug > 2) {
3053
            error_log('New LP - In learnpath::get_toc() - TOC array: ' . print_r($toc, true), 0);
3054
        }
3055
        return $toc;
3056
    }
3057
3058
    /**
3059
     * Generate and return the table of contents for this learnpath. The JS
3060
     * table returned is used inside of scorm_api.php
3061
     * @return  string  A JS array vairiable construction
3062
     */
3063
    public function get_items_details_as_js($varname = 'olms.lms_item_types')
3064
    {
3065
        if ($this->debug > 0) {
3066
            error_log('New LP - In learnpath::get_items_details_as_js()', 0);
3067
        }
3068
        $toc = $varname.' = new Array();';
3069
        foreach ($this->ordered_items as $item_id) {
3070
            $toc.= $varname."['i$item_id'] = '".$this->items[$item_id]->get_type()."';";
3071
        }
3072
        if ($this->debug > 2) {
3073
            error_log('New LP - In learnpath::get_items_details_as_js() - TOC array: ' . print_r($toc, true), 0);
3074
        }
3075
        return $toc;
3076
    }
3077
3078
    /**
3079
     * Gets the learning path type
3080
     * @param	boolean		Return the name? If false, return the ID. Default is false.
3081
     * @return	mixed		Type ID or name, depending on the parameter
3082
     */
3083
    public function get_type($get_name = false)
3084
    {
3085
        $res = false;
3086
        if ($this->debug > 0) {
3087
            error_log('New LP - In learnpath::get_type()', 0);
3088
        }
3089
        if (!empty ($this->type)) {
3090
            if ($get_name) {
3091
                // Get it from the lp_type table in main db.
3092
            } else {
3093
                $res = $this->type;
3094
            }
3095
        }
3096
        if ($this->debug > 2) {
3097
            error_log('New LP - In learnpath::get_type() - Returning ' . ($res ? $res : 'false'), 0);
3098
        }
3099
        return $res;
3100
    }
3101
3102
    /**
3103
     * Gets the learning path type as static method
3104
     * @param	boolean		Return the name? If false, return the ID. Default is false.
3105
     * @return	mixed		Type ID or name, depending on the parameter
3106
     */
3107
    public static function get_type_static($lp_id = 0)
3108
    {
3109
        $course_id = api_get_course_int_id();
3110
        $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
3111
        $lp_id = intval($lp_id);
3112
        $sql = "SELECT lp_type FROM $tbl_lp
3113
                WHERE c_id = $course_id AND id = '" . $lp_id . "'";
3114
        $res = Database::query($sql);
3115
        if ($res === false) {
3116
            return null;
3117
        }
3118
        if (Database :: num_rows($res) <= 0) {
3119
            return null;
3120
        }
3121
        $row = Database :: fetch_array($res);
3122
        return $row['lp_type'];
3123
    }
3124
3125
    /**
3126
     * Gets a flat list of item IDs ordered for display (level by level ordered by order_display)
3127
     * This method can be used as abstract and is recursive
3128
     * @param	integer	Learnpath ID
3129
     * @param	integer	Parent ID of the items to look for
3130
     * @return	mixed	Ordered list of item IDs or false on error
3131
     */
3132
    public static function get_flat_ordered_items_list($lp, $parent = 0, $course_id = null)
3133
    {
3134
        if (empty($course_id)) {
3135
            $course_id = api_get_course_int_id();
3136
        } else {
3137
            $course_id = intval($course_id);
3138
        }
3139
        $list = array();
3140
3141
        if (empty($lp)) {
3142
            return false;
3143
        }
3144
3145
        $lp = intval($lp);
3146
        $parent = intval($parent);
3147
3148
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
3149
        $sql = "SELECT id FROM $tbl_lp_item
3150
                WHERE c_id = $course_id AND lp_id = $lp AND parent_item_id = $parent
3151
                ORDER BY display_order";
3152
3153
        $res = Database::query($sql);
3154
        while ($row = Database :: fetch_array($res)) {
3155
            $sublist = learnpath :: get_flat_ordered_items_list($lp, $row['id'], $course_id);
3156
            $list[] = $row['id'];
3157
            foreach ($sublist as $item) {
3158
                $list[] = $item;
3159
            }
3160
        }
3161
        return $list;
3162
    }
3163
3164
    /**
3165
     * @return array
3166
     */
3167
    public static function getChapterTypes()
3168
    {
3169
        return array(
3170
            'dokeos_chapter',
3171
            'dokeos_module',
3172
            'chapter',
3173
            'dir'
3174
        );
3175
    }
3176
3177
    /**
3178
     * Uses the table generated by get_toc() and returns an HTML-formatted string ready to display
3179
     * @return	string	HTML TOC ready to display
3180
     */
3181
    public function get_html_toc($toc_list = null)
3182
    {
3183
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true, false, false);
3184
3185
        if ($this->debug > 0) {
3186
            error_log('In learnpath::get_html_toc()', 0);
3187
        }
3188
        if (empty($toc_list)) {
3189
            $toc_list = $this->get_toc();
3190
        }
3191
        //$html = '<div id="scorm_title" class="scorm-heading">'.Security::remove_XSS($this->get_name()) . '</div>';
3192
        $html = '<div class="scorm-body">';
3193
3194
3195
        $html .= '<div id="inner_lp_toc" class="inner_lp_toc scrollbar-light">';
3196
        require_once 'resourcelinker.inc.php';
3197
3198
        // Temporary variables.
3199
        $mycurrentitemid = $this->get_current_item_id();
3200
        $color_counter = 0;
3201
        $i = 0;
3202
3203
        foreach ($toc_list as $item) {
3204
3205
            // Style Status
3206
            $class_name = [
3207
                'not attempted' => 'scorm_not_attempted',
3208
                'incomplete' => 'scorm_not_attempted',
3209
                'failed' => 'scorm_failed',
3210
                'completed' => 'scorm_completed',
3211
                'passed' => 'scorm_completed',
3212
                'succeeded' => 'scorm_completed',
3213
                'browsed' => 'scorm_completed',
3214
            ];
3215
3216
            $scorm_color_background = 'row_odd';
3217
            $style_item = '';
3218
3219
            if ($color_counter % 2 == 0) {
3220
                $scorm_color_background = 'row_even';
3221
            }
3222
3223
            $dirTypes = self::getChapterTypes();
3224
3225
            if (in_array($item['type'], $dirTypes)) {
3226
                $scorm_color_background ='scorm_item_section ';
3227
                $style_item = '';
3228
            }
3229
            if ($item['id'] == $this->current) {
3230
                $scorm_color_background = 'scorm_item_normal '.$scorm_color_background.' scorm_highlight';
3231
            } elseif (!in_array($item['type'], $dirTypes)) {
3232
                $scorm_color_background = 'scorm_item_normal '.$scorm_color_background.' ';
3233
            }
3234
3235
            $html .= '<div id="toc_' . $item['id'] . '" class="' . $scorm_color_background . ' '.$class_name[$item['status']].' ">';
3236
3237
            // Learning path title
3238
            $title = $item['title'];
3239
            if (empty ($title)) {
3240
                $title = rl_get_resource_name(api_get_course_id(), $this->get_id(), $item['id']);
3241
            }
3242
            $title = Security::remove_XSS($title);
3243
3244
            // Learning path personalization
3245
            // build the LP tree
3246
            // The anchor atoc_ will let us center the TOC on the currently viewed item &^D
3247
            $description = $item['description'];
3248
            if (empty($description)) {
3249
                $description = $title;
3250
            }
3251
            if (in_array($item['type'], $dirTypes)) {
3252
                // Chapters
3253
                $html .= '<div class="'.$style_item.' scorm_section_level_'.$item['level'].'" title="'.$description.'" >';
3254
            } else {
3255
                $html .= '<div class="'.$style_item.' scorm_item_level_'.$item['level'].' scorm_type_'.learnpath::format_scorm_type_item($item['type']).'" title="'.$description.'" >';
3256
                $html .= '<a name="atoc_'.$item['id'].'"></a>';
3257
            }
3258
3259
            if (in_array($item['type'], $dirTypes)) {
3260
                // Chapter
3261
                // if you want to put an image before, you should use css
3262
                $html .= stripslashes($title);
3263
            } else {
3264
                $this->get_link('http', $item['id'], $toc_list);
3265
                $html .= '<a class="items-list" href="#" onclick="switch_item(' .$mycurrentitemid . ',' .$item['id'] . ');' .'return false;" >' . stripslashes($title) . '</a>';
3266
            }
3267
            $html .= "</div>";
3268
3269
            if ($scorm_color_background != '') {
3270
                $html .= '</div>';
3271
            }
3272
3273
            $color_counter++;
3274
        }
3275
        $html .= "</div>";
3276
        $html .= "</div>";
3277
        return $html;
3278
    }
3279
3280
    /**
3281
     * Returns an HTML-formatted string ready to display with teacher buttons
3282
     * in LP view menu
3283
     * @return	string	HTML TOC ready to display
3284
     */
3285
    public function get_teacher_toc_buttons()
3286
    {
3287
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true, false, false);
3288
        $hide_teacher_icons_lp = api_get_configuration_value('hide_teacher_icons_lp');
3289
        $html = '';
3290
3291
        if ($is_allowed_to_edit && $hide_teacher_icons_lp == false) {
3292
            $gradebook = '';
3293
            if (!empty($_GET['gradebook'])) {
3294
                $gradebook = Security:: remove_XSS($_GET['gradebook']);
3295
            }
3296
            if ($this->get_lp_session_id() == api_get_session_id()) {
3297
                $html .= '<div id="actions_lp" class="actions_lp"><hr>';
3298
                $html .= '<div class="btn-group">';
3299
                $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'>" .
3300
                    Display::returnFontAwesomeIcon('street-view') . get_lang('Overview') . "</a>";
3301
                $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'>" .
3302
                    Display::returnFontAwesomeIcon('pencil') . get_lang('Edit') . "</a>";
3303
                $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">' .
3304
                    Display::returnFontAwesomeIcon('cog') . get_lang('Settings').'</a>';
3305
                $html .= '</div>';
3306
                $html .= '</div>';
3307
            }
3308
        }
3309
3310
        return $html;
3311
3312
    }
3313
    /**
3314
     * Gets the learnpath maker name - generally the editor's name
3315
     * @return	string	Learnpath maker name
3316
     */
3317
    public function get_maker()
3318
    {
3319
        if ($this->debug > 0) {
3320
            error_log('New LP - In learnpath::get_maker()', 0);
3321
        }
3322
        if (!empty ($this->maker)) {
3323
            return $this->maker;
3324
        } else {
3325
            return '';
3326
        }
3327
    }
3328
3329
    /**
3330
     * Gets the learnpath name/title
3331
     * @return	string	Learnpath name/title
3332
     */
3333 View Code Duplication
    public function get_name()
3334
    {
3335
        if ($this->debug > 0) {
3336
            error_log('New LP - In learnpath::get_name()', 0);
3337
        }
3338
        if (!empty ($this->name)) {
3339
            return $this->name;
3340
        } else {
3341
            return 'N/A';
3342
        }
3343
    }
3344
3345
    /**
3346
     * Gets a link to the resource from the present location, depending on item ID.
3347
     * @param	string	$type Type of link expected
3348
     * @param	integer	$item_id Learnpath item ID
3349
     * @return	string	$provided_toc Link to the lp_item resource
3350
     */
3351
    public function get_link($type = 'http', $item_id = null, $provided_toc = false)
3352
    {
3353
        $course_id = $this->get_course_int_id();
3354
3355 View Code Duplication
        if ($this->debug > 0) {
3356
            error_log('New LP - In learnpath::get_link(' . $type . ',' . $item_id . ')', 0);
3357
        }
3358 View Code Duplication
        if (empty($item_id)) {
3359
            if ($this->debug > 2) {
3360
                error_log('New LP - In learnpath::get_link() - no item id given in learnpath::get_link(), using current: ' . $this->get_current_item_id(), 0);
3361
            }
3362
            $item_id = $this->get_current_item_id();
3363
        }
3364
3365 View Code Duplication
        if (empty($item_id)) {
3366
            if ($this->debug > 2) {
3367
                error_log('New LP - In learnpath::get_link() - no current item id found in learnpath object', 0);
3368
            }
3369
            //still empty, this means there was no item_id given and we are not in an object context or
3370
            //the object property is empty, return empty link
3371
            $item_id = $this->first();
3372
            return '';
3373
        }
3374
3375
        $file = '';
3376
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3377
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
3378
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3379
        $item_id = intval($item_id);
3380
3381
        $sql = "SELECT
3382
                    l.lp_type as ltype,
3383
                    l.path as lpath,
3384
                    li.item_type as litype,
3385
                    li.path as lipath,
3386
                    li.parameters as liparams
3387
        		FROM $lp_table l
3388
                INNER JOIN $lp_item_table li
3389
                    ON (li.lp_id = l.id AND l.c_id = $course_id AND li.c_id = $course_id )
3390
        		WHERE li.id = $item_id ";
3391
        if ($this->debug > 2) {
3392
            error_log('New LP - In learnpath::get_link() - selecting item ' . $sql, 0);
3393
        }
3394
        $res = Database::query($sql);
3395
        if (Database :: num_rows($res) > 0) {
3396
            $row = Database :: fetch_array($res);
3397
            $lp_type = $row['ltype'];
3398
            $lp_path = $row['lpath'];
3399
            $lp_item_type = $row['litype'];
3400
            $lp_item_path = $row['lipath'];
3401
            $lp_item_params = $row['liparams'];
3402
3403
            if (empty($lp_item_params) && strpos($lp_item_path, '?') !== false) {
3404
                list($lp_item_path, $lp_item_params) = explode('?', $lp_item_path);
3405
            }
3406
            $sys_course_path = api_get_path(SYS_COURSE_PATH) . api_get_course_path();
3407
            if ($type == 'http') {
3408
                $course_path = api_get_path(WEB_COURSE_PATH) . api_get_course_path(); //web path
3409
            } else {
3410
                $course_path = $sys_course_path; //system path
3411
            }
3412
3413
            // 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.
3414
            if (in_array($lp_item_type, array('quiz', 'document', 'final_item', 'link', 'forum', 'thread', 'student_publication'))) {
3415
                $lp_type = 1;
3416
            }
3417
3418
            if ($this->debug > 2) {
3419
                error_log('New LP - In learnpath::get_link() - $lp_type ' . $lp_type, 0);
3420
                error_log('New LP - In learnpath::get_link() - $lp_item_type ' . $lp_item_type, 0);
3421
            }
3422
3423
            // Now go through the specific cases to get the end of the path
3424
            // @todo Use constants instead of int values.
3425
            switch ($lp_type) {
3426
                case 1 :
3427
                    if ($lp_item_type == 'dokeos_chapter') {
3428
                        $file = 'lp_content.php?type=dir';
3429
                    } else {
3430
                        require_once 'resourcelinker.inc.php';
3431
                        $file = rl_get_resource_link_for_learnpath(
3432
                            $course_id,
3433
                            $this->get_id(),
3434
                            $item_id,
3435
                            $this->get_view_id()
3436
                        );
3437
3438
                        if ($this->debug > 0) {
3439
                            error_log('rl_get_resource_link_for_learnpath - file: ' . $file, 0);
3440
                        }
3441
3442
                        if ($lp_item_type == 'link') {
3443
                            if (Link::is_youtube_link($file)) {
3444
                                $src  = Link::get_youtube_video_id($file);
3445
                                $file = api_get_path(WEB_CODE_PATH).'newscorm/embed.php?type=youtube&source='.$src;
3446
                            } elseif (Link::isVimeoLink($file)) {
3447
                                $src  = Link::getVimeoLinkId($file);
3448
                                $file = api_get_path(WEB_CODE_PATH).'newscorm/embed.php?type=vimeo&source='.$src;
3449
                            } else {
3450
                                // If the current site is HTTPS and the link is
3451
                                // HTTP, browsers will refuse opening the link
3452
                                $urlId = api_get_current_access_url_id();
3453
                                $url = api_get_access_url($urlId, false);
3454
                                $protocol = substr($url['url'], 0, 5);
3455
                                if ($protocol === 'https') {
3456
                                    $linkProtocol = substr($file, 0, 5);
3457
                                    if ($linkProtocol === 'http:') {
3458
                                        //this is the special intervention case
3459
                                        $file = api_get_path(WEB_CODE_PATH).'newscorm/embed.php?type=nonhttps&source=' .  urlencode($file);
3460
                                    }
3461
                                }
3462
                            }
3463
                        } else {
3464
                            // Check how much attempts of a exercise exits in lp
3465
                            $lp_item_id = $this->get_current_item_id();
3466
                            $lp_view_id = $this->get_view_id();
3467
3468
                            $prevent_reinit = null;
3469
                            if (isset($this->items[$this->current])) {
3470
                                $prevent_reinit = $this->items[$this->current]->get_prevent_reinit();
3471
                            }
3472
3473
                            if (empty($provided_toc)) {
3474
                                if ($this->debug > 0) {
3475
                                    error_log('In learnpath::get_link() Loading get_toc ', 0);
3476
                                }
3477
                                $list = $this->get_toc();
3478
                            } else {
3479
                                if ($this->debug > 0) {
3480
                                    error_log('In learnpath::get_link() Loading get_toc from "cache" ', 0);
3481
                                }
3482
                                $list = $provided_toc;
3483
                            }
3484
3485
                            $type_quiz = false;
3486
3487 View Code Duplication
                            foreach ($list as $toc) {
3488
                                if ($toc['id'] == $lp_item_id && ($toc['type'] == 'quiz')) {
3489
                                    $type_quiz = true;
3490
                                }
3491
                            }
3492
3493
                            if ($type_quiz) {
3494
                                $lp_item_id = intval($lp_item_id);
3495
                                $lp_view_id = intval($lp_view_id);
3496
                                $sql = "SELECT count(*) FROM $lp_item_view_table
3497
                                        WHERE
3498
                                            c_id = $course_id AND
3499
                                            lp_item_id='" . $lp_item_id . "' AND
3500
                                            lp_view_id ='" . $lp_view_id . "' AND
3501
                                            status='completed'";
3502
                                $result = Database::query($sql);
3503
                                $row_count = Database :: fetch_row($result);
3504
                                $count_item_view = (int) $row_count[0];
3505
                                $not_multiple_attempt = 0;
3506
                                if ($prevent_reinit === 1 && $count_item_view > 0) {
3507
                                    $not_multiple_attempt = 1;
3508
                                }
3509
                                $file .= '&not_multiple_attempt=' . $not_multiple_attempt;
3510
                            }
3511
3512
                            $tmp_array = explode('/', $file);
3513
                            $document_name = $tmp_array[count($tmp_array) - 1];
3514
                            if (strpos($document_name, '_DELETED_')) {
3515
                                $file = 'blank.php?error=document_deleted';
3516
                            }
3517
                        }
3518
                    }
3519
                    break;
3520
                case 2 :
3521
                    if ($this->debug > 2) {
3522
                        error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Item type: ' . $lp_item_type, 0);
3523
                    }
3524
3525
                    if ($lp_item_type != 'dir') {
3526
                        // Quite complex here:
3527
                        // We want to make sure 'http://' (and similar) links can
3528
                        // be loaded as is (withouth the Chamilo path in front) but
3529
                        // some contents use this form: resource.htm?resource=http://blablabla
3530
                        // which means we have to find a protocol at the path's start, otherwise
3531
                        // it should not be considered as an external URL.
3532
3533
                        //if ($this->prerequisites_match($item_id)) {
3534
                        if (preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path) != 0) {
3535
                            if ($this->debug > 2) {
3536
                                error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Found match for protocol in ' . $lp_item_path, 0);
3537
                            }
3538
                            // Distant url, return as is.
3539
                            $file = $lp_item_path;
3540
                        } else {
3541
                            if ($this->debug > 2) {
3542
                                error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - No starting protocol in ' . $lp_item_path, 0);
3543
                            }
3544
                            // Prevent getting untranslatable urls.
3545
                            $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path);
3546
                            $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path);
3547
                            // Prepare the path.
3548
                            $file = $course_path . '/scorm/' . $lp_path . '/' . $lp_item_path;
3549
                            // TODO: Fix this for urls with protocol header.
3550
                            $file = str_replace('//', '/', $file);
3551
                            $file = str_replace(':/', '://', $file);
3552
                            if (substr($lp_path, -1) == '/') {
3553
                                $lp_path = substr($lp_path, 0, -1);
3554
                            }
3555
3556
                            if (!is_file(realpath($sys_course_path . '/scorm/' . $lp_path . '/' . $lp_item_path))) {
3557
                                // if file not found.
3558
                                $decoded = html_entity_decode($lp_item_path);
3559
                                list ($decoded) = explode('?', $decoded);
3560
                                if (!is_file(realpath($sys_course_path . '/scorm/' . $lp_path . '/' . $decoded))) {
3561
                                    require_once 'resourcelinker.inc.php';
3562
                                    $file = rl_get_resource_link_for_learnpath(
3563
                                        $course_id,
3564
                                        $this->get_id(),
3565
                                        $item_id,
3566
                                        $this->get_view_id()
3567
                                    );
3568
                                    if (empty($file)) {
3569
                                        $file = 'blank.php?error=document_not_found';
3570
                                    } else {
3571
                                        $tmp_array = explode('/', $file);
3572
                                        $document_name = $tmp_array[count($tmp_array) - 1];
3573
                                        if (strpos($document_name, '_DELETED_')) {
3574
                                            $file = 'blank.php?error=document_deleted';
3575
                                        } else {
3576
                                            $file = 'blank.php?error=document_not_found';
3577
                                        }
3578
                                    }
3579
                                } else {
3580
                                    $file = $course_path . '/scorm/' . $lp_path . '/' . $decoded;
3581
                                }
3582
                            }
3583
                        }
3584
3585
                        // We want to use parameters if they were defined in the imsmanifest
3586
                        if (strpos($file, 'blank.php') === false) {
3587
                            $file .= (strstr($file, '?') === false ? '?' : '') . $lp_item_params;
3588
                        }
3589
                    } else {
3590
                        $file = 'lp_content.php?type=dir';
3591
                    }
3592
                    break;
3593
                case 3 :
3594
                    if ($this->debug > 2) {
3595
                        error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Item type: ' . $lp_item_type, 0);
3596
                    }
3597
                    // Formatting AICC HACP append URL.
3598
                    $aicc_append = '?aicc_sid=' . urlencode(session_id()) . '&aicc_url=' . urlencode(api_get_path(WEB_CODE_PATH) . 'newscorm/aicc_hacp.php') . '&';
3599
                    if (!empty($lp_item_params)) {
3600
                        $aicc_append .= $lp_item_params . '&';
3601
                    }
3602
                    if ($lp_item_type != 'dir') {
3603
                        // Quite complex here:
3604
                        // We want to make sure 'http://' (and similar) links can
3605
                        // be loaded as is (withouth the Chamilo path in front) but
3606
                        // some contents use this form: resource.htm?resource=http://blablabla
3607
                        // which means we have to find a protocol at the path's start, otherwise
3608
                        // it should not be considered as an external URL.
3609
3610
                        if (preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path) != 0) {
3611
                            if ($this->debug > 2) {
3612
                                error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Found match for protocol in ' . $lp_item_path, 0);
3613
                            }
3614
                            // Distant url, return as is.
3615
                            $file = $lp_item_path;
3616
                            // Enabled and modified by Ivan Tcholakov, 16-OCT-2008.
3617
                            /*
3618
                            if (stristr($file,'<servername>') !== false) {
3619
                                $file = str_replace('<servername>', $course_path.'/scorm/'.$lp_path.'/', $lp_item_path);
3620
                            }
3621
                            */
3622
                            if (stripos($file, '<servername>') !== false) {
3623
                                //$file = str_replace('<servername>',$course_path.'/scorm/'.$lp_path.'/',$lp_item_path);
3624
                                $web_course_path = str_replace('https://', '', str_replace('http://', '', $course_path));
3625
                                $file = str_replace('<servername>', $web_course_path . '/scorm/' . $lp_path, $lp_item_path);
3626
                            }
3627
                            //
3628
                            $file .= $aicc_append;
3629
                        } else {
3630
                            if ($this->debug > 2) {
3631
                                error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - No starting protocol in ' . $lp_item_path, 0);
3632
                            }
3633
                            // Prevent getting untranslatable urls.
3634
                            $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path);
3635
                            $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path);
3636
                            // Prepare the path - lp_path might be unusable because it includes the "aicc" subdir name.
3637
                            $file = $course_path . '/scorm/' . $lp_path . '/' . $lp_item_path;
3638
                            // TODO: Fix this for urls with protocol header.
3639
                            $file = str_replace('//', '/', $file);
3640
                            $file = str_replace(':/', '://', $file);
3641
                            $file .= $aicc_append;
3642
                        }
3643
                    } else {
3644
                        $file = 'lp_content.php?type=dir';
3645
                    }
3646
                    break;
3647
                case 4 :
3648
                    break;
3649
                default :
3650
                    break;
3651
            }
3652
            // Replace &amp; by & because &amp; will break URL with params
3653
            $file = !empty($file) ? str_replace('&amp;', '&', $file) : '';
3654
        }
3655
        if ($this->debug > 2) {
3656
            error_log('New LP - In learnpath::get_link() - returning "' . $file . '" from get_link', 0);
3657
        }
3658
        return $file;
3659
    }
3660
3661
    /**
3662
     * Gets the latest usable view or generate a new one
3663
     * @param	integer	Optional attempt number. If none given, takes the highest from the lp_view table
3664
     * @return	integer	DB lp_view id
3665
     */
3666
    public function get_view($attempt_num = 0)
3667
    {
3668
        if ($this->debug > 0) {
3669
            error_log('New LP - In learnpath::get_view()', 0);
3670
        }
3671
        $search = '';
3672
        // Use $attempt_num to enable multi-views management (disabled so far).
3673
        if ($attempt_num != 0 AND intval(strval($attempt_num)) == $attempt_num) {
3674
            $search = 'AND view_count = ' . $attempt_num;
3675
        }
3676
        // When missing $attempt_num, search for a unique lp_view record for this lp and user.
3677
        $lp_view_table = Database :: get_course_table(TABLE_LP_VIEW);
3678
3679
        $course_id = api_get_course_int_id();
3680
        $sessionId = api_get_session_id();
3681
3682
        $sql = "SELECT id, view_count FROM $lp_view_table
3683
        		WHERE
3684
        		    c_id = " . $course_id . " AND
3685
        		    lp_id = " . $this->get_id() . " AND
3686
        		    user_id = " . $this->get_user_id() . " AND
3687
        		    session_id = $sessionId
3688
        		    $search
3689
                ORDER BY view_count DESC";
3690
        $res = Database::query($sql);
3691
        if (Database :: num_rows($res) > 0) {
3692
            $row = Database :: fetch_array($res);
3693
            $this->lp_view_id = $row['id'];
3694
        } else if (!api_is_invitee()) {
3695
            // There is no database record, create one.
3696
            $sql = "INSERT INTO $lp_view_table (c_id, lp_id,user_id, view_count, session_id) VALUES
3697
            		($course_id, " . $this->get_id() . "," . $this->get_user_id() . ", 1, $sessionId)";
3698
            Database::query($sql);
3699
            $id = Database :: insert_id();
3700
            $this->lp_view_id = $id;
3701
3702
            $sql = "UPDATE $lp_view_table SET id = iid WHERE iid = $id";
3703
            Database::query($sql);
3704
        }
3705
3706
        return $this->lp_view_id;
3707
    }
3708
3709
    /**
3710
     * Gets the current view id
3711
     * @return	integer	View ID (from lp_view)
3712
     */
3713
    public function get_view_id()
3714
    {
3715
        if ($this->debug > 0) {
3716
            error_log('New LP - In learnpath::get_view_id()', 0);
3717
        }
3718
        if (!empty ($this->lp_view_id)) {
3719
            return $this->lp_view_id;
3720
        } else {
3721
            return 0;
3722
        }
3723
    }
3724
3725
    /**
3726
     * Gets the update queue
3727
     * @return	array	Array containing IDs of items to be updated by JavaScript
3728
     */
3729
    public function get_update_queue()
3730
    {
3731
        if ($this->debug > 0) {
3732
            error_log('New LP - In learnpath::get_update_queue()', 0);
3733
        }
3734
        return $this->update_queue;
3735
    }
3736
3737
    /**
3738
     * Gets the user ID
3739
     * @return	integer	User ID
3740
     */
3741 View Code Duplication
    public function get_user_id()
3742
    {
3743
        if ($this->debug > 0) {
3744
            error_log('New LP - In learnpath::get_user_id()', 0);
3745
        }
3746
        if (!empty ($this->user_id)) {
3747
            return $this->user_id;
3748
        } else {
3749
            return false;
3750
        }
3751
    }
3752
3753
    /**
3754
     * Checks if any of the items has an audio element attached
3755
     * @return  bool    True or false
3756
     */
3757
    public function has_audio()
3758
    {
3759
        if ($this->debug > 1) {
3760
            error_log('New LP - In learnpath::has_audio()', 0);
3761
        }
3762
        $has = false;
3763
        foreach ($this->items as $i => $item) {
3764
            if (!empty ($this->items[$i]->audio)) {
3765
                $has = true;
3766
                break;
3767
            }
3768
        }
3769
        return $has;
3770
    }
3771
3772
    /**
3773
     * Logs a message into a file
3774
     * @param	string 	Message to log
3775
     * @return	boolean	True on success, false on error or if msg empty
3776
     */
3777
    public function log($msg)
3778
    {
3779
        if ($this->debug > 0) {
3780
            error_log('New LP - In learnpath::log()', 0);
3781
        }
3782
        // TODO
3783
        $this->error .= $msg;
3784
        return true;
3785
    }
3786
3787
    /**
3788
     * Moves an item up and down at its level
3789
     * @param	integer	Item to move up and down
3790
     * @param	string	Direction 'up' or 'down'
3791
     * @return	integer	New display order, or false on error
3792
     */
3793
    public function move_item($id, $direction)
3794
    {
3795
        $course_id = api_get_course_int_id();
3796
        if ($this->debug > 0) {
3797
            error_log('New LP - In learnpath::move_item(' . $id . ',' . $direction . ')', 0);
3798
        }
3799
        if (empty($id) || empty($direction)) {
3800
            return false;
3801
        }
3802
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
3803
        $sql_sel = "SELECT *
3804
                    FROM " . $tbl_lp_item . "
3805
                    WHERE c_id = ".$course_id." AND id = " . $id;
3806
        $res_sel = Database::query($sql_sel);
3807
        // Check if elem exists.
3808
        if (Database :: num_rows($res_sel) < 1) {
3809
            return false;
3810
        }
3811
        // Gather data.
3812
        $row = Database :: fetch_array($res_sel);
3813
        $previous = $row['previous_item_id'];
3814
        $next = $row['next_item_id'];
3815
        $display = $row['display_order'];
3816
        $parent = $row['parent_item_id'];
3817
        $lp = $row['lp_id'];
3818
        // Update the item (switch with previous/next one).
3819
        switch ($direction) {
3820
            case 'up':
3821
                if ($this->debug > 2) {
3822
                    error_log('Movement up detected', 0);
3823
                }
3824
                if ($display <= 1) { /*do nothing*/
3825
                } else {
3826
                    $sql_sel2 = "SELECT * FROM $tbl_lp_item
3827
                                 WHERE c_id = ".$course_id." AND id = $previous";
3828
3829
                    if ($this->debug > 2) {
3830
                        error_log('Selecting previous: ' . $sql_sel2, 0);
3831
                    }
3832
                    $res_sel2 = Database::query($sql_sel2);
3833
                    if (Database :: num_rows($res_sel2) < 1) {
3834
                        $previous_previous = 0;
3835
                    }
3836
                    // Gather data.
3837
                    $row2 = Database :: fetch_array($res_sel2);
3838
                    $previous_previous = $row2['previous_item_id'];
3839
                    // Update previous_previous item (switch "next" with current).
3840 View Code Duplication
                    if ($previous_previous != 0) {
3841
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3842
                                        next_item_id = $id
3843
                                    WHERE c_id = ".$course_id." AND id = $previous_previous";
3844
                        if ($this->debug > 2) {
3845
                            error_log($sql_upd2, 0);
3846
                        }
3847
                        Database::query($sql_upd2);
3848
                    }
3849
                    // Update previous item (switch with current).
3850 View Code Duplication
                    if ($previous != 0) {
3851
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3852
                                    next_item_id = $next,
3853
                                    previous_item_id = $id,
3854
                                    display_order = display_order +1
3855
                                    WHERE c_id = ".$course_id." AND id = $previous";
3856
                        if ($this->debug > 2) {
3857
                            error_log($sql_upd2, 0);
3858
                        }
3859
                        Database::query($sql_upd2);
3860
                    }
3861
3862
                    // Update current item (switch with previous).
3863 View Code Duplication
                    if ($id != 0) {
3864
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3865
                                        next_item_id = $previous,
3866
                                        previous_item_id = $previous_previous,
3867
                                        display_order = display_order-1
3868
                                    WHERE c_id = ".$course_id." AND id = $id";
3869
                        if ($this->debug > 2) {
3870
                            error_log($sql_upd2, 0);
3871
                        }
3872
                        Database::query($sql_upd2);
3873
                    }
3874
                    // Update next item (new previous item).
3875 View Code Duplication
                    if ($next != 0) {
3876
                        $sql_upd2 = "UPDATE $tbl_lp_item SET previous_item_id = $previous
3877
                                     WHERE c_id = ".$course_id." AND id = $next";
3878
                        if ($this->debug > 2) {
3879
                            error_log($sql_upd2, 0);
3880
                        }
3881
                        Database::query($sql_upd2);
3882
                    }
3883
                    $display = $display -1;
3884
                }
3885
                break;
3886
            case 'down':
3887
                if ($this->debug > 2) {
3888
                    error_log('Movement down detected', 0);
3889
                }
3890
                if ($next == 0) { /* Do nothing. */
3891
                } else {
3892
                    $sql_sel2 = "SELECT * FROM $tbl_lp_item WHERE c_id = ".$course_id." AND id = $next";
3893
                    if ($this->debug > 2) {
3894
                        error_log('Selecting next: ' . $sql_sel2, 0);
3895
                    }
3896
                    $res_sel2 = Database::query($sql_sel2);
3897
                    if (Database :: num_rows($res_sel2) < 1) {
3898
                        $next_next = 0;
3899
                    }
3900
                    // Gather data.
3901
                    $row2 = Database :: fetch_array($res_sel2);
3902
                    $next_next = $row2['next_item_id'];
3903
                    // Update previous item (switch with current).
3904 View Code Duplication
                    if ($previous != 0) {
3905
                        $sql_upd2 = "UPDATE $tbl_lp_item SET next_item_id = $next
3906
                                     WHERE c_id = ".$course_id." AND id = $previous";
3907
                        Database::query($sql_upd2);
3908
                    }
3909
                    // Update current item (switch with previous).
3910 View Code Duplication
                    if ($id != 0) {
3911
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3912
                                     previous_item_id = $next, next_item_id = $next_next, display_order = display_order+1
3913
                                     WHERE c_id = ".$course_id." AND id = $id";
3914
                        Database::query($sql_upd2);
3915
                    }
3916
3917
                    // Update next item (new previous item).
3918 View Code Duplication
                    if ($next != 0) {
3919
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3920
                                     previous_item_id = $previous, next_item_id = $id, display_order = display_order-1
3921
                                     WHERE c_id = ".$course_id." AND id = $next";
3922
                        Database::query($sql_upd2);
3923
                    }
3924
3925
                    // Update next_next item (switch "previous" with current).
3926 View Code Duplication
                    if ($next_next != 0) {
3927
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3928
                                     previous_item_id = $id
3929
                                     WHERE c_id = ".$course_id." AND id = $next_next";
3930
                        Database::query($sql_upd2);
3931
                    }
3932
                    $display = $display +1;
3933
                }
3934
                break;
3935
            default :
3936
                return false;
3937
        }
3938
        return $display;
3939
    }
3940
3941
    /**
3942
     * Move a learnpath up (display_order)
3943
     * @param	integer	$lp_id Learnpath ID
3944
     */
3945
    public static function move_up($lp_id)
3946
    {
3947
        $course_id = api_get_course_int_id();
3948
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
3949
        $sql = "SELECT * FROM $lp_table
3950
                WHERE c_id = ".$course_id."
3951
                ORDER BY display_order";
3952
        $res = Database::query($sql);
3953
        if ($res === false)
3954
            return false;
3955
        $lps = array ();
3956
        $lp_order = array ();
3957
        $num = Database :: num_rows($res);
3958
        // First check the order is correct, globally (might be wrong because
3959
        // of versions < 1.8.4)
3960 View Code Duplication
        if ($num > 0) {
3961
            $i = 1;
3962
            while ($row = Database :: fetch_array($res)) {
3963
                if ($row['display_order'] != $i) { // If we find a gap in the order, we need to fix it.
3964
                    $need_fix = true;
3965
                    $sql_u = "UPDATE $lp_table SET display_order = $i
3966
                              WHERE c_id = ".$course_id." AND id = " . $row['id'];
3967
                    Database::query($sql_u);
3968
                }
3969
                $row['display_order'] = $i;
3970
                $lps[$row['id']] = $row;
3971
                $lp_order[$i] = $row['id'];
3972
                $i++;
3973
            }
3974
        }
3975
        if ($num > 1) { // If there's only one element, no need to sort.
3976
            $order = $lps[$lp_id]['display_order'];
3977
            if ($order > 1) { // If it's the first element, no need to move up.
3978
                $sql_u1 = "UPDATE $lp_table SET display_order = $order
3979
                           WHERE c_id = ".$course_id." AND id = " . $lp_order[$order - 1];
3980
                Database::query($sql_u1);
3981
                $sql_u2 = "UPDATE $lp_table SET display_order = " . ($order - 1) . "
3982
                           WHERE c_id = ".$course_id." AND id = " . $lp_id;
3983
                Database::query($sql_u2);
3984
            }
3985
        }
3986
    }
3987
3988
    /**
3989
     * Move a learnpath down (display_order)
3990
     * @param	integer	$lp_id Learnpath ID
3991
     */
3992
    public static function move_down($lp_id)
3993
    {
3994
        $course_id = api_get_course_int_id();
3995
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
3996
        $sql = "SELECT * FROM $lp_table
3997
                WHERE c_id = ".$course_id."
3998
                ORDER BY display_order";
3999
        $res = Database::query($sql);
4000
        if ($res === false) {
4001
            return false;
4002
        }
4003
        $lps = array ();
4004
        $lp_order = array ();
4005
        $num = Database :: num_rows($res);
4006
        $max = 0;
4007
        // First check the order is correct, globally (might be wrong because
4008
        // of versions < 1.8.4).
4009 View Code Duplication
        if ($num > 0) {
4010
            $i = 1;
4011
            while ($row = Database :: fetch_array($res)) {
4012
                $max = $i;
4013
                if ($row['display_order'] != $i) { // If we find a gap in the order, we need to fix it.
4014
                    $need_fix = true;
4015
                    $sql_u = "UPDATE $lp_table SET display_order = $i
4016
                              WHERE c_id = ".$course_id." AND id = " . $row['id'];
4017
                    Database::query($sql_u);
4018
                }
4019
                $row['display_order'] = $i;
4020
                $lps[$row['id']] = $row;
4021
                $lp_order[$i] = $row['id'];
4022
                $i++;
4023
            }
4024
        }
4025
        if ($num > 1) { // If there's only one element, no need to sort.
4026
            $order = $lps[$lp_id]['display_order'];
4027
            if ($order < $max) { // If it's the first element, no need to move up.
4028
                $sql_u1 = "UPDATE $lp_table SET display_order = $order
4029
                           WHERE c_id = ".$course_id." AND id = " . $lp_order[$order + 1];
4030
                Database::query($sql_u1);
4031
                $sql_u2 = "UPDATE $lp_table SET display_order = " . ($order + 1) . "
4032
                           WHERE c_id = ".$course_id." AND id = " . $lp_id;
4033
                Database::query($sql_u2);
4034
            }
4035
        }
4036
    }
4037
4038
    /**
4039
     * Updates learnpath attributes to point to the next element
4040
     * The last part is similar to set_current_item but processing the other way around
4041
     */
4042
    public function next()
4043
    {
4044
        if ($this->debug > 0) {
4045
            error_log('New LP - In learnpath::next()', 0);
4046
        }
4047
        $this->last = $this->get_current_item_id();
4048
        $this->items[$this->last]->save(false, $this->prerequisites_match($this->last));
4049
        $this->autocomplete_parents($this->last);
4050
        $new_index = $this->get_next_index();
4051
        if ($this->debug > 2) {
4052
            error_log('New LP - New index: ' . $new_index, 0);
4053
        }
4054
        $this->index = $new_index;
4055
        if ($this->debug > 2) {
4056
            error_log('New LP - Now having orderedlist[' . $new_index . '] = ' . $this->ordered_items[$new_index], 0);
4057
        }
4058
        $this->current = $this->ordered_items[$new_index];
4059
        if ($this->debug > 2) {
4060
            error_log('New LP - new item id is ' . $this->current . '-' . $this->get_current_item_id(), 0);
4061
        }
4062
    }
4063
4064
    /**
4065
     * Open a resource = initialise all local variables relative to this resource. Depending on the child
4066
     * class, this might be redefined to allow several behaviours depending on the document type.
4067
     * @param integer Resource ID
4068
     * @return boolean True on success, false otherwise
4069
     */
4070
    public function open($id)
4071
    {
4072
        if ($this->debug > 0) {
4073
            error_log('New LP - In learnpath::open()', 0);
4074
        }
4075
        // TODO:
4076
        // set the current resource attribute to this resource
4077
        // switch on element type (redefine in child class?)
4078
        // set status for this item to "opened"
4079
        // start timer
4080
        // initialise score
4081
        $this->index = 0; //or = the last item seen (see $this->last)
4082
    }
4083
4084
    /**
4085
     * Check that all prerequisites are fulfilled. Returns true and an
4086
     * empty string on success, returns false
4087
     * and the prerequisite string on error.
4088
     * This function is based on the rules for aicc_script language as
4089
     * described in the SCORM 1.2 CAM documentation page 108.
4090
     * @param	integer	$itemId Optional item ID. If none given, uses the current open item.
4091
     * @return	boolean	True if prerequisites are matched, false otherwise -
4092
     * Empty string if true returned, prerequisites string otherwise.
4093
     */
4094
    public function prerequisites_match($itemId = null)
4095
    {
4096
        $debug = $this->debug;
4097
        if ($debug > 0) {
4098
            error_log('In learnpath::prerequisites_match()', 0);
4099
        }
4100
4101
        if (empty($itemId)) {
4102
            $itemId = $this->current;
4103
        }
4104
4105
        $currentItem = $this->getItem($itemId);
4106
4107
        if ($currentItem) {
4108
            if ($this->type == 2) {
4109
                // Getting prereq from scorm
4110
                $prereq_string = $this->get_scorm_prereq_string($itemId);
4111
            } else {
4112
                $prereq_string = $currentItem->get_prereq_string();
4113
            }
4114
4115
            if (empty($prereq_string)) {
4116
                if ($debug > 0) {
4117
                    error_log('Found prereq_string is empty return true');
4118
                }
4119
                return true;
4120
            }
4121
            // Clean spaces.
4122
            $prereq_string = str_replace(' ', '', $prereq_string);
4123
            if ($debug > 0) {
4124
                error_log('Found prereq_string: ' . $prereq_string, 0);
4125
            }
4126
            // Now send to the parse_prereq() function that will check this component's prerequisites.
4127
            $result = $currentItem->parse_prereq(
4128
                $prereq_string,
4129
                $this->items,
4130
                $this->refs_list,
4131
                $this->get_user_id()
4132
            );
4133
4134
            if ($result === false) {
4135
                $this->set_error_msg($currentItem->prereq_alert);
4136
            }
4137
        } else {
4138
            $result = true;
4139
            if ($debug > 1) {
4140
                error_log('$this->items[' . $itemId . '] was not an object', 0);
4141
            }
4142
        }
4143
4144
        if ($debug > 1) {
4145
            error_log('End of prerequisites_match(). Error message is now ' . $this->error, 0);
4146
        }
4147
        return $result;
4148
    }
4149
4150
    /**
4151
     * Updates learnpath attributes to point to the previous element
4152
     * The last part is similar to set_current_item but processing the other way around
4153
     */
4154
    public function previous()
4155
    {
4156
        if ($this->debug > 0) {
4157
            error_log('New LP - In learnpath::previous()', 0);
4158
        }
4159
        $this->last = $this->get_current_item_id();
4160
        $this->items[$this->last]->save(false, $this->prerequisites_match($this->last));
4161
        $this->autocomplete_parents($this->last);
4162
        $new_index = $this->get_previous_index();
4163
        $this->index = $new_index;
4164
        $this->current = $this->ordered_items[$new_index];
4165
    }
4166
4167
    /**
4168
     * Publishes a learnpath. This basically means show or hide the learnpath
4169
     * to normal users.
4170
     * Can be used as abstract
4171
     * @param	integer	Learnpath ID
4172
     * @param	string	New visibility
4173
     */
4174
    public static function toggle_visibility($lp_id, $set_visibility = 1)
4175
    {
4176
        $action = 'visible';
4177
        if ($set_visibility != 1) {
4178
            $action = 'invisible';
4179
            self::toggle_publish($lp_id, 'i');
4180
        }
4181
4182
        return api_item_property_update(
4183
            api_get_course_info(),
4184
            TOOL_LEARNPATH,
4185
            $lp_id,
4186
            $action,
4187
            api_get_user_id()
4188
        );
4189
    }
4190
4191
    /**
4192
     * Publishes a learnpath. This basically means show or hide the learnpath
4193
     * on the course homepage
4194
     * Can be used as abstract
4195
     * @param	integer	$lp_id Learnpath id
4196
     * @param	string	$set_visibility New visibility (v/i - visible/invisible)
4197
     * @return bool
4198
     */
4199
    public static function toggle_publish($lp_id, $set_visibility = 'v')
4200
    {
4201
        $course_id = api_get_course_int_id();
4202
        $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
4203
        $lp_id = intval($lp_id);
4204
        $sql = "SELECT * FROM $tbl_lp
4205
                WHERE c_id = ".$course_id." AND id = $lp_id";
4206
        $result = Database::query($sql);
4207
        if (Database::num_rows($result)) {
4208
            $row = Database :: fetch_array($result);
4209
            $name = domesticate($row['name']);
4210
            if ($set_visibility == 'i') {
4211
                $s = $name . " " . get_lang('LearnpathNotPublished');
4212
                $dialogBox = $s;
4213
                $v = 0;
4214
            }
4215
            if ($set_visibility == 'v') {
4216
                $s = $name . " " . get_lang('LearnpathPublished');
4217
                $dialogBox = $s;
4218
                $v = 1;
4219
            }
4220
        } else {
4221
            return false;
4222
        }
4223
4224
        $session_id = api_get_session_id();
4225
        $session_condition = api_get_session_condition($session_id);
4226
4227
        $tbl_tool = Database :: get_course_table(TABLE_TOOL_LIST);
4228
        $link = 'newscorm/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session='.$session_id;
4229
        $sql = "SELECT * FROM $tbl_tool
4230
                WHERE
4231
                    c_id = ".$course_id." AND
4232
                    link='$link' and
4233
                    image='scormbuilder.gif' and
4234
                    link LIKE '$link%'
4235
                    $session_condition
4236
                ";
4237
        $result = Database::query($sql);
4238
        $num = Database :: num_rows($result);
4239
        if ($set_visibility == 'i' && $num > 0) {
4240
            $sql = "DELETE FROM $tbl_tool
4241
                    WHERE c_id = ".$course_id." AND (link='$link' and image='scormbuilder.gif' $session_condition)";
4242
            Database::query($sql);
4243
4244
        } elseif ($set_visibility == 'v' && $num == 0) {
4245
            $sql = "INSERT INTO $tbl_tool (category, c_id, name, link, image, visibility, admin, address, added_tool, session_id) VALUES
4246
            	    ('authoring', $course_id, '$name', '$link', 'scormbuilder.gif', '$v', '0','pastillegris.gif', 0, $session_id)";
4247
            Database::query($sql);
4248
4249
            $insertId = Database::insert_id();
4250
            if ($insertId) {
4251
                $sql = "UPDATE $tbl_tool SET id = iid WHERE iid = $insertId";
4252
                Database::query($sql);
4253
            }
4254
        } elseif ($set_visibility == 'v' && $num > 0) {
4255
            $sql = "UPDATE $tbl_tool SET
4256
                        c_id = $course_id,
4257
                        name = '$name',
4258
                        link = '$link',
4259
                        image = 'scormbuilder.gif',
4260
                        visibility = '$v',
4261
                        admin = '0',
4262
                        address = 'pastillegris.gif',
4263
                        added_tool = 0,
4264
                        session_id = $session_id
4265
            	    WHERE
4266
            	        c_id = ".$course_id." AND
4267
            	        (link='$link' and image='scormbuilder.gif' $session_condition)
4268
                    ";
4269
            Database::query($sql);
4270
        } else {
4271
            // Parameter and database incompatible, do nothing, exit.
4272
            return false;
4273
        }
4274
4275
    }
4276
4277
    /**
4278
     * Restart the whole learnpath. Return the URL of the first element.
4279
     * Make sure the results are saved with anoter method. This method should probably be
4280
     * redefined in children classes.
4281
     * To use a similar method  statically, use the create_new_attempt() method
4282
     * @return string URL to load in the viewer
4283
     */
4284
    public function restart()
4285
    {
4286
        if ($this->debug > 0) {
4287
            error_log('New LP - In learnpath::restart()', 0);
4288
        }
4289
        // TODO
4290
        // Call autosave method to save the current progress.
4291
        //$this->index = 0;
4292
        if (api_is_invitee()) {
4293
            return false;
4294
        }
4295
        $session_id = api_get_session_id();
4296
        $course_id = api_get_course_int_id();
4297
        $lp_view_table = Database :: get_course_table(TABLE_LP_VIEW);
4298
        $sql = "INSERT INTO $lp_view_table (c_id, lp_id, user_id, view_count, session_id)
4299
                VALUES ($course_id, " . $this->lp_id . "," . $this->get_user_id() . "," . ($this->attempt + 1) . ", $session_id)";
4300
        if ($this->debug > 2) {
4301
            error_log('New LP - Inserting new lp_view for restart: ' . $sql, 0);
4302
        }
4303
        $res = Database::query($sql);
4304
        $view_id = Database::insert_id();
4305
4306
        if ($view_id) {
4307
4308
            $sql = "UPDATE $lp_view_table SET id = iid WHERE iid = $view_id";
4309
            Database::query($sql);
4310
4311
            $this->lp_view_id = $view_id;
4312
            $this->attempt = $this->attempt + 1;
4313
        } else {
4314
            $this->error = 'Could not insert into item_view table...';
4315
            return false;
4316
        }
4317
        $this->autocomplete_parents($this->current);
4318
        foreach ($this->items as $index => $dummy) {
4319
            $this->items[$index]->restart();
4320
            $this->items[$index]->set_lp_view($this->lp_view_id);
4321
        }
4322
        $this->first();
4323
4324
        return true;
4325
    }
4326
4327
    /**
4328
     * Saves the current item
4329
     * @return	boolean
4330
     */
4331
    public function save_current()
4332
    {
4333
        if ($this->debug > 0) {
4334
            error_log('learnpath::save_current()', 0);
4335
        }
4336
        // TODO: Do a better check on the index pointing to the right item (it is supposed to be working
4337
        // on $ordered_items[] but not sure it's always safe to use with $items[]).
4338
        if ($this->debug > 2) {
4339
            error_log('New LP - save_current() saving item ' . $this->current, 0);
4340
        }
4341
        if ($this->debug > 2) {
4342
            error_log('' . print_r($this->items, true), 0);
4343
        }
4344
        if (isset($this->items[$this->current]) &&
4345
            is_object($this->items[$this->current])
4346
        ) {
4347
            $res = $this->items[$this->current]->save(false, $this->prerequisites_match($this->current));
4348
            $this->autocomplete_parents($this->current);
4349
            $status = $this->items[$this->current]->get_status();
4350
            $this->update_queue[$this->current] = $status;
4351
            return $res;
4352
        }
4353
        return false;
4354
    }
4355
4356
    /**
4357
     * Saves the given item
4358
     * @param	integer	$item_id. Optional (will take from $_REQUEST if null)
4359
     * @param	boolean	$from_outside Save from url params (true) or from current attributes (false). Optional. Defaults to true
4360
     * @return	boolean
4361
     */
4362
    public function save_item($item_id = null, $from_outside = true)
4363
    {
4364
        $debug = $this->debug;
4365
        if ($debug) {
4366
            error_log('In learnpath::save_item(' . $item_id . ',' . intval($from_outside). ')', 0);
4367
        }
4368
        // TODO: Do a better check on the index pointing to the right item (it is supposed to be working
4369
        // on $ordered_items[] but not sure it's always safe to use with $items[]).
4370
        if (empty($item_id)) {
4371
            $item_id = intval($_REQUEST['id']);
4372
        }
4373
        if (empty($item_id)) {
4374
            $item_id = $this->get_current_item_id();
4375
        }
4376
        if (isset($this->items[$item_id]) && is_object($this->items[$item_id])) {
4377
            if ($debug) {
4378
                error_log('Object exists');
4379
            }
4380
4381
            // Saving the item.
4382
            $res = $this->items[$item_id]->save(
4383
                $from_outside,
4384
                $this->prerequisites_match($item_id)
4385
            );
4386
4387
            if ($debug) {
4388
                error_log('update_queue before:');
4389
                error_log(print_r($this->update_queue,1));
4390
            }
4391
            $this->autocomplete_parents($item_id);
4392
4393
            $status = $this->items[$item_id]->get_status();
4394
            $this->update_queue[$item_id] = $status;
4395
4396
            if ($debug) {
4397
                error_log('get_status(): ' . $status);
4398
                error_log('update_queue after:');
4399
                error_log(print_r($this->update_queue,1));
4400
            }
4401
            return $res;
4402
        }
4403
        return false;
4404
    }
4405
4406
    /**
4407
     * Saves the last item seen's ID only in case
4408
     */
4409
    public function save_last()
4410
    {
4411
        $course_id = api_get_course_int_id();
4412
        if ($this->debug > 0) {
4413
            error_log('New LP - In learnpath::save_last()', 0);
4414
        }
4415
        $session_condition = api_get_session_condition(api_get_session_id(), true, false);
4416
        $table = Database :: get_course_table(TABLE_LP_VIEW);
4417
4418
        if (isset($this->current) && !api_is_invitee()) {
4419
            if ($this->debug > 2) {
4420
                error_log('New LP - Saving current item (' . $this->current . ') for later review', 0);
4421
            }
4422
            $sql = "UPDATE $table SET
4423
                        last_item = " . intval($this->get_current_item_id()). "
4424
                    WHERE
4425
                        c_id = $course_id AND
4426
                        lp_id = " . $this->get_id() . " AND
4427
                        user_id = " . $this->get_user_id()." ".$session_condition;
4428
4429
            if ($this->debug > 2) {
4430
                error_log('New LP - Saving last item seen : ' . $sql, 0);
4431
            }
4432
            Database::query($sql);
4433
        }
4434
4435
        if (!api_is_invitee()) {
4436
            // Save progress.
4437
            list($progress, $text) = $this->get_progress_bar_text('%');
4438
            if ($progress >= 0 && $progress <= 100) {
4439
                $progress = (int) $progress;
4440
                $sql = "UPDATE $table SET
4441
                            progress = $progress
4442
                        WHERE
4443
                            c_id = ".$course_id." AND
4444
                            lp_id = " . $this->get_id() . " AND
4445
                            user_id = " . $this->get_user_id()." ".$session_condition;
4446
                // Ignore errors as some tables might not have the progress field just yet.
4447
                Database::query($sql);
4448
                $this->progress_db = $progress;
4449
            }
4450
        }
4451
    }
4452
4453
    /**
4454
     * Sets the current item ID (checks if valid and authorized first)
4455
     * @param	integer	$item_id New item ID. If not given or not authorized, defaults to current
4456
     */
4457
    public function set_current_item($item_id = null)
4458
    {
4459
        if ($this->debug > 0) {
4460
            error_log('New LP - In learnpath::set_current_item(' . $item_id . ')', 0);
4461
        }
4462
        if (empty ($item_id)) {
4463
            if ($this->debug > 2) {
4464
                error_log('New LP - No new current item given, ignore...', 0);
4465
            }
4466
            // Do nothing.
4467
        } else {
4468
            if ($this->debug > 2) {
4469
                error_log('New LP - New current item given is ' . $item_id . '...', 0);
4470
            }
4471
            if (is_numeric($item_id)) {
4472
                $item_id = intval($item_id);
4473
                // TODO: Check in database here.
4474
                $this->last = $this->current;
4475
                $this->current = $item_id;
4476
                // TODO: Update $this->index as well.
4477
                foreach ($this->ordered_items as $index => $item) {
4478
                    if ($item == $this->current) {
4479
                        $this->index = $index;
4480
                        break;
4481
                    }
4482
                }
4483 View Code Duplication
                if ($this->debug > 2) {
4484
                    error_log('New LP - set_current_item(' . $item_id . ') done. Index is now : ' . $this->index, 0);
4485
                }
4486
            } else {
4487
                error_log('New LP - set_current_item(' . $item_id . ') failed. Not a numeric value: ', 0);
4488
            }
4489
        }
4490
    }
4491
4492
    /**
4493
     * Sets the encoding
4494
     * @param	string	New encoding
4495
     * TODO (as of Chamilo 1.8.8): Check in the future whether this method is needed.
4496
     */
4497
    public function set_encoding($enc = 'UTF-8')
4498
    {
4499
        if ($this->debug > 0) {
4500
            error_log('New LP - In learnpath::set_encoding()', 0);
4501
        }
4502
4503
        $course_id = api_get_course_int_id();
4504
        $enc = api_refine_encoding_id($enc);
4505
        if (empty($enc)) {
4506
            $enc = api_get_system_encoding();
4507
        }
4508
        if (api_is_encoding_supported($enc)) {
4509
            $lp = $this->get_id();
4510
            if ($lp != 0) {
4511
                $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
4512
                $sql = "UPDATE $tbl_lp SET default_encoding = '$enc' WHERE c_id = ".$course_id." AND id = " . $lp;
4513
                $res = Database::query($sql);
4514
                return $res;
4515
            }
4516
        }
4517
        return false;
4518
    }
4519
4520
    /**
4521
     * Sets the JS lib setting in the database directly.
4522
     * This is the JavaScript library file this lp needs to load on startup
4523
     * @param	string	Proximity setting
4524
     * @return  boolean True on update success. False otherwise.
4525
     */
4526 View Code Duplication
    public function set_jslib($lib = '')
4527
    {
4528
        if ($this->debug > 0) {
4529
            error_log('New LP - In learnpath::set_jslib()', 0);
4530
        }
4531
        $lp = $this->get_id();
4532
        $course_id = api_get_course_int_id();
4533
4534
        if ($lp != 0) {
4535
            $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
4536
            $sql = "UPDATE $tbl_lp SET js_lib = '$lib' WHERE c_id = ".$course_id." AND id = " . $lp;
4537
            $res = Database::query($sql);
4538
            return $res;
4539
        } else {
4540
            return false;
4541
        }
4542
    }
4543
4544
    /**
4545
     * Sets the name of the LP maker (publisher) (and save)
4546
     * @param	string	Optional string giving the new content_maker of this learnpath
4547
     * @return  boolean True
4548
     */
4549 View Code Duplication
    public function set_maker($name = '')
4550
    {
4551
        if ($this->debug > 0) {
4552
            error_log('New LP - In learnpath::set_maker()', 0);
4553
        }
4554
        if (empty ($name))
4555
            return false;
4556
        $this->maker = $name;
4557
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4558
        $course_id = api_get_course_int_id();
4559
        $lp_id = $this->get_id();
4560
        $sql = "UPDATE $lp_table SET
4561
                content_maker = '" . Database::escape_string($this->maker) . "'
4562
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4563
        if ($this->debug > 2) {
4564
            error_log('New LP - lp updated with new content_maker : ' . $this->maker, 0);
4565
        }
4566
        Database::query($sql);
4567
        return true;
4568
    }
4569
4570
    /**
4571
     * Sets the name of the current learnpath (and save)
4572
     * @param	string	$name Optional string giving the new name of this learnpath
4573
     * @return  boolean True/False
4574
     */
4575
    public function set_name($name = null)
4576
    {
4577
        if ($this->debug > 0) {
4578
            error_log('New LP - In learnpath::set_name()', 0);
4579
        }
4580
        if (empty($name)) {
4581
            return false;
4582
        }
4583
        $this->name = Database::escape_string($name);
4584
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4585
        $lp_id = $this->get_id();
4586
        $course_id = $this->course_info['real_id'];
4587
        $sql = "UPDATE $lp_table SET
4588
                name = '" . Database::escape_string($this->name). "'
4589
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4590
        if ($this->debug > 2) {
4591
            error_log('New LP - lp updated with new name : ' . $this->name, 0);
4592
        }
4593
        $result = Database::query($sql);
4594
        // If the lp is visible on the homepage, change his name there.
4595
        if (Database::affected_rows($result)) {
4596
            $session_id = api_get_session_id();
4597
            $session_condition = api_get_session_condition($session_id);
4598
            $tbl_tool = Database :: get_course_table(TABLE_TOOL_LIST);
4599
            $link = 'newscorm/lp_controller.php?action=view&lp_id=' . $lp_id.'&id_session='.$session_id;
4600
            $sql = "UPDATE $tbl_tool SET name = '$this->name'
4601
            	    WHERE
4602
            	        c_id = $course_id AND
4603
            	        (link='$link' AND image='scormbuilder.gif' $session_condition)";
4604
            Database::query($sql);
4605
            return true;
4606
        } else {
4607
            return false;
4608
        }
4609
    }
4610
4611
    /**
4612
     * Set index specified prefix terms for all items in this path
4613
     * @param   string  Comma-separated list of terms
4614
     * @param   char Xapian term prefix
4615
     * @return  boolean False on error, true otherwise
4616
     */
4617
    public function set_terms_by_prefix($terms_string, $prefix)
4618
    {
4619
        $course_id = api_get_course_int_id();
4620
        if (api_get_setting('search_enabled') !== 'true')
4621
            return false;
4622
4623
        if (!extension_loaded('xapian')) {
4624
            return false;
4625
        }
4626
4627
        $terms_string = trim($terms_string);
4628
        $terms = explode(',', $terms_string);
4629
        array_walk($terms, 'trim_value');
4630
4631
        $stored_terms = $this->get_common_index_terms_by_prefix($prefix);
4632
4633
        // Don't do anything if no change, verify only at DB, not the search engine.
4634 View Code Duplication
        if ((count(array_diff($terms, $stored_terms)) == 0) && (count(array_diff($stored_terms, $terms)) == 0))
4635
            return false;
4636
4637
        require_once 'xapian.php'; // TODO: Try catch every xapian use or make wrappers on API.
4638
        require_once api_get_path(LIBRARY_PATH).'search/ChamiloIndexer.class.php';
4639
        require_once api_get_path(LIBRARY_PATH).'search/xapian/XapianQuery.php';
4640
        require_once api_get_path(LIBRARY_PATH).'search/IndexableChunk.class.php';
4641
4642
        $items_table = Database :: get_course_table(TABLE_LP_ITEM);
4643
        // TODO: Make query secure agains XSS : use member attr instead of post var.
4644
        $lp_id = intval($_POST['lp_id']);
4645
        $sql = "SELECT * FROM $items_table WHERE c_id = $course_id AND lp_id = $lp_id";
4646
        $result = Database::query($sql);
4647
        $di = new ChamiloIndexer();
4648
4649
        while ($lp_item = Database :: fetch_array($result)) {
4650
            // Get search_did.
4651
            $tbl_se_ref = Database :: get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
4652
            $sql = 'SELECT * FROM %s
4653
                    WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d
4654
                    LIMIT 1';
4655
            $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp_id, $lp_item['id']);
4656
4657
            //echo $sql; echo '<br>';
4658
            $res = Database::query($sql);
4659
            if (Database::num_rows($res) > 0) {
4660
                $se_ref = Database :: fetch_array($res);
4661
4662
                // Compare terms.
4663
                $doc = $di->get_document($se_ref['search_did']);
4664
                $xapian_terms = xapian_get_doc_terms($doc, $prefix);
4665
                $xterms = array();
4666
                foreach ($xapian_terms as $xapian_term) {
4667
                    $xterms[] = substr($xapian_term['name'], 1);
4668
                }
4669
4670
                $dterms = $terms;
4671
4672
                $missing_terms = array_diff($dterms, $xterms);
4673
                $deprecated_terms = array_diff($xterms, $dterms);
4674
4675
                // Save it to search engine.
4676
                foreach ($missing_terms as $term) {
4677
                    $doc->add_term($prefix . $term, 1);
4678
                }
4679
                foreach ($deprecated_terms as $term) {
4680
                    $doc->remove_term($prefix . $term);
4681
                }
4682
                $di->getDb()->replace_document((int) $se_ref['search_did'], $doc);
4683
                $di->getDb()->flush();
4684
            } else {
4685
                //@todo What we should do here?
4686
            }
4687
        }
4688
        return true;
4689
    }
4690
4691
    /**
4692
     * Sets the theme of the LP (local/remote) (and save)
4693
     * @param	string	Optional string giving the new theme of this learnpath
4694
     * @return   bool    Returns true if theme name is not empty
4695
     */
4696 View Code Duplication
    public function set_theme($name = '')
4697
    {
4698
        $course_id = api_get_course_int_id();
4699
        if ($this->debug > 0) {
4700
            error_log('New LP - In learnpath::set_theme()', 0);
4701
        }
4702
        $this->theme = $name;
4703
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4704
        $lp_id = $this->get_id();
4705
        $sql = "UPDATE $lp_table SET theme = '" . Database::escape_string($this->theme). "'
4706
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4707
        if ($this->debug > 2) {
4708
            error_log('New LP - lp updated with new theme : ' . $this->theme, 0);
4709
        }
4710
        Database::query($sql);
4711
4712
        return true;
4713
    }
4714
4715
    /**
4716
     * Sets the image of an LP (and save)
4717
     * @param	 string	Optional string giving the new image of this learnpath
4718
     * @return bool   Returns true if theme name is not empty
4719
     */
4720 View Code Duplication
    public function set_preview_image($name = '')
4721
    {
4722
        $course_id = api_get_course_int_id();
4723
        if ($this->debug > 0) {
4724
            error_log('New LP - In learnpath::set_preview_image()', 0);
4725
        }
4726
4727
        $this->preview_image = $name;
4728
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4729
        $lp_id = $this->get_id();
4730
        $sql = "UPDATE $lp_table SET
4731
                preview_image = '" . Database::escape_string($this->preview_image). "'
4732
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4733
        if ($this->debug > 2) {
4734
            error_log('New LP - lp updated with new preview image : ' . $this->preview_image, 0);
4735
        }
4736
        Database::query($sql);
4737
        return true;
4738
    }
4739
4740
    /**
4741
     * Sets the author of a LP (and save)
4742
     * @param	string	Optional string giving the new author of this learnpath
4743
     * @return   bool    Returns true if author's name is not empty
4744
     */
4745 View Code Duplication
    public function set_author($name = '')
4746
    {
4747
        $course_id = api_get_course_int_id();
4748
        if ($this->debug > 0) {
4749
            error_log('New LP - In learnpath::set_author()', 0);
4750
        }
4751
        $this->author = $name;
4752
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4753
        $lp_id = $this->get_id();
4754
        $sql = "UPDATE $lp_table SET author = '" . Database::escape_string($name). "'
4755
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4756
        if ($this->debug > 2) {
4757
            error_log('New LP - lp updated with new preview author : ' . $this->author, 0);
4758
        }
4759
        Database::query($sql);
4760
4761
        return true;
4762
    }
4763
4764
    /**
4765
     * Sets the hide_toc_frame parameter of a LP (and save)
4766
     * @param	int	1 if frame is hidden 0 then else
4767
     * @return   bool    Returns true if author's name is not empty
4768
     */
4769 View Code Duplication
    public function set_hide_toc_frame($hide)
4770
    {
4771
        $course_id = api_get_course_int_id();
4772
        if ($this->debug > 0) {
4773
            error_log('New LP - In learnpath::set_hide_toc_frame()', 0);
4774
        }
4775
        if (intval($hide) == $hide){
4776
            $this->hide_toc_frame = $hide;
4777
            $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4778
            $lp_id = $this->get_id();
4779
            $sql = "UPDATE $lp_table SET
4780
                    hide_toc_frame = '" . $this->hide_toc_frame . "'
4781
                    WHERE c_id = ".$course_id." AND id = '$lp_id'";
4782
            if ($this->debug > 2) {
4783
                error_log('New LP - lp updated with new preview hide_toc_frame : ' . $this->author, 0);
4784
            }
4785
            Database::query($sql);
4786
4787
            return true;
4788
        } else {
4789
            return false;
4790
        }
4791
    }
4792
4793
    /**
4794
     * Sets the prerequisite of a LP (and save)
4795
     * @param	int		integer giving the new prerequisite of this learnpath
4796
     * @return 	bool 	returns true if prerequisite is not empty
4797
     */
4798 View Code Duplication
    public function set_prerequisite($prerequisite)
4799
    {
4800
        $course_id = api_get_course_int_id();
4801
        if ($this->debug > 0) {
4802
            error_log('New LP - In learnpath::set_prerequisite()', 0);
4803
        }
4804
        $this->prerequisite = intval($prerequisite);
4805
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4806
        $lp_id = $this->get_id();
4807
        $sql = "UPDATE $lp_table SET prerequisite = '".$this->prerequisite."'
4808
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4809
        if ($this->debug > 2) {
4810
            error_log('New LP - lp updated with new preview requisite : ' . $this->requisite, 0);
4811
        }
4812
        Database::query($sql);
4813
        return true;
4814
    }
4815
4816
    /**
4817
     * Sets the location/proximity of the LP (local/remote) (and save)
4818
     * @param	string	Optional string giving the new location of this learnpath
4819
     * @return  boolean True on success / False on error
4820
     */
4821
    public function set_proximity($name = '')
4822
    {
4823
        $course_id = api_get_course_int_id();
4824
        if ($this->debug > 0) {
4825
            error_log('New LP - In learnpath::set_proximity()', 0);
4826
        }
4827
        if (empty ($name))
4828
            return false;
4829
4830
        $this->proximity = $name;
4831
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4832
        $lp_id = $this->get_id();
4833
        $sql = "UPDATE $lp_table SET
4834
                    content_local = '" . Database::escape_string($name) . "'
4835
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4836
        if ($this->debug > 2) {
4837
            error_log('New LP - lp updated with new proximity : ' . $this->proximity, 0);
4838
        }
4839
        Database::query($sql);
4840
        return true;
4841
    }
4842
4843
    /**
4844
     * Sets the previous item ID to a given ID. Generally, this should be set to the previous 'current' item
4845
     * @param	integer	DB ID of the item
4846
     */
4847
    public function set_previous_item($id)
4848
    {
4849
        if ($this->debug > 0) {
4850
            error_log('New LP - In learnpath::set_previous_item()', 0);
4851
        }
4852
        $this->last = $id;
4853
    }
4854
4855
    /**
4856
     * Sets use_max_score
4857
     * @param   string  $use_max_score Optional string giving the new location of this learnpath
4858
     * @return  boolean True on success / False on error
4859
     */
4860
    public function set_use_max_score($use_max_score = 1)
4861
    {
4862
        $course_id = api_get_course_int_id();
4863
        if ($this->debug > 0) {
4864
            error_log('New LP - In learnpath::set_use_max_score()', 0);
4865
        }
4866
        $use_max_score = intval($use_max_score);
4867
        $this->use_max_score = $use_max_score;
4868
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4869
        $lp_id = $this->get_id();
4870
        $sql = "UPDATE $lp_table SET
4871
                    use_max_score = '" . $this->use_max_score . "'
4872
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4873
4874
        if ($this->debug > 2) {
4875
            error_log('New LP - lp updated with new use_max_score : ' . $this->use_max_score, 0);
4876
        }
4877
        Database::query($sql);
4878
4879
        return true;
4880
    }
4881
4882
    /**
4883
     * Sets and saves the expired_on date
4884
     * @param   string  $expired_on Optional string giving the new author of this learnpath
4885
     * @return   bool    Returns true if author's name is not empty
4886
     */
4887 View Code Duplication
    public function set_expired_on($expired_on)
4888
    {
4889
        $course_id = api_get_course_int_id();
4890
        if ($this->debug > 0) {
4891
            error_log('New LP - In learnpath::set_expired_on()', 0);
4892
        }
4893
4894
        if (!empty($expired_on)) {
4895
            $this->expired_on = api_get_utc_datetime($expired_on);
4896
        } else {
4897
            $this->expired_on = '';
4898
        }
4899
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4900
        $lp_id = $this->get_id();
4901
        $sql = "UPDATE $lp_table SET
4902
                expired_on = '" . Database::escape_string($this->expired_on) . "'
4903
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4904
        if ($this->debug > 2) {
4905
            error_log('New LP - lp updated with new expired_on : ' . $this->expired_on, 0);
4906
        }
4907
        Database::query($sql);
4908
4909
        return true;
4910
    }
4911
4912
    /**
4913
     * Sets and saves the publicated_on date
4914
     * @param   string  $publicated_on Optional string giving the new author of this learnpath
4915
     * @return   bool    Returns true if author's name is not empty
4916
     */
4917 View Code Duplication
    public function set_publicated_on($publicated_on)
4918
    {
4919
        $course_id = api_get_course_int_id();
4920
        if ($this->debug > 0) {
4921
            error_log('New LP - In learnpath::set_expired_on()', 0);
4922
        }
4923
        if (!empty($publicated_on)) {
4924
            $this->publicated_on = api_get_utc_datetime($publicated_on);
4925
        } else {
4926
            $this->publicated_on = '';
4927
        }
4928
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4929
        $lp_id = $this->get_id();
4930
        $sql = "UPDATE $lp_table SET
4931
                publicated_on = '" . Database::escape_string($this->publicated_on) . "'
4932
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4933
        if ($this->debug > 2) {
4934
            error_log('New LP - lp updated with new publicated_on : ' . $this->publicated_on, 0);
4935
        }
4936
        Database::query($sql);
4937
4938
        return true;
4939
    }
4940
4941
    /**
4942
     * Sets and saves the expired_on date
4943
     * @return   bool    Returns true if author's name is not empty
4944
     */
4945 View Code Duplication
    public function set_modified_on()
4946
    {
4947
        $course_id = api_get_course_int_id();
4948
        if ($this->debug > 0) {
4949
            error_log('New LP - In learnpath::set_expired_on()', 0);
4950
        }
4951
        $this->modified_on = api_get_utc_datetime();
4952
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4953
        $lp_id = $this->get_id();
4954
        $sql = "UPDATE $lp_table SET modified_on = '" . $this->modified_on . "'
4955
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4956
        if ($this->debug > 2) {
4957
            error_log('New LP - lp updated with new expired_on : ' . $this->modified_on, 0);
4958
        }
4959
        Database::query($sql);
4960
        return true;
4961
    }
4962
4963
    /**
4964
     * Sets the object's error message
4965
     * @param	string	Error message. If empty, reinits the error string
4966
     * @return 	void
4967
     */
4968
    public function set_error_msg($error = '')
4969
    {
4970
        if ($this->debug > 0) {
4971
            error_log('New LP - In learnpath::set_error_msg()', 0);
4972
        }
4973
        if (empty ($error)) {
4974
            $this->error = '';
4975
        } else {
4976
            $this->error .= $error;
4977
        }
4978
    }
4979
4980
    /**
4981
     * Launches the current item if not 'sco'
4982
     * (starts timer and make sure there is a record ready in the DB)
4983
     * @param  boolean  $allow_new_attempt Whether to allow a new attempt or not
4984
     * @return boolean
4985
     */
4986
    public function start_current_item($allow_new_attempt = false)
4987
    {
4988
        if ($this->debug > 0) {
4989
            error_log('New LP - In learnpath::start_current_item()', 0);
4990
        }
4991
        if ($this->current != 0 && is_object($this->items[$this->current])) {
4992
            $type = $this->get_type();
4993
            $item_type = $this->items[$this->current]->get_type();
4994
            if (($type == 2 && $item_type != 'sco') ||
4995
                ($type == 3 && $item_type != 'au') ||
4996
                ($type == 1 && $item_type != TOOL_QUIZ && $item_type != TOOL_HOTPOTATOES)
4997
            ) {
4998
                $this->items[$this->current]->open($allow_new_attempt);
4999
                $this->autocomplete_parents($this->current);
5000
                $prereq_check = $this->prerequisites_match($this->current);
5001
                $this->items[$this->current]->save(false, $prereq_check);
5002
                //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
5003
            } else {
5004
                // If sco, then it is supposed to have been updated by some other call.
5005
            }
5006
            if ($item_type == 'sco') {
5007
                $this->items[$this->current]->restart();
5008
            }
5009
        }
5010
        if ($this->debug > 0) {
5011
            error_log('New LP - End of learnpath::start_current_item()', 0);
5012
        }
5013
        return true;
5014
    }
5015
5016
    /**
5017
     * Stops the processing and counters for the old item (as held in $this->last)
5018
     * @return boolean  True/False
5019
     */
5020
    public function stop_previous_item()
5021
    {
5022
        if ($this->debug > 0) {
5023
            error_log('New LP - In learnpath::stop_previous_item()', 0);
5024
        }
5025
5026
        if ($this->last != 0 && $this->last != $this->current && is_object($this->items[$this->last])) {
5027
            if ($this->debug > 2) {
5028
                error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' is object', 0);
5029
            }
5030
            switch ($this->get_type()) {
5031 View Code Duplication
                case '3' :
5032
                    if ($this->items[$this->last]->get_type() != 'au') {
5033
                        if ($this->debug > 2) {
5034
                            error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' in lp_type 3 is <> au', 0);
5035
                        }
5036
                        $this->items[$this->last]->close();
5037
                        //$this->autocomplete_parents($this->last);
5038
                        //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
5039
                    } else {
5040
                        if ($this->debug > 2) {
5041
                            error_log('New LP - In learnpath::stop_previous_item() - Item is an AU, saving is managed by AICC signals', 0);
5042
                        }
5043
                    }
5044 View Code Duplication
                case '2' :
5045
                    if ($this->items[$this->last]->get_type() != 'sco') {
5046
                        if ($this->debug > 2) {
5047
                            error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' in lp_type 2 is <> sco', 0);
5048
                        }
5049
                        $this->items[$this->last]->close();
5050
                        //$this->autocomplete_parents($this->last);
5051
                        //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
5052
                    } else {
5053
                        if ($this->debug > 2) {
5054
                            error_log('New LP - In learnpath::stop_previous_item() - Item is a SCO, saving is managed by SCO signals', 0);
5055
                        }
5056
                    }
5057
                    break;
5058
                case '1' :
5059
                default :
5060
                    if ($this->debug > 2) {
5061
                        error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' in lp_type 1 is asset', 0);
5062
                    }
5063
                    $this->items[$this->last]->close();
5064
                    break;
5065
            }
5066
        } else {
5067
            if ($this->debug > 2) {
5068
                error_log('New LP - In learnpath::stop_previous_item() - No previous element found, ignoring...', 0);
5069
            }
5070
            return false;
5071
        }
5072
        return true;
5073
    }
5074
5075
    /**
5076
     * Updates the default view mode from fullscreen to embedded and inversely
5077
     * @return	string The current default view mode ('fullscreen' or 'embedded')
5078
     */
5079
    public function update_default_view_mode()
5080
    {
5081
        $course_id = api_get_course_int_id();
5082
        if ($this->debug > 0) {
5083
            error_log('New LP - In learnpath::update_default_view_mode()', 0);
5084
        }
5085
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
5086
        $sql = "SELECT * FROM $lp_table
5087
                WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5088
        $res = Database::query($sql);
5089
        if (Database :: num_rows($res) > 0) {
5090
            $row = Database :: fetch_array($res);
5091
            $default_view_mode = $row['default_view_mod'];
5092
            $view_mode = $default_view_mode;
5093
            switch ($default_view_mode) {
5094
                case 'fullscreen': // default with popup
5095
                    $view_mode = 'embedded';
5096
                    break;
5097
                case 'embedded': // default view with left menu
5098
                    $view_mode = 'embedframe';
5099
                    break;
5100
                case 'embedframe': //folded menu
5101
                    $view_mode = 'impress';
5102
                    break;
5103
                case 'impress':
5104
                    $view_mode = 'fullscreen';
5105
                    break;
5106
            }
5107
            $sql = "UPDATE $lp_table SET default_view_mod = '$view_mode'
5108
                    WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5109
            Database::query($sql);
5110
            $this->mode = $view_mode;
5111
5112
            return $view_mode;
5113
        } else {
5114
            if ($this->debug > 2) {
5115
                error_log('New LP - Problem in update_default_view() - could not find LP ' . $this->get_id() . ' in DB', 0);
5116
            }
5117
        }
5118
        return -1;
5119
    }
5120
5121
    /**
5122
     * Updates the default behaviour about auto-commiting SCORM updates
5123
     * @return	boolean	True if auto-commit has been set to 'on', false otherwise
5124
     */
5125
    public function update_default_scorm_commit()
5126
    {
5127
        $course_id = api_get_course_int_id();
5128
        if ($this->debug > 0) {
5129
            error_log('New LP - In learnpath::update_default_scorm_commit()', 0);
5130
        }
5131
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
5132
        $sql = "SELECT * FROM $lp_table
5133
                WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5134
        $res = Database::query($sql);
5135
        if (Database :: num_rows($res) > 0) {
5136
            $row = Database :: fetch_array($res);
5137
            $force = $row['force_commit'];
5138
            if ($force == 1) {
5139
                $force = 0;
5140
                $force_return = false;
5141
            } elseif ($force == 0) {
5142
                $force = 1;
5143
                $force_return = true;
5144
            }
5145
            $sql = "UPDATE $lp_table SET force_commit = $force
5146
                    WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5147
            Database::query($sql);
5148
            $this->force_commit = $force_return;
5149
5150
            return $force_return;
5151
        } else {
5152
            if ($this->debug > 2) {
5153
                error_log('New LP - Problem in update_default_scorm_commit() - could not find LP ' . $this->get_id() . ' in DB', 0);
5154
            }
5155
        }
5156
        return -1;
5157
    }
5158
5159
    /**
5160
     * Updates the order of learning paths (goes through all of them by order and fills the gaps)
5161
     * @return	bool	True on success, false on failure
5162
     */
5163
    public function update_display_order()
5164
    {
5165
        $course_id = api_get_course_int_id();
5166
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
5167
5168
        $sql = "SELECT * FROM $lp_table WHERE c_id = ".$course_id." ORDER BY display_order";
5169
        $res = Database::query($sql);
5170
        if ($res === false)
5171
            return false;
5172
5173
        $num = Database :: num_rows($res);
5174
        // First check the order is correct, globally (might be wrong because
5175
        // of versions < 1.8.4).
5176
        if ($num > 0) {
5177
            $i = 1;
5178
            while ($row = Database :: fetch_array($res)) {
5179
                if ($row['display_order'] != $i) { // If we find a gap in the order, we need to fix it.
5180
                    $need_fix = true;
5181
                    $sql = "UPDATE $lp_table SET display_order = $i
5182
                            WHERE c_id = ".$course_id." AND id = " . $row['id'];
5183
                    Database::query($sql);
5184
                }
5185
                $i++;
5186
            }
5187
        }
5188
        return true;
5189
    }
5190
5191
    /**
5192
     * Updates the "prevent_reinit" value that enables control on reinitialising items on second view
5193
     * @return	boolean	True if prevent_reinit has been set to 'on', false otherwise (or 1 or 0 in this case)
5194
     */
5195 View Code Duplication
    public function update_reinit()
5196
    {
5197
        $course_id = api_get_course_int_id();
5198
        if ($this->debug > 0) {
5199
            error_log('New LP - In learnpath::update_reinit()', 0);
5200
        }
5201
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
5202
        $sql = "SELECT * FROM $lp_table
5203
                WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5204
        $res = Database::query($sql);
5205
        if (Database :: num_rows($res) > 0) {
5206
            $row = Database :: fetch_array($res);
5207
            $force = $row['prevent_reinit'];
5208
            if ($force == 1) {
5209
                $force = 0;
5210
            } elseif ($force == 0) {
5211
                $force = 1;
5212
            }
5213
            $sql = "UPDATE $lp_table SET prevent_reinit = $force
5214
                    WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5215
            Database::query($sql);
5216
            $this->prevent_reinit = $force;
5217
            return $force;
5218
        } else {
5219
            if ($this->debug > 2) {
5220
                error_log('New LP - Problem in update_reinit() - could not find LP ' . $this->get_id() . ' in DB', 0);
5221
            }
5222
        }
5223
        return -1;
5224
    }
5225
5226
    /**
5227
     * Determine the attempt_mode thanks to prevent_reinit and seriousgame_mode db flag
5228
     *
5229
     * @return string 'single', 'multi' or 'seriousgame'
5230
     * @author ndiechburg <[email protected]>
5231
     **/
5232
    public function get_attempt_mode()
5233
    {
5234
        //Set default value for seriousgame_mode
5235
        if (!isset($this->seriousgame_mode)) {
5236
            $this->seriousgame_mode=0;
5237
        }
5238
        // Set default value for prevent_reinit
5239
        if (!isset($this->prevent_reinit)) {
5240
            $this->prevent_reinit =1;
5241
        }
5242
        if ($this->seriousgame_mode == 1 && $this->prevent_reinit == 1) {
5243
            return 'seriousgame';
5244
        }
5245
        if ($this->seriousgame_mode == 0 && $this->prevent_reinit == 1) {
5246
            return 'single';
5247
        }
5248
        if ($this->seriousgame_mode == 0 && $this->prevent_reinit == 0) {
5249
            return 'multiple';
5250
        }
5251
        return 'single';
5252
    }
5253
5254
    /**
5255
     * Register the attempt mode into db thanks to flags prevent_reinit and seriousgame_mode flags
5256
     *
5257
     * @param string 'seriousgame', 'single' or 'multiple'
5258
     * @return boolean
5259
     * @author ndiechburg <[email protected]>
5260
     **/
5261
    public function set_attempt_mode($mode)
5262
    {
5263
        $course_id = api_get_course_int_id();
5264
        switch ($mode) {
5265
            case 'seriousgame' :
5266
                $sg_mode = 1;
5267
                $prevent_reinit = 1;
5268
                break;
5269
            case 'single' :
5270
                $sg_mode = 0;
5271
                $prevent_reinit = 1;
5272
                break;
5273
            case 'multiple' :
5274
                $sg_mode = 0;
5275
                $prevent_reinit = 0;
5276
                break;
5277
            default :
5278
                $sg_mode = 0;
5279
                $prevent_reinit = 0;
5280
                break;
5281
        }
5282
        $this->prevent_reinit = $prevent_reinit;
5283
        $this->seriousgame_mode = $sg_mode;
5284
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
5285
        $sql = "UPDATE $lp_table SET
5286
                prevent_reinit = $prevent_reinit ,
5287
                seriousgame_mode = $sg_mode
5288
                WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5289
        $res = Database::query($sql);
5290
        if ($res) {
5291
            return true;
5292
        } else {
5293
            return false;
5294
        }
5295
    }
5296
5297
    /**
5298
     * Switch between multiple attempt, single attempt or serious_game mode (only for scorm)
5299
     *
5300
     * @return boolean
5301
     * @author ndiechburg <[email protected]>
5302
     **/
5303
    public function switch_attempt_mode()
5304
    {
5305
        if ($this->debug > 0) {
5306
            error_log('New LP - In learnpath::switch_attempt_mode()', 0);
5307
        }
5308
        $mode = $this->get_attempt_mode();
5309
        switch ($mode) {
5310
            case 'single' :
5311
                $next_mode = 'multiple';
5312
                break;
5313
            case 'multiple' :
5314
                $next_mode = 'seriousgame';
5315
                break;
5316
            case 'seriousgame' :
5317
                $next_mode = 'single';
5318
                break;
5319
            default :
5320
                $next_mode = 'single';
5321
                break;
5322
        }
5323
        $this->set_attempt_mode($next_mode);
5324
    }
5325
5326
    /**
5327
     * Switch the lp in ktm mode. This is a special scorm mode with unique attempt
5328
     * but possibility to do again a completed item.
5329
     *
5330
     * @return boolean true if seriousgame_mode has been set to 1, false otherwise
5331
     * @author ndiechburg <[email protected]>
5332
     **/
5333 View Code Duplication
    public function set_seriousgame_mode()
5334
    {
5335
        $course_id = api_get_course_int_id();
5336
        if ($this->debug > 0) {
5337
            error_log('New LP - In learnpath::set_seriousgame_mode()', 0);
5338
        }
5339
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
5340
        $sql = "SELECT * FROM $lp_table WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5341
        $res = Database::query($sql);
5342
        if (Database :: num_rows($res) > 0) {
5343
            $row = Database :: fetch_array($res);
5344
            $force = $row['seriousgame_mode'];
5345
            if ($force == 1) {
5346
                $force = 0;
5347
            } elseif ($force == 0) {
5348
                $force = 1;
5349
            }
5350
            $sql = "UPDATE $lp_table SET seriousgame_mode = $force
5351
			        WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5352
            Database::query($sql);
5353
            $this->seriousgame_mode = $force;
5354
            return $force;
5355
        } else {
5356
            if ($this->debug > 2) {
5357
                error_log('New LP - Problem in set_seriousgame_mode() - could not find LP ' . $this->get_id() . ' in DB', 0);
5358
            }
5359
        }
5360
        return -1;
5361
    }
5362
5363
    /**
5364
     * Updates the "scorm_debug" value that shows or hide the debug window
5365
     * @return	boolean	True if scorm_debug has been set to 'on', false otherwise (or 1 or 0 in this case)
5366
     */
5367 View Code Duplication
    public function update_scorm_debug()
5368
    {
5369
        $course_id = api_get_course_int_id();
5370
        if ($this->debug > 0) {
5371
            error_log('New LP - In learnpath::update_scorm_debug()', 0);
5372
        }
5373
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
5374
        $sql = "SELECT * FROM $lp_table
5375
                WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5376
        $res = Database::query($sql);
5377
        if (Database :: num_rows($res) > 0) {
5378
            $row = Database :: fetch_array($res);
5379
            $force = $row['debug'];
5380
            if ($force == 1) {
5381
                $force = 0;
5382
            } elseif ($force == 0) {
5383
                $force = 1;
5384
            }
5385
            $sql = "UPDATE $lp_table SET debug = $force
5386
                    WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5387
            $res = Database::query($sql);
5388
            $this->scorm_debug = $force;
5389
            return $force;
5390
        } else {
5391
            if ($this->debug > 2) {
5392
                error_log('New LP - Problem in update_scorm_debug() - could not find LP ' . $this->get_id() . ' in DB', 0);
5393
            }
5394
        }
5395
        return -1;
5396
    }
5397
5398
    /**
5399
     * Function that makes a call to the function sort_tree_array and create_tree_array
5400
     * @author Kevin Van Den Haute
5401
     * @param  array
5402
     */
5403
    public function tree_array($array)
5404
    {
5405
        if ($this->debug > 1) {
5406
            error_log('New LP - In learnpath::tree_array()', 0);
5407
        }
5408
        $array = $this->sort_tree_array($array);
5409
        $this->create_tree_array($array);
5410
    }
5411
5412
    /**
5413
     * Creates an array with the elements of the learning path tree in it
5414
     *
5415
     * @author Kevin Van Den Haute
5416
     * @param array $array
5417
     * @param int $parent
5418
     * @param int $depth
5419
     * @param array $tmp
5420
     */
5421
    public function create_tree_array($array, $parent = 0, $depth = -1, $tmp = array ())
5422
    {
5423
        if ($this->debug > 1) {
5424
            error_log('New LP - In learnpath::create_tree_array())', 0);
5425
        }
5426
5427
        if (is_array($array)) {
5428
            for ($i = 0; $i < count($array); $i++) {
5429
                if ($array[$i]['parent_item_id'] == $parent) {
5430
                    if (!in_array($array[$i]['parent_item_id'], $tmp)) {
5431
                        $tmp[] = $array[$i]['parent_item_id'];
5432
                        $depth++;
5433
                    }
5434
                    $preq = (empty($array[$i]['prerequisite']) ? '' : $array[$i]['prerequisite']);
5435
                    $audio = isset($array[$i]['audio']) ? $array[$i]['audio'] : null;
5436
                    $path = isset($array[$i]['path']) ? $array[$i]['path'] : null;
5437
5438
                    $prerequisiteMinScore = isset($array[$i]['prerequisite_min_score']) ? $array[$i]['prerequisite_min_score'] : null;
5439
                    $prerequisiteMaxScore = isset($array[$i]['prerequisite_max_score']) ? $array[$i]['prerequisite_max_score'] : null;
5440
                    $ref = isset($array[$i]['ref']) ? $array[$i]['ref'] : '';
5441
                    $this->arrMenu[] = array(
5442
                        'id' => $array[$i]['id'],
5443
                        'ref' => $ref,
5444
                        'item_type' => $array[$i]['item_type'],
5445
                        'title' => $array[$i]['title'],
5446
                        'path' => $path,
5447
                        'description' => $array[$i]['description'],
5448
                        'parent_item_id' => $array[$i]['parent_item_id'],
5449
                        'previous_item_id' => $array[$i]['previous_item_id'],
5450
                        'next_item_id' => $array[$i]['next_item_id'],
5451
                        'min_score' => $array[$i]['min_score'],
5452
                        'max_score' => $array[$i]['max_score'],
5453
                        'mastery_score' => $array[$i]['mastery_score'],
5454
                        'display_order' => $array[$i]['display_order'],
5455
                        'prerequisite' => $preq,
5456
                        'depth' => $depth,
5457
                        'audio' => $audio,
5458
                        'prerequisite_min_score' => $prerequisiteMinScore,
5459
                        'prerequisite_max_score' => $prerequisiteMaxScore
5460
                    );
5461
5462
                    $this->create_tree_array($array, $array[$i]['id'], $depth, $tmp);
5463
                }
5464
            }
5465
        }
5466
    }
5467
5468
    /**
5469
     * Sorts a multi dimensional array by parent id and display order
5470
     * @author Kevin Van Den Haute
5471
     *
5472
     * @param array $array (array with al the learning path items in it)
5473
     *
5474
     * @return array
5475
     */
5476
    public function sort_tree_array($array) {
5477
        foreach ($array as $key => $row) {
5478
            $parent[$key] = $row['parent_item_id'];
5479
            $position[$key] = $row['display_order'];
5480
        }
5481
5482
        if (count($array) > 0)
5483
            array_multisort($parent, SORT_ASC, $position, SORT_ASC, $array);
5484
5485
        return $array;
5486
    }
5487
5488
    /**
5489
     * Function that creates a html list of learning path items so that we can add audio files to them
5490
     * @author Kevin Van Den Haute
5491
     * @param int $lp_id
5492
     * @return string
5493
     */
5494
    public function overview()
5495
    {
5496
        if ($this->debug > 0) {
5497
            error_log('New LP - In learnpath::overview()', 0);
5498
        }
5499
5500
        $_SESSION['gradebook'] = isset($_GET['gradebook']) ? Security :: remove_XSS($_GET['gradebook']) : null;
5501
        $return = '';
5502
5503
        $update_audio = isset($_GET['updateaudio']) ? $_GET['updateaudio'] : null;
5504
5505
        // we need to start a form when we want to update all the mp3 files
5506
        if ($update_audio == 'true') {
5507
            $return .= '<form action="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_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">';
5508
        }
5509
        $return .= '<div id="message"></div>';
5510
        if (count($this->items) == 0) {
5511
            $return .= Display::display_normal_message(get_lang('YouShouldAddItemsBeforeAttachAudio'));
5512
        } else {
5513
            $return_audio = '<table class="data_table">';
5514
            $return_audio .= '<tr>';
5515
            $return_audio .= '<th width="40%">' . get_lang('Title') . '</th>';
5516
            $return_audio .= '<th>' . get_lang('Audio') . '</th>';
5517
            $return_audio .= '</tr>';
5518
5519
            if ($update_audio != 'true') {
5520
                $return .= '<div class="col-md-12">';
5521
                $return .= self::return_new_tree($update_audio);
5522
                $return .='</div>';
5523
                $return .= Display::div(Display::url(get_lang('Save'), '#', array('id'=>'listSubmit', 'class'=>'btn btn-primary')), array('style'=>'float:left; margin-top:15px;width:100%'));
5524
            } else {
5525
                $return_audio .= self::return_new_tree($update_audio);
5526
                $return .= $return_audio.'</table>';
5527
            }
5528
5529
            // We need to close the form when we are updating the mp3 files.
5530
            if ($update_audio == 'true') {
5531
                $return .= '<div class="footer-audio">';
5532
                $return .= Display::button('save_audio','<em class="fa fa-file-audio-o"></em> '. get_lang('SaveAudioAndOrganization'),array('class'=>'btn btn-primary','type'=>'submit'));
5533
                $return .= '</div>';
5534
                //$return .= '<div><button class="btn btn-primary" type="submit" name="save_audio" id="save_audio">' . get_lang('SaveAudioAndOrganization') . '</button></div>'; // TODO: What kind of language variable is this?
5535
            }
5536
        }
5537
5538
        // We need to close the form when we are updating the mp3 files.
5539
        if ($update_audio == 'true' && count($this->arrMenu) != 0) {
5540
            $return .= '</form>';
5541
        }
5542
        return $return;
5543
    }
5544
5545
    /**
5546
     * @param string string $update_audio
5547
     * @param bool $drop_element_here
5548
     * @return string
5549
     */
5550
    public function return_new_tree($update_audio = 'false', $drop_element_here = false)
5551
    {
5552
        $return = '';
5553
        $is_allowed_to_edit = api_is_allowed_to_edit(null,true);
5554
5555
        $course_id = api_get_course_int_id();
5556
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
5557
5558
        $sql = "SELECT * FROM $tbl_lp_item
5559
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
5560
5561
        $result = Database::query($sql);
5562
        $arrLP = array();
5563
        while ($row = Database :: fetch_array($result)) {
5564
5565
            $arrLP[] = array(
5566
                'id' => $row['id'],
5567
                'item_type' => $row['item_type'],
5568
                'title' => Security :: remove_XSS($row['title']),
5569
                'path' => $row['path'],
5570
                'description' => Security::remove_XSS($row['description']),
5571
                'parent_item_id' => $row['parent_item_id'],
5572
                'previous_item_id' => $row['previous_item_id'],
5573
                'next_item_id' => $row['next_item_id'],
5574
                'max_score' => $row['max_score'],
5575
                'min_score' => $row['min_score'],
5576
                'mastery_score' => $row['mastery_score'],
5577
                'prerequisite' => $row['prerequisite'],
5578
                'display_order' => $row['display_order'],
5579
                'audio' => $row['audio'],
5580
                'prerequisite_max_score' => $row['prerequisite_max_score'],
5581
                'prerequisite_min_score' => $row['prerequisite_min_score']
5582
            );
5583
        }
5584
5585
        $this->tree_array($arrLP);
5586
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
5587
        unset ($this->arrMenu);
5588
        $default_data = null;
5589
        $default_content = null;
5590
5591
        $elements = array();
5592
        $return_audio = null;
5593
5594
        for ($i = 0; $i < count($arrLP); $i++) {
5595
            $title = $arrLP[$i]['title'];
5596
5597
            $title_cut = cut($arrLP[$i]['title'], 25);
5598
5599
            // Link for the documents
5600
            if ($arrLP[$i]['item_type'] == 'document') {
5601
                $url = api_get_self() . '?'.api_get_cidreq().'&action=view_item&mode=preview_document&id=' . $arrLP[$i]['id'] . '&lp_id=' . $this->lp_id;
5602
                $title_cut = Display::url(
5603
                    $title_cut,
5604
                    $url,
5605
                    array(
5606
                        'class' => 'ajax moved',
5607
                        'data-title' => $title_cut
5608
                    )
5609
                );
5610
            }
5611
5612
            // Detect if type is FINAL_ITEM to set path_id to SESSION
5613
            if ($arrLP[$i]['item_type'] == TOOL_LP_FINAL_ITEM) {
5614
                $_SESSION['pathItem'] = $arrLP[$i]['path'];
5615
            }
5616
5617
            if (($i % 2) == 0) {
5618
                $oddClass = 'row_odd';
5619
            } else {
5620
                $oddClass = 'row_even';
5621
            }
5622
            $return_audio .= '<tr id ="lp_item_'.$arrLP[$i]['id'] .'" class="' . $oddClass . '">';
5623
5624
            $icon_name = str_replace(' ', '', $arrLP[$i]['item_type']);
5625
5626
            if (file_exists('../img/lp_' . $icon_name . '.png')) {
5627
                $icon = Display::return_icon('lp_'.$icon_name.'.png');
5628
            } else {
5629
                if (file_exists('../img/lp_' . $icon_name . '.gif')) {
5630
                    $icon = Display::return_icon('lp_'.$icon_name.'.gif');
5631
                } else {
5632
                    if ($arrLP[$i]['item_type'] === TOOL_LP_FINAL_ITEM) {
5633
                        $icon = Display::return_icon('certificate.png');
5634
                    } else {
5635
                        $icon = Display::return_icon('folder_document.gif');
5636
                    }
5637
                }
5638
            }
5639
5640
            // The audio column.
5641
            $return_audio  .= '<td align="left" style="padding-left:10px;">';
5642
            $audio = '';
5643
5644
            if (!$update_audio || $update_audio <> 'true') {
5645
                if (!empty($arrLP[$i]['audio'])) {
5646
                } else {
5647
                    $audio .= '';
5648
                }
5649
            } else {
5650
                $types = self::getChapterTypes();
5651
                if (!in_array($arrLP[$i]['item_type'], $types)) {
5652
                    $audio .= '<input type="file" name="mp3file' . $arrLP[$i]['id'] . '" id="mp3file" />';
5653
                    if (!empty ($arrLP[$i]['audio'])) {
5654
                        $audio .= '<br />'.Security::remove_XSS($arrLP[$i]['audio']).'<br />
5655
                        <input type="checkbox" name="removemp3' . $arrLP[$i]['id'] . '" id="checkbox' . $arrLP[$i]['id'] . '" />' . get_lang('RemoveAudio');
5656
                    }
5657
                }
5658
            }
5659
            $return_audio .= Display::span($icon.' '.$title).Display::tag('td', $audio, array('style'=>''));
5660
            $return_audio .= '</td>';
5661
            $move_icon = '';
5662
            $move_item_icon = '';
5663
            $edit_icon = '';
5664
            $delete_icon = '';
5665
            $audio_icon = '';
5666
            $prerequisities_icon = '';
5667
            $forumIcon = '';
5668
5669
            if ($is_allowed_to_edit) {
5670
                if (!$update_audio || $update_audio <> 'true') {
5671 View Code Duplication
                    if ($arrLP[$i]['item_type'] !== TOOL_LP_FINAL_ITEM) {
5672
                        $move_icon .= '<a class="moved" href="#">';
5673
                        $move_icon .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
5674
                        $move_icon .= '</a>';
5675
                    }
5676
                }
5677
5678
                // No edit for this item types
5679
                if (!in_array($arrLP[$i]['item_type'], array('sco', 'asset', 'final_item'))) {
5680
                    if (!in_array($arrLP[$i]['item_type'], array('dokeos_chapter', 'dokeos_module'))) {
5681
                        $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">';
5682
                        $edit_icon .= Display::return_icon('edit.png', get_lang('LearnpathEditModule'), array(), ICON_SIZE_TINY);
5683
                        $edit_icon .= '</a>';
5684
5685
                        if (
5686
                        !in_array($arrLP[$i]['item_type'], ['forum', 'thread'])
5687
                        ) {
5688
                            if (
5689
                            $this->items[$arrLP[$i]['id']]->getForumThread(
5690
                                $this->course_int_id,
5691
                                $this->lp_session_id
5692
                            )
5693
                            ) {
5694
                                $forumIconUrl = api_get_self() . '?' . api_get_cidreq() . '&' . http_build_query([
5695
                                    'action' => 'dissociate_forum',
5696
                                    'id' => $arrLP[$i]['id'],
5697
                                    'lp_id' => $this->lp_id
5698
                                ]);
5699
                                $forumIcon = Display::url(
5700
                                    Display::return_icon('forum.png', get_lang('DissociateForumToLPItem'), [], ICON_SIZE_TINY),
5701
                                    $forumIconUrl,
5702
                                    ['class' => 'btn btn-default lp-btn-dissociate-forum']
5703
                                );
5704
                            } else {
5705
                                $forumIconUrl = api_get_self() . '?' . api_get_cidreq() . '&' . http_build_query([
5706
                                    'action' => 'create_forum',
5707
                                    'id' => $arrLP[$i]['id'],
5708
                                    'lp_id' => $this->lp_id
5709
                                ]);
5710
                                $forumIcon = Display::url(
5711
                                    Display::return_icon('forum.png', get_lang('AssociateForumToLPItem'), [], ICON_SIZE_TINY),
5712
                                    $forumIconUrl,
5713
                                    ['class' => "btn btn-default lp-btn-associate-forum"]
5714
                                );
5715
                            }
5716
                        }
5717
                    } else {
5718
                        $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">';
5719
                        $edit_icon .= Display::return_icon('edit.png', get_lang('LearnpathEditModule'), array(), ICON_SIZE_TINY);
5720
                        $edit_icon .= '</a>';
5721
                    }
5722
                }
5723
5724
                $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">';
5725
                $delete_icon .= Display::return_icon('delete.png', get_lang('LearnpathDeleteModule'), array(), ICON_SIZE_TINY);
5726
                $delete_icon .= '</a>';
5727
5728
                $url = api_get_self() . '?'.api_get_cidreq().'&view=build&id='.$arrLP[$i]['id'] .'&lp_id='.$this->lp_id;
5729
5730
                if (!in_array($arrLP[$i]['item_type'], array('dokeos_chapter', 'dokeos_module', 'dir'))) {
5731
                    $prerequisities_icon = Display::url(Display::return_icon('accept.png', get_lang('LearnpathPrerequisites'), array(), ICON_SIZE_TINY), $url.'&action=edit_item_prereq', ['class' => 'btn btn-default']);
5732
                    $move_item_icon = Display::url(Display::return_icon('move.png', get_lang('Move'), array(), ICON_SIZE_TINY), $url.'&action=move_item', ['class' => 'btn btn-default']);
5733
                    $audio_icon = Display::url(Display::return_icon('audio.png', get_lang('UplUpload'), array(), ICON_SIZE_TINY), $url.'&action=add_audio', ['class' => 'btn btn-default']);
5734
                }
5735
            }
5736
            if ($update_audio != 'true') {
5737
                $row = $move_icon . ' ' . $icon .
5738
                    Display::span($title_cut) .
5739
                    Display::tag(
5740
                        'div',
5741
                        "<div class=\"btn-group btn-group-xs\">$audio $edit_icon $forumIcon $prerequisities_icon $move_item_icon $audio_icon $delete_icon</div>",
5742
                        array('class'=>'btn-toolbar button_actions')
5743
                    );
5744
            } else {
5745
                $row = Display::span($title.$icon).Display::span($audio, array('class'=>'button_actions'));
5746
            }
5747
            $parent_id = $arrLP[$i]['parent_item_id'];
5748
5749
            $default_data[$arrLP[$i]['id']] = $row;
5750
            $default_content[$arrLP[$i]['id']] = $arrLP[$i];
5751
5752
            if (empty($parent_id)) {
5753
                $elements[$arrLP[$i]['id']]['data'] = $row;
5754
                $elements[$arrLP[$i]['id']]['type'] = $arrLP[$i]['item_type'];
5755
            } else {
5756
                $parent_arrays = array();
5757
                if ($arrLP[$i]['depth'] > 1) {
5758
                    //Getting list of parents
5759
                    for($j = 0; $j < $arrLP[$i]['depth']; $j++) {
5760
                        foreach($arrLP as $item) {
5761
                            if ($item['id'] == $parent_id) {
5762
                                if ($item['parent_item_id'] == 0) {
5763
                                    $parent_id = $item['id'];
5764
                                    break;
5765
                                } else {
5766
                                    $parent_id = $item['parent_item_id'];
5767
                                    if (empty($parent_arrays)) {
5768
                                        $parent_arrays[] = intval($item['id']);
5769
                                    }
5770
                                    $parent_arrays[] = $parent_id;
5771
                                    break;
5772
                                }
5773
                            }
5774
                        }
5775
                    }
5776
                }
5777
5778
                if (!empty($parent_arrays)) {
5779
                    $parent_arrays = array_reverse($parent_arrays);
5780
                    $val = '$elements';
5781
                    $x = 0;
5782
                    foreach($parent_arrays as $item) {
5783
                        if ($x != count($parent_arrays) -1) {
5784
                            $val .= '["'.$item.'"]["children"]';
5785
                        } else {
5786
                            $val .= '["'.$item.'"]["children"]';
5787
                        }
5788
                        $x++;
5789
                    }
5790
                    $val .= "";
5791
                    $code_str = $val."[".$arrLP[$i]['id']."][\"load_data\"] = '".$arrLP[$i]['id']."' ; ";
5792
                    eval($code_str);
5793
                } else {
5794
                    $elements[$parent_id]['children'][$arrLP[$i]['id']]['data'] = $row;
5795
                    $elements[$parent_id]['children'][$arrLP[$i]['id']]['type'] = $arrLP[$i]['item_type'];
5796
                }
5797
            }
5798
        }
5799
5800
        $list = '<ul id="lp_item_list">';
5801
        $tree = self::print_recursive($elements, $default_data, $default_content);
5802
5803
        if (!empty($tree)) {
5804
            $list .= $tree;
5805
        } else {
5806
            if ($drop_element_here) {
5807
                $list .= Display::return_message(get_lang("DragAndDropAnElementHere"));
5808
            }
5809
        }
5810
        $list .= '</ul>';
5811
5812
        //$return .= Display::panel($list, $this->name);
5813
        $return .= Display::panelCollapse($this->name, $list, 'scorm-list', null, 'scorm-list-accordion', 'scorm-list-collapse');
5814
5815
        if ($update_audio == 'true') {
5816
            $return = $return_audio;
5817
        }
5818
5819
        return $return;
5820
    }
5821
5822
    /**
5823
     * @param array $elements
5824
     * @param array $default_data
5825
     * @param array $default_content
5826
     * @return string
5827
     */
5828
    public function print_recursive($elements, $default_data, $default_content)
5829
    {
5830
        $return = '';
5831
        foreach ($elements as $key => $item) {
5832
            if (isset($item['load_data']) || empty($item['data'])) {
5833
                $item['data'] = $default_data[$item['load_data']];
5834
                $item['type'] = $default_content[$item['load_data']]['item_type'];
5835
            }
5836
            $sub_list = '';
5837
            if (isset($item['type']) && $item['type'] == 'dokeos_chapter') {
5838
                $sub_list = Display::tag('li', '', array('class'=>'sub_item empty')); // empty value
5839
            }
5840
            if (empty($item['children'])) {
5841
                $sub_list = Display::tag('ul', $sub_list, array('id'=>'UL_'.$key, 'class'=>'record li_container'));
5842
                $active = null;
5843
                if (isset($_REQUEST['id']) && $key == $_REQUEST['id']) {
5844
                    $active = 'active';
5845
                }
5846
                $return  .= Display::tag('li', Display::div($item['data'], array('class'=>"item_data $active")).$sub_list, array('id'=>$key, 'class'=>'record li_container'));
5847
            } else {
5848
                //sections
5849
                if (isset($item['children'])) {
5850
                    $data = self::print_recursive($item['children'], $default_data, $default_content);
5851
                }
5852
                $sub_list = Display::tag('ul', $sub_list.$data, array('id'=>'UL_'.$key, 'class'=>'record li_container'));
5853
                $return .= Display::tag('li', Display::div($item['data'], array('class'=>'item_data')).$sub_list, array('id'=>$key, 'class'=>'record li_container'));
5854
            }
5855
        }
5856
5857
        return $return;
5858
    }
5859
5860
    /**
5861
     * This function builds the action menu
5862
     * @param bool $returnContent
5863
     * @return void
5864
     */
5865
    public function build_action_menu($returnContent = false)
5866
    {
5867
        $gradebook = isset($_GET['gradebook']) ? Security :: remove_XSS($_GET['gradebook']) : null;
5868
        $return = '<div class="actions">';
5869
        $return .=  '<a href="lp_controller.php?'.api_get_cidreq().'&gradebook=' . $gradebook . '&action=view&lp_id=' . $_SESSION['oLP']->lp_id . '&isStudentView=true">' . Display :: return_icon('preview_view.png', get_lang('Display'),'',ICON_SIZE_MEDIUM).'</a> ';
5870
        $return .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=admin_view&lp_id=' . $_SESSION['oLP']->lp_id . '&updateaudio=true">' . Display :: return_icon('upload_audio.png', get_lang('UpdateAllAudioFragments'),'',ICON_SIZE_MEDIUM).'</a>';
5871
        $return .= '<a href="lp_controller.php?'.api_get_cidreq().'&action=edit&lp_id=' . $_SESSION['oLP']->lp_id . '">' . Display :: return_icon('settings.png', get_lang('CourseSettings'),'',ICON_SIZE_MEDIUM).'</a>';
5872
        $buttons = array(
5873
            array(
5874
                'title' => get_lang('SetPrerequisiteForEachItem'),
5875
                'href' => 'lp_controller.php?'.api_get_cidreq().'&action=set_previous_step_as_prerequisite&lp_id=' . $_SESSION['oLP']->lp_id,
5876
            ),
5877
            array(
5878
                'title' => get_lang('ClearAllPrerequisites'),
5879
                'href' => 'lp_controller.php?'.api_get_cidreq().'&action=clear_prerequisites&lp_id=' . $_SESSION['oLP']->lp_id,
5880
            ),
5881
        );
5882
        $return .= Display::group_button(get_lang('PrerequisitesOptions'), $buttons);
5883
        $return .= '</div>';
5884
5885
        if ($returnContent) {
5886
            return $return;
5887
        }
5888
        echo $return;
5889
    }
5890
5891
    /**
5892
     * Creates the default learning path folder
5893
     * @param array $course
5894
     * @param int $creatorId
5895
     *
5896
     * @return bool
5897
     */
5898
    public static function generate_learning_path_folder($course, $creatorId = 0)
5899
    {
5900
        // Creating learning_path folder
5901
        $dir = '/learning_path';
5902
        $filepath = api_get_path(SYS_COURSE_PATH).$course['path'] . '/document';
5903
        $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId;
5904
5905
        $folder = false;
5906
        if (!is_dir($filepath.'/'.$dir)) {
5907
            $folderData = create_unexisting_directory(
5908
                $course,
5909
                $creatorId,
5910
                api_get_session_id(),
5911
                0,
5912
                0,
5913
                $filepath,
5914
                $dir,
5915
                get_lang('LearningPaths'),
5916
                0
5917
            );
5918
            if (!empty($folderData)) {
5919
                $folder = true;
5920
            }
5921
        } else {
5922
            $folder = true;
5923
        }
5924
5925
        return $folder;
5926
    }
5927
5928
    /**
5929
     * @param array $course
5930
     * @param string $lp_name
5931
     * @param int $creatorId
5932
     *
5933
     * @return array
5934
     */
5935
    public function generate_lp_folder($course, $lp_name = '', $creatorId = 0)
5936
    {
5937
        $filepath = '';
5938
        $dir = '/learning_path/';
5939
5940
        if (empty($lp_name)) {
5941
            $lp_name = $this->name;
5942
        }
5943
        $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId;
5944
5945
        $folder = self::generate_learning_path_folder($course, $creatorId);
5946
        // Creating LP folder
5947
        if ($folder) {
5948
            //Limits title size
5949
            $title = api_substr(api_replace_dangerous_char($lp_name), 0 , 80);
5950
            $dir   = $dir.$title;
5951
            $filepath = api_get_path(SYS_COURSE_PATH) . $course['path'] . '/document';
5952
            if (!is_dir($filepath.'/'.$dir)) {
5953
                $folderData = create_unexisting_directory(
5954
                    $course,
5955
                    $creatorId,
5956
                    0,
5957
                    0,
5958
                    0,
5959
                    $filepath,
5960
                    $dir,
5961
                    $lp_name
5962
                );
5963
                if (!empty($folderData)) {
5964
                    $folder = true;
5965
                }
5966
            } else {
5967
                $folder = true;
5968
            }
5969
            $dir = $dir.'/';
5970
            if ($folder) {
5971
                $filepath = api_get_path(SYS_COURSE_PATH) . $course['path'] . '/document'.$dir;
5972
            }
5973
        }
5974
        $array = array(
5975
            'dir' => $dir,
5976
            'filepath' => $filepath,
5977
            'folder' => $folder
5978
        );
5979
        return $array;
5980
    }
5981
5982
    /**
5983
     * Create a new document //still needs some finetuning
5984
     * @param array $courseInfo
5985
     * @param string $content
5986
     * @param string $title
5987
     * @param string $extension
5988
     * @param int $creatorId creator id
5989
     *
5990
     * @return string
5991
     */
5992
    public function create_document($courseInfo, $content = '', $title = '', $extension = 'html', $creatorId = 0)
5993
    {
5994
        if (!empty($courseInfo)) {
5995
            $course_id = $courseInfo['real_id'];
5996
        } else {
5997
            $course_id = api_get_course_int_id();
5998
        }
5999
        $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId;
6000
        $sessionId = api_get_session_id();
6001
6002
        global $charset;
6003
        $postDir = isset($_POST['dir']) ? $_POST['dir'] : '';
6004
        $dir = isset ($_GET['dir']) ? $_GET['dir'] : $postDir; // Please, do not modify this dirname formatting.
6005
        // Please, do not modify this dirname formatting.
6006
        if (strstr($dir, '..')) {
6007
            $dir = '/';
6008
        }
6009
        if (!empty($dir[0]) && $dir[0] == '.') {
6010
            $dir = substr($dir, 1);
6011
        }
6012
        if (!empty($dir[0]) && $dir[0] != '/') {
6013
            $dir = '/' . $dir;
6014
        }
6015
        if (isset($dir[strlen($dir) - 1]) && $dir[strlen($dir) - 1] != '/') {
6016
            $dir .= '/';
6017
        }
6018
        $filepath = api_get_path(SYS_COURSE_PATH) . $courseInfo['path'] . '/document' . $dir;
6019
6020
        if (empty($_POST['dir']) && empty($_GET['dir'])) {
6021
            //Generates folder
6022
            $result = $this->generate_lp_folder($courseInfo, '', $creatorId);
6023
            $dir = $result['dir'];
6024
            $filepath = $result['filepath'];
6025
        }
6026
6027 View Code Duplication
        if (!is_dir($filepath)) {
6028
            $filepath = api_get_path(SYS_COURSE_PATH) . $courseInfo['path'] . '/document/';
6029
            $dir = '/';
6030
        }
6031
6032
        // stripslashes() before calling api_replace_dangerous_char() because $_POST['title']
6033
        // is already escaped twice when it gets here.
6034
6035
        $originalTitle = !empty($title) ? $title : $_POST['title'];
6036
        if (!empty($title)) {
6037
            $title = api_replace_dangerous_char(stripslashes($title));
6038
        } else {
6039
            $title = api_replace_dangerous_char(stripslashes($_POST['title']));
6040
        }
6041
6042
        $title = disable_dangerous_file($title);
6043
        $filename = $title;
6044
        $content = !empty($content) ? $content : $_POST['content_lp'];
6045
        $tmp_filename = $filename;
6046
6047
        $i = 0;
6048 View Code Duplication
        while (file_exists($filepath . $tmp_filename . '.'.$extension))
6049
            $tmp_filename = $filename . '_' . ++ $i;
6050
6051
        $filename = $tmp_filename . '.'.$extension;
6052
        if ($extension == 'html') {
6053
            $content = stripslashes($content);
6054
            $content = str_replace(
6055
                api_get_path(WEB_COURSE_PATH),
6056
                api_get_path(REL_PATH).'courses/',
6057
                $content
6058
            );
6059
6060
            // Change the path of mp3 to absolute.
6061
6062
            // The first regexp deals with :// urls.
6063
            $content = preg_replace(
6064
                "|(flashvars=\"file=)([^:/]+)/|",
6065
                "$1".api_get_path(
6066
                    REL_COURSE_PATH
6067
                ).$courseInfo['path'].'/document/',
6068
                $content
6069
            );
6070
            // The second regexp deals with audio/ urls.
6071
            $content = preg_replace(
6072
                "|(flashvars=\"file=)([^/]+)/|",
6073
                "$1".api_get_path(
6074
                    REL_COURSE_PATH
6075
                ).$courseInfo['path'].'/document/$2/',
6076
                $content
6077
            );
6078
            // For flv player: To prevent edition problem with firefox, we have to use a strange tip (don't blame me please).
6079
            $content = str_replace(
6080
                '</body>',
6081
                '<style type="text/css">body{}</style></body>',
6082
                $content
6083
            );
6084
        }
6085
6086
        if (!file_exists($filepath . $filename)) {
6087
            if ($fp = @ fopen($filepath . $filename, 'w')) {
6088
                fputs($fp, $content);
6089
                fclose($fp);
6090
6091
                $file_size = filesize($filepath . $filename);
6092
                $save_file_path = $dir.$filename;
6093
6094
                $document_id = add_document(
6095
                    $courseInfo,
6096
                    $save_file_path,
6097
                    'file',
6098
                    $file_size,
6099
                    $tmp_filename,
6100
                    '',
6101
                    0, //readonly
6102
                    true,
6103
                    null,
6104
                    $sessionId,
6105
                    $creatorId
6106
                );
6107
6108
                if ($document_id) {
6109
                    api_item_property_update(
6110
                        $courseInfo,
6111
                        TOOL_DOCUMENT,
6112
                        $document_id,
6113
                        'DocumentAdded',
6114
                        $creatorId,
6115
                        null,
6116
                        null,
6117
                        null,
6118
                        null,
6119
                        $sessionId
6120
                    );
6121
6122
                    $new_comment = isset($_POST['comment']) ? trim($_POST['comment']) : '';
6123
                    $new_title = $originalTitle;
6124
6125
                    if ($new_comment || $new_title) {
6126
                        $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
6127
                        $ct = '';
6128
                        if ($new_comment)
6129
                            $ct .= ", comment='" . Database::escape_string($new_comment). "'";
6130
                        if ($new_title)
6131
                            $ct .= ", title='" . Database::escape_string(htmlspecialchars($new_title, ENT_QUOTES, $charset))."' ";
6132
6133
                        $sql = "UPDATE " . $tbl_doc ." SET " . substr($ct, 1)."
6134
                               WHERE c_id = ".$course_id." AND id = " . $document_id;
6135
                        Database::query($sql);
6136
                    }
6137
                }
6138
                return $document_id;
6139
            }
6140
        }
6141
    }
6142
6143
    /**
6144
     * Edit a document based on $_POST and $_GET parameters 'dir' and 'path'
6145
     * @param 	array $_course array
6146
     * @return 	void
6147
     */
6148
    public function edit_document($_course)
6149
    {
6150
        $course_id = api_get_course_int_id();
6151
        global $_configuration;
6152
        // Please, do not modify this dirname formatting.
6153
        $dir = isset($_GET['dir']) ? $_GET['dir'] : $_POST['dir'];
6154
6155
        if (strstr($dir, '..'))
6156
            $dir = '/';
6157
6158
        if ($dir[0] == '.')
6159
            $dir = substr($dir, 1);
6160
6161
        if ($dir[0] != '/')
6162
            $dir = '/' . $dir;
6163
6164
        if ($dir[strlen($dir) - 1] != '/')
6165
            $dir .= '/';
6166
6167
        $filepath = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document' . $dir;
6168
6169 View Code Duplication
        if (!is_dir($filepath)) {
6170
            $filepath = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document/';
6171
        }
6172
6173
        $table_doc = Database :: get_course_table(TABLE_DOCUMENT);
6174
        if (isset($_POST['path']) && !empty($_POST['path'])) {
6175
            $document_id = intval($_POST['path']);
6176
            $sql = "SELECT path FROM " . $table_doc . "
6177
                    WHERE c_id = $course_id AND id = " . $document_id;
6178
            $res = Database::query($sql);
6179
            $row = Database :: fetch_array($res);
6180
            $content = stripslashes($_POST['content_lp']);
6181
            $file = $filepath . $row['path'];
6182
6183
            if ($fp = @ fopen($file, 'w')) {
6184
                $content = str_replace(api_get_path(WEB_COURSE_PATH), $_configuration['url_append'] . '/courses/', $content);
6185
6186
                // Change the path of mp3 to absolute.
6187
                // The first regexp deals with :// urls.
6188
                $content = preg_replace("|(flashvars=\"file=)([^:/]+)/|", "$1" . api_get_path(REL_COURSE_PATH) . $_course['path'] . '/document/', $content);
6189
                // The second regexp deals with audio/ urls.
6190
                $content = preg_replace("|(flashvars=\"file=)([^:/]+)/|", "$1" . api_get_path(REL_COURSE_PATH) . $_course['path'] . '/document/$2/', $content);
6191
                fputs($fp, $content);
6192
                fclose($fp);
6193
6194
                $sql = "UPDATE " . $table_doc ." SET
6195
                            title='".Database::escape_string($_POST['title'])."'
6196
                        WHERE c_id = ".$course_id." AND id = " . $document_id;
6197
                Database::query($sql);
6198
            }
6199
        }
6200
    }
6201
6202
    /**
6203
     * Displays the selected item, with a panel for manipulating the item
6204
     * @param int $item_id
6205
     * @param string $msg
6206
     * @return string
6207
     */
6208
    public function display_item($item_id, $msg = null, $show_actions = true)
6209
    {
6210
        $course_id = api_get_course_int_id();
6211
        $return = '';
6212
        if (is_numeric($item_id)) {
6213
            $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
6214
            $sql = "SELECT lp.* FROM " . $tbl_lp_item . " as lp
6215
                    WHERE c_id = ".$course_id." AND lp.id = " . intval($item_id);
6216
            $result = Database::query($sql);
6217
            while ($row = Database :: fetch_array($result,'ASSOC')) {
6218
                $_SESSION['parent_item_id'] = ($row['item_type'] == 'dokeos_chapter' || $row['item_type'] == 'dokeos_module' || $row['item_type'] == 'dir') ? $item_id : 0;
6219
6220
                // Prevents wrong parent selection for document, see Bug#1251.
6221
                if ($row['item_type'] != 'dokeos_chapter' || $row['item_type'] != 'dokeos_module') {
6222
                    $_SESSION['parent_item_id'] = $row['parent_item_id'];
6223
                }
6224
6225
                if ($show_actions) {
6226
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6227
                }
6228
                $return .= '<div style="padding:10px;">';
6229
6230
                if ($msg != '')
6231
                    $return .= $msg;
6232
6233
                $return .= '<h3>'.$row['title'].'</h3>';
6234
                switch ($row['item_type']) {
6235
                    case TOOL_QUIZ:
6236
                        if (!empty($row['path'])) {
6237
                            $exercise = new Exercise();
6238
                            $exercise->read($row['path']);
6239
                            $return .= $exercise->description.'<br />';
6240
                        }
6241
                        break;
6242
                    case TOOL_DOCUMENT:
6243
                        $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
6244
                        $sql_doc = "SELECT path FROM " . $tbl_doc . "
6245
                                    WHERE c_id = ".$course_id." AND id = " . intval($row['path']);
6246
                        $result = Database::query($sql_doc);
6247
                        $path_file = Database::result($result, 0, 0);
6248
                        $path_parts = pathinfo($path_file);
6249
                        // TODO: Correct the following naive comparisons, also, htm extension is missing.
6250
                        if (in_array($path_parts['extension'], array(
6251
                            'html',
6252
                            'txt',
6253
                            'png',
6254
                            'jpg',
6255
                            'JPG',
6256
                            'jpeg',
6257
                            'JPEG',
6258
                            'gif',
6259
                            'swf'
6260
                        ))) {
6261
                            $return .= $this->display_document($row['path'], true, true);
6262
                        }
6263
                        break;
6264
                }
6265
                $return .= '</div>';
6266
            }
6267
        }
6268
6269
        return $return;
6270
    }
6271
6272
    /**
6273
     * Shows the needed forms for editing a specific item
6274
     * @param int $item_id
6275
     * @return string
6276
     */
6277
    public function display_edit_item($item_id)
6278
    {
6279
        $course_id = api_get_course_int_id();
6280
        $return = '';
6281
        if (is_numeric($item_id)) {
6282
            $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
6283
            $sql = "SELECT * FROM $tbl_lp_item
6284
                    WHERE c_id = ".$course_id." AND id = " . intval($item_id);
6285
            $res = Database::query($sql);
6286
            $row = Database::fetch_array($res);
6287
            switch ($row['item_type']) {
6288
                case 'dokeos_chapter':
6289
                case 'dir':
6290
                case 'asset':
6291 View Code Duplication
                case 'sco':
6292
                    if (isset($_GET['view']) && $_GET['view'] == 'build') {
6293
                        $return .= $this->display_manipulate($item_id, $row['item_type']);
6294
                        $return .= $this->display_item_form($row['item_type'], get_lang('EditCurrentChapter') . ' :', 'edit', $item_id, $row);
6295
                    } else {
6296
                        $return .= $this->display_item_small_form($row['item_type'], get_lang('EditCurrentChapter') . ' :', $row);
6297
                    }
6298
                    break;
6299 View Code Duplication
                case TOOL_DOCUMENT:
6300
                    $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
6301
                    $sql = "SELECT lp.*, doc.path as dir
6302
                            FROM " . $tbl_lp_item . " as lp
6303
                            LEFT JOIN " . $tbl_doc . " as doc
6304
                            ON doc.id = lp.path
6305
                            WHERE
6306
                                lp.c_id = $course_id AND
6307
                                doc.c_id = $course_id AND
6308
                                lp.id = " . intval($item_id);
6309
                    $res_step = Database::query($sql);
6310
                    $row_step = Database :: fetch_array($res_step, 'ASSOC');
6311
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6312
                    $return .= $this->display_document_form('edit', $item_id, $row_step);
6313
                    break;
6314
                case TOOL_LINK:
6315
                    $link_id = (string) $row['path'];
6316
                    if (ctype_digit($link_id)) {
6317
                        $tbl_link = Database :: get_course_table(TABLE_LINK);
6318
                        $sql_select = 'SELECT url FROM ' . $tbl_link . '
6319
                                       WHERE c_id = '.$course_id.' AND id = ' . intval($link_id);
6320
                        $res_link = Database::query($sql_select);
6321
                        $row_link = Database :: fetch_array($res_link);
6322
                        if (is_array($row_link)) {
6323
                            $row['url'] = $row_link['url'];
6324
                        }
6325
                    }
6326
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6327
                    $return .= $this->display_link_form('edit', $item_id, $row);
6328
                    break;
6329 View Code Duplication
                case TOOL_LP_FINAL_ITEM:
6330
                    $_SESSION['finalItem'] = true;
6331
                    $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
6332
                    $sql = "SELECT lp.*, doc.path as dir
6333
                            FROM " . $tbl_lp_item . " as lp
6334
                            LEFT JOIN " . $tbl_doc . " as doc
6335
                            ON doc.id = lp.path
6336
                            WHERE
6337
                                lp.c_id = $course_id AND
6338
                                doc.c_id = $course_id AND
6339
                                lp.id = " . intval($item_id);
6340
                    $res_step = Database::query($sql);
6341
                    $row_step = Database :: fetch_array($res_step, 'ASSOC');
6342
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6343
                    $return .= $this->display_document_form('edit', $item_id, $row_step);
6344
                    break;
6345 View Code Duplication
                case 'dokeos_module':
6346
                    if (isset ($_GET['view']) && $_GET['view'] == 'build') {
6347
                        $return .= $this->display_manipulate($item_id, $row['item_type']);
6348
                        $return .= $this->display_item_form($row['item_type'], get_lang('EditCurrentModule') . ' :', 'edit', $item_id, $row);
6349
                    } else {
6350
                        $return .= $this->display_item_small_form($row['item_type'], get_lang('EditCurrentModule') . ' :', $row);
6351
                    }
6352
                    break;
6353 View Code Duplication
                case TOOL_QUIZ:
6354
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6355
                    $return .= $this->display_quiz_form('edit', $item_id, $row);
6356
                    break;
6357
                case TOOL_HOTPOTATOES:
6358
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6359
                    $return .= $this->display_hotpotatoes_form('edit', $item_id, $row);
6360
                    break;
6361 View Code Duplication
                case TOOL_STUDENTPUBLICATION:
6362
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6363
                    $return .= $this->display_student_publication_form('edit', $item_id, $row);
6364
                    break;
6365
                case TOOL_FORUM:
6366
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6367
                    $return .= $this->display_forum_form('edit', $item_id, $row);
6368
                    break;
6369 View Code Duplication
                case TOOL_THREAD:
6370
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6371
                    $return .= $this->display_thread_form('edit', $item_id, $row);
6372
                    break;
6373
            }
6374
        }
6375
6376
        return $return;
6377
    }
6378
6379
    /**
6380
     * Function that displays a list with al the resources that
6381
     * could be added to the learning path
6382
     * @return string
6383
     */
6384
    public function display_resources()
6385
    {
6386
        $course_code = api_get_course_id();
6387
6388
        // Get all the docs.
6389
        $documents = $this->get_documents(true);
6390
6391
        // Get all the exercises.
6392
        $exercises = $this->get_exercises();
6393
6394
        // Get all the links.
6395
        $links = $this->get_links();
6396
6397
        // Get all the student publications.
6398
        $works = $this->get_student_publications();
6399
6400
        // Get all the forums.
6401
        $forums = $this->get_forums(null, $course_code);
6402
6403
        // Get the final item form (see BT#11048) .
6404
        $finish = $this->getFinalItemForm();
6405
6406
        $headers = array(
6407
            Display::return_icon('folder_document.png', get_lang('Documents'), array(), ICON_SIZE_BIG),
6408
            Display::return_icon('quiz.png',  get_lang('Quiz'), array(), ICON_SIZE_BIG),
6409
            Display::return_icon('links.png', get_lang('Links'), array(), ICON_SIZE_BIG),
6410
            Display::return_icon('works.png', get_lang('Works'), array(), ICON_SIZE_BIG),
6411
            Display::return_icon('forum.png', get_lang('Forums'), array(), ICON_SIZE_BIG),
6412
            Display::return_icon('add_learnpath_section.png', get_lang('NewChapter'), array(), ICON_SIZE_BIG),
6413
            Display::return_icon('certificate.png', get_lang('Certificate'), [], ICON_SIZE_BIG),
6414
        );
6415
6416
        echo Display::display_normal_message(get_lang('ClickOnTheLearnerViewToSeeYourLearningPath'));
6417
        $chapter = $_SESSION['oLP']->display_item_form('chapter', get_lang('EnterDataNewChapter'), 'add_item');
6418
        echo Display::tabs(
6419
            $headers,
6420
            array($documents, $exercises, $links, $works, $forums, $chapter, $finish), 'resource_tab'
6421
        );
6422
6423
        return true;
6424
    }
6425
6426
    /**
6427
     * Returns the extension of a document
6428
     * @param string filename
6429
     * @return string Extension (part after the last dot)
6430
     */
6431
    public function get_extension($filename)
6432
    {
6433
        $explode = explode('.', $filename);
6434
        return $explode[count($explode) - 1];
6435
    }
6436
6437
    /**
6438
     * Displays a document by id
6439
     *
6440
     * @param int $id
6441
     * @return string
6442
     */
6443
    public function display_document($id, $show_title = false, $iframe = true, $edit_link = false)
6444
    {
6445
        $_course = api_get_course_info();
6446
        $course_id = api_get_course_int_id();
6447
        $return = '';
6448
        $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
6449
        $sql_doc = "SELECT * FROM " . $tbl_doc . "
6450
                    WHERE c_id = ".$course_id." AND id = " . $id;
6451
        $res_doc = Database::query($sql_doc);
6452
        $row_doc = Database :: fetch_array($res_doc);
6453
6454
        // TODO: Add a path filter.
6455
        if ($iframe) {
6456
            $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>';
6457
        } else {
6458
            $return .= file_get_contents(api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document' . $row_doc['path']);
6459
        }
6460
6461
        return $return;
6462
    }
6463
6464
    /**
6465
     * Return HTML form to add/edit a quiz
6466
     * @param	string	Action (add/edit)
6467
     * @param	integer	Item ID if already exists
6468
     * @param	mixed	Extra information (quiz ID if integer)
6469
     * @return	string	HTML form
6470
     */
6471
    public function display_quiz_form($action = 'add', $id = 0, $extra_info = '')
6472
    {
6473
        $course_id = api_get_course_int_id();
6474
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
6475
        $tbl_quiz = Database :: get_course_table(TABLE_QUIZ_TEST);
6476
6477 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
6478
            $item_title = $extra_info['title'];
6479
            $item_description = $extra_info['description'];
6480
        } elseif (is_numeric($extra_info)) {
6481
            $sql = "SELECT title, description
6482
                    FROM " . $tbl_quiz . "
6483
                    WHERE c_id = ".$course_id." AND id = " . $extra_info;
6484
6485
            $result = Database::query($sql);
6486
            $row = Database::fetch_array($result);
6487
            $item_title = $row['title'];
6488
            $item_description = $row['description'];
6489
        } else {
6490
            $item_title = '';
6491
            $item_description = '';
6492
        }
6493
        $item_title			= Security::remove_XSS($item_title);
6494
        $item_description 	= Security::remove_XSS($item_description);
6495
6496 View Code Duplication
        if ($id != 0 && is_array($extra_info))
6497
            $parent = $extra_info['parent_item_id'];
6498
        else
6499
            $parent = 0;
6500
6501
        $sql = "SELECT * FROM " . $tbl_lp_item . "
6502
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
6503
6504
        $result = Database::query($sql);
6505
        $arrLP = array ();
6506 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
6507
            $arrLP[] = array (
6508
                'id' => $row['id'],
6509
                'item_type' => $row['item_type'],
6510
                'title' => $row['title'],
6511
                'path' => $row['path'],
6512
                'description' => $row['description'],
6513
                'parent_item_id' => $row['parent_item_id'],
6514
                'previous_item_id' => $row['previous_item_id'],
6515
                'next_item_id' => $row['next_item_id'],
6516
                'display_order' => $row['display_order'],
6517
                'max_score' => $row['max_score'],
6518
                'min_score' => $row['min_score'],
6519
                'mastery_score' => $row['mastery_score'],
6520
                'prerequisite' => $row['prerequisite'],
6521
                'max_time_allowed' => $row['max_time_allowed']
6522
            );
6523
        }
6524
6525
        $this->tree_array($arrLP);
6526
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
6527
        unset ($this->arrMenu);
6528
6529
        $form = new FormValidator('quiz_form', 'POST', api_get_self() . '?' .$_SERVER['QUERY_STRING']);
6530
        $defaults = [];
6531
6532
        if ($action == 'add') {
6533
            $legend = get_lang('CreateTheExercise');
6534
        } elseif ($action == 'move') {
6535
            $legend = get_lang('MoveTheCurrentExercise');
6536
        } else {
6537
            $legend = get_lang('EditCurrentExecice');
6538
        }
6539
6540 View Code Duplication
        if (isset ($_GET['edit']) && $_GET['edit'] == 'true') {
6541
            $legend .= Display :: return_warning_message(get_lang('Warning') . ' ! ' . get_lang('WarningEditingDocument'));
6542
        }
6543
6544
        $form->addHeader($legend);
6545
6546
        if ($action != 'move') {
6547
            $form->addText('title', get_lang('Title'), true, ['id' => 'idTitle']);
6548
            $defaults['title'] = $item_title;
6549
        }
6550
6551
        // Select for Parent item, root or chapter
6552
        $selectParent = $form->addSelect(
6553
            'parent',
6554
            get_lang('Parent'),
6555
            [],
6556
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);']
6557
        );
6558
        $selectParent->addOption($this->name, 0);
6559
6560
        $arrHide = array (
6561
            $id
6562
        );
6563 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
6564
            if ($action != 'add') {
6565
                if (
6566
                    (
6567
                        $arrLP[$i]['item_type'] == 'dokeos_module' ||
6568
                        $arrLP[$i]['item_type'] == 'dokeos_chapter' ||
6569
                        $arrLP[$i]['item_type'] == 'dir'
6570
                    ) &&
6571
                    !in_array($arrLP[$i]['id'], $arrHide) &&
6572
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
6573
                ) {
6574
                    $selectParent->addOption(
6575
                        $arrLP[$i]['title'],
6576
                        $arrLP[$i]['id'],
6577
                        ['style' => 'padding-left: ' . (20 + $arrLP[$i]['depth'] * 20) . 'px']
6578
                    );
6579
6580
                    if ($parent == $arrLP[$i]['id']) {
6581
                        $selectParent->setSelected($arrLP[$i]['id']);
6582
                    }
6583
                } else {
6584
                    $arrHide[] = $arrLP[$i]['id'];
6585
                }
6586
            } else {
6587
                if (
6588
                    $arrLP[$i]['item_type'] == 'dokeos_module' ||
6589
                    $arrLP[$i]['item_type'] == 'dokeos_chapter' ||
6590
                    $arrLP[$i]['item_type'] == 'dir'
6591
                ) {
6592
                    $selectParent->addOption(
6593
                        $arrLP[$i]['title'],
6594
                        $arrLP[$i]['id'], ['style' => 'padding-left: ' . (20 + $arrLP[$i]['depth'] * 20) . 'px']
6595
                    );
6596
6597
                    if ($parent == $arrLP[$i]['id']) {
6598
                        $selectParent->setSelected($arrLP[$i]['id']);
6599
                    }
6600
                }
6601
            }
6602
        }
6603
        if (is_array($arrLP)) {
6604
            reset($arrLP);
6605
        }
6606
6607
        $selectPrevious = $form->addSelect('previous', get_lang('Position'), [], ['id' => 'previous']);
6608
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
6609
6610 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
6611
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
6612
                $selectPrevious->addOption(get_lang('After') . ' "' . $arrLP[$i]['title'] . '"', $arrLP[$i]['id']);
6613
6614
                if (is_array($extra_info)) {
6615
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
6616
                        $selectPrevious->setSelected($arrLP[$i]['id']);
6617
                    }
6618
                } elseif ($action == 'add') {
6619
                    $selectPrevious->setSelected($arrLP[$i]['id']);
6620
                }
6621
            }
6622
        }
6623
6624 View Code Duplication
        if ($action != 'move') {
6625
            $id_prerequisite = 0;
6626
            if (is_array($arrLP)) {
6627
                foreach ($arrLP as $key => $value) {
6628
                    if ($value['id'] == $id) {
6629
                        $id_prerequisite = $value['prerequisite'];
6630
                        break;
6631
                    }
6632
                }
6633
            }
6634
            $arrHide = array ();
6635
            for ($i = 0; $i < count($arrLP); $i++) {
6636
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
6637
                    if (is_array($extra_info)) {
6638
                        if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
6639
                            $s_selected_position = $arrLP[$i]['id'];
6640
                        }
6641
                    } elseif ($action == 'add') {
6642
                        $s_selected_position = 0;
6643
                    }
6644
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
6645
                }
6646
            }
6647
            /*// Commented the prerequisites, only visible in edit (exercise).
6648
            $return .= '<tr>';
6649
            $return .= '<td class="label"><label for="idPrerequisites">'.get_lang('LearnpathPrerequisites').'</label></td>';
6650
            $return .= '<td class="input"><select name="prerequisites" id="prerequisites" class="learnpath_item_form"><option value="0">'.get_lang('NoPrerequisites').'</option>';
6651
6652
                foreach($arrHide as $key => $value){
6653
                    if($key==$s_selected_position && $action == 'add'){
6654
                        $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
6655
                    }
6656
                    elseif($key==$id_prerequisite && $action == 'edit'){
6657
                        $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
6658
                    }
6659
                    else{
6660
                        $return .= '<option value="'.$key.'">'.$value['value'].'</option>';
6661
                    }
6662
                }
6663
6664
            $return .= "</select></td>";
6665
            */
6666
            /*$return .= '<tr>';
6667
            $return .= '<td class="label"><label for="maxTimeAllowed">' . get_lang('MaxTimeAllowed') . '</label></td>';
6668
            $return .= '<td class="input"><input name="maxTimeAllowed" style="width:98%;" id="maxTimeAllowed" value="' . $extra_info['max_time_allowed'] . '" /></td>';
6669
6670
            // Remove temporarily the test description.
6671
            //$return .= '<td class="label"><label for="idDescription">'.get_lang('Description').' :</label></td>';
6672
            //$return .= '<td class="input"><textarea id="idDescription" name="description" rows="4">' . $item_description . '</textarea></td>';
6673
6674
            $return .= '</tr>'; */
6675
        }
6676
6677
        if ($action == 'add') {
6678
            $form->addButtonSave(get_lang('AddExercise'), 'submit_button');
6679
        } else {
6680
            $form->addButtonSave(get_lang('EditCurrentExecice'), 'submit_button');
6681
        }
6682
6683
        if ($action == 'move') {
6684
            $form->addHidden('title', $item_title);
6685
            $form->addHidden('description', $item_description);
6686
        }
6687
6688 View Code Duplication
        if (is_numeric($extra_info)) {
6689
            $form->addHidden('path', $extra_info);
6690
        } elseif (is_array($extra_info)) {
6691
            $form->addHidden('path', $extra_info['path']);
6692
        }
6693
6694
        $form->addHidden('type', TOOL_QUIZ);
6695
        $form->addHidden('post_time', time());
6696
6697
        $form->setDefaults($defaults);
6698
6699
        return '<div class="sectioncomment">' . $form->returnForm() . '</div>';
6700
    }
6701
6702
    /**
6703
     * Addition of Hotpotatoes tests
6704
     * @param	string	Action
6705
     * @param	integer	Internal ID of the item
6706
     * @param	mixed	Extra information - can be an array with title and description indexes
6707
     * @return  string	HTML structure to display the hotpotatoes addition formular
6708
     */
6709
    public function display_hotpotatoes_form($action = 'add', $id = 0, $extra_info = '')
6710
    {
6711
        $course_id = api_get_course_int_id();
6712
        $uploadPath = DIR_HOTPOTATOES; //defined in main_api
6713
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
6714
6715
        if ($id != 0 && is_array($extra_info)) {
6716
            $item_title = stripslashes($extra_info['title']);
6717
            $item_description = stripslashes($extra_info['description']);
6718
        } elseif (is_numeric($extra_info)) {
6719
            $TBL_DOCUMENT = Database :: get_course_table(TABLE_DOCUMENT);
6720
6721
            $sql = "SELECT * FROM " . $TBL_DOCUMENT . "
6722
                    WHERE
6723
                        c_id = ".$course_id." AND
6724
                        path LIKE '" . $uploadPath . "/%/%htm%' AND
6725
                        id = " . (int) $extra_info . "
6726
                    ORDER BY id ASC";
6727
6728
            $res_hot = Database::query($sql);
6729
            $row = Database::fetch_array($res_hot);
6730
6731
            $item_title = $row['title'];
6732
            $item_description = $row['description'];
6733
6734
            if (!empty ($row['comment'])) {
6735
                $item_title = $row['comment'];
6736
            }
6737
        } else {
6738
            $item_title = '';
6739
            $item_description = '';
6740
        }
6741
6742 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
6743
            $parent = $extra_info['parent_item_id'];
6744
        } else {
6745
            $parent = 0;
6746
        }
6747
6748
        $sql = "SELECT * FROM $tbl_lp_item
6749
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
6750
        $result = Database::query($sql);
6751
        $arrLP = array ();
6752 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
6753
            $arrLP[] = array (
6754
                'id' => $row['id'],
6755
                'item_type' => $row['item_type'],
6756
                'title' => $row['title'],
6757
                'path' => $row['path'],
6758
                'description' => $row['description'],
6759
                'parent_item_id' => $row['parent_item_id'],
6760
                'previous_item_id' => $row['previous_item_id'],
6761
                'next_item_id' => $row['next_item_id'],
6762
                'display_order' => $row['display_order'],
6763
                'max_score' => $row['max_score'],
6764
                'min_score' => $row['min_score'],
6765
                'mastery_score' => $row['mastery_score'],
6766
                'prerequisite' => $row['prerequisite'],
6767
                'max_time_allowed' => $row['max_time_allowed']
6768
            );
6769
        }
6770
6771
        $legend = '<legend>';
6772
        if ($action == 'add')
6773
            $legend .= get_lang('CreateTheExercise');
6774
        elseif ($action == 'move') $legend .= get_lang('MoveTheCurrentExercise');
6775
        else
6776
            $legend .= get_lang('EditCurrentExecice');
6777 View Code Duplication
        if (isset ($_GET['edit']) && $_GET['edit'] == 'true') {
6778
            $legend .= Display :: return_warning_message(get_lang('Warning') . ' ! ' . get_lang('WarningEditingDocument'));
6779
        }
6780
        $legend .= '</legend>';
6781
6782
        $return = '<form method="POST">';
6783
        $return .= $legend;
6784
        $return .= '<table cellpadding="0" cellspacing="0" class="lp_form">';
6785
        $return .= '<tr>';
6786
        $return .= '<td class="label"><label for="idParent">' . get_lang('Parent') . ' :</label></td>';
6787
        $return .= '<td class="input">';
6788
        $return .= '<select id="idParent" name="parent" onChange="javascript: load_cbo(this.value);" size="1">';
6789
        $return .= '<option class="top" value="0">' . $this->name . '</option>';
6790
        $arrHide = array (
6791
            $id
6792
        );
6793
6794
        if (count($arrLP) > 0) {
6795
            for ($i = 0; $i < count($arrLP); $i++) {
6796
                if ($action != 'add') {
6797
                    if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
6798
                        $return .= '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
6799
                    } else {
6800
                        $arrHide[] = $arrLP[$i]['id'];
6801
                    }
6802
                } else {
6803
                    if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir')
6804
                        $return .= '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
6805
                }
6806
            }
6807
6808
            reset($arrLP);
6809
        }
6810
6811
        $return .= '</select>';
6812
        $return .= '</td>';
6813
        $return .= '</tr>';
6814
        $return .= '<tr>';
6815
        $return .= '<td class="label"><label for="previous">' . get_lang('Position') . ' :</label></td>';
6816
        $return .= '<td class="input">';
6817
        $return .= '<select id="previous" name="previous" size="1">';
6818
        $return .= '<option class="top" value="0">' . get_lang('FirstPosition') . '</option>';
6819
6820
        for ($i = 0; $i < count($arrLP); $i++) {
6821
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
6822
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
6823
                    $selected = 'selected="selected" ';
6824
                elseif ($action == 'add') $selected = 'selected="selected" ';
6825
                else
6826
                    $selected = '';
6827
6828
                $return .= '<option ' . $selected . 'value="' . $arrLP[$i]['id'] . '">' . get_lang('After') . ' "' . $arrLP[$i]['title'] . '"</option>';
6829
            }
6830
        }
6831
6832
        $return .= '</select>';
6833
        $return .= '</td>';
6834
        $return .= '</tr>';
6835
6836
        if ($action != 'move') {
6837
            $return .= '<tr>';
6838
            $return .= '<td class="label"><label for="idTitle">' . get_lang('Title') . ' :</label></td>';
6839
            $return .= '<td class="input"><input id="idTitle" name="title" type="text" value="' . $item_title . '" /></td>';
6840
            $return .= '</tr>';
6841
            $id_prerequisite = 0;
6842 View Code Duplication
            if (is_array($arrLP) && count($arrLP) > 0) {
6843
                foreach ($arrLP as $key => $value) {
6844
                    if ($value['id'] == $id) {
6845
                        $id_prerequisite = $value['prerequisite'];
6846
                        break;
6847
                    }
6848
                }
6849
6850
                $arrHide = array ();
6851
                for ($i = 0; $i < count($arrLP); $i++) {
6852
                    if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
6853
                        if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
6854
                            $s_selected_position = $arrLP[$i]['id'];
6855
                        elseif ($action == 'add') $s_selected_position = 0;
6856
                        $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
6857
6858
                    }
6859
                }
6860
            }
6861
        }
6862
6863
        $return .= '<tr>';
6864
        $return .= '<td>&nbsp; </td><td><button class="save" name="submit_button" action="edit" type="submit">' . get_lang('SaveHotpotatoes') . '</button></td>';
6865
        $return .= '</tr>';
6866
        $return .= '</table>';
6867
6868
        if ($action == 'move') {
6869
            $return .= '<input name="title" type="hidden" value="' . $item_title . '" />';
6870
            $return .= '<input name="description" type="hidden" value="' . $item_description . '" />';
6871
        }
6872
6873
        if (is_numeric($extra_info)) {
6874
            $return .= '<input name="path" type="hidden" value="' . $extra_info . '" />';
6875
        } elseif (is_array($extra_info)) {
6876
            $return .= '<input name="path" type="hidden" value="' . $extra_info['path'] . '" />';
6877
        }
6878
        $return .= '<input name="type" type="hidden" value="' . TOOL_HOTPOTATOES . '" />';
6879
        $return .= '<input name="post_time" type="hidden" value="' . time() . '" />';
6880
        $return .= '</form>';
6881
6882
        return $return;
6883
    }
6884
6885
    /**
6886
     * Return the form to display the forum edit/add option
6887
     * @param	string	Action (add/edit)
6888
     * @param	integer	ID of the lp_item if already exists
6889
     * @param	mixed	Forum ID or title
6890
     * @return	string	HTML form
6891
     */
6892
    public function display_forum_form($action = 'add', $id = 0, $extra_info = '')
6893
    {
6894
        $course_id = api_get_course_int_id();
6895
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
6896
        $tbl_forum = Database :: get_course_table(TABLE_FORUM);
6897
6898 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
6899
            $item_title = stripslashes($extra_info['title']);
6900
        } elseif (is_numeric($extra_info)) {
6901
            $sql = "SELECT forum_title as title, forum_comment as comment
6902
                    FROM " . $tbl_forum . "
6903
                    WHERE c_id = ".$course_id." AND forum_id = " . $extra_info;
6904
6905
            $result = Database::query($sql);
6906
            $row = Database :: fetch_array($result);
6907
6908
            $item_title = $row['title'];
6909
            $item_description = $row['comment'];
6910
        } else {
6911
            $item_title = '';
6912
            $item_description = '';
6913
        }
6914
6915 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
6916
            $parent = $extra_info['parent_item_id'];
6917
        } else {
6918
            $parent = 0;
6919
        }
6920
6921
        $sql = "SELECT * FROM " . $tbl_lp_item . "
6922
                WHERE
6923
                    c_id = ".$course_id." AND
6924
                    lp_id = " . $this->lp_id;
6925
        $result = Database::query($sql);
6926
        $arrLP = array();
6927
6928 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
6929
            $arrLP[] = array (
6930
                'id' => $row['id'],
6931
                'item_type' => $row['item_type'],
6932
                'title' => $row['title'],
6933
                'path' => $row['path'],
6934
                'description' => $row['description'],
6935
                'parent_item_id' => $row['parent_item_id'],
6936
                'previous_item_id' => $row['previous_item_id'],
6937
                'next_item_id' => $row['next_item_id'],
6938
                'display_order' => $row['display_order'],
6939
                'max_score' => $row['max_score'],
6940
                'min_score' => $row['min_score'],
6941
                'mastery_score' => $row['mastery_score'],
6942
                'prerequisite' => $row['prerequisite']
6943
            );
6944
        }
6945
6946
        $this->tree_array($arrLP);
6947
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
6948
        unset($this->arrMenu);
6949
6950 View Code Duplication
        if ($action == 'add') {
6951
            $legend = get_lang('CreateTheForum');
6952
        } elseif ($action == 'move') {
6953
            $legend = get_lang('MoveTheCurrentForum');
6954
        } else {
6955
            $legend = get_lang('EditCurrentForum');
6956
        }
6957
6958
        $form = new FormValidator('forum_form', 'POST', api_get_self() . '?' .$_SERVER['QUERY_STRING']);
6959
        $defaults = [];
6960
6961
        $form->addHeader($legend);
6962
6963 View Code Duplication
        if ($action != 'move') {
6964
            $form->addText('title', get_lang('Title'), true, ['id' => 'idTitle', 'class' => 'learnpath_item_form']);
6965
            $defaults['title'] = $item_title;
6966
        }
6967
6968
        $selectParent = $form->addSelect(
6969
            'parent',
6970
            get_lang('Parent'),
6971
            [],
6972
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);', 'class' => 'learnpath_item_form']
6973
        );
6974
        $selectParent->addOption($this->name, 0);
6975
6976
        $arrHide = array(
6977
            $id
6978
        );
6979
6980
        //$parent_item_id = $_SESSION['parent_item_id'];
6981 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
6982
            if ($action != 'add') {
6983
                if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
6984
                    $selectParent->addOption(
6985
                        $arrLP[$i]['title'],
6986
                        $arrLP[$i]['id'],
6987
                        ['style' => 'padding-left: ' . (20 + $arrLP[$i]['depth'] * 20) . 'px']
6988
                    );
6989
6990
                    if ($parent == $arrLP[$i]['id']) {
6991
                        $selectParent->setSelected($arrLP[$i]['id']);
6992
                    }
6993
                } else {
6994
                    $arrHide[] = $arrLP[$i]['id'];
6995
                }
6996
            } else {
6997
                if (
6998
                    $arrLP[$i]['item_type'] == 'dokeos_module' ||
6999
                    $arrLP[$i]['item_type'] == 'dokeos_chapter' ||
7000
                    $arrLP[$i]['item_type'] == 'dir'
7001
                ) {
7002
                    $selectParent->addOption(
7003
                        $arrLP[$i]['title'],
7004
                        $arrLP[$i]['id'],
7005
                        ['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(
7019
            'previous',
7020
            get_lang('Position'),
7021
            [],
7022
            ['id' => 'previous', 'class' => 'learnpath_item_form']
7023
        );
7024
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
7025
7026 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7027
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
7028
                $selectPrevious->addOption(get_lang('After') . ' "' . $arrLP[$i]['title'] . '"', $arrLP[$i]['id']);
7029
7030
                if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7031
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7032
                } elseif ($action == 'add') {
7033
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7034
                }
7035
            }
7036
        }
7037
7038 View Code Duplication
        if ($action != 'move') {
7039
            $id_prerequisite = 0;
7040
            if (is_array($arrLP)) {
7041
                foreach ($arrLP as $key => $value) {
7042
                    if ($value['id'] == $id) {
7043
                        $id_prerequisite = $value['prerequisite'];
7044
                        break;
7045
                    }
7046
                }
7047
            }
7048
7049
            $arrHide = array();
7050
            for ($i = 0; $i < count($arrLP); $i++) {
7051
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
7052
                    if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id'])
7053
                        $s_selected_position = $arrLP[$i]['id'];
7054
                    elseif ($action == 'add') $s_selected_position = 0;
7055
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7056
                }
7057
            }
7058
        }
7059
7060
        if ($action == 'add') {
7061
            $form->addButtonSave(get_lang('AddForumToCourse'), 'submit_button');
7062
        } else {
7063
            $form->addButtonSave(get_lang('EditCurrentForum'), '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
        $form->addHidden('type', TOOL_FORUM);
7077
        $form->addHidden('post_time', time());
7078
        $form->setDefaults($defaults);
7079
7080
        return '<div class="sectioncomment">' . $form->returnForm() . '</div>';
7081
    }
7082
7083
    /**
7084
     * Return HTML form to add/edit forum threads
7085
     * @param	string	Action (add/edit)
7086
     * @param	integer	Item ID if already exists in learning path
7087
     * @param	mixed	Extra information (thread ID if integer)
7088
     * @return 	string	HTML form
7089
     */
7090
    public function display_thread_form($action = 'add', $id = 0, $extra_info = '')
7091
    {
7092
        $course_id = api_get_course_int_id();
7093
        if (empty($course_id)) {
7094
            return null;
7095
        }
7096
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
7097
        $tbl_forum = Database :: get_course_table(TABLE_FORUM_THREAD);
7098
7099
        if ($id != 0 && is_array($extra_info)) {
7100
            $item_title = stripslashes($extra_info['title']);
7101 View Code Duplication
        } elseif (is_numeric($extra_info)) {
7102
            $sql = "SELECT thread_title as title FROM $tbl_forum
7103
                    WHERE c_id = $course_id AND thread_id = " . $extra_info;
7104
7105
            $result = Database::query($sql);
7106
            $row = Database :: fetch_array($result);
7107
7108
            $item_title = $row['title'];
7109
            $item_description = '';
7110
        } else {
7111
            $item_title = '';
7112
            $item_description = '';
7113
        }
7114
7115 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
7116
            $parent = $extra_info['parent_item_id'];
7117
        } else {
7118
            $parent = 0;
7119
        }
7120
7121
        $sql = "SELECT * FROM " . $tbl_lp_item . "
7122
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
7123
7124
        $result = Database::query($sql);
7125
7126
        $arrLP = array ();
7127
7128 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
7129
            $arrLP[] = array (
7130
                'id' => $row['id'],
7131
                'item_type' => $row['item_type'],
7132
                'title' => $row['title'],
7133
                'path' => $row['path'],
7134
                'description' => $row['description'],
7135
                'parent_item_id' => $row['parent_item_id'],
7136
                'previous_item_id' => $row['previous_item_id'],
7137
                'next_item_id' => $row['next_item_id'],
7138
                'display_order' => $row['display_order'],
7139
                'max_score' => $row['max_score'],
7140
                'min_score' => $row['min_score'],
7141
                'mastery_score' => $row['mastery_score'],
7142
                'prerequisite' => $row['prerequisite']
7143
            );
7144
        }
7145
7146
        $this->tree_array($arrLP);
7147
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
7148
        unset ($this->arrMenu);
7149
7150
        $form = new FormValidator('thread_form', 'POST', api_get_self() . '?' .$_SERVER['QUERY_STRING']);
7151
        $defaults = [];
7152
7153 View Code Duplication
        if ($action == 'add') {
7154
            $legend = get_lang('CreateTheForum');
7155
        } elseif ($action == 'move') {
7156
            $legend = get_lang('MoveTheCurrentForum');
7157
        } else {
7158
            $legend = get_lang('EditCurrentForum');
7159
        }
7160
7161
        $form->addHeader($legend);
7162
        $selectParent = $form->addSelect(
7163
            'parent',
7164
            get_lang('Parent'),
7165
            [],
7166
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);']
7167
        );
7168
        $selectParent->addOption($this->name, 0);
7169
7170
        $arrHide = array (
7171
            $id
7172
        );
7173
7174 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7175
            if ($action != 'add') {
7176
                if (
7177
                    (
7178
                        $arrLP[$i]['item_type'] == 'dokeos_module' ||
7179
                        $arrLP[$i]['item_type'] == 'dokeos_chapter' ||
7180
                        $arrLP[$i]['item_type'] == 'dir'
7181
                    ) &&
7182
                    !in_array($arrLP[$i]['id'], $arrHide) &&
7183
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
7184
                ) {
7185
                    $selectParent->addOption(
7186
                        $arrLP[$i]['title'],
7187
                        $arrLP[$i]['id'],
7188
                        ['style' => 'padding-left: ' . (20 + $arrLP[$i]['depth'] * 20) . 'px']
7189
                    );
7190
7191
                    if ($parent == $arrLP[$i]['id']) {
7192
                        $selectParent->setSelected($arrLP[$i]['id']);
7193
                    }
7194
                } else {
7195
                    $arrHide[] = $arrLP[$i]['id'];
7196
                }
7197
            } else {
7198
                if (
7199
                    $arrLP[$i]['item_type'] == 'dokeos_module' ||
7200
                    $arrLP[$i]['item_type'] == 'dokeos_chapter' ||
7201
                    $arrLP[$i]['item_type'] == 'dir'
7202
                ) {
7203
                    $selectParent->addOption(
7204
                        $arrLP[$i]['title'],
7205
                        $arrLP[$i]['id'],
7206
                        ['style' => 'padding-left: ' . (20 + $arrLP[$i]['depth'] * 20) . 'px']
7207
                    );
7208
7209
                    if ($parent == $arrLP[$i]['id']) {
7210
                        $selectParent->setSelected($arrLP[$i]['id']);
7211
                    }
7212
                }
7213
            }
7214
        }
7215
7216
        if ($arrLP != null) {
7217
            reset($arrLP);
7218
        }
7219
7220
        $selectPrevious = $form->addSelect('previous', get_lang('Position'), [], ['id' => 'previous']);
7221
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
7222
7223 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7224
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
7225
                $selectPrevious->addOption(
7226
                    get_lang('After')  . ' "' . $arrLP[$i]['title'] . '"',
7227
                    $arrLP[$i]['id']
7228
                );
7229
7230
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7231
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7232
                } elseif ($action == 'add') {
7233
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7234
                }
7235
            }
7236
        }
7237
7238
        if ($action != 'move') {
7239
            $form->addText('title', get_lang('Title'), true, ['id' => 'idTitle']);
7240
            $defaults['title'] = $item_title;
7241
7242
            $id_prerequisite = 0;
7243
            if ($arrLP != null) {
7244
                foreach ($arrLP as $key => $value) {
7245
                    if ($value['id'] == $id) {
7246
                        $id_prerequisite = $value['prerequisite'];
7247
                        break;
7248
                    }
7249
                }
7250
            }
7251
7252
            $arrHide = array();
7253
            $s_selected_position = 0;
7254
7255
            for ($i = 0; $i < count($arrLP); $i++) {
7256
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
7257
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
7258
                        $s_selected_position = $arrLP[$i]['id'];
7259
                    elseif ($action == 'add') $s_selected_position = 0;
7260
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7261
7262
                }
7263
            }
7264
7265
            $selectPrerequisites = $form->addSelect(
7266
                'prerequisites',
7267
                get_lang('LearnpathPrerequisites'),
7268
                [],
7269
                ['id' => 'prerequisites']
7270
            );
7271
            $selectPrerequisites->addOption(get_lang('NoPrerequisites'), 0);
7272
7273
            foreach ($arrHide as $key => $value) {
7274
                $selectPrerequisites->addOption($value['value'], $key);
7275
7276
                if ($key == $s_selected_position && $action == 'add') {
7277
                    $selectPrerequisites->setSelected($key);
7278
                } elseif ($key == $id_prerequisite && $action == 'edit') {
7279
                    $selectPrerequisites->setSelected($key);
7280
                }
7281
            }
7282
        }
7283
7284
        $form->addButtonSave(get_lang('Ok'), 'submit_button');
7285
7286
        if ($action == 'move') {
7287
            $form->addHidden('title', $item_title);
7288
            $form->addHidden('description', $item_description);
7289
        }
7290
7291 View Code Duplication
        if (is_numeric($extra_info)) {
7292
            $form->addHidden('path', $extra_info);
7293
        }
7294
        elseif (is_array($extra_info)) {
7295
            $form->addHidden('path', $extra_info['path']);
7296
        }
7297
7298
        $form->addHidden('type', TOOL_THREAD);
7299
        $form->addHidden('post_time', time());
7300
        $form->setDefaults($defaults);
7301
7302
        return $form->returnForm();
7303
    }
7304
7305
    /**
7306
     * Return the HTML form to display an item (generally a section/module item)
7307
     * @param	string	Item type (module/dokeos_module)
7308
     * @param	string	Title (optional, only when creating)
7309
     * @param	string	Action ('add'/'edit')
7310
     * @param	integer	lp_item ID
7311
     * @param	mixed	Extra info
7312
     * @return	string 	HTML form
7313
     */
7314
    public function display_item_form($item_type, $title = '', $action = 'add_item', $id = 0, $extra_info = 'new')
7315
    {
7316
        $course_id = api_get_course_int_id();
7317
        $_course = api_get_course_info();
7318
7319
        global $charset;
7320
7321
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
7322
7323
        if ($id != 0 && is_array($extra_info)) {
7324
            $item_title 		= $extra_info['title'];
7325
            $item_description 	= $extra_info['description'];
7326
            $item_path = api_get_path(WEB_COURSE_PATH) . $_course['path'] . '/scorm/' . $this->path . '/' . stripslashes($extra_info['path']);
7327
            $item_path_fck = '/scorm/' . $this->path . '/' . stripslashes($extra_info['path']);
7328
        } else {
7329
            $item_title = '';
7330
            $item_description = '';
7331
            $item_path_fck = '';
7332
        }
7333
7334 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
7335
            $parent = $extra_info['parent_item_id'];
7336
        } else {
7337
            $parent = 0;
7338
        }
7339
7340
        $id  = intval($id);
7341
        $sql = "SELECT * FROM " . $tbl_lp_item . "
7342
                WHERE
7343
                    c_id = ".$course_id." AND
7344
                    lp_id = " . $this->lp_id . " AND
7345
                    id != $id";
7346
7347
        if ($item_type == 'module')
7348
            $sql .= " AND parent_item_id = 0";
7349
7350
        $result = Database::query($sql);
7351
        $arrLP = array ();
7352
7353 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
7354
            $arrLP[] = array(
7355
                'id' => $row['id'],
7356
                'item_type' => $row['item_type'],
7357
                'title' => $row['title'],
7358
                'path' => $row['path'],
7359
                'description' => $row['description'],
7360
                'parent_item_id' => $row['parent_item_id'],
7361
                'previous_item_id' => $row['previous_item_id'],
7362
                'next_item_id' => $row['next_item_id'],
7363
                'max_score' => $row['max_score'],
7364
                'min_score' => $row['min_score'],
7365
                'mastery_score' => $row['mastery_score'],
7366
                'prerequisite' => $row['prerequisite'],
7367
                'display_order' => $row['display_order']
7368
            );
7369
        }
7370
7371
        $this->tree_array($arrLP);
7372
7373
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
7374
7375
        unset ($this->arrMenu);
7376
7377
        $gradebook = isset($_GET['gradebook']) ? Security :: remove_XSS($_GET['gradebook']) : null;
7378
7379
        $url = api_get_self() . '?' .api_get_cidreq().'&gradeboook='.$gradebook.'&action='.$action.'&type='.$item_type.'&lp_id='.$this->lp_id;
7380
7381
        $form = new FormValidator('form', 'POST',  $url);
7382
7383
        $defaults['title'] = api_html_entity_decode($item_title, ENT_QUOTES, $charset);
7384
        $defaults['description'] = $item_description;
7385
7386
        $form->addElement('header', $title);
7387
7388
        //$arrHide = array($id);
7389
        $arrHide[0]['value'] = Security :: remove_XSS($this->name);
7390
        $arrHide[0]['padding'] = 20;
7391
        $charset = api_get_system_encoding();
7392
7393
        if ($item_type != 'module' && $item_type != 'dokeos_module') {
7394 View Code Duplication
            for ($i = 0; $i < count($arrLP); $i++) {
7395
                if ($action != 'add') {
7396
                    if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
7397
                        $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7398
                        $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
7399
                        if ($parent == $arrLP[$i]['id']) {
7400
                            $s_selected_parent = $arrHide[$arrLP[$i]['id']];
7401
                        }
7402
                    }
7403
                } else {
7404
                    if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') {
7405
                        $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7406
                        $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
7407
                        if ($parent == $arrLP[$i]['id']) {
7408
                            $s_selected_parent = $arrHide[$arrLP[$i]['id']];
7409
                        }
7410
                    }
7411
                }
7412
            }
7413
7414
            if ($action != 'move') {
7415
                $form->addElement('text', 'title', get_lang('Title'));
7416
                $form->applyFilter('title', 'html_filter');
7417
                $form->addRule('title', get_lang('ThisFieldIsRequired'), 'required');
7418
            } else {
7419
                $form->addElement('hidden', 'title');
7420
            }
7421
7422
            $parent_select = $form->addElement('select', 'parent', get_lang('Parent'), '', array('id' => 'idParent', 'onchange' => "javascript: load_cbo(this.value);"));
7423
7424
            foreach ($arrHide as $key => $value) {
7425
                $parent_select->addOption($value['value'], $key, 'style="padding-left:' . $value['padding'] . 'px;"');
7426
            }
7427
            if (!empty($s_selected_parent)) {
7428
                $parent_select->setSelected($s_selected_parent);
7429
            }
7430
        }
7431
        if (is_array($arrLP)) {
7432
            reset($arrLP);
7433
        }
7434
7435
        $arrHide = array();
7436
7437
        // POSITION
7438
        for ($i = 0; $i < count($arrLP); $i++) {
7439
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
7440
                //this is the same!
7441
                if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7442
                    $s_selected_position = $arrLP[$i]['id'];
7443
                } elseif ($action == 'add') {
7444
                    $s_selected_position = $arrLP[$i]['id'];
7445
                }
7446
7447
                $arrHide[$arrLP[$i]['id']]['value'] = get_lang('After') . ' "' . $arrLP[$i]['title'] . '"';
7448
            }
7449
        }
7450
7451
        $position = $form->addElement('select', 'previous', get_lang('Position'), '', array('id' => 'previous'));
7452
        $padding = isset($value['padding']) ? $value['padding'] : 0;
7453
        $position->addOption(get_lang('FirstPosition'), 0, 'style="padding-left:' . $padding . 'px;"');
7454
7455
        foreach ($arrHide as $key => $value) {
7456
            $position->addOption($value['value'] . '"', $key, 'style="padding-left:' . $padding . 'px;"');
7457
        }
7458
7459
        if (!empty ($s_selected_position)) {
7460
            $position->setSelected($s_selected_position);
7461
        }
7462
7463
        if (is_array($arrLP)) {
7464
            reset($arrLP);
7465
        }
7466
7467
        $form->addButtonSave(get_lang('SaveSection'), 'submit_button');
7468
7469
        if ($item_type == 'module' || $item_type == 'dokeos_module') {
7470
            $form->addElement('hidden', 'parent', '0');
7471
        }
7472
        //fix in order to use the tab
7473
        if ($item_type == 'chapter') {
7474
            $form->addElement('hidden', 'type', 'chapter');
7475
        }
7476
7477
        $extension = null;
7478
        if (!empty($item_path)) {
7479
            $extension = pathinfo($item_path, PATHINFO_EXTENSION);
7480
        }
7481
7482
        //assets can't be modified
7483
7484
        //$item_type == 'asset' ||
7485
        if (( $item_type == 'sco') && ($extension == 'html' || $extension == 'htm')) {
7486
7487
            if ($item_type == 'sco') {
7488
                $form->addElement('html', '<script type="text/javascript">alert("' . get_lang('WarningWhenEditingScorm') . '")</script>');
7489
            }
7490
            $renderer = $form->defaultRenderer();
7491
            $renderer->setElementTemplate('<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{label}<br />{element}', 'content_lp');
7492
7493
            $relative_prefix = '';
7494
7495
            $editor_config = array( 'ToolbarSet' 			=> 'LearningPathDocuments',
7496
                'Width' 				=> '100%',
7497
                'Height' 				=> '500',
7498
                'FullPage' 				=> true,
7499
                'CreateDocumentDir' 	=> $relative_prefix,
7500
                'CreateDocumentWebDir' 	=> api_get_path(WEB_COURSE_PATH) . api_get_course_path().'/scorm/',
7501
                'BaseHref' 				=> api_get_path(WEB_COURSE_PATH) . api_get_course_path().$item_path_fck
7502
            );
7503
7504
            $form->addElement('html_editor', 'content_lp', '', null, $editor_config);
7505
            $content_path = (api_get_path(SYS_COURSE_PATH).api_get_course_path().$item_path_fck);
7506
            //$defaults['content_lp'] = file_get_contents($item_path);
7507
            $defaults['content_lp'] = file_get_contents($content_path);
7508
        }
7509
7510
        $form->addElement('hidden', 'type', 'dokeos_' . $item_type);
7511
        $form->addElement('hidden', 'post_time', time());
7512
        $form->setDefaults($defaults);
7513
        return $form->return_form();
7514
    }
7515
7516
    /**
7517
     * Returns the form to update or create a document
7518
     * @param	string	Action (add/edit)
7519
     * @param	integer	ID of the lp_item (if already exists)
7520
     * @param	mixed	Integer if document ID, string if info ('new')
7521
     * @return	string	HTML form
7522
     */
7523
    public function display_document_form($action = 'add', $id = 0, $extra_info = 'new')
7524
    {
7525
        $course_id = api_get_course_int_id();
7526
        $_course = api_get_course_info();
7527
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
7528
        $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
7529
7530
        $no_display_edit_textarea = false;
7531
        $item_description = '';
7532
        //If action==edit document
7533
        //We don't display the document form if it's not an editable document (html or txt file)
7534
        if ($action == "edit") {
7535
            if (is_array($extra_info)) {
7536
                $path_parts = pathinfo($extra_info['dir']);
7537
                if ($path_parts['extension'] != "txt" && $path_parts['extension'] != "html") {
7538
                    $no_display_edit_textarea = true;
7539
                }
7540
            }
7541
        }
7542
        $no_display_add = false;
7543
7544
        // If action==add an existing document
7545
        // We don't display the document form if it's not an editable document (html or txt file).
7546
        if ($action == "add") {
7547
            if (is_numeric($extra_info)) {
7548
                $sql_doc = "SELECT path FROM " . $tbl_doc . "
7549
                            WHERE c_id = ".$course_id." AND id = " . intval($extra_info);
7550
                $result = Database::query($sql_doc);
7551
                $path_file = Database :: result($result, 0, 0);
7552
                $path_parts = pathinfo($path_file);
7553
                if ($path_parts['extension'] != "txt" && $path_parts['extension'] != "html") {
7554
                    $no_display_add = true;
7555
                }
7556
            }
7557
        }
7558
        if ($id != 0 && is_array($extra_info)) {
7559
            $item_title = stripslashes($extra_info['title']);
7560
            $item_description = stripslashes($extra_info['description']);
7561
            $item_terms = stripslashes($extra_info['terms']);
7562 View Code Duplication
            if (empty ($item_title)) {
7563
                $path_parts = pathinfo($extra_info['path']);
7564
                $item_title = stripslashes($path_parts['filename']);
7565
            }
7566
        } elseif (is_numeric($extra_info)) {
7567
            $sql_doc = "SELECT path, title FROM " . $tbl_doc . "
7568
                        WHERE
7569
                            c_id = ".$course_id." AND
7570
                            id = " . intval($extra_info);
7571
7572
            $result = Database::query($sql_doc);
7573
            $row 	= Database::fetch_array($result);
7574
            $item_title = $row['title'];
7575
            $item_title = str_replace('_', ' ', $item_title);
7576 View Code Duplication
            if (empty ($item_title)) {
7577
                $path_parts = pathinfo($row['path']);
7578
                $item_title = stripslashes($path_parts['filename']);
7579
            }
7580
        } else {
7581
            $item_title = '';
7582
            $item_description = '';
7583
        }
7584
        $return = '<legend>';
7585
7586 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
7587
            $parent = $extra_info['parent_item_id'];
7588
        } else {
7589
            $parent = 0;
7590
        }
7591
7592
        $sql = "SELECT * FROM " . $tbl_lp_item . "
7593
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
7594
7595
        $result = Database::query($sql);
7596
        $arrLP = array ();
7597 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
7598
            $arrLP[] = array(
7599
                'id' => $row['id'],
7600
                'item_type' => $row['item_type'],
7601
                'title' => $row['title'],
7602
                'path' => $row['path'],
7603
                'description' => $row['description'],
7604
                'parent_item_id' => $row['parent_item_id'],
7605
                'previous_item_id' => $row['previous_item_id'],
7606
                'next_item_id' => $row['next_item_id'],
7607
                'display_order' => $row['display_order'],
7608
                'max_score' => $row['max_score'],
7609
                'min_score' => $row['min_score'],
7610
                'mastery_score' => $row['mastery_score'],
7611
                'prerequisite' => $row['prerequisite']
7612
            );
7613
        }
7614
7615
        $this->tree_array($arrLP);
7616
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
7617
        unset ($this->arrMenu);
7618
7619
        if ($action == 'add') {
7620
            $return .= get_lang('CreateTheDocument');
7621
        } elseif ($action == 'move') {
7622
            $return .= get_lang('MoveTheCurrentDocument');
7623
        } else {
7624
            $return .= get_lang('EditTheCurrentDocument');
7625
        }
7626
7627
        $return .= '</legend>';
7628
7629 View Code Duplication
        if (isset ($_GET['edit']) && $_GET['edit'] == 'true') {
7630
            $return .= Display :: return_warning_message('<strong>' . get_lang('Warning') . ' !</strong><br />' . get_lang('WarningEditingDocument'), false);
7631
        }
7632
        $form = new FormValidator('form', 'POST', api_get_self() . '?' .$_SERVER['QUERY_STRING'], '', array('enctype'=> "multipart/form-data"));
7633
        $defaults['title'] = Security :: remove_XSS($item_title);
7634
        if (empty($item_title)) {
7635
            $defaults['title'] = Security::remove_XSS($item_title);
7636
        }
7637
        $defaults['description'] = $item_description;
7638
        $form->addElement('html', $return);
7639
        if ($action != 'move') {
7640
            $form->addElement('text', 'title', get_lang('Title'), array('id' => 'idTitle', 'class' => 'col-md-4'));
7641
            $form->applyFilter('title', 'html_filter');
7642
        }
7643
7644
        $arrHide[0]['value'] = $this->name;
7645
        $arrHide[0]['padding'] = 20;
7646
7647 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7648
            if ($action != 'add') {
7649
                if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
7650
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7651
                    $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
7652
                    if ($parent == $arrLP[$i]['id']) {
7653
                        $s_selected_parent = $arrHide[$arrLP[$i]['id']];
7654
                    }
7655
                }
7656
            } else {
7657
                if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') {
7658
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7659
                    $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
7660
                    if ($parent == $arrLP[$i]['id']) {
7661
                        $s_selected_parent = $arrHide[$arrLP[$i]['id']];
7662
                    }
7663
                }
7664
            }
7665
        }
7666
7667
        $parent_select = $form->addSelect('parent', get_lang('Parent'), [], ['id' => 'idParent', 'onchange' => 'javascript: load_cbo(this.value);']);
7668
        $my_count=0;
7669
        foreach ($arrHide as $key => $value) {
7670
            if ($my_count!=0) {
7671
                // The LP name is also the first section and is not in the same charset like the other sections.
7672
                $value['value'] = Security :: remove_XSS($value['value']);
7673
                $parent_select->addOption($value['value'], $key, 'style="padding-left:' . $value['padding'] . 'px;"');
7674
            } else {
7675
                $value['value'] = Security :: remove_XSS($value['value']);
7676
                $parent_select->addOption($value['value'], $key, 'style="padding-left:' . $value['padding'] . 'px;"');
7677
            }
7678
            $my_count++;
7679
        }
7680
7681
        if (!empty($id)) {
7682
            $parent_select->setSelected($parent);
7683
        } else {
7684
            $parent_item_id = isset($_SESSION['parent_item_id']) ? $_SESSION['parent_item_id'] : 0 ;
7685
            $parent_select->setSelected($parent_item_id);
7686
        }
7687
7688
        if (is_array($arrLP)) {
7689
            reset($arrLP);
7690
        }
7691
7692
        $arrHide = array();
7693
        $s_selected_position = null;
7694
7695
        //POSITION
7696
        for ($i = 0; $i < count($arrLP); $i++) {
7697
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id || $arrLP[$i]['item_type'] == TOOL_LP_FINAL_ITEM) {
7698
                if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id'] || $action == 'add') {
7699
                    $s_selected_position = $arrLP[$i]['id'];
7700
                }
7701
                $arrHide[$arrLP[$i]['id']]['value'] = get_lang('After') . ' "' . $arrLP[$i]['title'] . '"';
7702
            }
7703
        }
7704
7705
        $position = $form->addSelect('previous', get_lang('Position'), [], ['id' => 'previous']);
7706
        $position->addOption(get_lang('FirstPosition'), 0);
7707
7708
        foreach ($arrHide as $key => $value) {
7709
            $padding = isset($value['padding']) ? $value['padding']: 20;
7710
            $position->addOption($value['value'], $key, 'style="padding-left:' . $padding . 'px;"');
7711
        }
7712
7713
        $position->setSelected($s_selected_position);
7714
7715
        if (is_array($arrLP)) {
7716
            reset($arrLP);
7717
        }
7718
7719
        if ($action != 'move') {
7720
            $id_prerequisite = 0;
7721
            if (is_array($arrLP)) {
7722
                foreach ($arrLP as $key => $value) {
7723
                    if ($value['id'] == $id) {
7724
                        $id_prerequisite = $value['prerequisite'];
7725
                        break;
7726
                    }
7727
                }
7728
            }
7729
7730
            $arrHide = array();
7731
7732
            for ($i = 0; $i < count($arrLP); $i++) {
7733
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter' && $arrLP[$i]['item_type'] !== TOOL_LP_FINAL_ITEM) {
7734
                    if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id'])
7735
                        $s_selected_position = $arrLP[$i]['id'];
7736
                    elseif ($action == 'add') $s_selected_position = $arrLP[$i]['id'];
7737
7738
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7739
7740
                }
7741
            }
7742
7743
            if (!$no_display_add) {
7744
                $item_type = isset($extra_info['item_type']) ? $extra_info['item_type'] : null;
7745
                $edit = isset($_GET['edit']) ? $_GET['edit'] : null;
7746
                if (($extra_info == 'new' || $item_type == TOOL_DOCUMENT || $item_type == TOOL_LP_FINAL_ITEM || $edit == 'true')) {
7747
                    if (isset ($_POST['content']))
7748
                        $content = stripslashes($_POST['content']);
7749
                    elseif (is_array($extra_info)) {
7750
                        //If it's an html document or a text file
7751
                        if (!$no_display_edit_textarea) {
7752
                            $content = $this->display_document($extra_info['path'], false, false);
7753
                        }
7754
                    } elseif (is_numeric($extra_info))
7755
                        $content = $this->display_document($extra_info, false, false);
7756
                    else
7757
                        $content = '';
7758
7759
                    if (!$no_display_edit_textarea) {
7760
                        // We need to calculate here some specific settings for the online editor.
7761
                        // The calculated settings work for documents in the Documents tool
7762
                        // (on the root or in subfolders).
7763
                        // For documents in native scorm packages it is unclear whether the
7764
                        // online editor should be activated or not.
7765
7766
                        // A new document, it is in the root of the repository.
7767
                        $relative_path 	 = '';
7768
                        $relative_prefix = '';
7769
7770
                        if (is_array($extra_info) && $extra_info != 'new') {
7771
                            // The document already exists. Whe have to determine its relative path towards the repository root.
7772
                            $relative_path = explode('/', $extra_info['dir']);
7773
                            $cnt = count($relative_path) - 2;
7774
                            if ($cnt < 0) {
7775
                                $cnt = 0;
7776
                            }
7777
                            $relative_prefix = str_repeat('../', $cnt);
7778
                            $relative_path 	 = array_slice($relative_path, 1, $cnt);
7779
                            $relative_path 	 = implode('/', $relative_path);
7780
                            if (strlen($relative_path) > 0) {
7781
                                $relative_path = $relative_path . '/';
7782
                            }
7783
                        } else {
7784
                            $result = $this->generate_lp_folder($_course);
7785
                            $relative_path = api_substr($result['dir'], 1, strlen($result['dir']));
7786
                            $relative_prefix = '../../';
7787
                        }
7788
7789
                        $editor_config = array(
7790
                            'ToolbarSet'=> 'LearningPathDocuments',
7791
                            'Width' 				=> '100%',
7792
                            'Height' 				=> '500',
7793
                            'FullPage' 				=> true,
7794
                            'CreateDocumentDir' 	=> $relative_prefix,
7795
                            'CreateDocumentWebDir' 	=> api_get_path(WEB_COURSE_PATH) . api_get_course_path().'/document/',
7796
                            'BaseHref' 				=> api_get_path(WEB_COURSE_PATH) . api_get_course_path().'/document/'.$relative_path
7797
                        );
7798
7799
                        if ($_GET['action'] == 'add_item') {
7800
                            $class = 'add';
7801
                            $text = get_lang('LPCreateDocument');
7802
                        } else {
7803
                            if ($_GET['action'] == 'edit_item') {
7804
                                $class = 'save';
7805
                                $text = get_lang('SaveDocument');
7806
                            }
7807
                        }
7808
7809
                        $form->addButtonSave($text, 'submit_button');
7810
                        $renderer = $form->defaultRenderer();
7811
                        $renderer->setElementTemplate('&nbsp;{label}{element}', 'content_lp');
7812
                        $form->addElement('html', '<div class="editor-lp">');
7813
                        $form->addHtmlEditor('content_lp', null, null, true, $editor_config, true);
7814
                        $form->addElement('html', '</div>');
7815
                        $defaults['content_lp'] = $content;
7816
                    }
7817
                } elseif (is_numeric($extra_info)) {
7818
                    $form->addButtonSave(get_lang('SaveDocument'), 'submit_button');
7819
7820
                    $return = $this->display_document($extra_info, true, true, true);
7821
                    $form->addElement('html', $return);
7822
                }
7823
            }
7824
        }
7825
7826
        if ($action == 'move') {
7827
            $form->addElement('hidden', 'title', $item_title);
7828
            $form->addElement('hidden', 'description', $item_description);
7829
        }
7830
        if (is_numeric($extra_info)) {
7831
            $form->addButtonSave(get_lang('SaveDocument'), 'submit_button');
7832
            $form->addElement('hidden', 'path', $extra_info);
7833
        } elseif (is_array($extra_info)) {
7834
            $form->addButtonSave(get_lang('SaveDocument'), 'submit_button');
7835
            $form->addElement('hidden', 'path', $extra_info['path']);
7836
        }
7837
        $form->addElement('hidden', 'type', TOOL_DOCUMENT);
7838
        $form->addElement('hidden', 'post_time', time());
7839
        $form->setDefaults($defaults);
7840
7841
        return $form->return_form();
7842
    }
7843
7844
    /**
7845
     * Return HTML form to add/edit a link item
7846
     * @param string	$action (add/edit)
7847
     * @param integer	$id Item ID if exists
7848
     * @param mixed		$extra_info
7849
     * @return	string	HTML form
7850
     */
7851
    public function display_link_form($action = 'add', $id = 0, $extra_info = '')
7852
    {
7853
        $course_id = api_get_course_int_id();
7854
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
7855
        $tbl_link = Database :: get_course_table(TABLE_LINK);
7856
7857
        if ($id != 0 && is_array($extra_info)) {
7858
            $item_title = stripslashes($extra_info['title']);
7859
            $item_description = stripslashes($extra_info['description']);
7860
            $item_url = stripslashes($extra_info['url']);
7861
        } elseif (is_numeric($extra_info)) {
7862
            $extra_info = intval($extra_info);
7863
            $sql = "SELECT title, description, url FROM " . $tbl_link . "
7864
                    WHERE c_id = ".$course_id." AND id = " . $extra_info;
7865
            $result = Database::query($sql);
7866
            $row = Database :: fetch_array($result);
7867
            $item_title       = $row['title'];
7868
            $item_description = $row['description'];
7869
            $item_url = $row['url'];
7870
        } else {
7871
            $item_title = '';
7872
            $item_description = '';
7873
            $item_url = '';
7874
        }
7875
7876
        $form = new FormValidator('edit_link', 'POST', api_get_self() . '?' .$_SERVER['QUERY_STRING']);
7877
        $defaults = [];
7878
7879 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
7880
            $parent = $extra_info['parent_item_id'];
7881
        } else {
7882
            $parent = 0;
7883
        }
7884
7885
        $sql = "SELECT * FROM " . $tbl_lp_item . "
7886
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
7887
        $result = Database::query($sql);
7888
        $arrLP = array();
7889
7890 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
7891
            $arrLP[] = array(
7892
                'id' => $row['id'],
7893
                'item_type' => $row['item_type'],
7894
                'title' => $row['title'],
7895
                'path' => $row['path'],
7896
                'description' => $row['description'],
7897
                'parent_item_id' => $row['parent_item_id'],
7898
                'previous_item_id' => $row['previous_item_id'],
7899
                'next_item_id' => $row['next_item_id'],
7900
                'display_order' => $row['display_order'],
7901
                'max_score' => $row['max_score'],
7902
                'min_score' => $row['min_score'],
7903
                'mastery_score' => $row['mastery_score'],
7904
                'prerequisite' => $row['prerequisite']
7905
            );
7906
        }
7907
7908
        $this->tree_array($arrLP);
7909
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
7910
        unset ($this->arrMenu);
7911
7912
        if ($action == 'add') {
7913
            $legend = get_lang('CreateTheLink');
7914
        } elseif ($action == 'move') {
7915
            $legend = get_lang('MoveCurrentLink');
7916
        } else {
7917
            $legend = get_lang('EditCurrentLink');
7918
        }
7919
7920
        $form->addHeader($legend);
7921
7922 View Code Duplication
        if ($action != 'move') {
7923
            $form->addText('title', get_lang('Title'), true, ['class' => 'learnpath_item_form']);
7924
            $defaults['title'] = $item_title;
7925
        }
7926
7927
        $selectParent = $form->addSelect(
7928
            'parent',
7929
            get_lang('Parent'),
7930
            [],
7931
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);', 'class' => 'learnpath_item_form']
7932
        );
7933
        $selectParent->addOption($this->name, 0);
7934
        $arrHide = array(
7935
            $id
7936
        );
7937
7938
        $parent_item_id = isset($_SESSION['parent_item_id']) ? $_SESSION['parent_item_id'] : 0;
7939
7940 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7941
            if ($action != 'add') {
7942
                if (
7943
                    (
7944
                        $arrLP[$i]['item_type'] == 'dokeos_module' ||
7945
                        $arrLP[$i]['item_type'] == 'dokeos_chapter' ||
7946
                        $arrLP[$i]['item_type'] == 'dir'
7947
                    ) &&
7948
                    !in_array($arrLP[$i]['id'], $arrHide) &&
7949
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
7950
                ) {
7951
                    $selectParent->addOption(
7952
                        $arrLP[$i]['title'],
7953
                        $arrLP[$i]['id'],
7954
                        ['style' => 'padding-left: ' . (20 + $arrLP[$i]['depth'] * 20) . 'px;']
7955
                    );
7956
7957
                    if ($parent == $arrLP[$i]['id']) {
7958
                        $selectParent->setSelected($arrLP[$i]['id']);
7959
                    }
7960
                } else {
7961
                    $arrHide[] = $arrLP[$i]['id'];
7962
                }
7963
            } else {
7964
                if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') {
7965
                    $selectParent->addOption(
7966
                        $arrLP[$i]['title'],
7967
                        $arrLP[$i]['id'],
7968
                        ['style' => 'padding-left: ' . (20 + $arrLP[$i]['depth'] * 20) . 'px']
7969
                    );
7970
7971
                    if ($parent_item_id == $arrLP[$i]['id']) {
7972
                        $selectParent->setSelected($arrLP[$i]['id']);
7973
                    }
7974
                }
7975
            }
7976
        }
7977
7978
        if (is_array($arrLP)) {
7979
            reset($arrLP);
7980
        }
7981
7982
        $selectPrevious = $form->addSelect(
7983
            'previous',
7984
            get_lang('Position'),
7985
            [],
7986
            ['id' => 'previous', 'class' => 'learnpath_item_form']
7987
        );
7988
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
7989
7990
        for ($i = 0; $i < count($arrLP); $i++) {
7991
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
7992
                $selectPrevious->addOption($arrLP[$i]['title'], $arrLP[$i]['id']);
7993
7994
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7995
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7996
                } elseif ($action == 'add') {
7997
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7998
                }
7999
            }
8000
        }
8001
8002
        if ($action != 'move') {
8003
            $urlAttributes = ['class' => 'learnpath_item_form'];
8004
8005
            if (is_numeric($extra_info)) {
8006
                $urlAttributes['disabled'] = 'disabled';
8007
            }
8008
8009
            $form->addElement('url', 'url', get_lang('Url'), $urlAttributes);
8010
            $defaults['url'] = $item_url;
8011
8012
            $id_prerequisite = 0;
8013
            if (is_array($arrLP)) {
8014
                foreach ($arrLP as $key => $value) {
8015
                    if ($value['id'] == $id) {
8016
                        $id_prerequisite = $value['prerequisite'];
8017
                        break;
8018
                    }
8019
                }
8020
            }
8021
8022
            $arrHide = array();
8023
            for ($i = 0; $i < count($arrLP); $i++) {
8024
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
8025
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
8026
                        $s_selected_position = $arrLP[$i]['id'];
8027
                    elseif ($action == 'add') $s_selected_position = 0;
8028
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8029
8030
                }
8031
            }
8032
        }
8033
8034
        if ($action == 'add') {
8035
            $form->addButtonSave(get_lang('AddLinkToCourse'), 'submit_button');
8036
        } else {
8037
            $form->addButtonSave(get_lang('EditCurrentLink'), 'submit_button');
8038
        }
8039
8040
        if ($action == 'move') {
8041
            $form->addHidden('title', $item_title);
8042
            $form->addHidden('description', $item_description);
8043
        }
8044
8045 View Code Duplication
        if (is_numeric($extra_info)) {
8046
            $form->addHidden('path', $extra_info);
8047
        } elseif (is_array($extra_info)) {
8048
            $form->addHidden('path', $extra_info['path']);
8049
        }
8050
        $form->addHidden('type', TOOL_LINK);
8051
        $form->addHidden('post_time', time());
8052
8053
        $form->setDefaults($defaults);
8054
8055
        return '<div class="sectioncomment">' . $form->returnForm() . '</div>';
8056
    }
8057
8058
    /**
8059
     * Return HTML form to add/edit a student publication (work)
8060
     * @param	string	Action (add/edit)
8061
     * @param	integer	Item ID if already exists
8062
     * @param	mixed	Extra info (work ID if integer)
8063
     * @return	string	HTML form
8064
     */
8065
    public function display_student_publication_form($action = 'add', $id = 0, $extra_info = '')
8066
    {
8067
        $course_id = api_get_course_int_id();
8068
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
8069
        $tbl_publication = Database :: get_course_table(TABLE_STUDENT_PUBLICATION);
8070
8071
        if ($id != 0 && is_array($extra_info)) {
8072
            $item_title = stripslashes($extra_info['title']);
8073
            $item_description = stripslashes($extra_info['description']);
8074
        } elseif (is_numeric($extra_info)) {
8075
            $extra_info = intval($extra_info);
8076
            $sql = "SELECT title, description
8077
                    FROM " . $tbl_publication . "
8078
                    WHERE c_id = ".$course_id." AND id = " . $extra_info;
8079
8080
            $result = Database::query($sql);
8081
            $row = Database :: fetch_array($result);
8082
8083
            $item_title = $row['title'];
8084
        } else {
8085
            $item_title = get_lang('Student_publication');
8086
        }
8087
8088 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
8089
            $parent = $extra_info['parent_item_id'];
8090
        } else {
8091
            $parent = 0;
8092
        }
8093
8094
        $sql = "SELECT * FROM " . $tbl_lp_item . "
8095
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
8096
8097
        $result = Database::query($sql);
8098
        $arrLP = array();
8099 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
8100
            $arrLP[] = array (
8101
                'id' => $row['id'],
8102
                'item_type' => $row['item_type'],
8103
                'title' => $row['title'],
8104
                'path' => $row['path'],
8105
                'description' => $row['description'],
8106
                'parent_item_id' => $row['parent_item_id'],
8107
                'previous_item_id' => $row['previous_item_id'],
8108
                'next_item_id' => $row['next_item_id'],
8109
                'display_order' => $row['display_order'],
8110
                'max_score' => $row['max_score'],
8111
                'min_score' => $row['min_score'],
8112
                'mastery_score' => $row['mastery_score'],
8113
                'prerequisite' => $row['prerequisite']
8114
            );
8115
        }
8116
8117
        $this->tree_array($arrLP);
8118
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
8119
        unset ($this->arrMenu);
8120
8121
        $form = new FormValidator('frm_student_publication', 'post', '#');
8122
8123
        if ($action == 'add') {
8124
            $form->addHeader(get_lang('Student_publication'));
8125
        } elseif ($action == 'move') {
8126
            $form->addHeader(get_lang('MoveCurrentStudentPublication'));
8127
        } else {
8128
            $form->addHeader(get_lang('EditCurrentStudentPublication'));
8129
        }
8130
8131 View Code Duplication
        if ($action != 'move') {
8132
            $form->addText('title', get_lang('Title'), true, ['class' => 'learnpath_item_form', 'id' => 'idTitle']);
8133
        }
8134
8135
        $parentSelect = $form->addSelect(
8136
            'parent',
8137
            get_lang('Parent'),
8138
            ['0' => $this->name],
8139
            [
8140
                'onchange' => 'javascript: load_cbo(this.value);',
8141
                'class' => 'learnpath_item_form',
8142
                'id' => 'idParent'
8143
            ]
8144
        );
8145
8146
        $arrHide = array (
8147
            $id
8148
        );
8149
8150 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
8151
            if ($action != 'add') {
8152
                if (
8153
                    (
8154
                        $arrLP[$i]['item_type'] == 'dokeos_module' ||
8155
                        $arrLP[$i]['item_type'] == 'dokeos_chapter' ||
8156
                        $arrLP[$i]['item_type'] == 'dir'
8157
                    ) &&
8158
                    !in_array($arrLP[$i]['id'], $arrHide) &&
8159
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
8160
                ) {
8161
                    $parentSelect->addOption(
8162
                        $arrLP[$i]['title'],
8163
                        $arrLP[$i]['id'],
8164
                        ['style' => 'padding-left: ' . (($arrLP[$i]['depth'] * 10) + 20) . 'px;']
8165
                    );
8166
8167
                    if ($parent == $arrLP[$i]['id']) {
8168
                        $parentSelect->setSelected($arrLP[$i]['id']);
8169
                    }
8170
                } else {
8171
                    $arrHide[] = $arrLP[$i]['id'];
8172
                }
8173
            } else {
8174
                if (
8175
                    $arrLP[$i]['item_type'] == 'dokeos_module' ||
8176
                    $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir'
8177
                ) {
8178
                    $parentSelect->addOption(
8179
                        $arrLP[$i]['title'],
8180
                        $arrLP[$i]['id'],
8181
                        ['style' => 'padding-left: ' . (($arrLP[$i]['depth'] * 10) + 20) . 'px;']
8182
                    );
8183
8184
                    if ($parent == $arrLP[$i]['id']) {
8185
                        $parentSelect->setSelected($arrLP[$i]['id']);
8186
                    }
8187
                }
8188
            }
8189
        }
8190
8191
        if (is_array($arrLP)) {
8192
            reset($arrLP);
8193
        }
8194
8195
        $previousSelect = $form->addSelect(
8196
            'previous',
8197
            get_lang('Position'),
8198
            ['0' => get_lang('FirstPosition')],
8199
            ['id' => 'previous', 'class' => 'learnpath_item_form']
8200
        );
8201
8202 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
8203
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
8204
                $previousSelect->addOption(
8205
                    get_lang('After') . ' "' . $arrLP[$i]['title'] . '"',
8206
                    $arrLP[$i]['id']
8207
                );
8208
8209
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
8210
                    $previousSelect->setSelected($arrLP[$i]['id']);
8211
                } elseif ($action == 'add') {
8212
                    $previousSelect->setSelected($arrLP[$i]['id']);
8213
                }
8214
            }
8215
        }
8216
8217 View Code Duplication
        if ($action != 'move') {
8218
            $id_prerequisite = 0;
8219
            if (is_array($arrLP)) {
8220
                foreach ($arrLP as $key => $value) {
8221
                    if ($value['id'] == $id) {
8222
                        $id_prerequisite = $value['prerequisite'];
8223
                        break;
8224
                    }
8225
                }
8226
            }
8227
            $arrHide = array ();
8228
            for ($i = 0; $i < count($arrLP); $i++) {
8229
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
8230
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
8231
                        $s_selected_position = $arrLP[$i]['id'];
8232
                    elseif ($action == 'add') $s_selected_position = 0;
8233
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8234
8235
                }
8236
            }
8237
        }
8238
8239
        if ($action == 'add') {
8240
            $form->addButtonCreate(get_lang('AddAssignmentToCourse'), 'submit_button');
8241
        } else {
8242
            $form->addButtonCreate(get_lang('EditCurrentStudentPublication'), 'submit_button');
8243
        }
8244
8245
        if ($action == 'move') {
8246
            $form->addHidden('title', $item_title);
8247
            $form->addHidden('description', $item_description);
8248
        }
8249
8250 View Code Duplication
        if (is_numeric($extra_info)) {
8251
            $form->addHidden('path', $extra_info);
8252
        } elseif (is_array($extra_info)) {
8253
            $form->addHidden('path', $extra_info['path']);
8254
        }
8255
8256
        $form->addHidden('type', TOOL_STUDENTPUBLICATION);
8257
        $form->addHidden('post_time', time());
8258
        $form->setDefaults(['title' => $item_title]);
8259
8260
        $return = '<div class="sectioncomment">';
8261
        $return .= $form->returnForm();
8262
        $return .= '</div>';
8263
8264
        return $return;
8265
    }
8266
8267
    /**
8268
     * Displays the menu for manipulating a step
8269
     *
8270
     * @param $item_id
8271
     * @param string $item_type
8272
     * @return string
8273
     */
8274
    public function display_manipulate($item_id, $item_type = TOOL_DOCUMENT)
8275
    {
8276
        $_course = api_get_course_info();
8277
        $course_id = api_get_course_int_id();
8278
        $course_code = api_get_course_id();
8279
8280
        $return = '<div class="actions">';
8281
8282
        switch ($item_type) {
8283
            case 'dokeos_chapter' :
8284
            case 'chapter' :
8285
                // Commented the message cause should not show it.
8286
                //$lang = get_lang('TitleManipulateChapter');
8287
                break;
8288
8289
            case 'dokeos_module' :
8290
            case 'module' :
8291
                // Commented the message cause should not show it.
8292
                //$lang = get_lang('TitleManipulateModule');
8293
                break;
8294
            case TOOL_LP_FINAL_ITEM :
8295
            case TOOL_DOCUMENT :
8296
                // Commented the message cause should not show it.
8297
                //$lang = get_lang('TitleManipulateDocument');
8298
                break;
8299
8300
            case TOOL_LINK :
8301
            case 'link' :
8302
                // Commented the message cause should not show it.
8303
                //$lang = get_lang('TitleManipulateLink');
8304
                break;
8305
8306
            case TOOL_QUIZ :
8307
                // Commented the message cause should not show it.
8308
                //$lang = get_lang('TitleManipulateQuiz');
8309
                break;
8310
8311
            case TOOL_STUDENTPUBLICATION :
8312
                // Commented the message cause should not show it.
8313
                //$lang = get_lang('TitleManipulateStudentPublication');
8314
                break;
8315
        }
8316
8317
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
8318
        $item_id = intval($item_id);
8319
        $sql = "SELECT * FROM " . $tbl_lp_item . " as lp
8320
                WHERE lp.c_id = ".$course_id." AND lp.id = " . $item_id;
8321
        $result = Database::query($sql);
8322
        $row = Database::fetch_assoc($result);
8323
8324
        $audio_player = null;
8325
        // We display an audio player if needed.
8326
        if (!empty($row['audio'])) {
8327
            $audio_player .= '<div class="lp_mediaplayer" id="container">
8328
                              <a href="http://www.macromedia.com/go/getflashplayer">Get the Flash Player</a> to see this player.
8329
                              </div>';
8330
            $audio_player .= '<script type="text/javascript" src="../inc/lib/mediaplayer/swfobject.js"></script>';
8331
            $audio_player .= '<script>
8332
                                var s1 = new SWFObject("../inc/lib/mediaplayer/player.swf","ply","250","20","9","#FFFFFF");
8333
                                s1.addParam("allowscriptaccess","always");
8334
                                s1.addParam("flashvars","file=../../courses/' . $_course['path'] . '/document/audio/' . $row['audio'] . '&autostart=true");
8335
                                s1.write("container");
8336
                            </script>';
8337
        }
8338
8339
        $url = api_get_self().'?cidReq='.Security::remove_XSS($_GET['cidReq']).'&view=build&id='.$item_id .'&lp_id='.$this->lp_id;
8340
8341
        $return .= Display::url(
8342
            Display::return_icon('edit.png', get_lang('Edit'), array(), ICON_SIZE_SMALL),
8343
            $url.'&action=edit_item&path_item=' . $row['path']
8344
        );
8345
8346
        $return .= Display::url(
8347
            Display::return_icon('move.png', get_lang('Move'), array(), ICON_SIZE_SMALL),
8348
            $url.'&action=move_item'
8349
        );
8350
8351
        // Commented for now as prerequisites cannot be added to chapters.
8352
        if ($item_type != 'dokeos_chapter' && $item_type != 'chapter') {
8353
            $return .= Display::url(
8354
                Display::return_icon('accept.png', get_lang('LearnpathPrerequisites'), array(), ICON_SIZE_SMALL),
8355
                $url.'&action=edit_item_prereq'
8356
            );
8357
        }
8358
        $return .= Display::url(
8359
            Display::return_icon('delete.png', get_lang('Delete'), array(), ICON_SIZE_SMALL),
8360
            $url.'&action=delete_item'
8361
        );
8362
8363 View Code Duplication
        if ($item_type == TOOL_HOTPOTATOES ) {
8364
            $document_data = DocumentManager::get_document_data_by_id($row['path'], $course_code);
8365
            $return .= get_lang('File').': '.$document_data['absolute_path_from_document'];
8366
        }
8367
8368 View Code Duplication
        if ($item_type == TOOL_DOCUMENT || $item_type == TOOL_LP_FINAL_ITEM) {
8369
            $document_data = DocumentManager::get_document_data_by_id($row['path'], $course_code);
8370
            $return .= get_lang('File').': '.$document_data['absolute_path_from_document'];
8371
        }
8372
8373
        $return .= '</div>';
8374
8375
        if (!empty($audio_player)) {
8376
            $return .= '<br />'.$audio_player;
8377
        }
8378
8379
        return $return;
8380
    }
8381
8382
    /**
8383
     * Creates the javascript needed for filling up the checkboxes without page reload
8384
     * @return string
8385
     */
8386
    public function get_js_dropdown_array()
8387
    {
8388
        $course_id = api_get_course_int_id();
8389
        $return = 'var child_name = new Array();' . "\n";
8390
        $return .= 'var child_value = new Array();' . "\n\n";
8391
        $return .= 'child_name[0] = new Array();' . "\n";
8392
        $return .= 'child_value[0] = new Array();' . "\n\n";
8393
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
8394
        $sql_zero = "SELECT * FROM " . $tbl_lp_item . "
8395
                    WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id . " AND parent_item_id = 0
8396
                    ORDER BY display_order ASC";
8397
        $res_zero = Database::query($sql_zero);
8398
        $i = 0;
8399
8400
        while ($row_zero = Database :: fetch_array($res_zero)) {
8401
            if ($row_zero['item_type'] !== TOOL_LP_FINAL_ITEM) {
8402
                if ($row_zero['item_type'] == TOOL_QUIZ) {
8403
                    $row_zero['title'] = Exercise::get_formated_title_variable($row_zero['title']);
8404
                }
8405
                $js_var = json_encode(get_lang('After').' '.$row_zero['title']);
8406
                $return .= 'child_name[0][' . $i . '] = '.$js_var.' ;' . "\n";
8407
                $return .= 'child_value[0][' . $i++ . '] = "' . $row_zero['id'] . '";' . "\n";
8408
            }
8409
        }
8410
        $return .= "\n";
8411
        $sql = "SELECT * FROM " . $tbl_lp_item . "
8412
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
8413
        $res = Database::query($sql);
8414
        while ($row = Database :: fetch_array($res)) {
8415
            $sql_parent = "SELECT * FROM " . $tbl_lp_item . "
8416
                           WHERE
8417
                                c_id = ".$course_id." AND
8418
                                parent_item_id = " . $row['id'] . "
8419
                           ORDER BY display_order ASC";
8420
            $res_parent = Database::query($sql_parent);
8421
            $i = 0;
8422
            $return .= 'child_name[' . $row['id'] . '] = new Array();' . "\n";
8423
            $return .= 'child_value[' . $row['id'] . '] = new Array();' . "\n\n";
8424
8425
            while ($row_parent = Database :: fetch_array($res_parent)) {
8426
                $js_var = json_encode(get_lang('After').' '.$row_parent['title']);
8427
                $return .= 'child_name[' . $row['id'] . '][' . $i . '] =   '.$js_var.' ;' . "\n";
8428
                $return .= 'child_value[' . $row['id'] . '][' . $i++ . '] = "' . $row_parent['id'] . '";' . "\n";
8429
            }
8430
            $return .= "\n";
8431
        }
8432
8433
        return $return;
8434
    }
8435
8436
    /**
8437
     * Display the form to allow moving an item
8438
     * @param	integer		Item ID
8439
     * @return	string		HTML form
8440
     */
8441
    public function display_move_item($item_id)
8442
    {
8443
        $course_id = api_get_course_int_id();
8444
        $return = '';
8445
8446
        if (is_numeric($item_id)) {
8447
            $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
8448
8449
            $sql = "SELECT * FROM " . $tbl_lp_item . "
8450
                    WHERE c_id = ".$course_id." AND id = " . $item_id;
8451
8452
            $res = Database::query($sql);
8453
            $row = Database :: fetch_array($res);
8454
8455
            switch ($row['item_type']) {
8456
                case 'dokeos_chapter' :
8457
                case 'dir' :
8458
                case 'asset' :
8459
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8460
                    $return .= $this->display_item_form($row['item_type'], get_lang('MoveCurrentChapter'), 'move', $item_id, $row);
8461
                    break;
8462
                case 'dokeos_module' :
8463
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8464
                    $return .= $this->display_item_form($row['item_type'], 'Move th current module:', 'move', $item_id, $row);
8465
                    break;
8466
                case TOOL_DOCUMENT :
8467
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8468
                    $return .= $this->display_document_form('move', $item_id, $row);
8469
                    break;
8470 View Code Duplication
                case TOOL_LINK :
8471
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8472
                    $return .= $this->display_link_form('move', $item_id, $row);
8473
                    break;
8474 View Code Duplication
                case TOOL_HOTPOTATOES :
8475
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8476
                    $return .= $this->display_link_form('move', $item_id, $row);
8477
                    break;
8478 View Code Duplication
                case TOOL_QUIZ :
8479
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8480
                    $return .= $this->display_quiz_form('move', $item_id, $row);
8481
                    break;
8482 View Code Duplication
                case TOOL_STUDENTPUBLICATION :
8483
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8484
                    $return .= $this->display_student_publication_form('move', $item_id, $row);
8485
                    break;
8486
                case TOOL_FORUM :
8487
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8488
                    $return .= $this->display_forum_form('move', $item_id, $row);
8489
                    break;
8490 View Code Duplication
                case TOOL_THREAD :
8491
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8492
                    $return .= $this->display_forum_form('move', $item_id, $row);
8493
                    break;
8494
            }
8495
        }
8496
8497
        return $return;
8498
    }
8499
8500
    /**
8501
     * Displays a basic form on the overview page for changing the item title and the item description.
8502
     * @param string $item_type
8503
     * @param string $title
8504
     * @param array $data
8505
     * @return string
8506
     */
8507
    public function display_item_small_form($item_type, $title = '', $data = array())
8508
    {
8509
        $url = api_get_self() . '?' .api_get_cidreq().'&action=edit_item&lp_id='.$this->lp_id;
8510
        $form = new FormValidator('small_form', 'post', $url);
8511
        $form->addElement('header', $title);
8512
        $form->addElement('text', 'title', get_lang('Title'));
8513
        $form->addButtonSave(get_lang('Save'), 'submit_button');
8514
        $form->addElement('hidden', 'id', $data['id']);
8515
        $form->addElement('hidden', 'parent', $data['parent_item_id']);
8516
        $form->addElement('hidden', 'previous', $data['previous_item_id']);
8517
        $form->setDefaults(array('title' => $data['title']));
8518
8519
        return $form->toHtml();
8520
    }
8521
8522
    /**
8523
     * Return HTML form to allow prerequisites selection
8524
     * @todo use FormValidator
8525
     * @param	integer Item ID
8526
     * @return	string	HTML form
8527
     */
8528
    public function display_item_prerequisites_form($item_id)
8529
    {
8530
        $course_id = api_get_course_int_id();
8531
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
8532
        $item_id = intval($item_id);
8533
        /* Current prerequisite */
8534
        $sql = "SELECT * FROM $tbl_lp_item
8535
                WHERE c_id = $course_id AND id = " . $item_id;
8536
        $result = Database::query($sql);
8537
        $row    = Database::fetch_array($result);
8538
        $prerequisiteId = $row['prerequisite'];
8539
        $return = '<legend>';
8540
        $return .= get_lang('AddEditPrerequisites');
8541
        $return .= '</legend>';
8542
        $return .= '<form method="POST">';
8543
        $return .= '<table class="data_table">';
8544
        $return .= '<tr>';
8545
        $return .= '<th height="24">' . get_lang('LearnpathPrerequisites') . '</th>';
8546
        $return .= '<th width="70" >' . get_lang('Minimum') . '</th>';
8547
        $return .= '<th width="70">' . get_lang('Maximum') . '</th>';
8548
        $return .= '</tr>';
8549
8550
        // Adding the none option to the prerequisites see http://www.chamilo.org/es/node/146
8551
        $return .= '<tr >';
8552
        $return .= '<td colspan="3" class="radio">';
8553
        $return .= '<input checked="checked" id="idNone" name="prerequisites"  style="margin-left:0px; margin-right:10px;" type="radio" />';
8554
        $return .= '<label for="idNone">' . get_lang('None') . '</label>';
8555
        $return .= '</tr>';
8556
8557
        $sql = "SELECT * FROM $tbl_lp_item
8558
                WHERE c_id = $course_id AND lp_id = " . $this->lp_id;
8559
        $result = Database::query($sql);
8560
        $arrLP = array();
8561
8562
        $selectedMinScore = array();
8563
        $selectedMaxScore = array();
8564
        while ($row = Database :: fetch_array($result)) {
8565
            if ($row['id'] == $item_id) {
8566
                $selectedMinScore[$row['prerequisite']] = $row['prerequisite_min_score'];
8567
                $selectedMaxScore[$row['prerequisite']] = $row['prerequisite_max_score'];
8568
            }
8569
            $arrLP[] = array(
8570
                'id' => $row['id'],
8571
                'item_type' => $row['item_type'],
8572
                'title' => $row['title'],
8573
                'ref' => $row['ref'],
8574
                'description' => $row['description'],
8575
                'parent_item_id' => $row['parent_item_id'],
8576
                'previous_item_id' => $row['previous_item_id'],
8577
                'next_item_id' => $row['next_item_id'],
8578
                'max_score' => $row['max_score'],
8579
                'min_score' => $row['min_score'],
8580
                'mastery_score' => $row['mastery_score'],
8581
                'prerequisite' => $row['prerequisite'],
8582
                'next_item_id' => $row['next_item_id'],
8583
                'display_order' => $row['display_order'],
8584
                'prerequisite_min_score' => $row['prerequisite_min_score'],
8585
                'prerequisite_max_score' => $row['prerequisite_max_score'],
8586
            );
8587
        }
8588
8589
        $this->tree_array($arrLP);
8590
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
8591
        unset($this->arrMenu);
8592
8593
        for ($i = 0; $i < count($arrLP); $i++) {
8594
            $item = $arrLP[$i];
8595
8596
            if ($item['id'] == $item_id) {
8597
                break;
8598
            }
8599
8600
            $selectedMaxScoreValue = isset($selectedMaxScore[$item['id']]) ? $selectedMaxScore[$item['id']] : $item['max_score'];
8601
            $selectedMinScoreValue = isset($selectedMinScore[$item['id']]) ? $selectedMinScore[$item['id']]: 0;
8602
8603
            $return .= '<tr>';
8604
            $return .= '<td class="radio"' . (($item['item_type'] != TOOL_QUIZ && $item['item_type'] != TOOL_HOTPOTATOES) ? ' colspan="3"' : '') . '>';
8605
            $return .= '<label for="id' . $item['id'] . '">';
8606
            $return .= '<input' . (in_array($prerequisiteId, array($item['id'], $item['ref'])) ? ' checked="checked" ' : '') . (($item['item_type'] == 'dokeos_module' || $item['item_type'] == 'dokeos_chapter') ? ' disabled="disabled" ' : ' ') . 'id="id' . $item['id'] . '" name="prerequisites" style="margin-left:' . $item['depth'] * 10 . 'px; margin-right:10px;" type="radio" value="' . $item['id'] . '" />';
8607
            $icon_name = str_replace(' ', '', $item['item_type']);
8608
8609
            if (file_exists('../img/lp_' . $icon_name . '.png')) {
8610
                $return .= Display::return_icon('lp_' . $icon_name . '.png');
8611
            } else {
8612
                if (file_exists('../img/lp_' . $icon_name . '.gif')) {
8613
                    $return .= Display::return_icon('lp_' . $icon_name . '.gif');
8614
                } else {
8615
                    $return .= Display::return_icon('folder_document.gif', '', array('style'=>'margin-right:5px;'));
8616
                }
8617
            }
8618
8619
            $return .=  $item['title'] . '</label>';
8620
            $return .= '</td>';
8621
8622
            if ($item['item_type'] == TOOL_QUIZ) {
8623
                // lets update max_score Quiz information depending of the Quiz Advanced properties
8624
                $tmp_obj_lp_item = new LpItem($course_id, $item['id']);
8625
                $tmp_obj_exercice = new Exercise();
8626
                $tmp_obj_exercice->read($tmp_obj_lp_item->path);
8627
                $tmp_obj_lp_item->max_score = $tmp_obj_exercice->get_max_score();
8628
8629
                $tmp_obj_lp_item->update_in_bdd();
8630
                $item['max_score'] = $tmp_obj_lp_item->max_score;
8631
8632
                $return .= '<td class="exercise">';
8633
                $return .= '<input size="4" maxlength="3" name="min_' . $item['id'] . '" type="number" min="0" step="any" max="'.$item['max_score'].'" value="' . $selectedMinScoreValue. '" />';
8634
                $return .= '</td>';
8635
                $return .= '<td class="exercise">';
8636
                $return .= '<input size="4" maxlength="3" name="max_' . $item['id'] . '" type="number" min="0" step="any" max="'.$item['max_score'].'" value="' . $selectedMaxScoreValue . '" />';
8637
                $return .= '</td>';
8638
            }
8639
8640
            if ($item['item_type'] == TOOL_HOTPOTATOES) {
8641
                $return .= '<td class="exercise">';
8642
                $return .= '<center><input size="4" maxlength="3" name="min_' . $item['id'] . '" type="number" min="0" step="any" max="'.$item['max_score'].'" value="' . $selectedMinScoreValue . '" /></center>';
8643
                $return .= '</td>';
8644
                $return .= '<td class="exercise"">';
8645
                $return .= '<center><input size="4" maxlength="3" name="max_' . $item['id'] . '" type="number" min="0" step="any" max="'.$item['max_score'].'"  value="'.$selectedMaxScoreValue . '" /></center>';
8646
                $return .= '</td>';
8647
            }
8648
            $return .= '</tr>';
8649
        }
8650
        $return .= '<tr>';
8651
        $return .= '</tr>';
8652
        $return .= '</table>';
8653
        $return .= '<div style="padding-top:3px;">';
8654
        $return .= '<button class="btn btn-primary" name="submit_button" type="submit">' . get_lang('ModifyPrerequisites') . '</button>';
8655
        $return .= '</form>';
8656
8657
        return $return;
8658
    }
8659
8660
    /**
8661
     * Return HTML list to allow prerequisites selection for lp
8662
     * @param	integer Item ID
8663
     * @return	string	HTML form
8664
     */
8665
    public function display_lp_prerequisites_list()
8666
    {
8667
        $course_id = api_get_course_int_id();
8668
        $lp_id = $this->lp_id;
8669
        $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
8670
8671
        // get current prerequisite
8672
        $sql = "SELECT * FROM $tbl_lp WHERE c_id = $course_id AND id = $lp_id ";
8673
        $result = Database::query($sql);
8674
        $row = Database :: fetch_array($result);
8675
        $prerequisiteId = $row['prerequisite'];
8676
        $session_id = api_get_session_id();
8677
        $session_condition = api_get_session_condition($session_id);
8678
        $sql = "SELECT * FROM $tbl_lp
8679
                WHERE c_id = $course_id $session_condition
8680
                ORDER BY display_order ";
8681
        $rs = Database::query($sql);
8682
        $return = '';
8683
        $return .= '<select name="prerequisites" class="form-control">';
8684
        $return .= '<option value="0">'.get_lang('None').'</option>';
8685
        if (Database::num_rows($rs) > 0) {
8686
            while ($row = Database::fetch_array($rs)) {
8687
                if ($row['id'] == $lp_id) {
8688
                    continue;
8689
                }
8690
                $return .= '<option value="'.$row['id'].'" '.(($row['id']==$prerequisiteId)?' selected ' : '').'>'.$row['name'].'</option>';
8691
            }
8692
        }
8693
        $return .= '</select>';
8694
8695
        return $return;
8696
    }
8697
8698
    /**
8699
     * Creates a list with all the documents in it
8700
     * @param bool $showInvisibleFiles
8701
     * @return string
8702
     */
8703
    public function get_documents($showInvisibleFiles = false)
8704
    {
8705
        $course_info = api_get_course_info();
8706
        $sessionId = api_get_session_id();
8707
8708
        $document_tree = DocumentManager::get_document_preview(
8709
            $course_info,
8710
            $this->lp_id,
8711
            null,
8712
            $sessionId,
8713
            true,
8714
            null,
8715
            null,
8716
            $showInvisibleFiles,
8717
            true
8718
        );
8719
8720
        return $document_tree;
8721
    }
8722
8723
    /**
8724
     * Creates a list with all the exercises (quiz) in it
8725
     * @return string
8726
     */
8727
    public function get_exercises()
8728
    {
8729
        $course_id = api_get_course_int_id();
8730
8731
        // New for hotpotatoes.
8732
        $uploadPath = DIR_HOTPOTATOES; //defined in main_api
8733
        $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
8734
        $tbl_quiz = Database :: get_course_table(TABLE_QUIZ_TEST);
8735
8736
        $session_id = api_get_session_id();
8737
        $condition_session = api_get_session_condition($session_id);
8738
8739
        $setting = api_get_configuration_value('show_invisible_exercise_in_lp_list');
8740
8741
        $activeCondition = " active <> -1 ";
8742
        if ($setting) {
8743
            $activeCondition = " active = 1 ";
8744
        }
8745
8746
        $sql_quiz = "SELECT * FROM $tbl_quiz
8747
                     WHERE c_id = $course_id AND $activeCondition $condition_session
8748
                     ORDER BY title ASC";
8749
8750
        $sql_hot  = "SELECT * FROM $tbl_doc
8751
                     WHERE c_id = $course_id AND path LIKE '" . $uploadPath . "/%/%htm%'  $condition_session
8752
                     ORDER BY id ASC";
8753
8754
        $res_quiz = Database::query($sql_quiz);
8755
        $res_hot  = Database::query($sql_hot);
8756
8757
        $return = '<ul class="lp_resource">';
8758
        $return .= '<li class="lp_resource_element">';
8759
        $return .= Display::return_icon('new_test_small.gif');
8760
        $return .= '<a href="' . api_get_path(WEB_CODE_PATH) . 'exercice/exercise_admin.php?'.api_get_cidreq().'&lp_id=' . $this->lp_id . '">' .
8761
            get_lang('NewExercise') . '</a>';
8762
        $return .= '</li>';
8763
8764
        // Display hotpotatoes
8765
        while ($row_hot = Database :: fetch_array($res_hot)) {
8766
            $return .= '<li class="lp_resource_element" data_id="'.$row_hot['id'].'" data_type="hotpotatoes" title="'.$row_hot['title'].'" >';
8767
8768
            $return .= '<a class="moved" href="#">';
8769
            $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
8770
            $return .= '</a> ';
8771
8772
            $return .= Display::return_icon('hotpotatoes_s.png');
8773
            $return .= '<a href="' . api_get_self() . '?' . api_get_cidreq().'&action=add_item&type=' . TOOL_HOTPOTATOES . '&file=' . $row_hot['id'] . '&lp_id=' . $this->lp_id . '">'.
8774
                ((!empty ($row_hot['comment'])) ? $row_hot['comment'] : Security :: remove_XSS($row_hot['title'])) . '</a>';
8775
            $return .= '</li>';
8776
        }
8777
8778
        while ($row_quiz = Database :: fetch_array($res_quiz)) {
8779
            $return .= '<li class="lp_resource_element" data_id="'.$row_quiz['id'].'" data_type="quiz" title="'.$row_quiz['title'].'" >';
8780
            $return .= '<a class="moved" href="#">';
8781
            $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
8782
            $return .= '</a> ';
8783
            $return .= Display::return_icon('quizz_small.gif', '', array(), ICON_SIZE_TINY);
8784
            $return .= '<a href="' . api_get_self() . '?'.api_get_cidreq().'&action=add_item&type=' . TOOL_QUIZ . '&file=' . $row_quiz['id'] . '&lp_id=' . $this->lp_id . '">' .
8785
                Security :: remove_XSS(cut($row_quiz['title'], 80)).
8786
                '</a>';
8787
            $return .= '</li>';
8788
        }
8789
8790
        $return .= '</ul>';
8791
8792
        return $return;
8793
    }
8794
8795
    /**
8796
     * Creates a list with all the links in it
8797
     * @return string
8798
     */
8799
    public function get_links()
8800
    {
8801
        $selfUrl = api_get_self();
8802
        $courseIdReq = api_get_cidreq();
8803
        $course = api_get_course_info();
8804
        $course_id = $course['real_id'];
8805
        $tbl_link = Database::get_course_table(TABLE_LINK);
8806
        $linkCategoryTable = Database::get_course_table(TABLE_LINK_CATEGORY);
8807
        $moveEverywhereIcon = Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
8808
8809
        $session_id = api_get_session_id();
8810
        $condition_session = api_get_session_condition($session_id, true, null, "link.session_id");
8811
8812
        $sql = "SELECT link.id as link_id,
8813
                    link.title as link_title,
8814
                    link.category_id as category_id,
8815
                    link_category.category_title as category_title
8816
                FROM $tbl_link as link
8817
                LEFT JOIN $linkCategoryTable as link_category
8818
                ON (link.category_id = link_category.id AND link.c_id = link_category.c_id)
8819
                WHERE link.c_id = ".$course_id." $condition_session
8820
                ORDER BY link_category.category_title ASC, link.title ASC";
8821
        $links = Database::query($sql);
8822
8823
        $categorizedLinks = array();
8824
        $categories = array();
8825
8826
        while ($link = Database :: fetch_array($links)) {
8827
            if (!$link['category_id']) {
8828
                $link['category_title'] = get_lang('Uncategorized');
8829
            }
8830
            $categories[$link['category_id']] = $link['category_title'];
8831
            $categorizedLinks[$link['category_id']][$link['link_id']] = $link['link_title'];
8832
        }
8833
8834
        $linksHtmlCode =
8835
            '<script>
8836
            function toggle_tool(tool, id){
8837
                if(document.getElementById(tool+"_"+id+"_content").style.display == "none"){
8838
                    document.getElementById(tool+"_"+id+"_content").style.display = "block";
8839
                    document.getElementById(tool+"_"+id+"_opener").src = "' . Display::returnIconPath('remove.gif').'";
8840
                } else {
8841
                    document.getElementById(tool+"_"+id+"_content").style.display = "none";
8842
                    document.getElementById(tool+"_"+id+"_opener").src = "'.Display::returnIconPath('add.gif').'";
8843
                }
8844
            }
8845
        </script>
8846
8847
        <ul class="lp_resource">
8848
            <li class="lp_resource_element">
8849
                '.Display::return_icon('linksnew.gif').'
8850
                <a href="'.api_get_path(WEB_CODE_PATH).'link/link.php?'.$courseIdReq.'&action=addlink&lp_id='.$this->lp_id.'" title="'.get_lang('LinkAdd').'">'.
8851
                get_lang('LinkAdd').'
8852
                </a>
8853
            </li>';
8854
8855
        foreach ($categorizedLinks as $categoryId => $links) {
8856
            $linkNodes = null;
8857
            foreach ($links as $key => $title) {
8858
                if (api_get_item_visibility($course, TOOL_LINK, $key, $session_id) != 2)  {
8859
                    $linkNodes .=
8860
                        '<li class="lp_resource_element" data_id="'.$key.'" data_type="'.TOOL_LINK.'" title="'.$title.'" >
8861
                        <a class="moved" href="#">'.
8862
                        $moveEverywhereIcon.
8863
                        '</a>
8864
                        '.Display::return_icon('lp_link.png').'
8865
                        <a href="'.$selfUrl.'?'.$courseIdReq.'&action=add_item&type='.
8866
                        TOOL_LINK.'&file='.$key.'&lp_id='.$this->lp_id.'">'.
8867
                        Security::remove_XSS($title).
8868
                        '</a>
8869
                    </li>';
8870
                }
8871
            }
8872
            $linksHtmlCode .=
8873
                '<li>
8874
                <a style="cursor:hand" onclick="javascript: toggle_tool(\''.TOOL_LINK.'\','.$categoryId.')" style="vertical-align:middle">
8875
                    <img src="'.Display::returnIconPath('add.gif').'" id="'.TOOL_LINK.'_'.$categoryId.'_opener"
8876
                    align="absbottom" />
8877
                </a>
8878
                <span style="vertical-align:middle">'.Security::remove_XSS($categories[$categoryId]).'</span>
8879
            </li>
8880
            <div style="display:none" id="'.TOOL_LINK.'_'.$categoryId.'_content">'.$linkNodes.'</div>';
8881
        }
8882
        $linksHtmlCode .= '</ul>';
8883
8884
        return $linksHtmlCode;
8885
    }
8886
8887
    /**
8888
     * Creates a list with all the student publications in it
8889
     * @return unknown
8890
     */
8891
    public function get_student_publications()
8892
    {
8893
        $return = '<div class="lp_resource" >';
8894
        $return .= '<div class="lp_resource_element">';
8895
        $return .= Display::return_icon('works_small.gif', '', array(), ICON_SIZE_TINY);
8896
        $return .= '<a href="' . api_get_self() . '?' . api_get_cidreq() . '&action=add_item&type=' . TOOL_STUDENTPUBLICATION . '&lp_id=' . $this->lp_id . '">' . get_lang('AddAssignmentPage') . '</a>';
8897
        $return .= '</div>';
8898
        $return .= '</div>';
8899
        return $return;
8900
    }
8901
8902
    /**
8903
     * Creates a list with all the forums in it
8904
     * @return string
8905
     */
8906
    public function get_forums()
8907
    {
8908
        require_once '../forum/forumfunction.inc.php';
8909
        require_once '../forum/forumconfig.inc.php';
8910
8911
        $forumCategories = get_forum_categories();
8912
        $forumsInNoCategory = get_forums_in_category(0);
8913 View Code Duplication
        if (!empty($forumsInNoCategory)) {
8914
            $forumCategories = array_merge(
8915
                $forumCategories,
8916
                array(
8917
                    array(
8918
                        'cat_id' => 0,
8919
                        'session_id' => 0,
8920
                        'visibility' => 1,
8921
                        'cat_comment' => null,
8922
                    ),
8923
                )
8924
            );
8925
        }
8926
8927
        $forumList = get_forums();
8928
        $a_forums = [];
8929
        foreach ($forumCategories as $forumCategory) {
8930
            // The forums in this category.
8931
            $forumsInCategory = get_forums_in_category($forumCategory['cat_id']);
8932
            if (!empty($forumsInCategory)) {
8933
                foreach ($forumList as $forum) {
8934
                    if (isset($forum['forum_category']) && $forum['forum_category'] == $forumCategory['cat_id']) {
8935
                        $a_forums[] = $forum;
8936
                    }
8937
                }
8938
            }
8939
        }
8940
8941
        $return = '<ul class="lp_resource">';
8942
8943
        //First add link
8944
        $return .= '<li class="lp_resource_element">';
8945
        $return .= Display::return_icon('forum_new_small.gif');
8946
        $return .= Display::url(
8947
            get_lang('CreateANewForum'),
8948
            api_get_path(WEB_CODE_PATH) . 'forum/index.php?' . api_get_cidreq() . '&' . http_build_query([
8949
                'action' => 'add',
8950
                'content' => 'forum',
8951
                'lp_id' => $this->lp_id
8952
            ]),
8953
            ['title' => get_lang('CreateANewForum')]
8954
        );
8955
        $return .= '</li>';
8956
8957
        $return .= '<script>
8958
                    function toggle_forum(forum_id){
8959
                        if(document.getElementById("forum_"+forum_id+"_content").style.display == "none"){
8960
                            document.getElementById("forum_"+forum_id+"_content").style.display = "block";
8961
                            document.getElementById("forum_"+forum_id+"_opener").src = "' . Display::returnIconPath('remove.gif').'";
8962
                        } else {
8963
                            document.getElementById("forum_"+forum_id+"_content").style.display = "none";
8964
                            document.getElementById("forum_"+forum_id+"_opener").src = "' . Display::returnIconPath('add.gif').'";
8965
                        }
8966
                    }
8967
                </script>';
8968
8969
        foreach ($a_forums as $forum) {
8970
            if (!empty($forum['forum_id'])) {
8971
                $return .= '<li class="lp_resource_element" data_id="'.$forum['forum_id'].'" data_type="'.TOOL_FORUM.'" title="'.$forum['forum_title'].'" >';
8972
                $return .= '<a class="moved" href="#">';
8973
                $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
8974
                $return .= ' </a>';
8975
                $return .= Display::return_icon('lp_forum.png', '', array(), ICON_SIZE_TINY);
8976
                $return .= '<a style="cursor:hand" onclick="javascript: toggle_forum(' . $forum['forum_id'] . ')" style="vertical-align:middle">
8977
                                <img src="' . Display::returnIconPath('add.gif').'" id="forum_' . $forum['forum_id'] . '_opener" align="absbottom" />
8978
                            </a>
8979
                            <a 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">' .
8980
                    Security :: remove_XSS($forum['forum_title']) . '</a>';
8981
8982
                $return .= '</li>';
8983
8984
                $return .= '<div style="display:none" id="forum_' . $forum['forum_id'] . '_content">';
8985
                $a_threads = get_threads($forum['forum_id']);
8986
                if (is_array($a_threads)) {
8987
                    foreach ($a_threads as $thread) {
8988
                        $return .= '<li class="lp_resource_element" data_id="'.$thread['thread_id'].'" data_type="'.TOOL_THREAD.'" title="'.$thread['thread_title'].'" >';
8989
                        $return .= '&nbsp;<a class="moved" href="#">';
8990
                        $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
8991
                        $return .= ' </a>';
8992
                        $return .= Display::return_icon('forumthread.png', get_lang('Thread'), array(), ICON_SIZE_TINY);
8993
                        $return .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type=' . TOOL_THREAD . '&thread_id=' . $thread['thread_id'] . '&lp_id=' . $this->lp_id . '">' .
8994
                            Security :: remove_XSS($thread['thread_title']) . '</a>';
8995
                        $return .= '</li>';
8996
                    }
8997
                }
8998
                $return .= '</div>';
8999
            }
9000
        }
9001
        $return .= '</ul>';
9002
9003
        return $return;
9004
    }
9005
9006
    /**
9007
     * // TODO: The output encoding should be equal to the system encoding.
9008
     *
9009
     * Exports the learning path as a SCORM package. This is the main function that
9010
     * gathers the content, transforms it, writes the imsmanifest.xml file, zips the
9011
     * whole thing and returns the zip.
9012
     *
9013
     * This method needs to be called in PHP5, as it will fail with non-adequate
9014
     * XML package (like the ones for PHP4), and it is *not* a static method, so
9015
     * you need to call it on a learnpath object.
9016
     * @TODO The method might be redefined later on in the scorm class itself to avoid
9017
     * creating a SCORM structure if there is one already. However, if the initial SCORM
9018
     * path has been modified, it should use the generic method here below.
9019
     * @TODO link this function with the export_lp() function in the same class
9020
     * @param	string	Optional name of zip file. If none, title of learnpath is
9021
     * 					domesticated and trailed with ".zip"
9022
     * @return	string	Returns the zip package string, or null if error
9023
     */
9024
    public function scorm_export()
9025
    {
9026
        $_course = api_get_course_info();
9027
        $course_id = $_course['real_id'];
9028
9029
        // Remove memory and time limits as much as possible as this might be a long process...
9030
        if (function_exists('ini_set')) {
9031
            api_set_memory_limit('128M');
9032
            ini_set('max_execution_time', 600);
9033
        }
9034
9035
        // Create the zip handler (this will remain available throughout the method).
9036
        $archive_path = api_get_path(SYS_ARCHIVE_PATH);
9037
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
9038
        $temp_dir_short = uniqid();
9039
        $temp_zip_dir = $archive_path.'/'.$temp_dir_short;
9040
        $temp_zip_file = $temp_zip_dir.'/'.md5(time()).'.zip';
9041
        $zip_folder = new PclZip($temp_zip_file);
9042
        $current_course_path = api_get_path(SYS_COURSE_PATH).api_get_course_path();
9043
        $root_path = $main_path = api_get_path(SYS_PATH);
9044
        $files_cleanup = array();
9045
9046
        // Place to temporarily stash the zip file.
9047
        // create the temp dir if it doesn't exist
9048
        // or do a cleanup before creating the zip file.
9049
        if (!is_dir($temp_zip_dir)) {
9050
            mkdir($temp_zip_dir, api_get_permissions_for_new_directories());
9051
        } else {
9052
            // Cleanup: Check the temp dir for old files and delete them.
9053
            $handle = opendir($temp_zip_dir);
9054
            while (false !== ($file = readdir($handle))) {
9055
                if ($file != '.' && $file != '..') {
9056
                    unlink("$temp_zip_dir/$file");
9057
                }
9058
            }
9059
            closedir($handle);
9060
        }
9061
        $zip_files = $zip_files_abs = $zip_files_dist = array();
9062
        if (is_dir($current_course_path.'/scorm/'.$this->path) && is_file($current_course_path.'/scorm/'.$this->path.'/imsmanifest.xml')) {
9063
            // Remove the possible . at the end of the path.
9064
            $dest_path_to_lp = substr($this->path, -1) == '.' ? substr($this->path, 0, -1) : $this->path;
9065
            $dest_path_to_scorm_folder = str_replace('//','/',$temp_zip_dir.'/scorm/'.$dest_path_to_lp);
9066
            mkdir($dest_path_to_scorm_folder, api_get_permissions_for_new_directories(), true);
9067
            copyr(
9068
                $current_course_path.'/scorm/'.$this->path,
9069
                $dest_path_to_scorm_folder,
9070
                array('imsmanifest'),
9071
                $zip_files
9072
            );
9073
        }
9074
9075
        // Build a dummy imsmanifest structure.
9076
        // Do not add to the zip yet (we still need it).
9077
        // This structure is developed following regulations for SCORM 1.2 packaging in the SCORM 1.2 Content
9078
        // Aggregation Model official document, section "2.3 Content Packaging".
9079
        // We are going to build a UTF-8 encoded manifest. Later we will recode it to the desired (and supported) encoding.
9080
        $xmldoc = new DOMDocument('1.0');
9081
        $root = $xmldoc->createElement('manifest');
9082
        $root->setAttribute('identifier', 'SingleCourseManifest');
9083
        $root->setAttribute('version', '1.1');
9084
        $root->setAttribute('xmlns', 'http://www.imsproject.org/xsd/imscp_rootv1p1p2');
9085
        $root->setAttribute('xmlns:adlcp', 'http://www.adlnet.org/xsd/adlcp_rootv1p2');
9086
        $root->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
9087
        $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');
9088
        // Build mandatory sub-root container elements.
9089
        $metadata = $xmldoc->createElement('metadata');
9090
        $md_schema = $xmldoc->createElement('schema', 'ADL SCORM');
9091
        $metadata->appendChild($md_schema);
9092
        $md_schemaversion = $xmldoc->createElement('schemaversion', '1.2');
9093
        $metadata->appendChild($md_schemaversion);
9094
        $root->appendChild($metadata);
9095
9096
        $organizations = $xmldoc->createElement('organizations');
9097
        $resources = $xmldoc->createElement('resources');
9098
9099
        // Build the only organization we will use in building our learnpaths.
9100
        $organizations->setAttribute('default', 'chamilo_scorm_export');
9101
        $organization = $xmldoc->createElement('organization');
9102
        $organization->setAttribute('identifier', 'chamilo_scorm_export');
9103
        // To set the title of the SCORM entity (=organization), we take the name given
9104
        // in Chamilo and convert it to HTML entities using the Chamilo charset (not the
9105
        // learning path charset) as it is the encoding that defines how it is stored
9106
        // in the database. Then we convert it to HTML entities again as the "&" character
9107
        // alone is not authorized in XML (must be &amp;).
9108
        // The title is then decoded twice when extracting (see scorm::parse_manifest).
9109
        $org_title = $xmldoc->createElement('title', api_utf8_encode($this->get_name()));
9110
        $organization->appendChild($org_title);
9111
9112
        $folder_name = 'document';
9113
9114
        // Removes the learning_path/scorm_folder path when exporting see #4841
9115
        $path_to_remove = null;
9116
        $result = $this->generate_lp_folder($_course);
9117
        $path_to_replace = '';
9118
        if (isset($result['dir']) && strpos($result['dir'], 'learning_path')) {
9119
            $path_to_remove = 'document'.$result['dir'];
9120
            $path_to_replace = $folder_name.'/';
9121
        }
9122
9123
        //Fixes chamilo scorm exports
9124
        if ($this->ref == 'chamilo_scorm_export') {
9125
            $path_to_remove = 'scorm/'.$this->path.'/document/';
9126
        }
9127
9128
        // For each element, add it to the imsmanifest structure, then add it to the zip.
9129
        // Always call the learnpathItem->scorm_export() method to change it to the SCORM format.
9130
        $link_updates = array();
9131
        $links_to_create = array();
9132
        foreach ($this->items as $index => $item) {
9133
            if (!in_array($item->type, array(TOOL_QUIZ, TOOL_FORUM, TOOL_THREAD, TOOL_LINK, TOOL_STUDENTPUBLICATION))) {
9134
                // Get included documents from this item.
9135
                if ($item->type === 'sco') {
9136
                    $inc_docs = $item->get_resources_from_source(
9137
                        null,
9138
                        api_get_path(SYS_COURSE_PATH) . api_get_course_path() . '/' . 'scorm/' . $this->path . '/' . $item->get_path()
9139
                    );
9140
                } else {
9141
                    $inc_docs = $item->get_resources_from_source();
9142
                }
9143
                // Give a child element <item> to the <organization> element.
9144
                $my_item_id = $item->get_id();
9145
                $my_item = $xmldoc->createElement('item');
9146
                $my_item->setAttribute('identifier', 'ITEM_'.$my_item_id);
9147
                $my_item->setAttribute('identifierref', 'RESOURCE_'.$my_item_id);
9148
                $my_item->setAttribute('isvisible', 'true');
9149
                // Give a child element <title> to the <item> element.
9150
                $my_title = $xmldoc->createElement('title', htmlspecialchars(api_utf8_encode($item->get_title()), ENT_QUOTES, 'UTF-8'));
9151
                $my_item->appendChild($my_title);
9152
                // Give a child element <adlcp:prerequisites> to the <item> element.
9153
                $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $this->get_scorm_prereq_string($my_item_id));
9154
                $my_prereqs->setAttribute('type', 'aicc_script');
9155
                $my_item->appendChild($my_prereqs);
9156
                // Give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported.
9157
                //$xmldoc->createElement('adlcp:maxtimeallowed','');
9158
                // Give a child element <adlcp:timelimitaction> to the <item> element - not yet supported.
9159
                //$xmldoc->createElement('adlcp:timelimitaction','');
9160
                // Give a child element <adlcp:datafromlms> to the <item> element - not yet supported.
9161
                //$xmldoc->createElement('adlcp:datafromlms','');
9162
                // Give a child element <adlcp:masteryscore> to the <item> element.
9163
                $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score());
9164
                $my_item->appendChild($my_masteryscore);
9165
9166
                // Attach this item to the organization element or hits parent if there is one.
9167
                if (!empty($item->parent) && $item->parent != 0) {
9168
                    $children = $organization->childNodes;
9169
                    $possible_parent = $this->get_scorm_xml_node($children, 'ITEM_'.$item->parent);
9170
                    if (is_object($possible_parent)) {
9171
                        $possible_parent->appendChild($my_item);
9172 View Code Duplication
                    } else {
9173
                        if ($this->debug > 0) { error_log('Parent ITEM_'.$item->parent.' of item ITEM_'.$my_item_id.' not found'); }
9174
                    }
9175
                } else {
9176
                    if ($this->debug > 0) { error_log('No parent'); }
9177
                    $organization->appendChild($my_item);
9178
                }
9179
9180
                // Get the path of the file(s) from the course directory root.
9181
                $my_file_path = $item->get_file_path('scorm/'.$this->path.'/');
9182
9183
                if (!empty($path_to_remove)) {
9184
                    //From docs
9185
                    $my_xml_file_path = str_replace($path_to_remove, $path_to_replace, $my_file_path);
9186
9187
                    //From quiz
9188
                    if ($this->ref == 'chamilo_scorm_export') {
9189
                        $path_to_remove = 'scorm/'.$this->path.'/';
9190
                        $my_xml_file_path = str_replace($path_to_remove, '', $my_file_path);
9191
                    }
9192
                } else {
9193
                    $my_xml_file_path = $my_file_path;
9194
                }
9195
9196
                $my_sub_dir = dirname($my_file_path);
9197
                $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
9198
                $my_xml_sub_dir = $my_sub_dir;
9199
                // Give a <resource> child to the <resources> element
9200
                $my_resource = $xmldoc->createElement('resource');
9201
                $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
9202
                $my_resource->setAttribute('type', 'webcontent');
9203
                $my_resource->setAttribute('href', $my_xml_file_path);
9204
                // adlcp:scormtype can be either 'sco' or 'asset'.
9205
                if ($item->type === 'sco') {
9206
                    $my_resource->setAttribute('adlcp:scormtype', 'sco');
9207
                } else {
9208
                    $my_resource->setAttribute('adlcp:scormtype', 'asset');
9209
                }
9210
                // xml:base is the base directory to find the files declared in this resource.
9211
                $my_resource->setAttribute('xml:base', '');
9212
                // Give a <file> child to the <resource> element.
9213
                $my_file = $xmldoc->createElement('file');
9214
                $my_file->setAttribute('href', $my_xml_file_path);
9215
                $my_resource->appendChild($my_file);
9216
9217
                // Dependency to other files - not yet supported.
9218
                $i = 1;
9219
                foreach ($inc_docs as $doc_info) {
9220
                    if (count($doc_info) < 1 || empty($doc_info[0])) {
9221
                        continue;
9222
                    }
9223
                    $my_dep = $xmldoc->createElement('resource');
9224
                    $res_id = 'RESOURCE_'.$item->get_id().'_'.$i;
9225
                    $my_dep->setAttribute('identifier', $res_id);
9226
                    $my_dep->setAttribute('type', 'webcontent');
9227
                    $my_dep->setAttribute('adlcp:scormtype', 'asset');
9228
                    $my_dep_file = $xmldoc->createElement('file');
9229
                    // Check type of URL.
9230
                    //error_log(__LINE__.'Now dealing with '.$doc_info[0].' of type '.$doc_info[1].'-'.$doc_info[2], 0);
9231
                    if ($doc_info[1] == 'remote') {
9232
                        // Remote file. Save url as is.
9233
                        $my_dep_file->setAttribute('href', $doc_info[0]);
9234
                        $my_dep->setAttribute('xml:base', '');
9235
                    } elseif ($doc_info[1] === 'local') {
9236
                        switch ($doc_info[2]) {
9237
                            case 'url': // Local URL - save path as url for now, don't zip file.
9238
                                $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]);
9239
                                $current_dir = dirname($abs_path);
9240
                                $current_dir = str_replace('\\', '/', $current_dir);
9241
                                $file_path = realpath($abs_path);
9242
                                $file_path = str_replace('\\', '/', $file_path);
9243
                                $my_dep_file->setAttribute('href', $file_path);
9244
                                $my_dep->setAttribute('xml:base', '');
9245
                                if (strstr($file_path, $main_path) !== false) {
9246
                                    // The calculated real path is really inside Chamilo's root path.
9247
                                    // Reduce file path to what's under the DocumentRoot.
9248
                                    $file_path = substr($file_path, strlen($root_path) - 1);
9249
                                    //echo $file_path;echo '<br /><br />';
9250
                                    //error_log(__LINE__.'Reduced url path: '.$file_path, 0);
9251
                                    $zip_files_abs[] = $file_path;
9252
                                    $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9253
                                    $my_dep_file->setAttribute('href', $file_path);
9254
                                    $my_dep->setAttribute('xml:base', '');
9255
                                } elseif (empty($file_path)) {
9256
                                    /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH), api_get_path(REL_PATH)));
9257
                                    if (strpos($document_root, -1) == '/') {
9258
                                        $document_root = substr(0, -1, $document_root);
9259
                                    }*/
9260
                                    $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path;
9261
                                    $file_path = str_replace('//', '/', $file_path);
9262
                                    if (file_exists($file_path)) {
9263
                                        $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
9264
                                        $zip_files[] = $my_sub_dir.'/'.$file_path;
9265
                                        $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9266
                                        $my_dep_file->setAttribute('href', $file_path);
9267
                                        $my_dep->setAttribute('xml:base', '');
9268
                                    }
9269
                                }
9270
                                break;
9271
                            case 'abs': // Absolute path from DocumentRoot. Save file and leave path as is in the zip.
9272
                                $my_dep_file->setAttribute('href', $doc_info[0]);
9273
                                $my_dep->setAttribute('xml:base', '');
9274
9275
                                // The next lines fix a bug when using the "subdir" mode of Chamilo, whereas
9276
                                // an image path would be constructed as /var/www/subdir/subdir/img/foo.bar
9277
                                $abs_img_path_without_subdir = $doc_info[0];
9278
                                $relp = api_get_path(REL_PATH); // The url-append config param.
9279
                                $pos = strpos($abs_img_path_without_subdir, $relp);
9280
                                if ($pos === 0) {
9281
                                    $abs_img_path_without_subdir = '/'.substr($abs_img_path_without_subdir, strlen($relp));
9282
                                }
9283
9284
                                //$file_path = realpath(api_get_path(SYS_PATH).$abs_img_path_without_subdir);
9285
                                $file_path = realpath(api_get_path(SYS_APP_PATH).$abs_img_path_without_subdir);
9286
9287
                                $file_path = str_replace('\\', '/', $file_path);
9288
                                $file_path = str_replace('//', '/', $file_path);
9289
9290
                                // Prepare the current directory path (until just under 'document') with a trailing slash.
9291
                                $cur_path = substr($current_course_path, -1) == '/' ? $current_course_path : $current_course_path.'/';
9292
                                // Check if the current document is in that path.
9293
                                if (strstr($file_path, $cur_path) !== false) {
9294
                                    // The document is in that path, now get the relative path
9295
                                    // to the containing document.
9296
                                    $orig_file_path = dirname($cur_path.$my_file_path).'/';
9297
                                    $orig_file_path = str_replace('\\', '/', $orig_file_path);
9298
                                    $relative_path = '';
9299
                                    // Image/src file is inside a course
9300
                                    $fixDirRelative = '';
9301
                                    // image file is inside a course
9302
                                    if (strstr($file_path, $cur_path) !== false) {
9303
                                        // html file that wraps the image is inside a course
9304
                                        if (strstr($orig_file_path, $cur_path) !== false) {
9305
                                            // Get count of directories from document until the last slash
9306
                                            // if $orig_file_path =
9307
                                            // /var/www/html/chamilo/app/courses/CLEAN123/document/learning_path/Le_parcours/
9308
                                            // $distance should be 2 (learning_path and Le_parcours)
9309
9310
                                            $origFilePathFixed = str_replace($path_to_remove, $path_to_replace, $orig_file_path);
9311
                                            $distance = array_filter(explode('/', str_replace($cur_path.'document/', '', $origFilePathFixed)));
9312
9313
                                            if ($distance) {
9314
                                                foreach ($distance as $relative) {
9315
                                                    $fixDirRelative .=  '../';
9316
                                                }
9317
                                            }
9318
                                        }
9319
                                        $relative_path = str_replace($cur_path, '', $file_path);
9320
                                        $file_path = substr($file_path, strlen($cur_path));
9321
9322
                                        if (!empty($fixDirRelative)) {
9323
                                            $relative_path = $fixDirRelative.$relative_path;
9324
                                        }
9325
                                    } else {
9326
                                        // This case is still a problem as it's difficult to calculate a relative path easily
9327
                                        // might still generate wrong links.
9328
                                        //$file_path = substr($file_path,strlen($cur_path));
9329
                                        // Calculate the directory path to the current file (without trailing slash).
9330
                                        $my_relative_path = dirname($file_path);
9331
                                        $my_relative_path = str_replace('\\', '/', $my_relative_path);
9332
                                        $my_relative_file = basename($file_path);
9333
                                        // Calculate the directory path to the containing file (without trailing slash).
9334
                                        $my_orig_file_path = substr($orig_file_path, 0, -1);
9335
                                        $dotdot = '';
9336
                                        $subdir = '';
9337
                                        while (strstr($my_relative_path, $my_orig_file_path) === false && (strlen($my_orig_file_path) > 1) && (strlen($my_relative_path) > 1)) {
9338
                                            $my_relative_path2 = dirname($my_relative_path);
9339
                                            $my_relative_path2 = str_replace('\\', '/', $my_relative_path2);
9340
                                            $my_orig_file_path = dirname($my_orig_file_path);
9341
                                            $my_orig_file_path = str_replace('\\', '/', $my_orig_file_path);
9342
                                            $subdir = substr($my_relative_path, strlen($my_relative_path2) + 1).'/'.$subdir;
9343
                                            $dotdot += '../';
9344
                                            $my_relative_path = $my_relative_path2;
9345
                                        }
9346
                                        $relative_path = $dotdot.$subdir.$my_relative_file;
9347
                                    }
9348
                                    // Put the current document in the zip (this array is the array
9349
                                    // that will manage documents already in the course folder - relative).
9350
                                    $zip_files[] = $file_path;
9351
                                    // Update the links to the current document in the containing document (make them relative).
9352
                                    $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $relative_path);
9353
                                    $my_dep_file->setAttribute('href', $file_path);
9354
                                    $my_dep->setAttribute('xml:base', '');
9355
                                } elseif (strstr($file_path, $main_path) !== false) {
9356
                                    // The calculated real path is really inside Chamilo's root path.
9357
                                    // Reduce file path to what's under the DocumentRoot.
9358
                                    $file_path = substr($file_path, strlen($root_path));
9359
                                    //echo $file_path;echo '<br /><br />';
9360
                                    //error_log('Reduced path: '.$file_path, 0);
9361
                                    $zip_files_abs[] = $file_path;
9362
                                    $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9363
                                    $my_dep_file->setAttribute('href', 'document/'.$file_path);
9364
                                    $my_dep->setAttribute('xml:base', '');
9365
                                } elseif (empty($file_path)) {
9366
                                    /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH), api_get_path(REL_PATH)));
9367
                                    if(strpos($document_root,-1) == '/') {
9368
                                        $document_root = substr(0, -1, $document_root);
9369
                                    }*/
9370
                                    $file_path = $_SERVER['DOCUMENT_ROOT'].$doc_info[0];
9371
                                    $file_path = str_replace('//', '/', $file_path);
9372
9373
                                    $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]);
9374
                                    $current_dir = dirname($abs_path);
9375
                                    $current_dir = str_replace('\\', '/', $current_dir);
9376
9377
                                    if (file_exists($file_path)) {
9378
                                        $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
9379
                                        $zip_files[] = $my_sub_dir.'/'.$file_path;
9380
                                        $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9381
                                        $my_dep_file->setAttribute('href','document/'.$file_path);
9382
                                        $my_dep->setAttribute('xml:base', '');
9383
                                    }
9384
                                }
9385
                                break;
9386
                            case 'rel':
9387
                                // Path relative to the current document.
9388
                                // Save xml:base as current document's directory and save file in zip as subdir.file_path
9389
                                if (substr($doc_info[0], 0, 2) == '..') {
9390
                                    // Relative path going up.
9391
                                    $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
9392
                                    $current_dir = str_replace('\\', '/', $current_dir);
9393
                                    $file_path = realpath($current_dir.$doc_info[0]);
9394
                                    $file_path = str_replace('\\', '/', $file_path);
9395
9396
                                    //error_log($file_path.' <-> '.$main_path,0);
9397
                                    if (strstr($file_path, $main_path) !== false) {
9398
                                        // The calculated real path is really inside Chamilo's root path.
9399
                                        // Reduce file path to what's under the DocumentRoot.
9400
                                        $file_path = substr($file_path, strlen($root_path));
9401
                                        //error_log('Reduced path: '.$file_path, 0);
9402
                                        $zip_files_abs[] = $file_path;
9403
                                        $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9404
                                        $my_dep_file->setAttribute('href', 'document/'.$file_path);
9405
                                        $my_dep->setAttribute('xml:base', '');
9406
                                    }
9407 View Code Duplication
                                } else {
9408
                                    $zip_files[] = $my_sub_dir.'/'.$doc_info[0];
9409
                                    $my_dep_file->setAttribute('href', $doc_info[0]);
9410
                                    $my_dep->setAttribute('xml:base', $my_xml_sub_dir);
9411
                                }
9412
                                break;
9413
                            default:
9414
                                $my_dep_file->setAttribute('href', $doc_info[0]);
9415
                                $my_dep->setAttribute('xml:base', '');
9416
                                break;
9417
                        }
9418
                    }
9419
                    $my_dep->appendChild($my_dep_file);
9420
                    $resources->appendChild($my_dep);
9421
                    $dependency = $xmldoc->createElement('dependency');
9422
                    $dependency->setAttribute('identifierref', $res_id);
9423
                    $my_resource->appendChild($dependency);
9424
                    $i++;
9425
                }
9426
                $resources->appendChild($my_resource);
9427
                $zip_files[] = $my_file_path;
9428
            } else {
9429
9430
                // If the item is a quiz or a link or whatever non-exportable, we include a step indicating it.
9431
                switch ($item->type) {
9432
                    case TOOL_LINK:
9433
                        $my_item = $xmldoc->createElement('item');
9434
                        $my_item->setAttribute('identifier', 'ITEM_'.$item->get_id());
9435
                        $my_item->setAttribute('identifierref', 'RESOURCE_'.$item->get_id());
9436
                        $my_item->setAttribute('isvisible', 'true');
9437
                        // Give a child element <title> to the <item> element.
9438
                        $my_title = $xmldoc->createElement('title', htmlspecialchars(api_utf8_encode($item->get_title()), ENT_QUOTES, 'UTF-8'));
9439
                        $my_item->appendChild($my_title);
9440
                        // Give a child element <adlcp:prerequisites> to the <item> element.
9441
                        $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $item->get_prereq_string());
9442
                        $my_prereqs->setAttribute('type', 'aicc_script');
9443
                        $my_item->appendChild($my_prereqs);
9444
                        // Give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported.
9445
                        //$xmldoc->createElement('adlcp:maxtimeallowed', '');
9446
                        // Give a child element <adlcp:timelimitaction> to the <item> element - not yet supported.
9447
                        //$xmldoc->createElement('adlcp:timelimitaction', '');
9448
                        // Give a child element <adlcp:datafromlms> to the <item> element - not yet supported.
9449
                        //$xmldoc->createElement('adlcp:datafromlms', '');
9450
                        // Give a child element <adlcp:masteryscore> to the <item> element.
9451
                        $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score());
9452
                        $my_item->appendChild($my_masteryscore);
9453
9454
                        // Attach this item to the organization element or its parent if there is one.
9455 View Code Duplication
                        if (!empty($item->parent) && $item->parent != 0) {
9456
                            $children = $organization->childNodes;
9457
                            for ($i = 0; $i < $children->length; $i++) {
9458
                                $item_temp = $children->item($i);
9459
                                if ($item_temp -> nodeName == 'item') {
9460
                                    if ($item_temp->getAttribute('identifier') == 'ITEM_'.$item->parent) {
9461
                                        $item_temp -> appendChild($my_item);
9462
                                    }
9463
                                }
9464
                            }
9465
                        } else {
9466
                            $organization->appendChild($my_item);
9467
                        }
9468
9469
                        $my_file_path = 'link_'.$item->get_id().'.html';
9470
                        $sql = 'SELECT url, title FROM '.Database :: get_course_table(TABLE_LINK).'
9471
                                WHERE c_id = '.$course_id.' AND id='.$item->path;
9472
                        $rs = Database::query($sql);
9473
                        if ($link = Database :: fetch_array($rs)) {
9474
                            $url = $link['url'];
9475
                            $title = stripslashes($link['title']);
9476
                            $links_to_create[$my_file_path] = array('title' => $title, 'url' => $url);
9477
                            $my_xml_file_path = $my_file_path;
9478
                            $my_sub_dir = dirname($my_file_path);
9479
                            $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
9480
                            $my_xml_sub_dir = $my_sub_dir;
9481
                            // Give a <resource> child to the <resources> element.
9482
                            $my_resource = $xmldoc->createElement('resource');
9483
                            $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
9484
                            $my_resource->setAttribute('type', 'webcontent');
9485
                            $my_resource->setAttribute('href', $my_xml_file_path);
9486
                            // adlcp:scormtype can be either 'sco' or 'asset'.
9487
                            $my_resource->setAttribute('adlcp:scormtype', 'asset');
9488
                            // xml:base is the base directory to find the files declared in this resource.
9489
                            $my_resource->setAttribute('xml:base', '');
9490
                            // give a <file> child to the <resource> element.
9491
                            $my_file = $xmldoc->createElement('file');
9492
                            $my_file->setAttribute('href', $my_xml_file_path);
9493
                            $my_resource->appendChild($my_file);
9494
                            $resources->appendChild($my_resource);
9495
                        }
9496
                        break;
9497
                    case TOOL_QUIZ:
9498
                        $exe_id = $item->path; // Should be using ref when everything will be cleaned up in this regard.
9499
                        $exe = new Exercise();
9500
                        $exe->read($exe_id);
9501
                        $my_item = $xmldoc->createElement('item');
9502
                        $my_item->setAttribute('identifier', 'ITEM_'.$item->get_id());
9503
                        $my_item->setAttribute('identifierref', 'RESOURCE_'.$item->get_id());
9504
                        $my_item->setAttribute('isvisible', 'true');
9505
                        // Give a child element <title> to the <item> element.
9506
                        $my_title = $xmldoc->createElement('title', htmlspecialchars(api_utf8_encode($item->get_title()), ENT_QUOTES, 'UTF-8'));
9507
                        $my_item->appendChild($my_title);
9508
                        $my_max_score = $xmldoc->createElement('max_score', $item->get_max());
9509
                        //$my_item->appendChild($my_max_score);
9510
                        // Give a child element <adlcp:prerequisites> to the <item> element.
9511
                        $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $item->get_prereq_string());
9512
                        $my_prereqs->setAttribute('type','aicc_script');
9513
                        $my_item->appendChild($my_prereqs);
9514
                        // Give a child element <adlcp:masteryscore> to the <item> element.
9515
                        $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score());
9516
                        $my_item->appendChild($my_masteryscore);
9517
9518
                        // Attach this item to the organization element or hits parent if there is one.
9519 View Code Duplication
                        if (!empty($item->parent) && $item->parent != 0) {
9520
                            $children = $organization->childNodes;
9521
                            for ($i = 0; $i < $children->length; $i++) {
9522
                                $item_temp = $children->item($i);
9523
                                if ($item_temp -> nodeName == 'item') {
9524
                                    if ($item_temp->getAttribute('identifier') == 'ITEM_'.$item->parent) {
9525
                                        $item_temp -> appendChild($my_item);
9526
                                    }
9527
                                }
9528
                            }
9529
                        } else {
9530
                            $organization->appendChild($my_item);
9531
                        }
9532
9533
                        // Get the path of the file(s) from the course directory root
9534
                        //$my_file_path = $item->get_file_path('scorm/'.$this->path.'/');
9535
                        $my_file_path = 'quiz_'.$item->get_id().'.html';
9536
                        // Write the contents of the exported exercise into a (big) html file
9537
                        // to later pack it into the exported SCORM. The file will be removed afterwards.
9538
                        $contents = ScormSection::export_exercise_to_scorm($exe_id, true);
9539
                        $tmp_file_path = $archive_path.$temp_dir_short.'/'.$my_file_path;
9540
                        $res = file_put_contents($tmp_file_path, $contents);
9541
                        if ($res === false) { error_log('Could not write into file '.$tmp_file_path.' '.__FILE__.' '.__LINE__, 0); }
9542
                        $files_cleanup[] = $tmp_file_path;
9543
                        //error_log($tmp_path); die();
9544
                        //$my_xml_file_path = api_htmlentities(api_utf8_encode($my_file_path), ENT_QUOTES, 'UTF-8');
9545
                        $my_xml_file_path = $my_file_path;
9546
                        $my_sub_dir = dirname($my_file_path);
9547
                        $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
9548
                        //$my_xml_sub_dir = api_htmlentities(api_utf8_encode($my_sub_dir), ENT_QUOTES, 'UTF-8');
9549
                        $my_xml_sub_dir = $my_sub_dir;
9550
                        // Give a <resource> child to the <resources> element.
9551
                        $my_resource = $xmldoc->createElement('resource');
9552
                        $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
9553
                        $my_resource->setAttribute('type', 'webcontent');
9554
                        $my_resource->setAttribute('href', $my_xml_file_path);
9555
                        // adlcp:scormtype can be either 'sco' or 'asset'.
9556
                        $my_resource->setAttribute('adlcp:scormtype', 'sco');
9557
                        // xml:base is the base directory to find the files declared in this resource.
9558
                        $my_resource->setAttribute('xml:base', '');
9559
                        // Give a <file> child to the <resource> element.
9560
                        $my_file = $xmldoc->createElement('file');
9561
                        $my_file->setAttribute('href', $my_xml_file_path);
9562
                        $my_resource->appendChild($my_file);
9563
9564
                        // Get included docs.
9565
                        $inc_docs = $item->get_resources_from_source(null,$tmp_file_path);
9566
                        // Dependency to other files - not yet supported.
9567
                        $i = 1;
9568
                        foreach ($inc_docs as $doc_info) {
9569
                            if (count($doc_info) < 1 || empty($doc_info[0])) { continue; }
9570
                            $my_dep = $xmldoc->createElement('resource');
9571
                            $res_id = 'RESOURCE_'.$item->get_id().'_'.$i;
9572
                            $my_dep->setAttribute('identifier', $res_id);
9573
                            $my_dep->setAttribute('type', 'webcontent');
9574
                            $my_dep->setAttribute('adlcp:scormtype', 'asset');
9575
                            $my_dep_file = $xmldoc->createElement('file');
9576
                            // Check type of URL.
9577
                            if ($doc_info[1] == 'remote') {
9578
                                // Remote file. Save url as is.
9579
                                $my_dep_file->setAttribute('href', $doc_info[0]);
9580
                                $my_dep->setAttribute('xml:base', '');
9581
                            } elseif ($doc_info[1] == 'local') {
9582
                                switch ($doc_info[2]) {
9583
                                    case 'url': // Local URL - save path as url for now, don't zip file.
9584
                                        // Save file but as local file (retrieve from URL).
9585
                                        $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]);
9586
                                        $current_dir = dirname($abs_path);
9587
                                        $current_dir = str_replace('\\', '/', $current_dir);
9588
                                        $file_path = realpath($abs_path);
9589
                                        $file_path = str_replace('\\', '/', $file_path);
9590
                                        $my_dep_file->setAttribute('href', 'document/'.$file_path);
9591
                                        $my_dep->setAttribute('xml:base', '');
9592 View Code Duplication
                                        if (strstr($file_path, $main_path) !== false) {
9593
                                            // The calculated real path is really inside the chamilo root path.
9594
                                            // Reduce file path to what's under the DocumentRoot.
9595
                                            $file_path = substr($file_path, strlen($root_path));
9596
                                            //echo $file_path;echo '<br /><br />';
9597
                                            //error_log('Reduced path: '.$file_path, 0);
9598
                                            $zip_files_abs[] = $file_path;
9599
                                            $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => 'document/'.$file_path);
9600
                                            $my_dep_file->setAttribute('href', 'document/'.$file_path);
9601
                                            $my_dep->setAttribute('xml:base', '');
9602
                                        } elseif (empty($file_path)) {
9603
                                            /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH),api_get_path(REL_PATH)));
9604
                                            if (strpos($document_root,-1) == '/') {
9605
                                                $document_root = substr(0, -1, $document_root);
9606
                                            }*/
9607
                                            $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path;
9608
                                            $file_path = str_replace('//', '/', $file_path);
9609
                                            if (file_exists($file_path)) {
9610
                                                $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
9611
                                                $zip_files[] = $my_sub_dir.'/'.$file_path;
9612
                                                $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => 'document/'.$file_path);
9613
                                                $my_dep_file->setAttribute('href', 'document/'.$file_path);
9614
                                                $my_dep->setAttribute('xml:base', '');
9615
                                            }
9616
                                        }
9617
                                        break;
9618
                                    case 'abs': // Absolute path from DocumentRoot. Save file and leave path as is in the zip.
9619
                                        $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
9620
                                        $current_dir = str_replace('\\', '/', $current_dir);
9621
                                        $file_path = realpath($doc_info[0]);
9622
                                        $file_path = str_replace('\\', '/', $file_path);
9623
                                        $my_dep_file->setAttribute('href', $file_path);
9624
                                        $my_dep->setAttribute('xml:base', '');
9625
9626 View Code Duplication
                                        if (strstr($file_path,$main_path) !== false) {
9627
                                            // The calculated real path is really inside the chamilo root path.
9628
                                            // Reduce file path to what's under the DocumentRoot.
9629
                                            $file_path = substr($file_path, strlen($root_path));
9630
                                            //echo $file_path;echo '<br /><br />';
9631
                                            //error_log('Reduced path: '.$file_path, 0);
9632
                                            $zip_files_abs[] = $file_path;
9633
                                            $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9634
                                            $my_dep_file->setAttribute('href', 'document/'.$file_path);
9635
                                            $my_dep->setAttribute('xml:base', '');
9636
                                        } elseif (empty($file_path)) {
9637
                                            /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH), api_get_path(REL_PATH)));
9638
                                            if (strpos($document_root,-1) == '/') {
9639
                                                $document_root = substr(0, -1, $document_root);
9640
                                            }*/
9641
                                            $file_path = $_SERVER['DOCUMENT_ROOT'].$doc_info[0];
9642
                                            $file_path = str_replace('//', '/', $file_path);
9643
                                            if (file_exists($file_path)) {
9644
                                                $file_path = substr($file_path,strlen($current_dir)); // We get the relative path.
9645
                                                $zip_files[] = $my_sub_dir.'/'.$file_path;
9646
                                                $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9647
                                                $my_dep_file->setAttribute('href', 'document/'.$file_path);
9648
                                                $my_dep->setAttribute('xml:base', '');
9649
                                            }
9650
                                        }
9651
                                        break;
9652
                                    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
9653
                                        if (substr($doc_info[0], 0, 2) == '..') {
9654
                                            // Relative path going up.
9655
                                            $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
9656
                                            $current_dir = str_replace('\\', '/', $current_dir);
9657
                                            $file_path = realpath($current_dir.$doc_info[0]);
9658
                                            $file_path = str_replace('\\', '/', $file_path);
9659
                                            //error_log($file_path.' <-> '.$main_path, 0);
9660
                                            if (strstr($file_path, $main_path) !== false) {
9661
                                                // The calculated real path is really inside Chamilo's root path.
9662
                                                // Reduce file path to what's under the DocumentRoot.
9663
9664
                                                $file_path = substr($file_path, strlen($root_path));
9665
                                                $file_path_dest = $file_path;
9666
9667
                                                // File path is courses/CHAMILO/document/....
9668
                                                $info_file_path = explode('/', $file_path);
9669
                                                if ($info_file_path[0] == 'courses') { // Add character "/" in file path.
9670
                                                    $file_path_dest = 'document/'.$file_path;
9671
                                                }
9672
9673
                                                //error_log('Reduced path: '.$file_path, 0);
9674
                                                $zip_files_abs[] = $file_path;
9675
9676
                                                $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path_dest);
9677
                                                $my_dep_file->setAttribute('href', 'document/'.$file_path);
9678
                                                $my_dep->setAttribute('xml:base', '');
9679
                                            }
9680 View Code Duplication
                                        } else {
9681
                                            $zip_files[] = $my_sub_dir.'/'.$doc_info[0];
9682
                                            $my_dep_file->setAttribute('href', $doc_info[0]);
9683
                                            $my_dep->setAttribute('xml:base', $my_xml_sub_dir);
9684
                                        }
9685
                                        break;
9686
                                    default:
9687
                                        $my_dep_file->setAttribute('href', $doc_info[0]); // ../../courses/
9688
                                        $my_dep->setAttribute('xml:base', '');
9689
                                        break;
9690
                                }
9691
                            }
9692
                            $my_dep->appendChild($my_dep_file);
9693
                            $resources->appendChild($my_dep);
9694
                            $dependency = $xmldoc->createElement('dependency');
9695
                            $dependency->setAttribute('identifierref', $res_id);
9696
                            $my_resource->appendChild($dependency);
9697
                            $i++;
9698
                        }
9699
                        $resources->appendChild($my_resource);
9700
                        $zip_files[] = $my_file_path;
9701
                        break;
9702
                    default:
9703
                        // Get the path of the file(s) from the course directory root
9704
                        $my_file_path = 'non_exportable.html';
9705
                        //$my_xml_file_path = api_htmlentities(api_utf8_encode($my_file_path), ENT_COMPAT, 'UTF-8');
9706
                        $my_xml_file_path = $my_file_path;
9707
                        $my_sub_dir = dirname($my_file_path);
9708
                        $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
9709
                        //$my_xml_sub_dir = api_htmlentities(api_utf8_encode($my_sub_dir), ENT_COMPAT, 'UTF-8');
9710
                        $my_xml_sub_dir = $my_sub_dir;
9711
                        // Give a <resource> child to the <resources> element.
9712
                        $my_resource = $xmldoc->createElement('resource');
9713
                        $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
9714
                        $my_resource->setAttribute('type', 'webcontent');
9715
                        $my_resource->setAttribute('href', $folder_name.'/'.$my_xml_file_path);
9716
                        // adlcp:scormtype can be either 'sco' or 'asset'.
9717
                        $my_resource->setAttribute('adlcp:scormtype', 'asset');
9718
                        // xml:base is the base directory to find the files declared in this resource.
9719
                        $my_resource->setAttribute('xml:base', '');
9720
                        // Give a <file> child to the <resource> element.
9721
                        $my_file = $xmldoc->createElement('file');
9722
                        $my_file->setAttribute('href', 'document/'.$my_xml_file_path);
9723
                        $my_resource->appendChild($my_file);
9724
                        $resources->appendChild($my_resource);
9725
                        break;
9726
                }
9727
            }
9728
        }
9729
9730
        $organizations->appendChild($organization);
9731
        $root->appendChild($organizations);
9732
        $root->appendChild($resources);
9733
        $xmldoc->appendChild($root);
9734
9735
        $copyAll = api_get_configuration_value('add_all_files_in_lp_export');
9736
9737
        // TODO: Add a readme file here, with a short description and a link to the Reload player
9738
        // then add the file to the zip, then destroy the file (this is done automatically).
9739
        // http://www.reload.ac.uk/scormplayer.html - once done, don't forget to close FS#138
9740
9741
        foreach ($zip_files as $file_path) {
9742
            if (empty($file_path)) {
9743
                continue;
9744
            }
9745
9746
            $dest_file = $archive_path.$temp_dir_short.'/'.$file_path;
9747
            if (!empty($path_to_remove) && !empty($path_to_replace)) {
9748
                $dest_file = str_replace($path_to_remove, $path_to_replace, $dest_file);
9749
            }
9750
            $this->create_path($dest_file);
9751
            @copy($sys_course_path.$_course['path'].'/'.$file_path, $dest_file);
9752
            // Check if the file needs a link update.
9753
            if (in_array($file_path, array_keys($link_updates))) {
9754
                $string = file_get_contents($dest_file);
9755
                unlink($dest_file);
9756
                foreach ($link_updates[$file_path] as $old_new) {
9757
                    // This is an ugly hack that allows .flv files to be found by the flv player that
9758
                    // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs
9759
                    // to find the flv to play in document/main/, so we replace main/ in the flv path by
9760
                    // ../../.. to return from inc/lib/flv_player to the document/main path.
9761
                    if (substr($old_new['dest'], -3) == 'flv' && substr($old_new['dest'], 0, 5) == 'main/') {
9762
                        $old_new['dest'] = str_replace('main/', '../../../', $old_new['dest']);
9763 View Code Duplication
                    } elseif (substr($old_new['dest'], -3) == 'flv' && substr($old_new['dest'], 0, 6) == 'video/') {
9764
                        $old_new['dest'] = str_replace('video/', '../../../../video/', $old_new['dest']);
9765
                    }
9766
                    //Fix to avoid problems with default_course_document
9767
                    if (strpos("main/default_course_document", $old_new['dest'] === false)) {
9768
                        //$newDestination = str_replace('document/', $mult.'document/', $old_new['dest']);
9769
                        $newDestination = $old_new['dest'];
9770
                    } else {
9771
                        $newDestination = str_replace('document/', '', $old_new['dest']);
9772
                    }
9773
                    $string = str_replace($old_new['orig'], $newDestination, $string);
9774
9775
                    //Add files inside the HTMLs
9776
                    $new_path = str_replace('/courses/', '', $old_new['orig']);
9777
                    $destinationFile = $archive_path.$temp_dir_short.'/'.$old_new['dest'];
9778
                    if (file_exists($sys_course_path.$new_path)) {
9779
                        copy($sys_course_path.$new_path, $destinationFile);
9780
                    }
9781
                }
9782
                file_put_contents($dest_file, $string);
9783
            }
9784
9785
            if (file_exists($filePath) && $copyAll) {
9786
                $extension = $this->get_extension($filePath);
0 ignored issues
show
The variable $filePath does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
9787
                if (in_array($extension, ['html', 'html'])) {
9788
                    $containerOrigin = dirname($filePath);
9789
                    $containerDestination = dirname($dest_file);
9790
9791
                    $finder = new Finder();
9792
                    $finder->files()->in($containerOrigin)
9793
                        ->exclude('share_folder')
9794
                        ->exclude('chat_files')
9795
                        ->exclude('certificates');
9796
9797
                    if (is_dir($containerOrigin) && is_dir($containerDestination)) {
9798
                        $fs = new Filesystem();
9799
                        $fs->mirror(
9800
                            $containerOrigin,
9801
                            $containerDestination,
9802
                            $finder
9803
                        );
9804
                    }
9805
                }
9806
            }
9807
        }
9808
9809
        foreach ($zip_files_abs as $file_path) {
9810
            if (empty($file_path)) {
9811
                continue;
9812
            }
9813
            if (!is_file($main_path.$file_path) || !is_readable($main_path.$file_path)) {
9814
                continue;
9815
            }
9816
9817
            $dest_file = $archive_path.$temp_dir_short.'/document/'.$file_path;
9818
            $this->create_path($dest_file);
9819
            copy($main_path.$file_path, $dest_file);
9820
            // Check if the file needs a link update.
9821
            if (in_array($file_path, array_keys($link_updates))) {
9822
                $string = file_get_contents($dest_file);
9823
                unlink($dest_file);
9824
                foreach ($link_updates[$file_path] as $old_new) {
9825
                    // This is an ugly hack that allows .flv files to be found by the flv player that
9826
                    // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs
9827
                    // to find the flv to play in document/main/, so we replace main/ in the flv path by
9828
                    // ../../.. to return from inc/lib/flv_player to the document/main path.
9829 View Code Duplication
                    if (substr($old_new['dest'], -3) == 'flv' && substr($old_new['dest'], 0, 5) == 'main/') {
9830
                        $old_new['dest'] = str_replace('main/', '../../../', $old_new['dest']);
9831
                    }
9832
                    $string = str_replace($old_new['orig'], $old_new['dest'], $string);
9833
                }
9834
                file_put_contents($dest_file, $string);
9835
            }
9836
        }
9837
9838
        if (is_array($links_to_create)) {
9839
            foreach ($links_to_create as $file => $link) {
9840
                $file_content = '<!DOCTYPE html><head>
9841
                                <meta charset="'.api_get_language_isocode().'" />
9842
                                <title>'.$link['title'].'</title>
9843
                                </head>
9844
                                <body dir="'.api_get_text_direction().'">
9845
                                <div style="text-align:center">
9846
                                <a href="'.$link['url'].'">'.$link['title'].'</a></div>
9847
                                </body>
9848
                                </html>';
9849
                file_put_contents($archive_path.$temp_dir_short.'/'.$file, $file_content);
9850
            }
9851
        }
9852
9853
        // Add non exportable message explanation.
9854
        $lang_not_exportable = get_lang('ThisItemIsNotExportable');
9855
        $file_content = '<!DOCTYPE html><head>
9856
                        <meta charset="'.api_get_language_isocode().'" />
9857
                        <title>'.$lang_not_exportable.'</title>
9858
                        <meta http-equiv="Content-Type" content="text/html; charset='.api_get_system_encoding().'" />
9859
                        </head>
9860
                        <body dir="'.api_get_text_direction().'">';
9861
        $file_content .=
9862
            <<<EOD
9863
                    <style>
9864
            .error-message {
9865
                font-family: arial, verdana, helvetica, sans-serif;
9866
                border-width: 1px;
9867
                border-style: solid;
9868
                left: 50%;
9869
                margin: 10px auto;
9870
                min-height: 30px;
9871
                padding: 5px;
9872
                right: 50%;
9873
                width: 500px;
9874
                background-color: #FFD1D1;
9875
                border-color: #FF0000;
9876
                color: #000;
9877
            }
9878
        </style>
9879
    <body>
9880
        <div class="error-message">
9881
            $lang_not_exportable
9882
        </div>
9883
    </body>
9884
</html>
9885
EOD;
9886
        if (!is_dir($archive_path.$temp_dir_short.'/document')) {
9887
            @mkdir($archive_path.$temp_dir_short.'/document', api_get_permissions_for_new_directories());
9888
        }
9889
        file_put_contents($archive_path.$temp_dir_short.'/document/non_exportable.html', $file_content);
9890
9891
        // Add the extra files that go along with a SCORM package.
9892
        $main_code_path = api_get_path(SYS_CODE_PATH).'newscorm/packaging/';
9893
        $extra_files = scandir($main_code_path);
9894
        foreach ($extra_files as $extra_file) {
9895
            if (strpos($extra_file, '.') === 0) {
9896
                continue;
9897
            } else {
9898
                $dest_file = $archive_path.$temp_dir_short.'/'.$extra_file;
9899
                $this->create_path($dest_file);
9900
                copy($main_code_path.$extra_file, $dest_file);
9901
            }
9902
        }
9903
9904
        // Finalize the imsmanifest structure, add to the zip, then return the zip.
9905
9906
        $manifest = @$xmldoc->saveXML();
9907
        $manifest = api_utf8_decode_xml($manifest); // The manifest gets the system encoding now.
9908
        file_put_contents($archive_path.'/'.$temp_dir_short.'/imsmanifest.xml', $manifest);
9909
        $zip_folder->add(
9910
            $archive_path.'/'.$temp_dir_short,
9911
            PCLZIP_OPT_REMOVE_PATH,
9912
            $archive_path.'/'.$temp_dir_short.'/'
9913
        );
9914
9915
        // Clean possible temporary files.
9916
        foreach ($files_cleanup as $file) {
9917
            $res = unlink($file);
9918
            if ($res === false) {
9919
                error_log(
9920
                    'Could not delete temp file '.$file.' '.__FILE__.' '.__LINE__,
9921
                    0
9922
                );
9923
            }
9924
        }
9925
        $name = api_replace_dangerous_char($this->get_name()).'.zip';
9926
        DocumentManager::file_send_for_download($temp_zip_file, true, $name);
9927
    }
9928
9929
    /**
9930
     * @param int $lp_id
9931
     * @return bool
9932
     */
9933
    public function scorm_export_to_pdf($lp_id)
9934
    {
9935
        $lp_id = intval($lp_id);
9936
        $files_to_export = array();
9937
        $course_data = api_get_course_info($this->cc);
9938
        if (!empty($course_data)) {
9939
            $scorm_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/scorm/'.$this->path;
9940
9941
            $list = self::get_flat_ordered_items_list($lp_id);
9942
            if (!empty($list)) {
9943
                foreach ($list as $item_id) {
9944
                    $item = $this->items[$item_id];
9945
                    switch ($item->type) {
9946
                        case 'document':
9947
                            //Getting documents from a LP with chamilo documents
9948
                            $file_data = DocumentManager::get_document_data_by_id($item->path, $this->cc);
9949
                            // Try loading document from the base course.
9950
                            if (empty($file_data) && !empty($sessionId)) {
9951
                                $file_data = DocumentManager::get_document_data_by_id($item->path, $this->cc, false, 0);
9952
                            }
9953
                            $file_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/document'.$file_data['path'];
9954
                            if (file_exists($file_path)) {
9955
                                $files_to_export[] = array('title'=>$item->get_title(),'path'=>$file_path);
9956
                            }
9957
                            break;
9958
                        case 'asset': //commes from a scorm package generated by chamilo
9959
                        case 'sco':
9960
                            $file_path = $scorm_path.'/'.$item->path;
9961
                            if (file_exists($file_path)) {
9962
                                $files_to_export[] = array('title'=>$item->get_title(), 'path' => $file_path);
9963
                            }
9964
                            break;
9965
                        case 'dokeos_chapter':
9966
                        case 'dir':
9967
                        case 'chapter':
9968
                            $files_to_export[] = array('title'=> $item->get_title(), 'path'=>null);
9969
                            break;
9970
                    }
9971
                }
9972
            }
9973
            $pdf = new PDF();
9974
            $result = $pdf->html_to_pdf($files_to_export, $this->name, $this->cc, true);
9975
            return $result;
9976
        }
9977
9978
        return false;
9979
    }
9980
9981
    /**
9982
     * Temp function to be moved in main_api or the best place around for this.
9983
     * Creates a file path if it doesn't exist
9984
     * @param string $path
9985
     */
9986
    public function create_path($path)
9987
    {
9988
        $path_bits = explode('/', dirname($path));
9989
9990
        // IS_WINDOWS_OS has been defined in main_api.lib.php
9991
        $path_built = IS_WINDOWS_OS ? '' : '/';
9992
9993
        foreach ($path_bits as $bit) {
9994
            if (!empty ($bit)) {
9995
                $new_path = $path_built . $bit;
9996
                if (is_dir($new_path)) {
9997
                    $path_built = $new_path . '/';
9998
                } else {
9999
                    mkdir($new_path, api_get_permissions_for_new_directories());
10000
                    $path_built = $new_path . '/';
10001
                }
10002
            }
10003
        }
10004
    }
10005
10006
    /**
10007
     * Delete the image relative to this learning path. No parameter. Only works on instanciated object.
10008
     * @return	boolean	The results of the unlink function, or false if there was no image to start with
10009
     */
10010
    public function delete_lp_image()
10011
    {
10012
        $img = $this->get_preview_image();
10013
        if ($img != '') {
10014
            $del_file = $this->get_preview_image_path(null, 'sys');
10015
            if (isset($del_file) && file_exists($del_file)) {
10016
                $del_file_2 = $this->get_preview_image_path(64, 'sys');
10017
                if (file_exists($del_file_2)) {
10018
                    unlink($del_file_2);
10019
                }
10020
                $this->set_preview_image('');
10021
                return @unlink($del_file);
10022
            }
10023
        }
10024
        return false;
10025
    }
10026
10027
    /**
10028
     * Uploads an author image to the upload/learning_path/images directory
10029
     * @param	array	The image array, coming from the $_FILES superglobal
10030
     * @return	boolean	True on success, false on error
10031
     */
10032
    public function upload_image($image_array)
10033
    {
10034
        $image_moved = false;
10035
        if (!empty ($image_array['name'])) {
10036
            $upload_ok = process_uploaded_file($image_array);
10037
            $has_attachment = true;
10038
        } else {
10039
            $image_moved = true;
10040
        }
10041
10042
        if ($upload_ok) {
10043
            if ($has_attachment) {
10044
                $courseDir = api_get_course_path() . '/upload/learning_path/images';
10045
                $sys_course_path = api_get_path(SYS_COURSE_PATH);
10046
                $updir = $sys_course_path . $courseDir;
10047
                // Try to add an extension to the file if it hasn't one.
10048
                $new_file_name = add_ext_on_mime(stripslashes($image_array['name']), $image_array['type']);
10049
10050
                if (!filter_extension($new_file_name)) {
10051
                    //Display :: display_error_message(get_lang('UplUnableToSaveFileFilteredExtension'));
10052
                    $image_moved = false;
10053
                } else {
10054
                    $file_extension = explode('.', $image_array['name']);
10055
                    $file_extension = strtolower($file_extension[sizeof($file_extension) - 1]);
10056
                    $filename = uniqid('');
10057
                    $new_file_name = $filename.'.'.$file_extension;
10058
                    $new_path = $updir.'/'.$new_file_name;
10059
10060
                    // Resize the image.
10061
                    $temp = new Image($image_array['tmp_name']);
10062
                    $temp->resize(104);
10063
                    $result = $temp->send_image($new_path);
10064
10065
                    // Storing the image filename.
10066
                    if ($result) {
10067
                        $image_moved = true;
10068
                        $this->set_preview_image($new_file_name);
10069
10070
                        //Resize to 64px to use on course homepage
10071
                        $temp->resize(64);
10072
                        $temp->send_image($updir.'/'.$filename.'.64.'.$file_extension);
10073
                        return true;
10074
                    }
10075
                }
10076
            }
10077
        }
10078
10079
        return false;
10080
    }
10081
10082
    /**
10083
     * @param int $lp_id
10084
     * @param string $status
10085
     */
10086
    public function set_autolaunch($lp_id, $status)
10087
    {
10088
        $course_id = api_get_course_int_id();
10089
        $lp_id   = intval($lp_id);
10090
        $status  = intval($status);
10091
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
10092
10093
        // Setting everything to autolaunch = 0
10094
        $attributes['autolaunch'] = 0;
10095
        $where = array('session_id = ? AND c_id = ? '=> array(api_get_session_id(), $course_id));
10096
        Database::update($lp_table, $attributes, $where);
10097
        if ($status == 1) {
10098
            //Setting my lp_id to autolaunch = 1
10099
            $attributes['autolaunch'] = 1;
10100
            $where = array('id = ? AND session_id = ? AND c_id = ?'=> array($lp_id, api_get_session_id(), $course_id));
10101
            Database::update($lp_table, $attributes, $where );
10102
        }
10103
    }
10104
10105
    /**
10106
     * Gets previous_item_id for the next element of the lp_item table
10107
     * @author Isaac flores paz
10108
     * @return	integer	Previous item ID
10109
     */
10110
    public function select_previous_item_id()
10111
    {
10112
        $course_id = api_get_course_int_id();
10113
        if ($this->debug > 0) {
10114
            error_log('New LP - In learnpath::select_previous_item_id()', 0);
10115
        }
10116
        $table_lp_item = Database::get_course_table(TABLE_LP_ITEM);
10117
10118
        // Get the max order of the items
10119
        $sql_max_order = "SELECT max(display_order) AS display_order FROM $table_lp_item
10120
    	                  WHERE c_id = $course_id AND lp_id = '" . $this->lp_id . "'";
10121
        $rs_max_order = Database::query($sql_max_order);
10122
        $row_max_order = Database::fetch_object($rs_max_order);
10123
        $max_order = $row_max_order->display_order;
10124
        // Get the previous item ID
10125
        $sql = "SELECT id as previous FROM $table_lp_item
10126
                WHERE c_id = $course_id AND lp_id = '" . $this->lp_id . "' AND display_order = '".$max_order."' ";
10127
        $rs_max = Database::query($sql);
10128
        $row_max = Database::fetch_object($rs_max);
10129
10130
        // Return the previous item ID
10131
        return $row_max->previous;
10132
    }
10133
10134
    /**
10135
     * Copies an LP
10136
     */
10137
    public function copy()
10138
    {
10139
        $main_path = api_get_path(SYS_CODE_PATH);
10140
        require_once $main_path.'coursecopy/classes/CourseBuilder.class.php';
10141
        require_once $main_path.'coursecopy/classes/CourseArchiver.class.php';
10142
        require_once $main_path.'coursecopy/classes/CourseRestorer.class.php';
10143
        require_once $main_path.'coursecopy/classes/CourseSelectForm.class.php';
10144
10145
        //Course builder
10146
        $cb = new CourseBuilder();
10147
10148
        //Setting tools that will be copied
10149
        $cb->set_tools_to_build(array('learnpaths'));
10150
10151
        //Setting elements that will be copied
10152
        $cb->set_tools_specific_id_list(
10153
            array('learnpaths' => array($this->lp_id))
10154
        );
10155
10156
        $course = $cb->build();
10157
10158
        //Course restorer
10159
        $course_restorer = new CourseRestorer($course);
10160
        $course_restorer->set_add_text_in_items(true);
10161
        $course_restorer->set_tool_copy_settings(array('learnpaths' => array('reset_dates' => true)));
10162
        $course_restorer->restore(api_get_course_id(), api_get_session_id(), false, false);
10163
    }
10164
10165
    public function verify_document_size($s)
10166
    {
10167
        $post_max = ini_get('post_max_size');
10168 View Code Duplication
        if (substr($post_max, -1, 1) == 'M') {
10169
            $post_max = intval(substr($post_max, 0, -1)) * 1024 * 1024;
10170
        } elseif (substr($post_max, -1, 1) == 'G') {
10171
            $post_max = intval(substr($post_max, 0, -1)) * 1024 * 1024 * 1024;
10172
        }
10173
        $upl_max = ini_get('upload_max_filesize');
10174 View Code Duplication
        if (substr($upl_max, -1, 1) == 'M') {
10175
            $upl_max = intval(substr($upl_max, 0, -1)) * 1024 * 1024;
10176
        } elseif (substr($upl_max, -1, 1) == 'G') {
10177
            $upl_max = intval(substr($upl_max, 0, -1)) * 1024 * 1024 * 1024;
10178
        }
10179
        $documents_total_space = DocumentManager::documents_total_space();
10180
        $course_max_space = DocumentManager::get_course_quota();
10181
        $total_size = filesize($s) + $documents_total_space;
10182
        if (filesize($s)>$post_max || filesize($s)>$upl_max  || $total_size>$course_max_space ){
10183
            return true;
10184
        } else{
10185
            return false;
10186
        }
10187
    }
10188
10189
    /**
10190
     * Clear LP prerequisites
10191
     */
10192
    public function clear_prerequisites()
10193
    {
10194
        $course_id = $this->get_course_int_id();
10195
        if ($this->debug > 0) {
10196
            error_log('New LP - In learnpath::clear_prerequisites()', 0);
10197
        }
10198
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
10199
        $lp_id = $this->get_id();
10200
        //Cleaning prerequisites
10201
        $sql = "UPDATE $tbl_lp_item SET prerequisite = ''
10202
                WHERE c_id = ".$course_id." AND lp_id = '$lp_id'";
10203
        Database::query($sql);
10204
10205
        //Cleaning mastery score for exercises
10206
        $sql = "UPDATE $tbl_lp_item SET mastery_score = ''
10207
                WHERE c_id = ".$course_id." AND lp_id = '$lp_id' AND item_type = 'quiz'";
10208
        Database::query($sql);
10209
    }
10210
10211
    public function set_previous_step_as_prerequisite_for_all_items()
10212
    {
10213
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
10214
        $course_id = $this->get_course_int_id();
10215
        $lp_id = $this->get_id();
10216
10217
        if (!empty($this->items)) {
10218
            $previous_item_id = null;
10219
            $previous_item_max = 0;
10220
            $previous_item_type = null;
10221
            $last_item_not_chapter = null;
10222
            $last_item_not_chapter_type = null;
10223
            $last_item_not_chapter_max = null;
10224
            foreach ($this->items as $item) {
10225
                // if there was a previous item... (otherwise jump to set it)
10226
                if (!empty($previous_item_id)) {
10227
                    $current_item_id = $item->get_id(); //save current id
10228
                    if (!in_array($item->get_type(), array('dokeos_chapter', 'chapter'))) {
10229
                        // Current item is not a folder, so it qualifies to get a prerequisites
10230
                        if ($last_item_not_chapter_type == 'quiz') {
10231
                            // if previous is quiz, mark its max score as default score to be achieved
10232
                            $sql = "UPDATE $tbl_lp_item SET mastery_score = '$last_item_not_chapter_max'
10233
                                    WHERE c_id = ".$course_id." AND lp_id = '$lp_id' AND id = '$last_item_not_chapter'";
10234
                            Database::query($sql);
10235
                        }
10236
                        // now simply update the prerequisite to set it to the last non-chapter item
10237
                        $sql = "UPDATE $tbl_lp_item SET prerequisite = '$last_item_not_chapter'
10238
                                WHERE c_id = ".$course_id." AND lp_id = '$lp_id' AND id = '$current_item_id'";
10239
                        Database::query($sql);
10240
                        // record item as 'non-chapter' reference
10241
                        $last_item_not_chapter = $item->get_id();
10242
                        $last_item_not_chapter_type = $item->get_type();
10243
                        $last_item_not_chapter_max = $item->get_max();
10244
                    }
10245
                } else {
10246
                    if (!in_array($item->get_type(), array('dokeos_chapter', 'chapter'))) {
10247
                        // Current item is not a folder (but it is the first item) so record as last "non-chapter" item
10248
                        $last_item_not_chapter = $item->get_id();
10249
                        $last_item_not_chapter_type = $item->get_type();
10250
                        $last_item_not_chapter_max = $item->get_max();
10251
                    }
10252
                }
10253
                // Saving the item as "previous item" for the next loop
10254
                $previous_item_id = $item->get_id();
10255
                $previous_item_max = $item->get_max();
10256
                $previous_item_type = $item->get_type();
10257
            }
10258
        }
10259
    }
10260
10261
    /**
10262
     * @param array $params
10263
     */
10264
    public static function createCategory($params)
10265
    {
10266
        $em = Database::getManager();
10267
        $item = new CLpCategory();
10268
        $item->setName($params['name']);
10269
        $item->setCId($params['c_id']);
10270
        $em->persist($item);
10271
        $em->flush();
10272
    }
10273
    /**
10274
     * @param array $params
10275
     */
10276
    public static function updateCategory($params)
10277
    {
10278
        $em = Database::getManager();
10279
        /** @var CLpCategory $item */
10280
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $params['id']);
10281
        if ($item) {
10282
            $item->setName($params['name']);
10283
            $em->merge($item);
10284
            $em->flush();
10285
        }
10286
    }
10287
10288
    /**
10289
     * @param int $id
10290
     */
10291 View Code Duplication
    public static function moveUpCategory($id)
10292
    {
10293
        $em = Database::getManager();
10294
        /** @var CLpCategory $item */
10295
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
10296
        if ($item) {
10297
            $position = $item->getPosition() - 1;
10298
            $item->setPosition($position);
10299
            $em->persist($item);
10300
            $em->flush();
10301
        }
10302
    }
10303
10304
    /**
10305
     * @param int $id
10306
     */
10307 View Code Duplication
    public static function moveDownCategory($id)
10308
    {
10309
        $em = Database::getManager();
10310
        /** @var CLpCategory $item */
10311
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
10312
        if ($item) {
10313
            $position = $item->getPosition() + 1;
10314
            $item->setPosition($position);
10315
            $em->persist($item);
10316
            $em->flush();
10317
        }
10318
    }
10319
10320
    /**
10321
     * @param int $courseId
10322
     * @return int|mixed
10323
     */
10324
    public static function getCountCategories($courseId)
10325
    {
10326
        if (empty($course_id)) {
10327
            return 0;
10328
        }
10329
        $em = Database::getManager();
10330
        $query = $em->createQuery('SELECT COUNT(u.id) FROM ChamiloCourseBundle:CLpCategory u WHERE u.cId = :id');
10331
        $query->setParameter('id', $courseId);
10332
10333
        return $query->getSingleScalarResult();
10334
    }
10335
10336
    /**
10337
     * @param int $courseId
10338
     *
10339
     * @return mixed
10340
     */
10341 View Code Duplication
    public static function getCategories($courseId)
10342
    {
10343
        $em = Database::getManager();
10344
        //Default behaviour
10345
        /*$items = $em->getRepository('ChamiloCourseBundle:CLpCategory')->findBy(
10346
            array('cId' => $course_id),
10347
            array('name' => 'ASC')
10348
        );*/
10349
10350
        // Using doctrine extensions
10351
        $items = $em->getRepository('ChamiloCourseBundle:CLpCategory')->getBySortableGroupsQuery(
10352
            array('cId' => $courseId)
10353
        )->getResult();
10354
10355
        return $items;
10356
    }
10357
10358
    /**
10359
     * @param int $id
10360
     *
10361
     * @return mixed
10362
     */
10363
    public static function getCategory($id)
10364
    {
10365
        $em = Database::getManager();
10366
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
10367
10368
        return $item;
10369
    }
10370
10371
    /**
10372
     * @param int $courseId
10373
     * @return array
10374
     */
10375 View Code Duplication
    public static function getCategoryByCourse($courseId)
10376
    {
10377
        $em = Database::getManager();
10378
        $items = $em->getRepository('ChamiloCourseBundle:CLpCategory')->findBy(array('cId' => $courseId));
10379
10380
        return $items;
10381
    }
10382
10383
    /**
10384
     * @param int $id
10385
     *
10386
     * @return mixed
10387
     */
10388
    public static function deleteCategory($id)
10389
    {
10390
        $em = Database::getManager();
10391
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
10392
        if ($item) {
10393
10394
            $courseId = $item->getCId();
10395
            $query = $em->createQuery('SELECT u FROM ChamiloCourseBundle:CLp u WHERE u.cId = :id AND u.categoryId = :catId');
10396
            $query->setParameter('id', $courseId);
10397
            $query->setParameter('catId', $item->getId());
10398
            $lps = $query->getResult();
10399
10400
            // Setting category = 0.
10401
            if ($lps) {
10402
                foreach ($lps as $lpItem) {
10403
                    $lpItem->setCategoryId(0);
10404
                }
10405
            }
10406
10407
            // Removing category.
10408
            $em->remove($item);
10409
            $em->flush();
10410
        }
10411
    }
10412
10413
    /**
10414
     * @param int $courseId
10415
     * @param bool $addSelectOption
10416
     *
10417
     * @return mixed
10418
     */
10419
    static function getCategoryFromCourseIntoSelect($courseId, $addSelectOption = false)
10420
    {
10421
        $items = self::getCategoryByCourse($courseId);
10422
        $cats = array();
10423
        if ($addSelectOption) {
10424
            $cats = array(get_lang('SelectACategory'));
10425
        }
10426
10427
        if (!empty($items)) {
10428
            foreach ($items as $cat) {
10429
                $cats[$cat->getId()] = $cat->getName();
10430
            }
10431
        }
10432
10433
        return $cats;
10434
    }
10435
10436
    /**
10437
     * Return the scorm item type object with spaces replaced with _
10438
     * The return result is use to build a css classname like scorm_type_$return
10439
     * @param $in_type
10440
     * @return mixed
10441
     */
10442
    private static function format_scorm_type_item($in_type)
10443
    {
10444
        return str_replace(' ', '_', $in_type);
10445
    }
10446
10447
    /**
10448
     * @return \learnpath
10449
     */
10450
    public static function getLpFromSession($courseCode, $lp_id, $user_id)
10451
    {
10452
        $lpObject = Session::read('lpobject');
10453
        $learnPath = null;
10454
        if (isset($lpObject)) {
10455
            $learnPath = unserialize($lpObject);
10456
        }
10457
10458
        if (!is_object($learnPath)) {
10459
            $learnPath = new learnpath($courseCode, $lp_id, $user_id);
10460
        }
10461
10462
        return $learnPath;
10463
    }
10464
10465
    /**
10466
     * @param int $itemId
10467
     * @return learnpathItem|false
10468
     */
10469
    public function getItem($itemId)
10470
    {
10471
        if (isset($this->items[$itemId]) && is_object($this->items[$itemId])) {
10472
            return $this->items[$itemId];
10473
        }
10474
10475
        return false;
10476
    }
10477
10478
    /**
10479
     * @return int
10480
     */
10481
    public function getCategoryId()
10482
    {
10483
        return $this->categoryId;
10484
    }
10485
10486
    /**
10487
     * @param int $categoryId
10488
     * @return bool
10489
     */
10490
    public function setCategoryId($categoryId)
10491
    {
10492
        $this->categoryId = intval($categoryId);
10493
10494
        $courseId = api_get_course_int_id();
10495
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
10496
        $lp_id = $this->get_id();
10497
        $sql = "UPDATE $lp_table SET category_id = ".$this->categoryId."
10498
                WHERE c_id = $courseId AND id = $lp_id";
10499
        Database::query($sql);
10500
10501
        return true;
10502
    }
10503
10504
    /**
10505
     * Get whether this is a learning path with the possibility to subscribe
10506
     * users or not
10507
     * @return int
10508
     */
10509
    public function getSubscribeUsers()
10510
    {
10511
        return $this->subscribeUsers;
10512
    }
10513
10514
    /**
10515
     * Set whether this is a learning path with the possibility to subscribe
10516
     * users or not
10517
     * @param int $subscribeUsers (0 = false, 1 = true)
10518
     */
10519
    public function setSubscribeUsers($value)
10520
    {
10521
        if ($this->debug > 0) {
10522
            error_log('New LP - In learnpath::set_subscribe_users()', 0);
10523
        }
10524
        $this->subscribeUsers = intval($value);;
10525
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
10526
        $lp_id = $this->get_id();
10527
        $sql = "UPDATE $lp_table SET subscribe_users = ".$this->subscribeUsers."
10528
                WHERE c_id = ".$this->course_int_id." AND id = $lp_id";
10529
        Database::query($sql);
10530
10531
        return true;
10532
    }
10533
10534
    /**
10535
     * Calculate the count of stars for a user in this LP
10536
     * This calculation is based on the following rules:
10537
     * - the student gets one star when he gets to 50% of the learning path
10538
     * - the student gets a second star when the average score of all tests inside the learning path >= 50%
10539
     * - the student gets a third star when the average score of all tests inside the learning path >= 80%
10540
     * - the student gets the final star when the score for the *last* test is >= 80%
10541
     * @param int $sessionId Optional. The session ID
10542
     * @return int The count of stars
10543
     */
10544
    public function getCalculateStars($sessionId = 0)
10545
    {
10546
        $stars = 0;
10547
10548
        $progress = self::getProgress($this->lp_id, $this->user_id, $this->course_int_id, $sessionId);
10549
10550
        if ($progress >= 50) {
10551
            $stars++;
10552
        }
10553
10554
        // Calculate stars chapters evaluation
10555
        $exercisesItems = $this->getExercisesItems();
10556
10557
        if (!empty($exercisesItems)) {
10558
            $totalResult = 0;
10559
10560
            foreach ($exercisesItems as $exerciseItem) {
10561
                $exerciseResultInfo = Event::getExerciseResultsByUser(
10562
                    $this->user_id,
10563
                    $exerciseItem->path,
10564
                    $this->course_int_id,
10565
                    $sessionId,
10566
                    $this->lp_id,
10567
                    $exerciseItem->db_id
10568
                );
10569
10570
                $exerciseResultInfo = end($exerciseResultInfo);
10571
10572
                if (!$exerciseResultInfo) {
10573
                    continue;
10574
                }
10575
10576
                $exerciseResult = $exerciseResultInfo['exe_result'] * 100 / $exerciseResultInfo['exe_weighting'];
10577
10578
                $totalResult += $exerciseResult;
10579
            }
10580
10581
            $totalExerciseAverage = $totalResult / (count($exercisesItems) > 0 ? count($exercisesItems) : 1);
10582
10583
            if ($totalExerciseAverage >= 50) {
10584
                $stars++;
10585
            }
10586
10587
            if ($totalExerciseAverage >= 80) {
10588
                $stars++;
10589
            }
10590
        }
10591
10592
        // Calculate star for final evaluation
10593
        $finalEvaluationItem = $this->getFinalEvaluationItem();
10594
10595
        if (!empty($finalEvaluationItem)) {
10596
            $evaluationResultInfo = Event::getExerciseResultsByUser(
10597
                $this->user_id,
10598
                $finalEvaluationItem->path,
10599
                $this->course_int_id,
10600
                $sessionId,
10601
                $this->lp_id,
10602
                $finalEvaluationItem->db_id
10603
            );
10604
10605
            $evaluationResultInfo = end($evaluationResultInfo);
10606
10607
            if ($evaluationResultInfo) {
10608
                $evaluationResult = $evaluationResultInfo['exe_result'] * 100 / $evaluationResultInfo['exe_weighting'];
10609
10610
                if ($evaluationResult >= 80) {
10611
                    $stars++;
10612
                }
10613
            }
10614
        }
10615
10616
        return $stars;
10617
    }
10618
10619
    /**
10620
     * Get the items of exercise type
10621
     * @return array The items. Otherwise return false
10622
     */
10623 View Code Duplication
    public function getExercisesItems()
10624
    {
10625
        $exercises = [];
10626
10627
        foreach ($this->items as $item) {
10628
            if ($item->type != 'quiz') {
10629
                continue;
10630
            }
10631
10632
            $exercises[] = $item;
10633
        }
10634
10635
        array_pop($exercises);
10636
10637
        return $exercises;
10638
    }
10639
10640
    /**
10641
     * Get the item of exercise type (evaluation type)
10642
     * @return array The final evaluation. Otherwise return false
10643
     */
10644 View Code Duplication
    public function getFinalEvaluationItem()
10645
    {
10646
        $exercises = [];
10647
10648
        foreach ($this->items as $item) {
10649
            if ($item->type != 'quiz') {
10650
                continue;
10651
            }
10652
10653
            $exercises[] = $item;
10654
        }
10655
10656
        return array_pop($exercises);
10657
    }
10658
10659
    /**
10660
     * Calculate the total points achieved for the current user in this learning path
10661
     * @param int $sessionId Optional. The session Id
10662
     * @return int
10663
     */
10664
    public function getCalculateScore($sessionId = 0)
10665
    {
10666
        // Calculate stars chapters evaluation
10667
        $exercisesItems = $this->getExercisesItems();
10668
        $finalEvaluationItem = $this->getFinalEvaluationItem();
10669
10670
        $totalExercisesResult = 0;
10671
        $totalEvaluationResult = 0;
10672
10673
        if ($exercisesItems !== false) {
10674
            foreach ($exercisesItems as $exerciseItem) {
10675
                $exerciseResultInfo = Event::getExerciseResultsByUser(
10676
                    $this->user_id,
10677
                    $exerciseItem->path,
10678
                    $this->course_int_id,
10679
                    $sessionId,
10680
                    $this->lp_id,
10681
                    $exerciseItem->db_id
10682
                );
10683
10684
                $exerciseResultInfo = end($exerciseResultInfo);
10685
10686
                if (!$exerciseResultInfo) {
10687
                    continue;
10688
                }
10689
10690
                $totalExercisesResult += $exerciseResultInfo['exe_result'];
10691
            }
10692
        }
10693
10694
        if (!empty($finalEvaluationItem)) {
10695
            $evaluationResultInfo = Event::getExerciseResultsByUser(
10696
                $this->user_id,
10697
                $finalEvaluationItem->path,
10698
                $this->course_int_id,
10699
                $sessionId,
10700
                $this->lp_id,
10701
                $finalEvaluationItem->db_id
10702
            );
10703
10704
            $evaluationResultInfo = end($evaluationResultInfo);
10705
10706
            if ($evaluationResultInfo) {
10707
                $totalEvaluationResult += $evaluationResultInfo['exe_result'];
10708
            }
10709
        }
10710
10711
        return $totalExercisesResult + $totalEvaluationResult;
10712
    }
10713
10714
    /**
10715
     * Check if URL is not allowed to be show in a iframe
10716
     * @param string $src
10717
     *
10718
     * @return string
10719
     */
10720
    public function fixBlockedLinks($src)
10721
    {
10722
        $urlInfo = parse_url($src);
10723
        //$platformProtocol = api_get_protocol();
10724
10725
        $platformProtocol = 'https';
10726
        if (strpos(api_get_path(WEB_CODE_PATH), 'https') === false) {
10727
            $platformProtocol = 'http';
10728
        }
10729
10730
        $protocolFixApplied = false;
10731
        //Scheme validation to avoid "Notices" when the lesson doesn't contain a valid scheme
10732
        $scheme = isset($urlInfo['scheme']) ? $urlInfo['scheme'] : null;
10733
        if ($platformProtocol != $scheme) {
10734
            $_SESSION['x_frame_source'] = $src;
10735
            $src = 'blank.php?error=x_frames_options';
10736
            $protocolFixApplied = true;
10737
        }
10738
10739
        if ($protocolFixApplied == false) {
10740
            if (strpos($src, api_get_path(WEB_CODE_PATH)) === false) {
10741
                // Check X-Frame-Options
10742
                $ch = curl_init();
10743
10744
                $options = array(
10745
                    CURLOPT_URL => $src,
10746
                    CURLOPT_RETURNTRANSFER => true,
10747
                    CURLOPT_HEADER => true,
10748
                    CURLOPT_FOLLOWLOCATION => true,
10749
                    CURLOPT_ENCODING => "",
10750
                    CURLOPT_AUTOREFERER => true,
10751
                    CURLOPT_CONNECTTIMEOUT => 120,
10752
                    CURLOPT_TIMEOUT => 120,
10753
                    CURLOPT_MAXREDIRS => 10,
10754
                );
10755
                curl_setopt_array($ch, $options);
10756
                $response = curl_exec($ch);
10757
                $httpCode = curl_getinfo($ch);
10758
                $headers = substr($response, 0, $httpCode['header_size']);
10759
10760
                $error = false;
10761
                if (stripos($headers, 'X-Frame-Options: DENY') > -1
10762
                    //|| stripos($headers, 'X-Frame-Options: SAMEORIGIN') > -1
10763
                ) {
10764
                    $error = true;
10765
                }
10766
10767
                if ($error) {
10768
                    $_SESSION['x_frame_source'] = $src;
10769
                    $src = 'blank.php?error=x_frames_options';
10770
                }
10771
            }
10772
        }
10773
10774
        return $src;
10775
    }
10776
10777
    /**
10778
     * Check if this LP has a created forum in the basis course
10779
     * @return boolean
10780
     */
10781
    public function lpHasForum()
10782
    {
10783
        $forumTable = Database::get_course_table(TABLE_FORUM);
10784
        $itemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
10785
10786
        $fakeFrom = "
10787
            $forumTable f
10788
            INNER JOIN $itemProperty ip
10789
            ON (f.forum_id = ip.ref AND f.c_id = ip.c_id)
10790
        ";
10791
10792
        $resultData = Database::select(
10793
            'COUNT(f.iid) AS qty',
10794
            $fakeFrom,
10795
            [
10796
                'where' => [
10797
                    'ip.visibility != ? AND ' => 2,
10798
                    'ip.tool = ? AND ' => TOOL_FORUM,
10799
                    'f.c_id = ? AND ' => intval($this->course_int_id),
10800
                    'f.lp_id = ?' => intval($this->lp_id)
10801
                ]
10802
            ],
10803
            'first'
10804
        );
10805
10806
        if ($resultData['qty'] > 0) {
10807
            return true;
10808
        }
10809
10810
        return false;
10811
    }
10812
10813
    /**
10814
     * Get the forum for this learning path
10815
     * @return boolean
10816
     */
10817
    public function getForum($sessionId = 0)
10818
    {
10819
        $forumTable = Database::get_course_table(TABLE_FORUM);
10820
        $itemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
10821
10822
        $fakeFrom = "$forumTable f
10823
            INNER JOIN $itemProperty ip ";
10824
10825
        if ($this->lp_session_id == 0) {
10826
            $fakeFrom .= "
10827
                ON (
10828
                    f.forum_id = ip.ref AND f.c_id = ip.c_id AND (
10829
                        f.session_id = ip.session_id OR ip.session_id IS NULL
10830
                    )
10831
                )
10832
            ";
10833
        } else {
10834
            $fakeFrom .= "
10835
                ON (
10836
                    f.forum_id = ip.ref AND f.c_id = ip.c_id AND f.session_id = ip.session_id
10837
                )
10838
            ";
10839
        }
10840
10841
        $resultData = Database::select(
10842
            'f.*',
10843
            $fakeFrom,
10844
            [
10845
                'where' => [
10846
                    'ip.visibility != ? AND ' => 2,
10847
                    'ip.tool = ? AND ' => TOOL_FORUM,
10848
                    'f.session_id = ? AND ' => $sessionId,
10849
                    'f.c_id = ? AND ' => intval($this->course_int_id),
10850
                    'f.lp_id = ?' => intval($this->lp_id)
10851
                ]
10852
            ],
10853
            'first'
10854
        );
10855
10856
        if (empty($resultData)) {
10857
            return false;
10858
        }
10859
10860
        return $resultData;
10861
    }
10862
10863
    /**
10864
     * Create a forum for this learning path
10865
     * @param type $forumCategoryId
10866
     * @return int The forum ID if was created. Otherwise return false
10867
     */
10868
    public function createForum($forumCategoryId)
10869
    {
10870
        require_once api_get_path(SYS_CODE_PATH) . '/forum/forumfunction.inc.php';
10871
10872
        $forumId = store_forum(
10873
            [
10874
                'lp_id' => $this->lp_id,
10875
                'forum_title' => $this->name,
10876
                'forum_comment' => null,
10877
                'forum_category' => intval($forumCategoryId),
10878
                'students_can_edit_group' => ['students_can_edit' => 0],
10879
                'allow_new_threads_group' => ['allow_new_threads' => 0],
10880
                'default_view_type_group' => ['default_view_type' => 'flat'],
10881
                'group_forum' => 0,
10882
                'public_private_group_forum_group' => ['public_private_group_forum' => 'public']
10883
            ],
10884
            [],
10885
            true
10886
        );
10887
10888
        return $forumId;
10889
    }
10890
10891
    /**
10892
     * Check and obtain the lp final item if exist
10893
     *
10894
     * @return array lp items
10895
     */
10896
    private function getFinalItem()
10897
    {
10898
10899
        if (empty($this->items)) {
10900
            return null;
10901
        }
10902
10903
        foreach ($this->items as $item) {
10904
            if ($item->type !== 'final_item') {
10905
                continue;
10906
            }
10907
10908
            return $item;
10909
        }
10910
    }
10911
10912
    /**
10913
     * Get the LP Final Item Template
10914
     *
10915
     * @return html
10916
     */
10917
    private function getFinalItemTemplate()
10918
    {
10919
        return file_get_contents(api_get_path(SYS_CODE_PATH) . 'newscorm/final_item_template/template.html');
10920
    }
10921
10922
    /**
10923
     * Get the LP Final Item Url
10924
     *
10925
     * @return String
10926
     */
10927
    private function getSavedFinalItem()
10928
    {
10929
        $finalItem = $this->getFinalItem();
10930
        $doc = DocumentManager::get_document_data_by_id($finalItem->path, $this->cc);
10931
10932
        return file_get_contents($doc['absolute_path']);
10933
    }
10934
10935
    /**
10936
     * Get the LP Final Item form
10937
     *
10938
     * @return html
10939
     */
10940
    public function getFinalItemForm()
10941
    {
10942
        $finalItem = $this->getFinalItem();
10943
        $title = '';
10944
        $content = '';
10945
10946
        if ($finalItem) {
10947
            $title = $finalItem->title;
10948
            $buttonText = get_lang('Save');
10949
            $content = $this->getSavedFinalItem();
10950
        } else {
10951
            $buttonText = get_lang('LPCreateDocument');
10952
            $content = $this->getFinalItemTemplate();
10953
        }
10954
10955
        $courseInfo = api_get_course_info();
10956
        $result = $this->generate_lp_folder($courseInfo);
10957
        $relative_path = api_substr($result['dir'], 1, strlen($result['dir']));
10958
        $relative_prefix = '../../';
10959
10960
        $editorConfig = [
10961
            'ToolbarSet' => 'LearningPathDocuments',
10962
            'Width' => '100%',
10963
            'Height' => '500',
10964
            'FullPage' => true,
10965
            'CreateDocumentDir' => $relative_prefix,
10966
            'CreateDocumentWebDir' => api_get_path(WEB_COURSE_PATH) . api_get_course_path() . '/document/',
10967
            'BaseHref' => api_get_path(WEB_COURSE_PATH) . api_get_course_path() . '/document/' . $relative_path
10968
        ];
10969
10970
        $url = api_get_self() . '?' . api_get_cidreq() . '&' . http_build_query([
10971
            'type' => 'document',
10972
            'lp_id' => $this->lp_id
10973
        ]);
10974
10975
        $form = new FormValidator('final_item', 'POST', $url);
10976
        $form->addText('title', get_lang('Title'));
10977
        $form->addButtonSave($buttonText);
10978
        $form->addHtml('<div class="alert alert-info">Variables :</br></br> <b>((certificate))</b> </br> <b>((skill))</b></div>');
10979
        $renderer = $form->defaultRenderer();
10980
        $renderer->setElementTemplate('<div class="editor-lp">&nbsp;{label}{element}</div>', 'content_lp');
10981
        $form->addHtmlEditor('content_lp', null, null, true, $editorConfig, true);
10982
        $form->addHidden('action', 'add_final_item');
10983
        $form->addHidden('path', isset($_SESSION['pathItem']) ? $_SESSION['pathItem'] : '');
10984
        $form->addHidden('previous', $this->get_last());
10985
10986
        $form->setDefaults(['title' => $title, 'content_lp' => $content]);
10987
10988
        if ($form->validate()) {
10989
            $values = $form->exportValues();
10990
10991
            $lastItemId = $this->get_last();
10992
10993
            if (!$finalItem) {
10994
                $documentId = $this->create_document($this->course_info, $values['content_lp'], $values['title']);
10995
                $this->add_item(
10996
                    0,
10997
                    $lastItemId,
10998
                    'final_item',
10999
                    $documentId,
11000
                    $values['title'],
11001
                    ''
11002
                );
11003
            } else {
11004
                $this->edit_document($this->course_info);
11005
            }
11006
        }
11007
11008
        return $form->returnForm();
11009
    }
11010
}
11011
11012
if (!function_exists('trim_value')) {
11013
    function trim_value(& $value) {
11014
        $value = trim($value);
11015
    }
11016
}
11017