Test Setup Failed
Push — master ( 4e700f...c7183e )
by Julito
63:12
created

learnpath::create_document()   F

Complexity

Conditions 30
Paths > 20000

Size

Total Lines 167
Code Lines 114

Duplication

Lines 9
Ratio 5.39 %

Importance

Changes 0
Metric Value
cc 30
eloc 114
nc 429496.7295
nop 6
dl 9
loc 167
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

Please refer to the PHP core documentation on constructors.

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

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

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

An additional type check may prevent trouble.

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

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

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

An additional type check may prevent trouble.

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

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

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

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

could be turned into

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

This is much more concise to read.

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

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

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

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

could be turned into

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

This is much more concise to read.

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

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

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

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

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

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

There are different options of fixing this problem.

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

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

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

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

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

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

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

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

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

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

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

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

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

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

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

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

    return array();
}

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

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

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

There are different options of fixing this problem.

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

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

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

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

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

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

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

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

    return array();
}

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

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

Loading history...
5101
        $em->persist($lp);
5102
        $em->flush();
5103
5104
        if ($this->debug > 2) {
5105
            error_log('New LP - lp updated with new expired_on : '.$this->expired_on, 0);
5106
        }
5107
5108
        return true;
5109
    }
5110
5111
    /**
5112
     * Sets and saves the publicated_on date
5113
     * @param   string  $publicated_on Optional string giving the new author of this learnpath
5114
     * @return   bool    Returns true if author's name is not empty
5115
     */
5116 View Code Duplication
    public function set_publicated_on($publicated_on)
5117
    {
5118
        if ($this->debug > 0) {
5119
            error_log('New LP - In learnpath::set_expired_on()', 0);
5120
        }
5121
5122
        $em = Database::getManager();
5123
        /** @var CLp $lp */
5124
        $lp = $em
5125
            ->getRepository('ChamiloCourseBundle:CLp')
5126
            ->findOneBy(
5127
                [
5128
                    'id' => $this->get_id(),
5129
                    'cId' => api_get_course_int_id()
5130
                ]
5131
            );
5132
5133
        if (!$lp) {
5134
            return false;
5135
        }
5136
5137
        $this->publicated_on = !empty($publicated_on) ? api_get_utc_datetime($publicated_on, false, true) : null;
0 ignored issues
show
Documentation Bug introduced by
It seems like !empty($publicated_on) ?...on, false, true) : null can also be of type object<DateTime>. However, the property $publicated_on is declared as type string. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

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

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

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

    return array();
}

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

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

Loading history...
5139
        $em->persist($lp);
5140
        $em->flush();
5141
5142
        if ($this->debug > 2) {
5143
            error_log('New LP - lp updated with new publicated_on : '.$this->publicated_on, 0);
5144
        }
5145
5146
        return true;
5147
    }
5148
5149
    /**
5150
     * Sets and saves the expired_on date
5151
     * @return   bool    Returns true if author's name is not empty
5152
     */
5153 View Code Duplication
    public function set_modified_on()
5154
    {
5155
        $course_id = api_get_course_int_id();
5156
        if ($this->debug > 0) {
5157
            error_log('New LP - In learnpath::set_expired_on()', 0);
5158
        }
5159
        $this->modified_on = api_get_utc_datetime();
0 ignored issues
show
Documentation Bug introduced by
It seems like api_get_utc_datetime() can also be of type object<DateTime>. However, the property $modified_on is declared as type string. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

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

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

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

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

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

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

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

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

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

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

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

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

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
12054
        $value = trim($value);
12055
    }
12056
}
12057