Completed
Push — 1.10.x ( d467f0...2137a4 )
by Julito
164:57 queued 122:53
created

learnpath::get_forums()   C

Complexity

Conditions 11
Paths 40

Size

Total Lines 99
Code Lines 63

Duplication

Lines 13
Ratio 13.13 %

Importance

Changes 0
Metric Value
cc 11
eloc 63
nc 40
nop 0
dl 13
loc 99
rs 5.2653
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 Chamilo\CourseBundle\Entity\CLpCategory;
5
use ChamiloSession as Session;
6
7
/**
8
 * Class learnpath
9
 * This class defines the parent attributes and methods for Chamilo learnpaths
10
 * and SCORM learnpaths. It is used by the scorm class.
11
 *
12
 * @package chamilo.learnpath
13
 * @author	Yannick Warnier <[email protected]>
14
 * @author	Julio Montoya   <[email protected]> Several improvements and fixes
15
 */
16
class learnpath
17
{
18
    public $attempt = 0; // The number for the current ID view.
19
    public $cc; // Course (code) this learnpath is located in. @todo change name for something more comprensible ...
20
    public $current; // Id of the current item the user is viewing.
21
    public $current_score; // The score of the current item.
22
    public $current_time_start; // The time the user loaded this resource (this does not mean he can see it yet).
23
    public $current_time_stop; // The time the user closed this resource.
24
    public $default_status = 'not attempted';
25
    public $encoding = 'UTF-8';
26
    public $error = '';
27
    public $extra_information = ''; // This string can be used by proprietary SCORM contents to store data about the current learnpath.
28
    public $force_commit = false; // For SCORM only - if set to true, will send a scorm LMSCommit() request on each LMSSetValue().
29
    public $index; // The index of the active learnpath_item in $ordered_items array.
30
    public $items = array();
31
    public $last; // item_id of last item viewed in the learning path.
32
    public $last_item_seen = 0; // In case we have already come in this learnpath, reuse the last item seen if authorized.
33
    public $license; // Which license this course has been given - not used yet on 20060522.
34
    public $lp_id; // DB ID for this learnpath.
35
    public $lp_view_id; // DB ID for lp_view
36
    public $log_file; // File where to log learnpath API msg.
37
    public $maker; // Which maker has conceived the content (ENI, Articulate, ...).
38
    public $message = '';
39
    public $mode = 'embedded'; // Holds the video display mode (fullscreen or embedded).
40
    public $name; // Learnpath name (they generally have one).
41
    public $ordered_items = array(); // List of the learnpath items in the order they are to be read.
42
    public $path = ''; // Path inside the scorm directory (if scorm).
43
    public $theme; // The current theme of the learning path.
44
    public $preview_image; // The current image of the learning path.
45
46
    // Tells if all the items of the learnpath can be tried again. Defaults to "no" (=1).
47
    public $prevent_reinit = 1;
48
49
    // Describes the mode of progress bar display.
50
    public $seriousgame_mode = 0;
51
    public $progress_bar_mode = '%';
52
53
    // Percentage progress as saved in the db.
54
    public $progress_db = '0';
55
    public $proximity; // Wether the content is distant or local or unknown.
56
    public $refs_list = array (); //list of items by ref => db_id. Used only for prerequisites match.
57
    // !!!This array (refs_list) is built differently depending on the nature of the LP.
58
    // If SCORM, uses ref, if Chamilo, uses id to keep a unique value.
59
    public $type; //type of learnpath. Could be 'dokeos', 'scorm', 'scorm2004', 'aicc', ...
60
    // TODO: Check if this type variable is useful here (instead of just in the controller script).
61
    public $user_id; //ID of the user that is viewing/using the course
62
    public $update_queue = array();
63
    public $scorm_debug = 0;
64
    public $arrMenu = array(); // Array for the menu items.
65
    public $debug = 0; // Logging level.
66
    public $lp_session_id = 0;
67
    public $lp_view_session_id = 0; // The specific view might be bound to a session.
68
    public $prerequisite = 0;
69
    public $use_max_score = 1; // 1 or 0
70
    public $subscribeUsers = 0; // Subscribe users or not
71
    public $created_on      = '';
72
    public $modified_on     = '';
73
    public $publicated_on   = '';
74
    public $expired_on      = '';
75
    public $ref = null;
76
    public $course_int_id;
77
    public $course_info = array();
78
    public $categoryId;
79
80
    /**
81
     * Constructor.
82
     * Needs a database handler, a course code and a learnpath id from the database.
83
     * Also builds the list of items into $this->items.
84
     * @param	string	$course Course code
85
     * @param	integer	$lp_id
86
     * @param	integer	$user_id
87
     * @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...
88
     */
89
    public function __construct($course, $lp_id, $user_id)
90
    {
91
        $this->encoding = api_get_system_encoding();
92 View Code Duplication
        if ($this->debug > 0) {
93
            error_log('New LP - In learnpath::__construct('.$course.','.$lp_id.','.$user_id.')', 0);
94
        }
95
        if (empty($course)) {
96
            $this->error = 'Course code is empty';
97
            return false;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
98
        } else {
99
            $course_info = api_get_course_info($course);
100
            if (!empty($course_info)) {
101
                $this->cc = $course_info['code'];
102
                $this->course_info = $course_info;
103
                $course_id = $course_info['real_id'];
104
            } else {
105
                $this->error = 'Course code does not exist in database.';
106
                return false;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
107
            }
108
        }
109
110
        $this->set_course_int_id($course_id);
111
112
        // Check learnpath ID.
113
        if (empty($lp_id)) {
114
            $this->error = 'Learnpath ID is empty';
115
            return false;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
116
        } else {
117
            // TODO: Make it flexible to use any course_code (still using env course code here).
118
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
119
            $lp_id = intval($lp_id);
120
            $sql = "SELECT * FROM $lp_table
121
                    WHERE id = '$lp_id' AND c_id = $course_id";
122
            if ($this->debug > 2) {
123
                error_log('New LP - learnpath::__construct() '.__LINE__.' - Querying lp: '.$sql, 0);
124
            }
125
            $res = Database::query($sql);
126
127
            if (Database::num_rows($res) > 0) {
128
                $this->lp_id = $lp_id;
129
                $row = Database::fetch_array($res);
130
                $this->type = $row['lp_type'];
131
                $this->name = stripslashes($row['name']);
132
                $this->proximity = $row['content_local'];
133
                $this->theme = $row['theme'];
134
                $this->maker = $row['content_maker'];
135
                $this->prevent_reinit = $row['prevent_reinit'];
136
                $this->seriousgame_mode = $row['seriousgame_mode'];
137
                $this->license = $row['content_license'];
138
                $this->scorm_debug = $row['debug'];
139
                $this->js_lib = $row['js_lib'];
140
                $this->path = $row['path'];
141
                $this->preview_image = $row['preview_image'];
142
                $this->author = $row['author'];
143
                $this->hide_toc_frame = $row['hide_toc_frame'];
144
                $this->lp_session_id = $row['session_id'];
145
                $this->use_max_score = $row['use_max_score'];
146
                $this->subscribeUsers = $row['subscribe_users'];
147
                $this->created_on = $row['created_on'];
148
                $this->modified_on = $row['modified_on'];
149
                $this->ref = $row['ref'];
150
                $this->categoryId = $row['category_id'];
151
152
                if ($row['publicated_on'] != '0000-00-00 00:00:00') {
153
                    $this->publicated_on = $row['publicated_on'];
154
                }
155
156
                if ($row['expired_on'] != '0000-00-00 00:00:00') {
157
                    $this->expired_on  = $row['expired_on'];
158
                }
159 View Code Duplication
                if ($this->type == 2) {
160
                    if ($row['force_commit'] == 1) {
161
                        $this->force_commit = true;
162
                    }
163
                }
164
                $this->mode = $row['default_view_mod'];
165
            } else {
166
                $this->error = 'Learnpath ID does not exist in database ('.$sql.')';
167
168
                return false;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
169
            }
170
        }
171
172
        // Check user ID.
173
        if (empty($user_id)) {
174
            $this->error = 'User ID is empty';
175
176
            return false;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
177
        } else {
178
            $user_info = api_get_user_info($user_id);
179
            if (!empty($user_info)) {
180
                $this->user_id = $user_info['user_id'];
181
            } else {
182
                $this->error = 'User ID does not exist in database ('.$sql.')';
183
184
                return false;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
185
            }
186
        }
187
188
        // End of variables checking.
189
        $session_id = api_get_session_id();
190
        //  Get the session condition for learning paths of the base + session.
191
        $session = api_get_session_condition($session_id);
192
        // Now get the latest attempt from this user on this LP, if available, otherwise create a new one.
193
        $lp_table = Database::get_course_table(TABLE_LP_VIEW);
194
195
        // Selecting by view_count descending allows to get the highest view_count first.
196
        $sql = "SELECT * FROM $lp_table
197
                WHERE c_id = $course_id AND lp_id = '$lp_id' AND user_id = '$user_id' $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
        } else if (!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
            Database::insert($lp_table, $params);
228
            $this->lp_view_id = Database::insert_id();
229
230
            if ($this->debug > 2) {
231
                error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - inserting new lp_view: ' . $sql, 0);
232
            }
233
234
            $sql = "UPDATE $lp_table SET id = iid WHERE iid = ".$this->lp_view_id;
235
            Database::query($sql);
236
        }
237
238
        // Initialise items.
239
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
240
        $sql = "SELECT * FROM $lp_item_table
241
                WHERE c_id = $course_id AND lp_id = '".$this->lp_id."'
242
                ORDER BY parent_item_id, display_order";
243
        $res = Database::query($sql);
244
245
        if ($this->debug > 2) {
246
            error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - query lp items: ' . $sql, 0);
247
            error_log('-- Start while--', 0);
248
        }
249
250
        $lp_item_id_list = array();
251
252
        while ($row = Database::fetch_array($res)) {
253
            $lp_item_id_list[] = $row['id'];
254
            switch ($this->type) {
255 View Code Duplication
                case 3: //aicc
256
                    $oItem = new aiccItem('db', $row['id'], $course_id);
257
                    if (is_object($oItem)) {
258
                        $my_item_id = $oItem->get_id();
259
                        $oItem->set_lp_view($this->lp_view_id, $course_id);
260
                        $oItem->set_prevent_reinit($this->prevent_reinit);
261
                        // Don't use reference here as the next loop will make the pointed object change.
262
                        $this->items[$my_item_id] = $oItem;
263
                        $this->refs_list[$oItem->ref] = $my_item_id;
264
                        if ($this->debug > 2) {
265
                            error_log(
266
                                'New LP - learnpath::__construct() - ' .
267
                                'aicc object with id ' . $my_item_id .
268
                                ' set in items[]',
269
                                0
270
                            );
271
                        }
272
                    }
273
                    break;
274 View Code Duplication
                case 2:
275
                    $oItem = new scormItem('db', $row['id'], $course_id);
276
                    if (is_object($oItem)) {
277
                        $my_item_id = $oItem->get_id();
278
                        $oItem->set_lp_view($this->lp_view_id, $course_id);
279
                        $oItem->set_prevent_reinit($this->prevent_reinit);
280
                        // Don't use reference here as the next loop will make the pointed object change.
281
282
                        $this->items[$my_item_id] = $oItem;
283
284
                        $this->refs_list[$oItem->ref] = $my_item_id;
285
                        if ($this->debug > 2) {
286
                            error_log('New LP - object with id ' . $my_item_id . ' set in items[]', 0);
287
                        }
288
                    }
289
                    break;
290
                case 1:
291
                default:
292
                    if ($this->debug > 2) {
293
                        error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - calling learnpathItem', 0);
294
                    }
295
                    $oItem = new learnpathItem($row['id'], $user_id, $course_id, $row);
296
297
                    if ($this->debug > 2) {
298
                        error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - end calling learnpathItem', 0);
299
                    }
300
                    if (is_object($oItem)) {
301
                        $my_item_id = $oItem->get_id();
302
                        //$oItem->set_lp_view($this->lp_view_id); // Moved down to when we are sure the item_view exists.
303
                        $oItem->set_prevent_reinit($this->prevent_reinit);
304
                        // Don't use reference here as the next loop will make the pointed object change.
305
                        $this->items[$my_item_id] = $oItem;
306
                        $this->refs_list[$my_item_id] = $my_item_id;
307 View Code Duplication
                        if ($this->debug > 2) {
308
                            error_log(
309
                                'New LP - learnpath::__construct() ' . __LINE__ .
310
                                ' - object with id ' . $my_item_id . ' set in items[]',
311
                                0);
312
                        }
313
                    }
314
                    break;
315
            }
316
317
            // Setting the object level with variable $this->items[$i][parent]
318
            foreach ($this->items as $itemLPObject) {
319
                $level = learnpath::get_level_for_item($this->items, $itemLPObject->db_id);
320
                $itemLPObject->level = $level;
321
            }
322
323
            // Setting the view in the item object.
324
            if (is_object($this->items[$row['id']])) {
325
                $this->items[$row['id']]->set_lp_view($this->lp_view_id, $course_id);
326
                if ($this->items[$row['id']]->get_type() == TOOL_HOTPOTATOES) {
327
                    $this->items[$row['id']]->current_start_time = 0;
328
                    $this->items[$row['id']]->current_stop_time	= 0;
329
                }
330
            }
331
        }
332
333
        if ($this->debug > 2) {
334
            error_log('New LP - learnpath::__construct() ' . __LINE__ . ' ----- end while ----', 0);
335
        }
336
337
        if (!empty($lp_item_id_list)) {
338
            $lp_item_id_list_to_string = implode("','", $lp_item_id_list);
339
            if (!empty($lp_item_id_list_to_string)) {
340
                // Get last viewing vars.
341
                $lp_item_view_table = Database:: get_course_table(
342
                    TABLE_LP_ITEM_VIEW
343
                );
344
                // This query should only return one or zero result.
345
                $sql = "SELECT lp_item_id, status
346
                        FROM $lp_item_view_table
347
                        WHERE
348
                            c_id = $course_id AND
349
                            lp_view_id = ".$this->lp_view_id." AND
350
                            lp_item_id IN ('".$lp_item_id_list_to_string."')
351
                        ORDER BY view_count DESC ";
352
353
                if ($this->debug > 2) {
354
                    error_log(
355
                        'New LP - learnpath::__construct() - Selecting item_views: '.$sql,
356
                        0
357
                    );
358
                }
359
360
                $status_list = array();
361
                $res = Database::query($sql);
362
                while ($row = Database:: fetch_array($res)) {
363
                    $status_list[$row['lp_item_id']] = $row['status'];
364
                }
365
366
                foreach ($lp_item_id_list as $item_id) {
367
                    if (isset($status_list[$item_id])) {
368
                        $status = $status_list[$item_id];
369
                        if (is_object($this->items[$item_id])) {
370
                            $this->items[$item_id]->set_status($status);
371
                            if (empty($status)) {
372
                                $this->items[$item_id]->set_status(
373
                                    $this->default_status
374
                                );
375
                            }
376
                        }
377
                    } else {
378
                        if (!api_is_invitee()) {
379
                            if (is_object($this->items[$item_id])) {
380
                                $this->items[$item_id]->set_status(
381
                                    $this->default_status
382
                                );
383
                            }
384
385
                            // Add that row to the lp_item_view table so that we have something to show in the stats page.
386
                            $sql = "INSERT INTO $lp_item_view_table (c_id, lp_item_id, lp_view_id, view_count, status, start_time, total_time, score)
387
                                    VALUES ($course_id, ".$item_id.",".$this->lp_view_id.", 1, 'not attempted', '".time()."', 0, 0)";
388
389
                            if ($this->debug > 2) {
390
                                error_log(
391
                                    'New LP - learnpath::__construct() '.__LINE__.' - Inserting blank item_view : '.$sql,
392
                                    0
393
                                );
394
                            }
395
                            $this->items[$item_id]->set_lp_view(
396
                                $this->lp_view_id,
397
                                $course_id
398
                            );
399
400
                            Database::query($sql);
401
                            $insertId = Database::insert_id();
402
403
                            $sql = "UPDATE $lp_item_view_table SET id = iid
404
                                    WHERE iid = $insertId";
405
                            Database::query($sql);
406
                        }
407
                    }
408
                }
409
            }
410
        }
411
412
        $this->ordered_items = self::get_flat_ordered_items_list(
0 ignored issues
show
Documentation Bug introduced by
It seems like self::get_flat_ordered_i...et_id(), 0, $course_id) can also be of type false. However, the property $ordered_items is declared as type array. 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...
413
            $this->get_id(),
414
            0,
415
            $course_id
416
        );
417
        $this->max_ordered_items = 0;
418
        foreach ($this->ordered_items as $index => $dummy) {
0 ignored issues
show
Bug introduced by
The expression $this->ordered_items of type false|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...
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
        return true;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
429
    }
430
431
    /**
432
     * @return string
433
     */
434
    public function getCourseCode()
435
    {
436
        return $this->cc;
437
    }
438
439
    /**
440
     * @return int
441
     */
442
    public function get_course_int_id()
443
    {
444
        return isset($this->course_int_id) ? $this->course_int_id : api_get_course_int_id();
445
    }
446
447
    /**
448
     * @param $course_id
449
     * @return int
450
     */
451
    public function set_course_int_id($course_id)
452
    {
453
        return $this->course_int_id = intval($course_id);
454
    }
455
456
    /**
457
     * Get the depth level of LP item
458
     * @param $in_tab_items
459
     * @param $in_current_item_id
460
     * @return int
461
     */
462
    private static function get_level_for_item($in_tab_items, $in_current_item_id)
463
    {
464
        $parent_item_id = $in_tab_items[$in_current_item_id]->parent;
465
        if ($parent_item_id == 0) {
466
            return 0;
467
        } else {
468
            return learnpath::get_level_for_item($in_tab_items, $parent_item_id) + 1;
469
        }
470
    }
471
472
    /**
473
     * Function rewritten based on old_add_item() from Yannick Warnier.
474
     * Due the fact that users can decide where the item should come, I had to overlook this function and
475
     * I found it better to rewrite it. Old function is still available.
476
     * Added also the possibility to add a description.
477
     *
478
     * @param int $parent
479
     * @param int $previous
480
     * @param string $type
481
     * @param int  resource ID (ref)
482
     * @param string $title
483
     * @param string $description
484
     * @param int $prerequisites
485
     * @param int $max_time_allowed
486
     * @param int $userId
487
     *
488
     * @return int
489
     */
490
    public function add_item(
491
        $parent,
492
        $previous,
493
        $type = 'dokeos_chapter',
494
        $id,
495
        $title,
496
        $description,
497
        $prerequisites = 0,
498
        $max_time_allowed = 0,
499
        $userId = 0
500
    ) {
501
        $course_id = $this->course_info['real_id'];
502
        if ($this->debug > 0) {
503
            error_log('New LP - In learnpath::add_item(' . $parent . ',' . $previous . ',' . $type . ',' . $id . ',' . $title . ')', 0);
504
        }
505
        if (empty($course_id)) {
506
            // Sometimes Oogie doesn't catch the course info but sets $this->cc
507
            $this->course_info = api_get_course_info($this->cc);
508
            $course_id = $this->course_info['real_id'];
509
        }
510
        $userId = empty($userId) ? api_get_user_id() : $userId;
511
        $sessionId = api_get_session_id();
512
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
513
        $_course = $this->course_info;
514
        $parent = intval($parent);
515
        $previous = intval($previous);
516
        $id = intval($id);
517
        $max_time_allowed = htmlentities($max_time_allowed);
518
        if (empty($max_time_allowed)) {
519
            $max_time_allowed = 0;
520
        }
521
        $sql = "SELECT COUNT(id) AS num
522
                FROM $tbl_lp_item
523
                WHERE
524
                    c_id = $course_id AND
525
                    lp_id = " . $this->get_id() . " AND
526
                    parent_item_id = " . $parent;
527
528
        $res_count = Database::query($sql);
529
        $row = Database :: fetch_array($res_count);
530
        $num = $row['num'];
531
532
        if ($num > 0) {
533
            if ($previous == 0) {
534
                $sql = "SELECT id, next_item_id, display_order
535
                        FROM " . $tbl_lp_item . "
536
                        WHERE
537
                            c_id = $course_id AND
538
                            lp_id = " . $this->get_id() . " AND
539
                            parent_item_id = " . $parent . " AND
540
                            previous_item_id = 0 OR
541
                            previous_item_id=" . $parent;
542
                $result = Database::query($sql);
543
                $row = Database :: fetch_array($result);
544
545
                $tmp_previous = 0;
546
                $next = $row['id'];
547
                $display_order = 0;
548
            } else {
549
                $previous = (int) $previous;
550
                $sql = "SELECT id, previous_item_id, next_item_id, display_order
551
						FROM $tbl_lp_item
552
                        WHERE
553
                            c_id = $course_id AND
554
                            lp_id = " . $this->get_id() . " AND
555
                            id = " . $previous;
556
557
                $result = Database::query($sql);
558
                $row = Database:: fetch_array($result);
559
560
                $tmp_previous = $row['id'];
561
                $next = $row['next_item_id'];
562
                $display_order = $row['display_order'];
563
            }
564
        } else {
565
            $tmp_previous = 0;
566
            $next = 0;
567
            $display_order = 0;
568
        }
569
570
        $id = intval($id);
571
        $typeCleaned = Database::escape_string($type);
572
        if ($type == 'quiz') {
573
            $sql = 'SELECT SUM(ponderation)
574
                    FROM ' . Database :: get_course_table(TABLE_QUIZ_QUESTION) . ' as quiz_question
575
                    INNER JOIN  ' . Database :: get_course_table(TABLE_QUIZ_TEST_QUESTION) . ' as quiz_rel_question
576
                    ON
577
                        quiz_question.id = quiz_rel_question.question_id AND
578
                        quiz_question.c_id = quiz_rel_question.c_id
579
                    WHERE
580
                        quiz_rel_question.exercice_id = '.$id." AND
581
                        quiz_question.c_id = $course_id AND
582
                        quiz_rel_question.c_id = $course_id ";
583
            $rsQuiz = Database::query($sql);
584
            $max_score = Database :: result($rsQuiz, 0, 0);
585
586
            // Disabling the exercise if we add it inside a LP
587
            $exercise = new Exercise($course_id);
588
            $exercise->read($id);
589
            $exercise->disable();
590
            $exercise->save();
591
        } else {
592
            $max_score = 100;
593
        }
594
595
        $params = array(
596
            "c_id" => $course_id,
597
            "lp_id" => $this->get_id(),
598
            "item_type" => $typeCleaned,
599
            "ref" => '',
600
            "title" => $title,
601
            "description" => $description,
602
            "path" => $id,
603
            "max_score" => $max_score,
604
            "parent_item_id" => $parent,
605
            "previous_item_id" => $previous,
606
            "next_item_id" => intval($next),
607
            "display_order" => $display_order + 1,
608
            "prerequisite" => $prerequisites,
609
            "max_time_allowed" => $max_time_allowed,
610
            'min_score' => 0,
611
            'launch_data' => '',
612
        );
613
614
        if ($prerequisites != 0) {
615
            $params['prerequisite'] = $prerequisites;
616
        }
617
618
        $new_item_id = Database::insert($tbl_lp_item, $params);
619
620
        if ($this->debug > 2) {
621
            error_log('New LP - Inserting chapter: ' . $new_item_id, 0);
622
        }
623
624
        if ($new_item_id) {
625
626
            $sql = "UPDATE $tbl_lp_item SET id = iid WHERE iid = $new_item_id";
627
            Database::query($sql);
628
629
            // Update the item that should come after the new item.
630
            $sql = " UPDATE $tbl_lp_item SET
631
                        previous_item_id =  $new_item_id,
632
                        next_item_id = $new_item_id,
633
                        id = $new_item_id
634
                     WHERE iid = $new_item_id";
635
            Database::query($sql);
636
637
            // Update all the items after the new item.
638
            $sql = "UPDATE " . $tbl_lp_item . "
639
                        SET display_order = display_order + 1
640
                    WHERE
641
                        c_id = $course_id AND
642
                        lp_id = " . $this->get_id() . " AND
643
                        id <> " . $new_item_id . " AND
644
                        parent_item_id = " . $parent . " AND
645
                        display_order > " . $display_order;
646
            Database::query($sql);
647
648
            // Update the item that should come after the new item.
649
            $sql = "UPDATE " . $tbl_lp_item . "
650
                    SET ref = " . $new_item_id . "
651
                    WHERE c_id = $course_id AND id = " . $new_item_id;
652
            Database::query($sql);
653
654
            // Upload audio.
655
            if (!empty($_FILES['mp3']['name'])) {
656
                // Create the audio folder if it does not exist yet.
657
                $filepath = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document/';
658
                if (!is_dir($filepath . 'audio')) {
659
                    mkdir($filepath . 'audio', api_get_permissions_for_new_directories());
660
                    $audio_id = add_document(
661
                        $_course,
662
                        '/audio',
663
                        'folder',
664
                        0,
665
                        'audio',
666
                        '',
667
                        0,
668
                        true,
669
                        null,
670
                        $sessionId,
671
                        $userId
672
                    );
673
                    api_item_property_update(
674
                        $_course,
675
                        TOOL_DOCUMENT,
676
                        $audio_id,
677
                        'FolderCreated',
678
                        $userId,
679
                        null,
680
                        null,
681
                        null,
682
                        null,
683
                        $sessionId
684
                    );
685
                    api_item_property_update(
686
                        $_course,
687
                        TOOL_DOCUMENT,
688
                        $audio_id,
689
                        'invisible',
690
                        $userId,
691
                        null,
692
                        null,
693
                        null,
694
                        null,
695
                        $sessionId
696
                    );
697
                }
698
699
                $file_path = handle_uploaded_document(
700
                    $_course,
701
                    $_FILES['mp3'],
702
                    api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document',
703
                    '/audio',
704
                    $userId,
705
                    '',
706
                    '',
707
                    '',
708
                    '',
709
                    false
710
                );
711
712
                // Getting the filename only.
713
                $file_components = explode('/', $file_path);
714
                $file = $file_components[count($file_components) - 1];
715
716
                // Store the mp3 file in the lp_item table.
717
                $sql = "UPDATE $tbl_lp_item SET
718
                            audio = '" . Database::escape_string($file) . "'
719
                        WHERE id = '" . intval($new_item_id) . "'";
720
                Database::query($sql);
721
            }
722
        }
723
724
        return $new_item_id;
725
    }
726
727
    /**
728
     * Static admin function allowing addition of a learnpath to a course.
729
     * @param	string	Course code
730
     * @param	string	Learnpath name
731
     * @param	string	Learnpath description string, if provided
732
     * @param	string	Type of learnpath (default = 'guess', others = 'dokeos', 'aicc',...)
733
     * @param	string	Type of files origin (default = 'zip', others = 'dir','web_dir',...)
734
     * @param	string	Zip file containing the learnpath or directory containing the learnpath
735
     * @return	integer	The new learnpath ID on success, 0 on failure
736
     */
737
    public static function add_lp(
738
        $courseCode,
739
        $name,
740
        $description = '',
741
        $learnpath = 'guess',
742
        $origin = 'zip',
743
        $zipname = '',
744
        $publicated_on = '',
745
        $expired_on = '',
746
        $categoryId = 0,
747
        $userId = 0
748
    ) {
749
        global $charset;
750
751
        if (!empty($courseCode)) {
752
            $courseInfo = api_get_course_info($courseCode);
753
            $course_id = $courseInfo['real_id'];
754
        } else {
755
            $course_id = api_get_course_int_id();
756
            $courseInfo = api_get_course_info();
757
        }
758
759
        $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
760
        // Check course code exists.
761
        // Check lp_name doesn't exist, otherwise append something.
762
        $i = 0;
763
        $name = Database::escape_string($name);
764
        $categoryId = intval($categoryId);
765
766
        // Session id.
767
        $session_id = api_get_session_id();
768
769
        $userId = empty($userId) ? api_get_user_id() : $userId;
770
771
        $check_name = "SELECT * FROM $tbl_lp
772
                       WHERE c_id = $course_id AND name = '$name'";
773
774
        $res_name = Database::query($check_name);
775
776
        if ($publicated_on == '0000-00-00 00:00:00' || empty($publicated_on)) {
777
            //by default the publication date is the same that the creation date
778
            //The behaviour above was changed due BT#2800
779
            global $_custom;
780
            if (isset($_custom['lps_hidden_when_no_start_date']) && $_custom['lps_hidden_when_no_start_date']) {
781
                $publicated_on = '';
782
            } else {
783
                $publicated_on = api_get_utc_datetime();
784
            }
785
        } else {
786
            $publicated_on = Database::escape_string(api_get_utc_datetime($publicated_on));
787
        }
788
789
        if ($expired_on == '0000-00-00 00:00:00' || empty($expired_on)) {
790
            $expired_on = '';
791
        } else {
792
            $expired_on = Database::escape_string(api_get_utc_datetime($expired_on));
793
        }
794
795
        while (Database :: num_rows($res_name)) {
0 ignored issues
show
Bug introduced by
It seems like $res_name can be null; however, num_rows() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
796
            // There is already one such name, update the current one a bit.
797
            $i++;
798
            $name = $name . ' - ' . $i;
799
            $check_name = "SELECT * FROM $tbl_lp WHERE c_id = $course_id AND name = '$name'";
800
            $res_name = Database::query($check_name);
801
        }
802
        // New name does not exist yet; keep it.
803
        // Escape description.
804
        // Kevin: added htmlentities().
805
        $description = Database::escape_string(api_htmlentities($description, ENT_QUOTES, $charset));
806
        $type = 1;
807
        switch ($learnpath) {
808
            case 'guess':
809
                break;
810
            case 'dokeos':
811
            case 'chamilo':
812
                $type = 1;
813
                break;
814
            case 'aicc':
815
                break;
816
        }
817
818
        switch ($origin) {
819
            case 'zip':
820
                // Check zip name string. If empty, we are currently creating a new Chamilo learnpath.
821
                break;
822
            case 'manual':
823
            default:
824
                $get_max = "SELECT MAX(display_order) FROM $tbl_lp WHERE c_id = $course_id";
825
                $res_max = Database::query($get_max);
826
                if (Database :: num_rows($res_max) < 1) {
827
                    $dsp = 1;
828
                } else {
829
                    $row = Database :: fetch_array($res_max);
830
                    $dsp = $row[0] + 1;
831
                }
832
833
                $params = [
834
                    'c_id' => $course_id,
835
                    'lp_type' => $type,
836
                    'name' => $name,
837
                    'description' => $description,
838
                    'path' => '',
839
                    'default_view_mod' => 'embedded',
840
                    'default_encoding' => 'UTF-8',
841
                    'display_order' => $dsp,
842
                    'content_maker' => 'Chamilo',
843
                    'content_local' => 'local',
844
                    'js_lib' => '',
845
                    'session_id' => $session_id,
846
                    'created_on' => api_get_utc_datetime(),
847
                    'modified_on'  => api_get_utc_datetime(),
848
                    'publicated_on' => $publicated_on,
849
                    'expired_on' => $expired_on,
850
                    'category_id' => $categoryId,
851
                    'force_commit' => 0,
852
                    'content_license' => '',
853
                    'debug' => 0,
854
                    'theme' => '',
855
                    'preview_image' => '',
856
                    'author' => '',
857
                    'prerequisite' => 0,
858
                    'hide_toc_frame' => 0,
859
                    'seriousgame_mode' => 0,
860
                    'autolaunch' => 0,
861
                    'max_attempts' => 0,
862
                    'subscribe_users' => 0
863
                ];
864
865
                $id = Database::insert($tbl_lp, $params);
866
867 View Code Duplication
                if ($id > 0) {
868
                    $sql = "UPDATE $tbl_lp SET id = iid WHERE iid = $id";
869
                    Database::query($sql);
870
871
                    // Insert into item_property.
872
                    api_item_property_update(
873
                        $courseInfo,
874
                        TOOL_LEARNPATH,
875
                        $id,
876
                        'LearnpathAdded',
877
                        $userId
878
                    );
879
                    api_set_default_visibility($id, TOOL_LEARNPATH, 0, $courseInfo, $session_id, $userId);
880
                    return $id;
881
                }
882
                break;
883
        }
884
    }
885
886
    /**
887
     * Auto completes the parents of an item in case it's been completed or passed
888
     * @param	integer	$item Optional ID of the item from which to look for parents
889
     */
890
    public function autocomplete_parents($item)
891
    {
892
        $debug = $this->debug;
893
894
        if ($debug) {
895
            error_log('Learnpath::autocomplete_parents()', 0);
896
        }
897
898
        if (empty($item)) {
899
            $item = $this->current;
900
        }
901
902
        $currentItem = $this->getItem($item);
903
        if ($currentItem) {
904
            $parent_id = $currentItem->get_parent();
905
            $parent = $this->getItem($parent_id);
906
            if ($parent) {
907
                // if $item points to an object and there is a parent.
908
                if ($debug) {
909
                    error_log(
910
                        'Autocompleting parent of item ' . $item . ' "'.$currentItem->get_title().'" (item ' . $parent_id . ' "'.$parent->get_title().'") ',
911
                        0
912
                    );
913
                }
914
915
                // New experiment including failed and browsed in completed status.
916
                //$current_status = $currentItem->get_status();
917
                //if ($currentItem->is_done() || $current_status == 'browsed' || $current_status == 'failed') {
918
                // Fixes chapter auto complete
919
                if (true) {
0 ignored issues
show
Bug introduced by
Avoid IF statements that are always true or false
Loading history...
920
                    // If the current item is completed or passes or succeeded.
921
                    $updateParentStatus = true;
922
                    if ($debug) {
923
                        error_log('Status of current item is alright', 0);
924
                    }
925
926
                    foreach ($parent->get_children() as $childItemId) {
927
                        $childItem = $this->getItem($childItemId);
928
929
                        // If children was not set try to get the info
930
                        if (empty($childItem->db_item_view_id)) {
931
                            $childItem->set_lp_view($this->lp_view_id, $this->course_int_id);
932
                        }
933
934
                        // Check all his brothers (parent's children) for completion status.
935
                        if ($childItemId != $item) {
936
                            if ($debug) {
937
                                error_log(
938
                                    'Looking at brother #'.$childItemId . ' "' . $childItem->get_title() . '", status is ' . $childItem->get_status(),
939
                                    0
940
                                );
941
                            }
942
                            // Trying completing parents of failed and browsed items as well.
943
                            if ($childItem->status_is(
944
                                array(
945
                                    'completed',
946
                                    'passed',
947
                                    'succeeded',
948
                                    'browsed',
949
                                    'failed'
950
                                )
951
                            )
952
                            ) {
953
                                // Keep completion status to true.
954
                                continue;
955
                            } else {
956
                                if ($debug > 2) {
957
                                    error_log(
958
                                        '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,
959
                                        0
960
                                    );
961
                                }
962
                                $updateParentStatus = false;
963
                                break;
964
                            }
965
                        }
966
                    }
967
968
                    if ($updateParentStatus) {
969
                        // If all the children were completed:
970
                        $parent->set_status('completed');
971
                        $parent->save(false, $this->prerequisites_match($parent->get_id()));
972
                        // Force the status to "completed"
973
                        //$this->update_queue[$parent->get_id()] = $parent->get_status();
974
                        $this->update_queue[$parent->get_id()] = 'completed';
975
                        if ($debug) {
976
                            error_log(
977
                                'Added parent #'.$parent->get_id().' "'.$parent->get_title().'" to update queue status: completed '.
978
                                print_r($this->update_queue, 1),
979
                                0
980
                            );
981
                        }
982
                        // Recursive call.
983
                        $this->autocomplete_parents($parent->get_id());
984
                    }
985
                }
986
            } else {
987
                if ($debug) {
988
                    error_log("Parent #$parent_id does not exists");
989
                }
990
            }
991
        } else {
992
            if ($debug) {
993
                error_log("#$item is an item that doesn't have parents");
994
            }
995
        }
996
    }
997
998
    /**
999
     * Auto saves the current results into the database for the whole learnpath
1000
     */
1001
    public function autosave()
1002
    {
1003
        if ($this->debug > 0) {
1004
            error_log('New LP - In learnpath::autosave()', 0);
1005
        }
1006
        // TODO: Add save operations for the learnpath itself.
1007
    }
1008
1009
    /**
1010
     * Closes the current resource
1011
     *
1012
     * Stops the timer
1013
     * Saves into the database if required
1014
     * Clears the current resource data from this object
1015
     * @return	boolean	True on success, false on failure
1016
     */
1017
    public function close()
1018
    {
1019
        if ($this->debug > 0) {
1020
            error_log('New LP - In learnpath::close()', 0);
1021
        }
1022
        if (empty ($this->lp_id)) {
1023
            $this->error = 'Trying to close this learnpath but no ID is set';
1024
            return false;
1025
        }
1026
        $this->current_time_stop = time();
1027
        $this->ordered_items = array();
1028
        $this->index = 0;
1029
        unset ($this->lp_id);
1030
        //unset other stuff
1031
        return true;
1032
    }
1033
1034
    /**
1035
     * Static admin function allowing removal of a learnpath
1036
     * @param	array $courseInfo
1037
     * @param	integer	Learnpath ID
1038
     * @param	string	Whether to delete data or keep it (default: 'keep', others: 'remove')
1039
     * @return	boolean	True on success, false on failure (might change that to return number of elements deleted)
1040
     */
1041
    public function delete($courseInfo = null, $id = null, $delete = 'keep')
1042
    {
1043
        $course_id = api_get_course_int_id();
1044
        if (!empty($courseInfo)) {
1045
            $course_id = isset($courseInfo['real_id']) ? $courseInfo['real_id'] : $course_id;
1046
        }
1047
1048
        // TODO: Implement a way of getting this to work when the current object is not set.
1049
        // In clear: implement this in the item class as well (abstract class) and use the given ID in queries.
1050
        // If an ID is specifically given and the current LP is not the same, prevent delete.
1051
        if (!empty ($id) && ($id != $this->lp_id)) {
1052
            return false;
1053
        }
1054
1055
        $lp = Database:: get_course_table(TABLE_LP_MAIN);
1056
        $lp_item = Database:: get_course_table(TABLE_LP_ITEM);
1057
        $lp_view = Database:: get_course_table(TABLE_LP_VIEW);
1058
        $lp_item_view = Database:: get_course_table(TABLE_LP_ITEM_VIEW);
1059
1060
        // Delete lp item id.
1061
        foreach ($this->items as $id => $dummy) {
1062
            $sql = "DELETE FROM $lp_item_view
1063
                    WHERE c_id = $course_id AND lp_item_id = '" . $id . "'";
1064
            Database::query($sql);
1065
        }
1066
1067
        // Proposed by Christophe (nickname: clefevre)
1068
        $sql = "DELETE FROM $lp_item WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
1069
        Database::query($sql);
1070
1071
        $sql = "DELETE FROM $lp_view WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
1072
        Database::query($sql);
1073
1074
        self::toggle_publish($this->lp_id, 'i');
1075
1076
        if ($this->type == 2 || $this->type == 3) {
1077
            // This is a scorm learning path, delete the files as well.
1078
            $sql = "SELECT path FROM $lp
1079
                    WHERE c_id = ".$course_id." AND id = " . $this->lp_id;
1080
            $res = Database::query($sql);
1081
            if (Database :: num_rows($res) > 0) {
1082
                $row = Database :: fetch_array($res);
1083
                $path = $row['path'];
1084
                $sql = "SELECT id FROM $lp
1085
                        WHERE c_id = ".$course_id." AND path = '$path' AND id != " . $this->lp_id;
1086
                $res = Database::query($sql);
1087
                if (Database :: num_rows($res) > 0) { // Another learning path uses this directory, so don't delete it.
1088
                    if ($this->debug > 2) {
1089
                        error_log('New LP - In learnpath::delete(), found other LP using path ' . $path . ', keeping directory', 0);
1090
                    }
1091
                } else {
1092
                    // No other LP uses that directory, delete it.
1093
                    $course_rel_dir = api_get_course_path() . '/scorm/'; // scorm dir web path starting from /courses
1094
                    $course_scorm_dir = api_get_path(SYS_COURSE_PATH) . $course_rel_dir; // The absolute system path for this course.
1095
                    if ($delete == 'remove' && is_dir($course_scorm_dir . $path) and !empty ($course_scorm_dir)) {
1096
                        if ($this->debug > 2) {
1097
                            error_log('New LP - In learnpath::delete(), found SCORM, deleting directory: ' . $course_scorm_dir . $path, 0);
1098
                        }
1099
                        // Proposed by Christophe (clefevre).
1100
                        if (strcmp(substr($path, -2), "/.") == 0) {
1101
                            $path = substr($path, 0, -1); // Remove "." at the end.
1102
                        }
1103
                        //exec('rm -rf ' . $course_scorm_dir . $path); // See Bug #5208, this is not OS-portable way.
1104
                        rmdirr($course_scorm_dir . $path);
1105
                    }
1106
                }
1107
            }
1108
        }
1109
1110
        $tbl_tool = Database :: get_course_table(TABLE_TOOL_LIST);
1111
        $link = 'newscorm/lp_controller.php?action=view&lp_id='.$this->lp_id;
1112
        // Delete tools
1113
        $sql = "DELETE FROM $tbl_tool
1114
                WHERE c_id = ".$course_id." AND (link LIKE '$link%' AND image='scormbuilder.gif')";
1115
        Database::query($sql);
1116
1117
        $sql = "DELETE FROM $lp WHERE c_id = ".$course_id." AND id = " . $this->lp_id;
1118
        Database::query($sql);
1119
        // Updates the display order of all lps.
1120
        $this->update_display_order();
1121
1122
        api_item_property_update(
1123
            api_get_course_info(),
1124
            TOOL_LEARNPATH,
1125
            $this->lp_id,
1126
            'delete',
1127
            api_get_user_id()
1128
        );
1129
1130
        $link_info = GradebookUtils::is_resource_in_course_gradebook(api_get_course_id(), 4 , $id, api_get_session_id());
1131
        if ($link_info !== false) {
1132
            GradebookUtils::remove_resource_from_course_gradebook($link_info['id']);
1133
        }
1134
1135
        if (api_get_setting('search_enabled') == 'true') {
1136
            require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1137
            delete_all_values_for_item($this->cc, TOOL_LEARNPATH, $this->lp_id);
1138
        }
1139
    }
1140
1141
    /**
1142
     * Removes all the children of one item - dangerous!
1143
     * @param	integer	$id Element ID of which children have to be removed
1144
     * @return	integer	Total number of children removed
1145
     */
1146
    public function delete_children_items($id)
1147
    {
1148
        $course_id = $this->course_info['real_id'];
1149
        if ($this->debug > 0) {
1150
            error_log('New LP - In learnpath::delete_children_items(' . $id . ')', 0);
1151
        }
1152
        $num = 0;
1153
        if (empty ($id) || $id != strval(intval($id))) {
1154
            return false;
1155
        }
1156
        $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
1157
        $sql = "SELECT * FROM $lp_item WHERE c_id = ".$course_id." AND parent_item_id = $id";
1158
        $res = Database::query($sql);
1159
        while ($row = Database :: fetch_array($res)) {
1160
            $num += $this->delete_children_items($row['id']);
1161
            $sql_del = "DELETE FROM $lp_item WHERE c_id = ".$course_id." AND id = " . $row['id'];
1162
            Database::query($sql_del);
1163
            $num++;
1164
        }
1165
        return $num;
1166
    }
1167
1168
    /**
1169
     * Removes an item from the current learnpath
1170
     * @param	integer	$id Elem ID (0 if first)
1171
     * @param	integer	$remove Whether to remove the resource/data from the
1172
     * system or leave it (default: 'keep', others 'remove')
1173
     * @return	integer	Number of elements moved
1174
     * @todo implement resource removal
1175
     */
1176
    public function delete_item($id, $remove = 'keep')
1177
    {
1178
        $course_id = api_get_course_int_id();
1179
        if ($this->debug > 0) {
1180
            error_log('New LP - In learnpath::delete_item()', 0);
1181
        }
1182
        // TODO: Implement the resource removal.
1183
        if (empty ($id) || $id != strval(intval($id))) {
1184
            return false;
1185
        }
1186
        // First select item to get previous, next, and display order.
1187
        $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
1188
        $sql_sel = "SELECT * FROM $lp_item WHERE c_id = ".$course_id." AND id = $id";
1189
        $res_sel = Database::query($sql_sel);
1190
        if (Database :: num_rows($res_sel) < 1) {
1191
            return false;
1192
        }
1193
        $row = Database :: fetch_array($res_sel);
1194
        $previous = $row['previous_item_id'];
1195
        $next = $row['next_item_id'];
1196
        $display = $row['display_order'];
1197
        $parent = $row['parent_item_id'];
1198
        $lp = $row['lp_id'];
1199
        // Delete children items.
1200
        $num = $this->delete_children_items($id);
1201
        if ($this->debug > 2) {
1202
            error_log('New LP - learnpath::delete_item() - deleted ' . $num . ' children of element ' . $id, 0);
1203
        }
1204
        // Now delete the item.
1205
        $sql_del = "DELETE FROM $lp_item WHERE c_id = $course_id AND id = $id";
1206
        if ($this->debug > 2) {
1207
            error_log('New LP - Deleting item: ' . $sql_del, 0);
1208
        }
1209
        Database::query($sql_del);
1210
        // Now update surrounding items.
1211
        $sql_upd = "UPDATE $lp_item SET next_item_id = $next
1212
                    WHERE c_id = ".$course_id." AND id = $previous";
1213
        Database::query($sql_upd);
1214
        $sql_upd = "UPDATE $lp_item SET previous_item_id = $previous
1215
                    WHERE c_id = ".$course_id." AND id = $next";
1216
        Database::query($sql_upd);
1217
        // Now update all following items with new display order.
1218
        $sql_all = "UPDATE $lp_item SET display_order = display_order-1
1219
                    WHERE c_id = ".$course_id." AND lp_id = $lp AND parent_item_id = $parent AND display_order > $display";
1220
        Database::query($sql_all);
1221
1222
        //Removing prerequisites since the item will not longer exist
1223
        $sql_all = "UPDATE $lp_item SET prerequisite = '' WHERE c_id = ".$course_id." AND prerequisite = $id";
1224
        Database::query($sql_all);
1225
1226
        // Remove from search engine if enabled.
1227
        if (api_get_setting('search_enabled') == 'true') {
1228
            $tbl_se_ref = Database :: get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
1229
            $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';
1230
            $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp, $id);
1231
            $res = Database::query($sql);
1232
            if (Database :: num_rows($res) > 0) {
1233
                $row2 = Database :: fetch_array($res);
1234
                require_once api_get_path(LIBRARY_PATH).'search/ChamiloIndexer.class.php';
1235
                $di = new ChamiloIndexer();
1236
                $di->remove_document((int) $row2['search_did']);
1237
            }
1238
            $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';
1239
            $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp, $id);
1240
            Database::query($sql);
1241
        }
1242
    }
1243
1244
    /**
1245
     * Updates an item's content in place
1246
     * @param   integer $id Element ID
1247
     * @param   integer $parent Parent item ID
1248
     * @param   integer $previous Previous item ID
1249
     * @param   string  $title Item title
1250
     * @param   string  $description Item description
1251
     * @param   string  $prerequisites Prerequisites (optional)
1252
     * @param   array   $audio The array resulting of the $_FILES[mp3] element
1253
     * @param   int     $max_time_allowed
1254
     * @param   string  $url
1255
     * @return  boolean True on success, false on error
1256
     */
1257
    public function edit_item(
1258
        $id,
1259
        $parent,
1260
        $previous,
1261
        $title,
1262
        $description,
1263
        $prerequisites = '0',
1264
        $audio = array(),
1265
        $max_time_allowed = 0,
1266
        $url = ''
1267
    ) {
1268
        $course_id = api_get_course_int_id();
1269
        $_course = api_get_course_info();
1270
1271
        if ($this->debug > 0) {
1272
            error_log('New LP - In learnpath::edit_item()', 0);
1273
        }
1274
        if (empty ($max_time_allowed)) {
1275
            $max_time_allowed = 0;
1276
        }
1277
        if (empty ($id) || ($id != strval(intval($id))) || empty ($title)) {
1278
            return false;
1279
        }
1280
1281
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
1282
        $sql_select = "SELECT * FROM " . $tbl_lp_item . " WHERE c_id = ".$course_id." AND id = " . $id;
1283
        $res_select = Database::query($sql_select);
1284
        $row_select = Database :: fetch_array($res_select);
1285
        $audio_update_sql = '';
1286
        if (is_array($audio) && !empty ($audio['tmp_name']) && $audio['error'] === 0) {
1287
            // Create the audio folder if it does not exist yet.
1288
            $filepath = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document/';
1289 View Code Duplication
            if (!is_dir($filepath . 'audio')) {
1290
                mkdir($filepath . 'audio', api_get_permissions_for_new_directories());
1291
                $audio_id = add_document(
1292
                    $_course,
1293
                    '/audio',
1294
                    'folder',
1295
                    0,
1296
                    'audio'
1297
                );
1298
                api_item_property_update(
1299
                    $_course,
1300
                    TOOL_DOCUMENT,
1301
                    $audio_id,
1302
                    'FolderCreated',
1303
                    api_get_user_id(),
1304
                    null,
1305
                    null,
1306
                    null,
1307
                    null,
1308
                    api_get_session_id()
1309
                );
1310
                api_item_property_update(
1311
                    $_course,
1312
                    TOOL_DOCUMENT,
1313
                    $audio_id,
1314
                    'invisible',
1315
                    api_get_user_id(),
1316
                    null,
1317
                    null,
1318
                    null,
1319
                    null,
1320
                    api_get_session_id()
1321
                );
1322
            }
1323
1324
            // Upload file in documents.
1325
            $pi = pathinfo($audio['name']);
1326
            if ($pi['extension'] == 'mp3') {
1327
                $c_det = api_get_course_info($this->cc);
1328
                $bp = api_get_path(SYS_COURSE_PATH) . $c_det['path'] . '/document';
1329
                $path = handle_uploaded_document($c_det, $audio, $bp, '/audio', api_get_user_id(), 0, null, 0, 'rename', false, 0);
1330
                $path = substr($path, 7);
1331
                // Update reference in lp_item - audio path is the path from inside de document/audio/ dir.
1332
                $audio_update_sql = ", audio = '" . Database::escape_string($path) . "' ";
1333
            }
1334
        }
1335
1336
        $same_parent = ($row_select['parent_item_id'] == $parent) ? true : false;
1337
        $same_previous = ($row_select['previous_item_id'] == $previous) ? true : false;
1338
1339
        // TODO: htmlspecialchars to be checked for encoding related problems.
1340
        if ($same_parent && $same_previous) {
1341
            // Only update title and description.
1342
            $sql = "UPDATE " . $tbl_lp_item . "
1343
                    SET title = '" . Database::escape_string($title) . "',
1344
                        prerequisite = '" . $prerequisites . "',
1345
                        description = '" . Database::escape_string($description) . "'
1346
                        " . $audio_update_sql . ",
1347
                        max_time_allowed = '" . Database::escape_string($max_time_allowed) . "'
1348
                    WHERE c_id = ".$course_id." AND id = " . $id;
1349
            Database::query($sql);
1350
        } else {
1351
            $old_parent = $row_select['parent_item_id'];
1352
            $old_previous = $row_select['previous_item_id'];
1353
            $old_next = $row_select['next_item_id'];
1354
            $old_order = $row_select['display_order'];
1355
            $old_prerequisite = $row_select['prerequisite'];
1356
            $old_max_time_allowed = $row_select['max_time_allowed'];
1357
1358
            /* BEGIN -- virtually remove the current item id */
1359
            /* for the next and previous item it is like the current item doesn't exist anymore */
1360
1361
            if ($old_previous != 0) {
1362
                // Next
1363
                $sql = "UPDATE " . $tbl_lp_item . "
1364
                        SET next_item_id = " . $old_next . "
1365
                        WHERE c_id = ".$course_id." AND id = " . $old_previous;
1366
                Database::query($sql);
1367
            }
1368
1369
            if ($old_next != 0) {
1370
                // Previous
1371
                $sql = "UPDATE " . $tbl_lp_item . "
1372
                        SET previous_item_id = " . $old_previous . "
1373
                        WHERE c_id = ".$course_id." AND id = " . $old_next;
1374
                Database::query($sql);
1375
            }
1376
1377
            // display_order - 1 for every item with a display_order bigger then the display_order of the current item.
1378
            $sql = "UPDATE " . $tbl_lp_item . "
1379
                    SET display_order = display_order - 1
1380
                    WHERE
1381
                        c_id = ".$course_id." AND
1382
                        display_order > " . $old_order . " AND
1383
                        lp_id = " . $this->lp_id . " AND
1384
                        parent_item_id = " . $old_parent;
1385
            Database::query($sql);
1386
            /* END -- virtually remove the current item id */
1387
1388
            /* BEGIN -- update the current item id to his new location */
1389
1390
            if ($previous == 0) {
1391
                // Select the data of the item that should come after the current item.
1392
                $sql = "SELECT id, display_order
1393
                        FROM " . $tbl_lp_item . "
1394
                        WHERE
1395
                            c_id = ".$course_id." AND
1396
                            lp_id = " . $this->lp_id . " AND
1397
                            parent_item_id = " . $parent . " AND
1398
                            previous_item_id = " . $previous;
1399
                $res_select_old = Database::query($sql);
1400
                $row_select_old = Database::fetch_array($res_select_old);
1401
1402
                // If the new parent didn't have children before.
1403
                if (Database :: num_rows($res_select_old) == 0) {
1404
                    $new_next = 0;
1405
                    $new_order = 1;
1406
                } else {
1407
                    $new_next = $row_select_old['id'];
1408
                    $new_order = $row_select_old['display_order'];
1409
                }
1410
            } else {
1411
                // Select the data of the item that should come before the current item.
1412
                $sql = "SELECT next_item_id, display_order
1413
                        FROM " . $tbl_lp_item . "
1414
                        WHERE c_id = ".$course_id." AND id = " . $previous;
1415
                $res_select_old = Database::query($sql);
1416
                $row_select_old = Database :: fetch_array($res_select_old);
1417
                $new_next = $row_select_old['next_item_id'];
1418
                $new_order = $row_select_old['display_order'] + 1;
1419
            }
1420
1421
            // TODO: htmlspecialchars to be checked for encoding related problems.
1422
            // Update the current item with the new data.
1423
            $sql = "UPDATE " . $tbl_lp_item . "
1424
                    SET
1425
                        title = '" . Database::escape_string($title) . "',
1426
                        description = '" . Database::escape_string($description) . "',
1427
                        parent_item_id = " . $parent . ",
1428
                        previous_item_id = " . $previous . ",
1429
                        next_item_id = " . $new_next . ",
1430
                        display_order = " . $new_order . "
1431
                        " . $audio_update_sql . "
1432
                    WHERE c_id = ".$course_id." AND id = " . $id;
1433
            Database::query($sql);
1434
1435
            if ($previous != 0) {
1436
                // Update the previous item's next_item_id.
1437
                $sql = "UPDATE " . $tbl_lp_item . "
1438
                        SET next_item_id = " . $id . "
1439
                        WHERE c_id = ".$course_id." AND id = " . $previous;
1440
                Database::query($sql);
1441
            }
1442
1443
            if ($new_next != 0) {
1444
                // Update the next item's previous_item_id.
1445
                $sql = "UPDATE " . $tbl_lp_item . "
1446
                        SET previous_item_id = " . $id . "
1447
                        WHERE c_id = ".$course_id." AND id = " . $new_next;
1448
                Database::query($sql);
1449
            }
1450
1451
            if ($old_prerequisite != $prerequisites) {
1452
                $sql = "UPDATE " . $tbl_lp_item . "
1453
                        SET prerequisite = '" . $prerequisites . "'
1454
                        WHERE c_id = ".$course_id." AND id = " . $id;
1455
                Database::query($sql);
1456
            }
1457
1458
            if ($old_max_time_allowed != $max_time_allowed) {
1459
                // update max time allowed
1460
                $sql = "UPDATE " . $tbl_lp_item . "
1461
                        SET max_time_allowed = " . $max_time_allowed . "
1462
                        WHERE c_id = ".$course_id." AND id = " . $id;
1463
                Database::query($sql);
1464
            }
1465
1466
            // Update all the items with the same or a bigger display_order than the current item.
1467
            $sql = "UPDATE " . $tbl_lp_item . "
1468
                    SET display_order = display_order + 1
1469
                    WHERE
1470
                       c_id = ".$course_id." AND
1471
                       lp_id = " . $this->get_id() . " AND
1472
                       id <> " . $id . " AND
1473
                       parent_item_id = " . $parent . " AND
1474
                       display_order >= " . $new_order;
1475
1476
            Database::query($sql);
1477
        }
1478
1479
        if ($row_select['item_type'] == 'link') {
1480
            $link = new Link();
0 ignored issues
show
Bug introduced by
The call to Link::__construct() misses some required arguments starting with $id.
Loading history...
1481
            $linkId = $row_select['path'];
1482
            $link->updateLink($linkId, $url);
1483
        }
1484
    }
1485
1486
    /**
1487
     * Updates an item's prereq in place
1488
     * @param	integer	$id Element ID
1489
     * @param	string	$prerequisite_id Prerequisite Element ID
1490
     * @param	int 	$mastery_score Prerequisite min score
1491
     * @param	int 	$max_score Prerequisite max score
1492
     *
1493
     * @return	boolean	True on success, false on error
1494
     */
1495
    public function edit_item_prereq($id, $prerequisite_id, $mastery_score = 0, $max_score = 100)
1496
    {
1497
        $course_id = api_get_course_int_id();
1498
        if ($this->debug > 0) {
1499
            error_log('New LP - In learnpath::edit_item_prereq(' . $id . ',' . $prerequisite_id . ',' . $mastery_score . ',' . $max_score . ')', 0);
1500
        }
1501
1502
        if (empty($id) || ($id != strval(intval($id))) || empty ($prerequisite_id)) {
1503
            return false;
1504
        }
1505
1506
        $prerequisite_id = intval($prerequisite_id);
1507
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
1508
1509
        if (!is_numeric($mastery_score) || $mastery_score < 0) {
1510
            $mastery_score = 0;
1511
        }
1512
1513
        if (!is_numeric($max_score) || $max_score < 0) {
1514
            $max_score = 100;
1515
        }
1516
1517
        /*if ($mastery_score > $max_score) {
1518
            $max_score = $mastery_score;
1519
        }*/
1520
1521
        if (!is_numeric($prerequisite_id)) {
1522
            $prerequisite_id = 'NULL';
1523
        }
1524
1525
        $mastery_score = floatval($mastery_score);
1526
        $max_score = floatval($max_score);
1527
1528
        $sql = " UPDATE $tbl_lp_item
1529
                 SET
1530
                    prerequisite = $prerequisite_id ,
1531
                    prerequisite_min_score = $mastery_score ,
1532
                    prerequisite_max_score = $max_score
1533
                 WHERE c_id = $course_id AND id = $id";
1534
        Database::query($sql);
1535
1536
        if ($prerequisite_id != 'NULL' && $prerequisite_id != '') {
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...
1537
            // Will this be enough to ensure unicity?
1538
            /*$sql = " UPDATE $tbl_lp_item
1539
                     SET mastery_score = $mastery_score
1540
                     WHERE c_id = $course_id AND ref = '$prerequisite_id'";
1541
1542
            Database::query($sql);*/
1543
        }
1544
        // TODO: Update the item object (can be ignored for now because refreshed).
1545
        return true;
1546
    }
1547
1548
    /**
1549
     * Escapes a string with the available database escape function
1550
     * @param	string	String to escape
1551
     * @return	string	String escaped
1552
     * @deprecated use  Database::escape_string
1553
     */
1554
    public function escape_string($string)
1555
    {
1556
        return Database::escape_string($string);
1557
    }
1558
1559
    /**
1560
     * Static admin function exporting a learnpath into a zip file
1561
     * @param	string	Export type (scorm, zip, cd)
1562
     * @param	string	Course code
1563
     * @param	integer Learnpath ID
1564
     * @param	string	Zip file name
1565
     * @return	string	Zip file path (or false on error)
1566
     */
1567
    public function export_lp($type, $course, $id, $zipname)
1568
    {
1569
        if (empty($type) || empty($course) || empty($id) || empty($zipname)) {
1570
            return false;
1571
        }
1572
        $url = '';
1573
        switch ($type) {
1574
            case 'scorm':
1575
                break;
1576
            case 'zip':
1577
                break;
1578
            case 'cdrom':
1579
                break;
1580
        }
1581
        return $url;
1582
    }
1583
1584
    /**
1585
     * Gets all the chapters belonging to the same parent as the item/chapter given
1586
     * Can also be called as abstract method
1587
     * @param	integer	Item ID
1588
     * @return	array	A list of all the "brother items" (or an empty array on failure)
1589
     */
1590 View Code Duplication
    public function get_brother_chapters($id)
1591
    {
1592
        $course_id = api_get_course_int_id();
1593
        if ($this->debug > 0) {
1594
            error_log('New LP - In learnpath::get_brother_chapters()', 0);
1595
        }
1596
1597
        if (empty($id) || $id != strval(intval($id))) {
1598
1599
            return array ();
1600
        }
1601
1602
        $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
1603
        $sql_parent = "SELECT * FROM $lp_item
1604
                       WHERE c_id = ".$course_id." AND id = $id AND item_type='dokeos_chapter'";
1605
        $res_parent = Database::query($sql_parent);
1606
        if (Database :: num_rows($res_parent) > 0) {
1607
            $row_parent = Database :: fetch_array($res_parent);
1608
            $parent = $row_parent['parent_item_id'];
1609
            $sql = "SELECT * FROM $lp_item
1610
                    WHERE
1611
                        c_id = ".$course_id." AND
1612
                        parent_item_id = $parent AND
1613
                        id = $id AND
1614
                        item_type='dokeos_chapter'
1615
                    ORDER BY display_order";
1616
            $res_bros = Database::query($sql);
1617
1618
            $list = array();
1619
            while ($row_bro = Database :: fetch_array($res_bros)) {
1620
                $list[] = $row_bro;
1621
            }
1622
1623
            return $list;
1624
        }
1625
1626
        return array();
1627
    }
1628
1629
    /**
1630
     * Gets all the items belonging to the same parent as the item given
1631
     * Can also be called as abstract method
1632
     * @param	integer	Item ID
1633
     * @return	array	A list of all the "brother items" (or an empty array on failure)
1634
     */
1635 View Code Duplication
    public function get_brother_items($id)
1636
    {
1637
        $course_id = api_get_course_int_id();
1638
        if ($this->debug > 0) {
1639
            error_log('New LP - In learnpath::get_brother_items(' . $id . ')', 0);
1640
        }
1641
1642
        if (empty ($id) || $id != strval(intval($id))) {
1643
            return array ();
1644
        }
1645
1646
        $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
1647
        $sql_parent = "SELECT * FROM $lp_item WHERE c_id = $course_id AND id = $id";
1648
        $res_parent = Database::query($sql_parent);
1649
        if (Database :: num_rows($res_parent) > 0) {
1650
            $row_parent = Database :: fetch_array($res_parent);
1651
            $parent = $row_parent['parent_item_id'];
1652
            $sql_bros = "SELECT * FROM $lp_item WHERE c_id = ".$course_id." AND parent_item_id = $parent
1653
                         ORDER BY display_order";
1654
            $res_bros = Database::query($sql_bros);
1655
            $list = array ();
1656
            while ($row_bro = Database :: fetch_array($res_bros)) {
1657
                $list[] = $row_bro;
1658
            }
1659
            return $list;
1660
        }
1661
        return array ();
1662
    }
1663
1664
    /**
1665
     * Get the specific prefix index terms of this learning path
1666
     * @param string $prefix
1667
     * @return  array Array of terms
1668
     */
1669
    public function get_common_index_terms_by_prefix($prefix)
1670
    {
1671
        require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1672
        $terms = get_specific_field_values_list_by_prefix(
1673
            $prefix,
1674
            $this->cc,
1675
            TOOL_LEARNPATH,
1676
            $this->lp_id
1677
        );
1678
        $prefix_terms = array();
1679
        if (!empty($terms)) {
1680
            foreach ($terms as $term) {
1681
                $prefix_terms[] = $term['value'];
1682
            }
1683
        }
1684
        return $prefix_terms;
1685
    }
1686
1687
    /**
1688
     * Gets the number of items currently completed
1689
     * @param bool $failedStatusException flag to determine the failed status is not considered progressed
1690
     * @return integer The number of items currently completed
1691
     */
1692
    public function get_complete_items_count($failedStatusException = false)
1693
    {
1694
        if ($this->debug > 0) {
1695
            error_log('New LP - In learnpath::get_complete_items_count()', 0);
1696
        }
1697
        $i = 0;
1698
        $completedStatusList = array(
1699
            'completed',
1700
            'passed',
1701
            'succeeded',
1702
            'browsed'
1703
        );
1704
1705
        if (!$failedStatusException) {
1706
            $completedStatusList[] = 'failed';
1707
        }
1708
1709
        foreach ($this->items as $id => $dummy) {
1710
            // Trying failed and browsed considered "progressed" as well.
1711
            if ($this->items[$id]->status_is($completedStatusList) &&
1712
                $this->items[$id]->get_type() != 'dokeos_chapter' &&
1713
                $this->items[$id]->get_type() != 'dir'
1714
            ) {
1715
                $i++;
1716
            }
1717
        }
1718
        return $i;
1719
    }
1720
1721
    /**
1722
     * Gets the current item ID
1723
     * @return	integer	The current learnpath item id
1724
     */
1725
    public function get_current_item_id()
1726
    {
1727
        $current = 0;
1728
        if ($this->debug > 0) {
1729
            error_log('New LP - In learnpath::get_current_item_id()', 0);
1730
        }
1731
        if (!empty($this->current)) {
1732
            $current = $this->current;
1733
        }
1734
        if ($this->debug > 2) {
1735
            error_log('New LP - In learnpath::get_current_item_id() - Returning ' . $current, 0);
1736
        }
1737
        return $current;
1738
    }
1739
1740
    /**
1741
     * Force to get the first learnpath item id
1742
     * @return	integer	The current learnpath item id
1743
     */
1744
    public function get_first_item_id()
1745
    {
1746
        $current = 0;
1747
        if (is_array($this->ordered_items)) {
1748
            $current = $this->ordered_items[0];
1749
        }
1750
        return $current;
1751
    }
1752
1753
    /**
1754
     * Gets the total number of items available for viewing in this SCORM
1755
     * @return	integer	The total number of items
1756
     */
1757
    public function get_total_items_count()
1758
    {
1759
        if ($this->debug > 0) {
1760
            error_log('New LP - In learnpath::get_total_items_count()', 0);
1761
        }
1762
        return count($this->items);
1763
    }
1764
1765
    /**
1766
     * Gets the total number of items available for viewing in this SCORM but without chapters
1767
     * @return	integer	The total no-chapters number of items
1768
     */
1769
    public function get_total_items_count_without_chapters()
1770
    {
1771
        if ($this->debug > 0) {
1772
            error_log('New LP - In learnpath::get_total_items_count_without_chapters()', 0);
1773
        }
1774
        $total = 0;
1775
        $typeListNotToCount = self::getChapterTypes();
1776
        foreach ($this->items as $temp2) {
1777
            if (!in_array($temp2->get_type(), $typeListNotToCount)) {
1778
                $total++;
1779
            }
1780
        }
1781
        return $total;
1782
    }
1783
1784
    /**
1785
     * Gets the first element URL.
1786
     * @return	string	URL to load into the viewer
1787
     */
1788
    public function first()
1789
    {
1790
        if ($this->debug > 0) {
1791
            error_log('New LP - In learnpath::first()', 0);
1792
            error_log('$this->last_item_seen '.$this->last_item_seen);
1793
        }
1794
1795
        // Test if the last_item_seen exists and is not a dir.
1796
        if (count($this->ordered_items) == 0) {
1797
            $this->index = 0;
1798
        }
1799
1800
        if ($this->debug > 0) {
1801
            if (isset($this->items[$this->last_item_seen])) {
1802
                $status = $this->items[$this->last_item_seen]->get_status();
1803
            }
1804
            error_log('status '.$status);
1805
        }
1806
1807
        if (!empty($this->last_item_seen) &&
1808
            !empty($this->items[$this->last_item_seen]) &&
1809
            $this->items[$this->last_item_seen]->get_type() != 'dir' &&
1810
            $this->items[$this->last_item_seen]->get_type() != 'dokeos_chapter'
1811
            //with this change (below) the LP will NOT go to the next item, it will take lp item we left
1812
            //&& !$this->items[$this->last_item_seen]->is_done()
1813
        ) {
1814
1815
            if ($this->debug > 2) {
1816
                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);
1817
            }
1818
            $index = -1;
1819
            foreach ($this->ordered_items as $myindex => $item_id) {
1820
                if ($item_id == $this->last_item_seen) {
1821
                    $index = $myindex;
1822
                    break;
1823
                }
1824
            }
1825
            if ($index == -1) {
1826
                // Index hasn't changed, so item not found - panic (this shouldn't happen).
1827
                if ($this->debug > 2) {
1828
                    error_log('New LP - Last item (' . $this->last_item_seen . ') was found in items but not in ordered_items, panic!', 0);
1829
                }
1830
                return false;
1831
            } else {
1832
                $this->last     = $this->last_item_seen;
1833
                $this->current  = $this->last_item_seen;
1834
                $this->index    = $index;
1835
            }
1836
        } else {
1837
            if ($this->debug > 2) {
1838
                error_log('New LP - In learnpath::first() - No last item seen', 0);
1839
            }
1840
            $index = 0;
1841
            // Loop through all ordered items and stop at the first item that is
1842
            // not a directory *and* that has not been completed yet.
1843
            while ( !empty($this->ordered_items[$index]) AND
1844
                is_a($this->items[$this->ordered_items[$index]], 'learnpathItem') AND
1845
                (
1846
                    $this->items[$this->ordered_items[$index]]->get_type() == 'dir' OR
1847
                    $this->items[$this->ordered_items[$index]]->get_type() == 'dokeos_chapter' OR
1848
                    $this->items[$this->ordered_items[$index]]->is_done() === true
1849
                ) AND $index < $this->max_ordered_items) {
1850
                $index++;
1851
            }
1852
            $this->last     = $this->current;
1853
            // current is
1854
            $this->current  = isset($this->ordered_items[$index]) ? $this->ordered_items[$index] : null;
1855
            $this->index    = $index;
1856
            if ($this->debug > 2) {
1857
                error_log('$index ' . $index);
1858
            }
1859 View Code Duplication
            if ($this->debug > 2) {
1860
                error_log('New LP - In learnpath::first() - No last item seen. New last = ' . $this->last . '(' . $this->ordered_items[$index] . ')', 0);
1861
            }
1862
        }
1863
        if ($this->debug > 2) {
1864
            error_log('New LP - In learnpath::first() - First item is ' . $this->get_current_item_id());
1865
        }
1866
    }
1867
1868
    /**
1869
     * Gets the information about an item in a format usable as JavaScript to update
1870
     * the JS API by just printing this content into the <head> section of the message frame
1871
     * @param	int $item_id
1872
     * @return	string
1873
     */
1874
    public function get_js_info($item_id = '')
1875
    {
1876
        if ($this->debug > 0) {
1877
            error_log('New LP - In learnpath::get_js_info(' . $item_id . ')', 0);
1878
        }
1879
1880
        $info = '';
1881
        $item_id = intval($item_id);
1882
1883
        if (!empty($item_id) && is_object($this->items[$item_id])) {
1884
            //if item is defined, return values from DB
1885
            $oItem = $this->items[$item_id];
1886
            $info .= '<script language="javascript">';
1887
            $info .= "top.set_score(" . $oItem->get_score() . ");\n";
1888
            $info .= "top.set_max(" . $oItem->get_max() . ");\n";
1889
            $info .= "top.set_min(" . $oItem->get_min() . ");\n";
1890
            $info .= "top.set_lesson_status('" . $oItem->get_status() . "');";
1891
            $info .= "top.set_session_time('" . $oItem->get_scorm_time('js') . "');";
1892
            $info .= "top.set_suspend_data('" . $oItem->get_suspend_data() . "');";
1893
            $info .= "top.set_saved_lesson_status('" . $oItem->get_status() . "');";
1894
            $info .= "top.set_flag_synchronized();";
1895
            $info .= '</script>';
1896
            if ($this->debug > 2) {
1897
                error_log('New LP - in learnpath::get_js_info(' . $item_id . ') - returning: ' . $info, 0);
1898
            }
1899
            return $info;
1900
1901
        } else {
1902
1903
            // If item_id is empty, just update to default SCORM data.
1904
            $info .= '<script language="javascript">';
1905
            $info .= "top.set_score(" . learnpathItem :: get_score() . ");\n";
1906
            $info .= "top.set_max(" . learnpathItem :: get_max() . ");\n";
1907
            $info .= "top.set_min(" . learnpathItem :: get_min() . ");\n";
1908
            $info .= "top.set_lesson_status('" . learnpathItem :: get_status() . "');";
1909
            $info .= "top.set_session_time('" . learnpathItem :: getScormTimeFromParameter('js') . "');";
1910
            $info .= "top.set_suspend_data('" . learnpathItem :: get_suspend_data() . "');";
1911
            $info .= "top.set_saved_lesson_status('" . learnpathItem :: get_status() . "');";
1912
            $info .= "top.set_flag_synchronized();";
1913
            $info .= '</script>';
1914
            if ($this->debug > 2) {
1915
                error_log('New LP - in learnpath::get_js_info(' . $item_id . ') - returning: ' . $info, 0);
1916
            }
1917
            return $info;
1918
        }
1919
    }
1920
1921
    /**
1922
     * Gets the js library from the database
1923
     * @return	string	The name of the javascript library to be used
1924
     */
1925
    public function get_js_lib()
1926
    {
1927
        $lib = '';
1928
        if (!empty ($this->js_lib)) {
1929
            $lib = $this->js_lib;
1930
        }
1931
        return $lib;
1932
    }
1933
1934
    /**
1935
     * Gets the learnpath database ID
1936
     * @return	integer	Learnpath ID in the lp table
1937
     */
1938
    public function get_id()
1939
    {
1940
        if (!empty ($this->lp_id)) {
1941
            return $this->lp_id;
1942
        } else {
1943
            return 0;
1944
        }
1945
    }
1946
1947
    /**
1948
     * Gets the last element URL.
1949
     * @return string URL to load into the viewer
1950
     */
1951
    public function get_last()
1952
    {
1953
        if ($this->debug > 0) {
1954
            error_log('New LP - In learnpath::get_last()', 0);
1955
        }
1956
        //This is just in case the lesson doesn't cointain a valid scheme, just to avoid "Notices"
1957
        if (count($this->ordered_items) > 0) {
1958
            $this->index = count($this->ordered_items) - 1;
1959
            return $this->ordered_items[$this->index];
1960
        }
1961
1962
        return false;
1963
    }
1964
1965
    /**
1966
     * Gets the navigation bar for the learnpath display screen
1967
     * @return	string	The HTML string to use as a navigation bar
1968
     */
1969
    public function get_navigation_bar($idBar = null, $display = null) {
1970
        if ($this->debug > 0) {
1971
            error_log('New LP - In learnpath::get_navigation_bar()', 0);
1972
        }
1973
        if(empty($idBar)){
1974
            $idBar='control-top';
1975
        }
1976
        /* if(empty($display)){
1977
            $display='display:block';
1978
        } */
1979
        $navbar = null;
1980
        $lp_id = $this->lp_id;
1981
        $mycurrentitemid = $this->get_current_item_id();
1982
1983
        if ($this->mode == 'fullscreen') {
1984
            $navbar = '
1985
                  <span id="'.$idBar.'" class="buttons">
1986
                    <a class="icon-toolbar" href="lp_controller.php?action=stats&'.api_get_cidreq(true).'&lp_id='.$lp_id.'" onclick="window.parent.API.save_asset();return true;" target="content_name" title="stats" id="stats_link">
1987
                        <span class="fa fa-info"></span><span class="sr-only">' . get_lang('Reporting') . '</span>
1988
                    </a>
1989
                    <a class="icon-toolbar" id="scorm-previous" href="#" onclick="switch_item(' . $mycurrentitemid . ',\'previous\');return false;" title="previous">
1990
                        <span class="fa fa-chevron-left"></span><span class="sr-only">' . get_lang('ScormPrevious') . '</span>
1991
                    </a>
1992
                    <a class="icon-toolbar" id="scorm-next" href="#" onclick="switch_item(' . $mycurrentitemid . ',\'next\');return false;" title="next">
1993
                        <span class="fa fa-chevron-right"></span><span class="sr-only">' . get_lang('ScormNext') . '</span>
1994
                    </a>
1995
                    <a class="icon-toolbar" id="view-embedded" href="lp_controller.php?action=mode&mode=embedded" target="_top" title="embedded mode">
1996
                        <span class="fa fa-columns"></span><span class="sr-only">' . get_lang('ScormExitFullScreen') . '</span>
1997
                    </a>
1998
                  </span>';
1999
2000
        } else {
2001
            $navbar = '
2002
                <span id="'.$idBar.'" class="buttons text-right">
2003
                    <a class="icon-toolbar" href="lp_controller.php?action=stats&'.api_get_cidreq(true).'&lp_id='.$lp_id.'" onclick="window.parent.API.save_asset();return true;" target="content_name" title="stats" id="stats_link">
2004
                        <span class="fa fa-info"></span><span class="sr-only">' . get_lang('Reporting') . '</span>
2005
                    </a>
2006
                    <a class="icon-toolbar" id="scorm-previous" href="#" onclick="switch_item(' . $mycurrentitemid . ',\'previous\');return false;" title="previous">
2007
                        <span class="fa fa-chevron-left"></span><span class="sr-only">' . get_lang('ScormPrevious') . '</span>
2008
                    </a>
2009
                    <a class="icon-toolbar" id="scorm-next" href="#" onclick="switch_item(' . $mycurrentitemid . ',\'next\');return false;" title="next">
2010
                        <span class="fa fa-chevron-right"></span><span class="sr-only">' . get_lang('ScormNext') . '</span>
2011
                    </a>
2012
                </span>';
2013
        }
2014
2015
        return $navbar;
2016
    }
2017
2018
    /**
2019
     * Gets the next resource in queue (url).
2020
     * @return	string	URL to load into the viewer
2021
     */
2022
    public function get_next_index()
2023
    {
2024
        if ($this->debug > 0) {
2025
            error_log('New LP - In learnpath::get_next_index()', 0);
2026
        }
2027
        // TODO
2028
        $index = $this->index;
2029
        $index++;
2030 View Code Duplication
        if ($this->debug > 2) {
2031
            error_log('New LP - Now looking at ordered_items[' . ($index) . '] - type is ' . $this->items[$this->ordered_items[$index]]->type, 0);
2032
        }
2033
        while (!empty ($this->ordered_items[$index]) AND ($this->items[$this->ordered_items[$index]]->get_type() == 'dir' || $this->items[$this->ordered_items[$index]]->get_type() == 'dokeos_chapter') AND $index < $this->max_ordered_items) {
2034
            $index++;
2035
            if ($index == $this->max_ordered_items){
2036
                if ($this->items[$this->ordered_items[$index]]->get_type() == 'dir' || $this->items[$this->ordered_items[$index]]->get_type() == 'dokeos_chapter') {
2037
                    return $this->index;
2038
                } else {
2039
                    return $index;
2040
                }
2041
            }
2042
        }
2043
        if (empty ($this->ordered_items[$index])) {
2044
            return $this->index;
2045
        }
2046
        if ($this->debug > 2) {
2047
            error_log('New LP - index is now ' . $index, 0);
2048
        }
2049
        return $index;
2050
    }
2051
2052
    /**
2053
     * Gets item_id for the next element
2054
     * @return	integer	Next item (DB) ID
2055
     */
2056
    public function get_next_item_id()
2057
    {
2058
        if ($this->debug > 0) {
2059
            error_log('New LP - In learnpath::get_next_item_id()', 0);
2060
        }
2061
        $new_index = $this->get_next_index();
2062
        if (!empty ($new_index)) {
2063
            if (isset ($this->ordered_items[$new_index])) {
2064
                if ($this->debug > 2) {
2065
                    error_log('New LP - In learnpath::get_next_index() - Returning ' . $this->ordered_items[$new_index], 0);
2066
                }
2067
                return $this->ordered_items[$new_index];
2068
            }
2069
        }
2070
        if ($this->debug > 2) {
2071
            error_log('New LP - In learnpath::get_next_index() - Problem - Returning 0', 0);
2072
        }
2073
        return 0;
2074
    }
2075
2076
    /**
2077
     * Returns the package type ('scorm','aicc','scorm2004','dokeos','ppt'...)
2078
     *
2079
     * Generally, the package provided is in the form of a zip file, so the function
2080
     * has been written to test a zip file. If not a zip, the function will return the
2081
     * default return value: ''
2082
     * @param	string	the path to the file
2083
     * @param	string 	the original name of the file
2084
     * @return	string	'scorm','aicc','scorm2004','dokeos' or '' if the package cannot be recognized
2085
     */
2086
    public static function get_package_type($file_path, $file_name)
2087
    {
2088
        // Get name of the zip file without the extension.
2089
        $file_info = pathinfo($file_name);
2090
        $filename = $file_info['basename']; // Name including extension.
2091
        $extension = $file_info['extension']; // Extension only.
2092
2093
        if (!empty($_POST['ppt2lp']) && !in_array(strtolower($extension), array(
2094
                'dll',
2095
                'exe'
2096
            ))) {
2097
            return 'oogie';
2098
        }
2099
        if (!empty($_POST['woogie']) && !in_array(strtolower($extension), array(
2100
                'dll',
2101
                'exe'
2102
            ))) {
2103
            return 'woogie';
2104
        }
2105
2106
        // Filename without its extension.
2107
        $file_base_name = str_replace('.' . $extension, '', $filename);
2108
2109
        $zipFile = new PclZip($file_path);
2110
        // Check the zip content (real size and file extension).
2111
        $zipContentArray = $zipFile->listContent();
2112
        $package_type = '';
2113
        $at_root = false;
2114
        $manifest = '';
2115
        $aicc_match_crs = 0;
2116
        $aicc_match_au = 0;
2117
        $aicc_match_des = 0;
2118
        $aicc_match_cst = 0;
2119
2120
        // The following loop should be stopped as soon as we found the right imsmanifest.xml (how to recognize it?).
2121
        if (is_array($zipContentArray) && count($zipContentArray) > 0) {
2122
            foreach ($zipContentArray as $thisContent) {
2123
                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...
2124
                    // New behaviour: Don't do anything. These files will be removed in scorm::import_package.
2125
                } elseif (stristr($thisContent['filename'], 'imsmanifest.xml') !== false) {
2126
                    $manifest = $thisContent['filename']; // Just the relative directory inside scorm/
2127
                    $package_type = 'scorm';
2128
                    break; // Exit the foreach loop.
2129
                } elseif (
2130
                    preg_match('/aicc\//i', $thisContent['filename']) ||
2131
                    in_array(strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION)), array( 'crs','au','des','cst'))
2132
                ) {
2133
                    $ext = strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION));
2134
                    switch ($ext) {
2135
                        case 'crs':
2136
                            $aicc_match_crs = 1;
2137
                            break;
2138
                        case 'au':
2139
                            $aicc_match_au = 1;
2140
                            break;
2141
                        case 'des':
2142
                            $aicc_match_des = 1;
2143
                            break;
2144
                        case 'cst':
2145
                            $aicc_match_cst = 1;
2146
                            break;
2147
                        default:
2148
                            break;
2149
                    }
2150
                    //break; // Don't exit the loop, because if we find an imsmanifest afterwards, we want it, not the AICC.
2151
                } else {
2152
                    $package_type = '';
2153
                }
2154
            }
2155
        }
2156
        if (empty($package_type) && 4 == ($aicc_match_crs + $aicc_match_au + $aicc_match_des + $aicc_match_cst)) {
2157
            // If found an aicc directory... (!= false means it cannot be false (error) or 0 (no match)).
2158
            $package_type = 'aicc';
2159
        }
2160
        return $package_type;
2161
    }
2162
2163
    /**
2164
     * Gets the previous resource in queue (url). Also initialises time values for this viewing
2165
     * @return string URL to load into the viewer
2166
     */
2167
    public function get_previous_index()
2168
    {
2169
        if ($this->debug > 0) {
2170
            error_log('New LP - In learnpath::get_previous_index()', 0);
2171
        }
2172
        $index = $this->index;
2173
        if (isset ($this->ordered_items[$index -1])) {
2174
            $index--;
2175
            while (isset($this->ordered_items[$index]) && ($this->items[$this->ordered_items[$index]]->get_type() == 'dir' || $this->items[$this->ordered_items[$index]]->get_type() == 'dokeos_chapter')) {
2176
                $index--;
2177
                if ($index < 0) {
2178
                    return $this->index;
2179
                }
2180
            }
2181
        } else {
2182
            if ($this->debug > 2) {
2183
                error_log('New LP - get_previous_index() - there was no previous index available, reusing ' . $index, 0);
2184
            }
2185
            // There is no previous item.
2186
        }
2187
        return $index;
2188
    }
2189
2190
    /**
2191
     * Gets item_id for the next element
2192
     * @return	integer	Previous item (DB) ID
2193
     */
2194
    public function get_previous_item_id()
2195
    {
2196
        if ($this->debug > 0) {
2197
            error_log('New LP - In learnpath::get_previous_item_id()', 0);
2198
        }
2199
        $new_index = $this->get_previous_index();
2200
        return $this->ordered_items[$new_index];
2201
    }
2202
2203
    /**
2204
     * Gets the progress value from the progress_db attribute
2205
     * @return	integer	Current progress value
2206
     */
2207
    public function get_progress()
2208
    {
2209
        if ($this->debug > 0) {
2210
            error_log('New LP - In learnpath::get_progress()', 0);
2211
        }
2212
        if (!empty ($this->progress_db)) {
2213
            return $this->progress_db;
2214
        }
2215
        return 0;
2216
    }
2217
2218
    /**
2219
     * Returns the HTML necessary to print a mediaplayer block inside a page
2220
     * @return string	The mediaplayer HTML
2221
     */
2222
    public function get_mediaplayer($autostart = 'true')
2223
    {
2224
        $course_id = api_get_course_int_id();
2225
        $_course = api_get_course_info();
2226
        $tbl_lp_item 		= Database :: get_course_table(TABLE_LP_ITEM);
2227
        $tbl_lp_item_view 	= Database :: get_course_table(TABLE_LP_ITEM_VIEW);
2228
2229
        // Getting all the information about the item.
2230
        $sql = "SELECT * FROM ".$tbl_lp_item." as lp
2231
                INNER JOIN ".$tbl_lp_item_view." as lp_view
2232
                ON lp.id = lp_view.lp_item_id
2233
                WHERE
2234
                    lp.id = '".$_SESSION['oLP']->current."' AND
2235
                    lp.c_id = $course_id AND
2236
                    lp_view.c_id = $course_id";
2237
        $result = Database::query($sql);
2238
        $row 	= Database::fetch_assoc($result);
2239
        $output = '';
2240
2241
        if (!empty ($row['audio'])) {
2242
2243
            $list = $_SESSION['oLP']->get_toc();
2244
            $type_quiz = false;
2245
2246
            foreach($list as $toc) {
2247
                if ($toc['id'] == $_SESSION['oLP']->current && ($toc['type']=='quiz') ) {
2248
                    $type_quiz = true;
2249
                }
2250
            }
2251
2252
            if ($type_quiz) {
2253
                if ($_SESSION['oLP']->prevent_reinit == 1) {
2254
                    $row['status'] === 'completed' ? $autostart_audio = 'false' : $autostart_audio = 'true';
2255
                } else {
2256
                    $autostart_audio = $autostart;
2257
                }
2258
            } else {
2259
                $autostart_audio = 'true';
2260
            }
2261
2262
            $courseInfo = api_get_course_info();
2263
2264
            $audio = $row['audio'];
2265
2266
            $file = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document/audio/'.$audio;
2267
            $url = api_get_path(WEB_COURSE_PATH).$courseInfo['path'].'/document/audio/'.$audio.'?'.api_get_cidreq();
2268
2269
            if (!file_exists($file)) {
2270
                $lpPathInfo = $_SESSION['oLP']->generate_lp_folder(api_get_course_info());
2271
                $file = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'.$lpPathInfo['dir'].$audio;
2272
                $url = api_get_path(WEB_COURSE_PATH).$_course['path'].'/document'.$lpPathInfo['dir'].$audio.'?'.api_get_cidreq();
2273
            }
2274
2275
            $player = Display::getMediaPlayer(
2276
                $file,
2277
                array(
2278
                    'id' => 'lp_audio_media_player',
2279
                    'url' => $url,
2280
                    'autoplay' => $autostart_audio,
2281
                    'width' => '100%'
2282
                )
2283
            );
2284
2285
            // The mp3 player.
2286
            $output  = '<div id="container">';
2287
            $output .= $player;
2288
            $output .= '</div>';
2289
        }
2290
2291
        return $output;
2292
    }
2293
2294
    /**
2295
     * Checks if the learning path is visible for student after the progress
2296
     * of its prerequisite is completed, considering the time availability and
2297
     * the LP visibility.
2298
     * @param int $lp_id
2299
     * @param int $student_id
2300
     * @param string Course code (optional)
2301
     * @param int $sessionId
2302
     * @return	bool
2303
     */
2304
    public static function is_lp_visible_for_student(
2305
        $lp_id,
2306
        $student_id,
2307
        $courseCode = null,
2308
        $sessionId = null
2309
    ) {
2310
        $lp_id = (int)$lp_id;
2311
        $courseInfo = api_get_course_info($courseCode);
2312
        $sessionId = intval($sessionId);
2313
2314
        if (empty($sessionId)) {
2315
            $sessionId = api_get_session_id();
2316
        }
2317
2318
        $tbl_learnpath = Database::get_course_table(TABLE_LP_MAIN);
2319
        // Get current prerequisite
2320
        $sql = "SELECT id, prerequisite, subscribe_users, publicated_on, expired_on
2321
                FROM $tbl_learnpath
2322
                WHERE c_id = ".$courseInfo['real_id']." AND id = $lp_id";
2323
2324
        $itemInfo = api_get_item_property_info(
2325
            $courseInfo['real_id'],
2326
            TOOL_LEARNPATH,
2327
            $lp_id,
2328
            $sessionId
2329
        );
2330
2331
        // If the item was deleted.
2332
        if (isset($itemInfo['visibility']) && $itemInfo['visibility'] == 2) {
2333
            return false;
2334
        }
2335
2336
        $rs  = Database::query($sql);
2337
        $now = time();
2338
        if (Database::num_rows($rs) > 0) {
2339
            $row = Database::fetch_array($rs, 'ASSOC');
2340
2341
            $prerequisite = $row['prerequisite'];
2342
            $is_visible = true;
2343
2344
            if (!empty($prerequisite)) {
2345
                $progress = self::getProgress(
2346
                    $prerequisite,
2347
                    $student_id,
2348
                    $courseInfo['real_id'],
2349
                    $sessionId
2350
                );
2351
                $progress = intval($progress);
2352
                if ($progress < 100) {
2353
                    $is_visible = false;
2354
                }
2355
            }
2356
2357
            // Also check the time availability of the LP
2358
            if ($is_visible) {
2359
                // Adding visibility restrictions
2360 View Code Duplication
                if (!empty($row['publicated_on']) &&
2361
                    $row['publicated_on'] != '0000-00-00 00:00:00'
2362
                ) {
2363
                    if ($now < api_strtotime($row['publicated_on'], 'UTC')) {
2364
                        //api_not_allowed();
2365
                        $is_visible = false;
2366
                    }
2367
                }
2368
2369
                // Blocking empty start times see BT#2800
2370
                global $_custom;
2371
                if (isset($_custom['lps_hidden_when_no_start_date']) &&
2372
                    $_custom['lps_hidden_when_no_start_date']
2373
                ) {
2374
                    if (empty($row['publicated_on']) || $row['publicated_on'] == '0000-00-00 00:00:00') {
2375
                        //api_not_allowed();
2376
                        $is_visible = false;
2377
                    }
2378
                }
2379
2380 View Code Duplication
                if (!empty($row['expired_on']) && $row['expired_on'] != '0000-00-00 00:00:00') {
2381
                    if ($now > api_strtotime($row['expired_on'], 'UTC')) {
2382
                        //api_not_allowed();
2383
                        $is_visible = false;
2384
                    }
2385
                }
2386
            }
2387
2388
            // Check if the subscription users/group to a LP is ON
2389
            if (isset($row['subscribe_users']) && $row['subscribe_users'] == 1) {
2390
                // Try group
2391
                $is_visible = false;
2392
2393
                // Checking only the user visibility
2394
                $userVisibility = api_get_item_visibility(
2395
                    $courseInfo,
2396
                    'learnpath',
2397
                    $row['id'],
2398
                    $sessionId,
2399
                    $student_id,
2400
                    'LearnpathSubscription'
2401
                );
2402
2403
                if ($userVisibility == 1) {
2404
                    $is_visible = true;
2405
                } else {
2406
                    $userGroups = GroupManager::getAllGroupPerUserSubscription($student_id);
2407
                    if (!empty($userGroups)) {
2408
                        foreach ($userGroups as $groupInfo) {
2409
                            $groupId = $groupInfo['iid'];
2410
2411
                            $userVisibility = api_get_item_visibility(
2412
                                $courseInfo,
2413
                                'learnpath',
2414
                                $row['id'],
2415
                                $sessionId,
2416
                                null,
2417
                                'LearnpathSubscription',
2418
                                $groupId
2419
                            );
2420
2421
                            if ($userVisibility == 1) {
2422
                                $is_visible = true;
2423
                                break;
2424
                            }
2425
                        }
2426
                    }
2427
                }
2428
            }
2429
2430
            return $is_visible;
2431
        }
2432
2433
        return false;
2434
    }
2435
2436
    /**
2437
     * @param int $lpId
2438
     * @param int $userId
2439
     * @param int $courseId
2440
     * @param int $sessionId
2441
     * @return int
2442
     */
2443
    public static function getProgress($lpId, $userId, $courseId, $sessionId = 0)
2444
    {
2445
        $lpId = intval($lpId);
2446
        $userId = intval($userId);
2447
        $courseId = intval($courseId);
2448
        $sessionId = intval($sessionId);
2449
        $progress = 0;
2450
2451
        $sessionCondition = api_get_session_condition($sessionId);
2452
        $table = Database :: get_course_table(TABLE_LP_VIEW);
2453
        $sql = "SELECT * FROM $table
2454
                WHERE
2455
                    c_id = ".$courseId." AND
2456
                    lp_id = $lpId AND
2457
                    user_id = $userId $sessionCondition";
2458
        $res = Database::query($sql);
2459
        if (Database :: num_rows($res) > 0) {
2460
            $row = Database:: fetch_array($res);
2461
            $progress = $row['progress'];
2462
        }
2463
        return $progress;
2464
2465
    }
2466
2467
    /**
2468
     * Displays a progress bar
2469
     * completed so far.
2470
     * @param	integer	$percentage Progress value to display
2471
     * @param	string	$text_add Text to display near the progress value
2472
     * @return	string	HTML string containing the progress bar
2473
     */
2474
    public static function get_progress_bar($percentage = -1, $text_add = '')
2475
    {
2476
        $text = $percentage . $text_add;
2477
        $output = '<div class="progress">
2478
                        <div id="progress_bar_value" class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="' .$percentage. '" aria-valuemin="0" aria-valuemax="100" style="width: '.$text.';">
2479
                        '. $text .'
2480
                        </div>
2481
                    </div>';
2482
2483
        return $output;
2484
    }
2485
2486
    /**
2487
     * @param string $mode can be '%' or 'abs'
2488
     * otherwise this value will be used $this->progress_bar_mode
2489
     * @return string
2490
     */
2491
    public function getProgressBar($mode = null)
2492
    {
2493
        list($percentage, $text_add) = $this->get_progress_bar_text($mode);
2494
        return self::get_progress_bar($percentage, $text_add);
2495
    }
2496
2497
    /**
2498
     * Gets the progress bar info to display inside the progress bar.
2499
     * Also used by scorm_api.php
2500
     * @param	string	$mode Mode of display (can be '%' or 'abs').abs means
2501
     * we display a number of completed elements per total elements
2502
     * @param	integer	$add Additional steps to fake as completed
2503
     * @return	list array Percentage or number and symbol (% or /xx)
2504
     */
2505
    public function get_progress_bar_text($mode = '', $add = 0)
2506
    {
2507
        if ($this->debug > 0) {
2508
            error_log('New LP - In learnpath::get_progress_bar_text()', 0);
2509
        }
2510
        if (empty($mode)) {
2511
            $mode = $this->progress_bar_mode;
2512
        }
2513
        $total_items = $this->get_total_items_count_without_chapters();
2514
        if ($this->debug > 2) {
2515
            error_log('New LP - Total items available in this learnpath: ' . $total_items, 0);
2516
        }
2517
        $completeItems = $this->get_complete_items_count();
2518
        if ($this->debug > 2) {
2519
            error_log('New LP - Items completed so far: ' . $completeItems, 0);
2520
        }
2521
        if ($add != 0) {
2522
            $completeItems += $add;
2523
            if ($this->debug > 2) {
2524
                error_log('New LP - Items completed so far (+modifier): ' . $completeItems, 0);
2525
            }
2526
        }
2527
        $text = '';
2528
        if ($completeItems > $total_items) {
2529
            $completeItems = $total_items;
2530
        }
2531
        $percentage = 0;
2532
        if ($mode == '%') {
2533
            if ($total_items > 0) {
2534
                $percentage = ((float) $completeItems / (float) $total_items) * 100;
2535
            } else {
2536
                $percentage = 0;
2537
            }
2538
            $percentage = number_format($percentage, 0);
2539
            $text = '%';
2540
        } elseif ($mode == 'abs') {
2541
            $percentage = $completeItems;
2542
            $text = '/' . $total_items;
2543
        }
2544
2545
        return array(
2546
            $percentage,
2547
            $text
2548
        );
2549
    }
2550
2551
    /**
2552
     * Gets the progress bar mode
2553
     * @return	string	The progress bar mode attribute
2554
     */
2555
    public function get_progress_bar_mode()
2556
    {
2557
        if ($this->debug > 0) {
2558
            error_log('New LP - In learnpath::get_progress_bar_mode()', 0);
2559
        }
2560
        if (!empty ($this->progress_bar_mode)) {
2561
            return $this->progress_bar_mode;
2562
        } else {
2563
            return '%';
2564
        }
2565
    }
2566
2567
    /**
2568
     * Gets the learnpath proximity (remote or local)
2569
     * @return	string	Learnpath proximity
2570
     */
2571
    public function get_proximity()
2572
    {
2573
        if ($this->debug > 0) {
2574
            error_log('New LP - In learnpath::get_proximity()', 0);
2575
        }
2576
        if (!empty ($this->proximity)) {
2577
            return $this->proximity;
2578
        } else {
2579
            return '';
2580
        }
2581
    }
2582
2583
    /**
2584
     * Gets the learnpath theme (remote or local)
2585
     * @return	string	Learnpath theme
2586
     */
2587
    public function get_theme()
2588
    {
2589
        if ($this->debug > 0) {
2590
            error_log('New LP - In learnpath::get_theme()', 0);
2591
        }
2592
        if (!empty ($this->theme)) {
2593
            return $this->theme;
2594
        } else {
2595
            return '';
2596
        }
2597
    }
2598
2599
    /**
2600
     * Gets the learnpath session id
2601
     * @return	string	Learnpath theme
2602
     */
2603
    public function get_lp_session_id()
2604
    {
2605
        if ($this->debug > 0) {
2606
            error_log('New LP - In learnpath::get_lp_session_id()', 0);
2607
        }
2608
        if (!empty ($this->lp_session_id)) {
2609
            return $this->lp_session_id;
2610
        } else {
2611
            return 0;
2612
        }
2613
    }
2614
2615
    /**
2616
     * Gets the learnpath image
2617
     * @return	string	Web URL of the LP image
2618
     */
2619
    public function get_preview_image()
2620
    {
2621
        if ($this->debug > 0) {
2622
            error_log('New LP - In learnpath::get_preview_image()', 0);
2623
        }
2624
        if (!empty($this->preview_image)) {
2625
            return $this->preview_image;
2626
        } else {
2627
            return '';
2628
        }
2629
    }
2630
2631
    /**
2632
     * @param string $size
2633
     * @param string $path_type
2634
     * @return bool|string
2635
     */
2636
    public function get_preview_image_path($size = null, $path_type = 'web')
2637
    {
2638
        $preview_image = $this->get_preview_image();
2639
        if (isset($preview_image) && !empty($preview_image)) {
2640
            $image_sys_path = api_get_path(SYS_COURSE_PATH).$this->course_info['path'].'/upload/learning_path/images/';
2641
            $image_path = api_get_path(WEB_COURSE_PATH).$this->course_info['path'].'/upload/learning_path/images/';
2642
2643
            if (isset($size)) {
2644
                $info = pathinfo($preview_image);
2645
                $image_custom_size = $info['filename'].'.'.$size.'.'.$info['extension'];
2646
                if (file_exists($image_sys_path.$image_custom_size)) {
2647
                    if ($path_type == 'web') {
2648
                        return $image_path.$image_custom_size;
2649
                    } else {
2650
                        return $image_sys_path.$image_custom_size;
2651
                    }
2652
                }
2653
            } else {
2654
                if ($path_type == 'web') {
2655
                    return $image_path.$preview_image;
2656
                } else {
2657
                    return $image_sys_path.$preview_image;
2658
                }
2659
            }
2660
        }
2661
2662
        return false;
2663
    }
2664
2665
    /**
2666
     * Gets the learnpath author
2667
     * @return string	LP's author
2668
     */
2669
    public function get_author()
2670
    {
2671
        if ($this->debug > 0) {
2672
            error_log('New LP - In learnpath::get_author()', 0);
2673
        }
2674
        if (!empty ($this->author)) {
2675
            return $this->author;
2676
        } else {
2677
            return '';
2678
        }
2679
    }
2680
2681
    /**
2682
     * Gets the learnpath author
2683
     * @return	string	LP's author
2684
     */
2685
    public function get_hide_toc_frame()
2686
    {
2687
        if ($this->debug > 0) {
2688
            error_log('New LP - In learnpath::get_author()', 0);
2689
        }
2690
        if (!empty ($this->hide_toc_frame)) {
2691
            return $this->hide_toc_frame;
2692
        } else {
2693
            return '';
2694
        }
2695
    }
2696
2697
    /**
2698
     * Generate a new prerequisites string for a given item. If this item was a sco and
2699
     * its prerequisites were strings (instead of IDs), then transform those strings into
2700
     * IDs, knowing that SCORM IDs are kept in the "ref" field of the lp_item table.
2701
     * Prefix all item IDs that end-up in the prerequisites string by "ITEM_" to use the
2702
     * same rule as the scorm_export() method
2703
     * @param	integer		Item ID
2704
     * @return	string		Prerequisites string ready for the export as SCORM
2705
     */
2706
    public function get_scorm_prereq_string($item_id)
2707
    {
2708
        if ($this->debug > 0) {
2709
            error_log('New LP - In learnpath::get_scorm_prereq_string()', 0);
2710
        }
2711
        if (!is_object($this->items[$item_id])) {
2712
            return false;
2713
        }
2714
        /** @var learnpathItem $oItem */
2715
        $oItem = $this->items[$item_id];
2716
        $prereq = $oItem->get_prereq_string();
2717
2718
        if (empty($prereq)) {
2719
            return '';
2720
        }
2721
        if (preg_match('/^\d+$/', $prereq) && is_object($this->items[$prereq])) {
2722
            // If the prerequisite is a simple integer ID and this ID exists as an item ID,
2723
            // then simply return it (with the ITEM_ prefix).
2724
            //return 'ITEM_' . $prereq;
2725
            return $this->items[$prereq]->ref;
2726
        } else {
2727
            if (isset($this->refs_list[$prereq])) {
2728
                // It's a simple string item from which the ID can be found in the refs list,
2729
                // so we can transform it directly to an ID for export.
2730
                return $this->items[$this->refs_list[$prereq]]->ref;
2731
            } else if (isset($this->refs_list['ITEM_'.$prereq])) {
2732
                return $this->items[$this->refs_list['ITEM_'.$prereq]]->ref;
2733
            } else {
2734
                // The last case, if it's a complex form, then find all the IDs (SCORM strings)
2735
                // and replace them, one by one, by the internal IDs (chamilo db)
2736
                // TODO: Modify the '*' replacement to replace the multiplier in front of it
2737
                // by a space as well.
2738
                $find = array (
2739
                    '&',
2740
                    '|',
2741
                    '~',
2742
                    '=',
2743
                    '<>',
2744
                    '{',
2745
                    '}',
2746
                    '*',
2747
                    '(',
2748
                    ')'
2749
                );
2750
                $replace = array (
2751
                    ' ',
2752
                    ' ',
2753
                    ' ',
2754
                    ' ',
2755
                    ' ',
2756
                    ' ',
2757
                    ' ',
2758
                    ' ',
2759
                    ' ',
2760
                    ' '
2761
                );
2762
                $prereq_mod = str_replace($find, $replace, $prereq);
2763
                $ids = explode(' ', $prereq_mod);
2764
                foreach ($ids as $id) {
2765
                    $id = trim($id);
2766
                    if (isset ($this->refs_list[$id])) {
2767
                        $prereq = preg_replace('/[^a-zA-Z_0-9](' . $id . ')[^a-zA-Z_0-9]/', 'ITEM_' . $this->refs_list[$id], $prereq);
2768
                    }
2769
                }
2770
2771
                return $prereq;
2772
            }
2773
        }
2774
    }
2775
2776
    /**
2777
     * Returns the XML DOM document's node
2778
     * @param	resource	Reference to a list of objects to search for the given ITEM_*
2779
     * @param	string		The identifier to look for
2780
     * @return	mixed		The reference to the element found with that identifier. False if not found
2781
     */
2782
    public function get_scorm_xml_node(& $children, $id)
2783
    {
2784
        for ($i = 0; $i < $children->length; $i++) {
2785
            $item_temp = $children->item($i);
2786
            if ($item_temp->nodeName == 'item') {
2787
                if ($item_temp->getAttribute('identifier') == $id) {
2788
                    return $item_temp;
2789
                }
2790
            }
2791
            $subchildren = $item_temp->childNodes;
2792
            if ($subchildren->length > 0) {
2793
                $val = $this->get_scorm_xml_node($subchildren, $id);
2794
                if (is_object($val)) {
2795
2796
                    return $val;
2797
                }
2798
            }
2799
        }
2800
2801
        return false;
2802
    }
2803
2804
    /**
2805
     * Returns a usable array of stats related to the current learnpath and user
2806
     * @return array	Well-formatted array containing status for the current learnpath
2807
     */
2808
    public function get_stats()
2809
    {
2810
        if ($this->debug > 0) {
2811
            error_log('New LP - In learnpath::get_stats()', 0);
2812
        }
2813
    }
2814
2815
    /**
2816
     * Static method. Can be re-implemented by children. Gives an array of statistics for
2817
     * the given course (for all learnpaths and all users)
2818
     * @param	string	Course code
2819
     * @return array	Well-formatted array containing status for the course's learnpaths
2820
     */
2821
    public function get_stats_course($course)
2822
    {
2823
        //if ($this->debug > 0) { error_log('New LP - In learnpath::get_stats_course()', 0); }
2824
        // TODO
2825
    }
2826
2827
    /**
2828
     * Static method. Can be re-implemented by children. Gives an array of statistics for
2829
     * the given course and learnpath (for all users)
2830
     * @param	string	Course code
2831
     * @param	integer	Learnpath ID
2832
     * @return array	Well-formatted array containing status for the specified learnpath
2833
     */
2834
    public function get_stats_lp($course, $lp)
2835
    {
2836
        //if ($this->debug > 0) { error_log('New LP - In learnpath::get_stats_lp()', 0); }
2837
        // TODO
2838
    }
2839
2840
    /**
2841
     * Static method. Can be re-implemented by children. Gives an array of statistics for
2842
     * the given course, learnpath and user.
2843
     * @param	string	Course code
2844
     * @param	integer	Learnpath ID
2845
     * @param	integer	User ID
2846
     * @return array	Well-formatted array containing status for the specified learnpath and user
2847
     */
2848
    public function get_stats_lp_user($course, $lp, $user)
2849
    {
2850
        //if ($this->debug > 0) { error_log('New LP - In learnpath::get_stats_lp_user()', 0); }
2851
        // TODO
2852
    }
2853
2854
    /**
2855
     * Static method. Can be re-implemented by children. Gives an array of statistics for
2856
     * the given course and learnpath (for all users)
2857
     * @param	string	Course code
2858
     * @param	integer	User ID
2859
     * @return array	Well-formatted array containing status for the user's learnpaths
2860
     */
2861
    public function get_stats_user($course, $user) {
2862
        //if ($this->debug > 0) { error_log('New LP - In learnpath::get_stats_user()', 0); }
2863
        // TODO
2864
    }
2865
2866
    /**
2867
     * Gets the status list for all LP's items
2868
     * @return	array	Array of [index] => [item ID => current status]
2869
     */
2870
    public function get_items_status_list()
2871
    {
2872
        if ($this->debug > 0) {
2873
            error_log('New LP - In learnpath::get_items_status_list()', 0);
2874
        }
2875
        $list = array ();
2876
        foreach ($this->ordered_items as $item_id) {
2877
            $list[] = array (
2878
                $item_id => $this->items[$item_id]->get_status()
2879
            );
2880
        }
2881
        return $list;
2882
    }
2883
2884
    /**
2885
     * Return the number of interactions for the given learnpath Item View ID.
2886
     * This method can be used as static.
2887
     * @param	integer	Item View ID
2888
     * @param   integer course id
2889
     * @return	integer	Number of interactions
2890
     */
2891 View Code Duplication
    public static function get_interactions_count_from_db($lp_iv_id, $course_id)
2892
    {
2893
        $table = Database :: get_course_table(TABLE_LP_IV_INTERACTION);
2894
        $lp_iv_id = intval($lp_iv_id);
2895
        $course_id = intval($course_id);
2896
2897
        $sql = "SELECT count(*) FROM $table
2898
                WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id";
2899
        $res = Database::query($sql);
2900
        $num = 0;
2901
        if (Database::num_rows($res)) {
2902
            $row = Database::fetch_array($res);
2903
            $num = $row[0];
2904
        }
2905
        return $num;
2906
    }
2907
2908
    /**
2909
     * Return the interactions as an array for the given lp_iv_id.
2910
     * This method can be used as static.
2911
     * @param	integer	Learnpath Item View ID
2912
     * @return	array
2913
     * @todo 	Transcode labels instead of switching to HTML (which requires to know the encoding of the LP)
2914
     */
2915
    public static function get_iv_interactions_array($lp_iv_id)
2916
    {
2917
        $course_id = api_get_course_int_id();
2918
        $list = array();
2919
        $table = Database :: get_course_table(TABLE_LP_IV_INTERACTION);
2920
2921
        if (empty($lp_iv_id)) {
2922
            return array();
2923
        }
2924
2925
        $sql = "SELECT * FROM $table
2926
                WHERE c_id = ".$course_id." AND lp_iv_id = $lp_iv_id
2927
                ORDER BY order_id ASC";
2928
        $res = Database::query($sql);
2929
        $num = Database :: num_rows($res);
2930
        if ($num > 0) {
2931
            $list[] = array (
2932
                'order_id' => api_htmlentities(get_lang('Order'), ENT_QUOTES),
2933
                'id' => api_htmlentities(get_lang('InteractionID'), ENT_QUOTES),
2934
                'type' => api_htmlentities(get_lang('Type'), ENT_QUOTES),
2935
                'time' => api_htmlentities(get_lang('TimeFinished'), ENT_QUOTES),
2936
                'correct_responses' => api_htmlentities(get_lang('CorrectAnswers'), ENT_QUOTES),
2937
                'student_response' => api_htmlentities(get_lang('StudentResponse'), ENT_QUOTES),
2938
                'result' => api_htmlentities(get_lang('Result'), ENT_QUOTES),
2939
                'latency' => api_htmlentities(get_lang('LatencyTimeSpent'), ENT_QUOTES)
2940
            );
2941
            while ($row = Database :: fetch_array($res)) {
2942
                $list[] = array (
2943
                    'order_id' => ($row['order_id'] + 1),
2944
                    'id' => urldecode($row['interaction_id']), //urldecode because they often have %2F or stuff like that
2945
                    'type' => $row['interaction_type'],
2946
                    'time' => $row['completion_time'],
2947
                    //'correct_responses' => $row['correct_responses'],
2948
                    'correct_responses' => '', // Hide correct responses from students.
2949
                    'student_response' => $row['student_response'],
2950
                    'result' => $row['result'],
2951
                    'latency' => $row['latency']
2952
                );
2953
            }
2954
        }
2955
2956
        return $list;
2957
    }
2958
2959
    /**
2960
     * Return the number of objectives for the given learnpath Item View ID.
2961
     * This method can be used as static.
2962
     * @param	integer	Item View ID
2963
     * @return	integer	Number of objectives
2964
     */
2965 View Code Duplication
    public static function get_objectives_count_from_db($lp_iv_id, $course_id)
2966
    {
2967
        $table = Database :: get_course_table(TABLE_LP_IV_OBJECTIVE);
2968
        $course_id = intval($course_id);
2969
        $lp_iv_id = intval($lp_iv_id);
2970
        $sql = "SELECT count(*) FROM $table
2971
                WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id";
2972
        //@todo seems that this always returns 0
2973
        $res = Database::query($sql);
2974
        $num = 0;
2975
        if (Database::num_rows($res)) {
2976
            $row = Database :: fetch_array($res);
2977
            $num = $row[0];
2978
        }
2979
2980
        return $num;
2981
    }
2982
2983
    /**
2984
     * Return the objectives as an array for the given lp_iv_id.
2985
     * This method can be used as static.
2986
     * @param	integer	Learnpath Item View ID
2987
     * @return	array
2988
     * @todo 	Translate labels
2989
     */
2990
    public static function get_iv_objectives_array($lp_iv_id = 0)
2991
    {
2992
        $course_id = api_get_course_int_id();
2993
        $table = Database :: get_course_table(TABLE_LP_IV_OBJECTIVE);
2994
        $sql = "SELECT * FROM $table
2995
                WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id
2996
                ORDER BY order_id ASC";
2997
        $res = Database::query($sql);
2998
        $num = Database :: num_rows($res);
2999
        $list = array();
3000
        if ($num > 0) {
3001
            $list[] = array(
3002
                'order_id' => api_htmlentities(get_lang('Order'), ENT_QUOTES),
3003
                'objective_id' => api_htmlentities(get_lang('ObjectiveID'), ENT_QUOTES),
3004
                'score_raw' => api_htmlentities(get_lang('ObjectiveRawScore'), ENT_QUOTES),
3005
                'score_max' => api_htmlentities(get_lang('ObjectiveMaxScore'), ENT_QUOTES),
3006
                'score_min' => api_htmlentities(get_lang('ObjectiveMinScore'), ENT_QUOTES),
3007
                'status' => api_htmlentities(get_lang('ObjectiveStatus'), ENT_QUOTES)
3008
            );
3009
            while ($row = Database :: fetch_array($res)) {
3010
                $list[] = array (
3011
                    'order_id' => ($row['order_id'] + 1),
3012
                    'objective_id' => urldecode($row['objective_id']), // urldecode() because they often have %2F or stuff like that.
3013
                    'score_raw' => $row['score_raw'],
3014
                    'score_max' => $row['score_max'],
3015
                    'score_min' => $row['score_min'],
3016
                    'status' => $row['status']
3017
                );
3018
            }
3019
        }
3020
3021
        return $list;
3022
    }
3023
3024
    /**
3025
     * Generate and return the table of contents for this learnpath. The (flat) table returned can be
3026
     * used by get_html_toc() to be ready to display
3027
     * @return	array	TOC as a table with 4 elements per row: title, link, status and level
3028
     */
3029
    public function get_toc()
3030
    {
3031
        if ($this->debug > 0) {
3032
            error_log('learnpath::get_toc()', 0);
3033
        }
3034
        $toc = array();
3035
        foreach ($this->ordered_items as $item_id) {
3036
            if ($this->debug > 2) {
3037
                error_log('learnpath::get_toc(): getting info for item ' . $item_id, 0);
3038
            }
3039
            // TODO: Change this link generation and use new function instead.
3040
            $toc[] = array (
3041
                'id'            => $item_id,
3042
                'title'         => $this->items[$item_id]->get_title(),
3043
                'status'        => $this->items[$item_id]->get_status(),
3044
                'level'         => $this->items[$item_id]->get_level(),
3045
                'type'          => $this->items[$item_id]->get_type(),
3046
                'description'   => $this->items[$item_id]->get_description(),
3047
                'path'          => $this->items[$item_id]->get_path(),
3048
            );
3049
        }
3050
        if ($this->debug > 2) {
3051
            error_log('New LP - In learnpath::get_toc() - TOC array: ' . print_r($toc, true), 0);
3052
        }
3053
        return $toc;
3054
    }
3055
3056
    /**
3057
     * Generate and return the table of contents for this learnpath. The JS
3058
     * table returned is used inside of scorm_api.php
3059
     * @return  string  A JS array vairiable construction
3060
     */
3061
    public function get_items_details_as_js($varname = 'olms.lms_item_types')
3062
    {
3063
        if ($this->debug > 0) {
3064
            error_log('New LP - In learnpath::get_items_details_as_js()', 0);
3065
        }
3066
        $toc = $varname.' = new Array();';
3067
        foreach ($this->ordered_items as $item_id) {
3068
            $toc.= $varname."['i$item_id'] = '".$this->items[$item_id]->get_type()."';";
3069
        }
3070
        if ($this->debug > 2) {
3071
            error_log('New LP - In learnpath::get_items_details_as_js() - TOC array: ' . print_r($toc, true), 0);
3072
        }
3073
        return $toc;
3074
    }
3075
3076
    /**
3077
     * Gets the learning path type
3078
     * @param	boolean		Return the name? If false, return the ID. Default is false.
3079
     * @return	mixed		Type ID or name, depending on the parameter
3080
     */
3081
    public function get_type($get_name = false)
3082
    {
3083
        $res = false;
3084
        if ($this->debug > 0) {
3085
            error_log('New LP - In learnpath::get_type()', 0);
3086
        }
3087
        if (!empty ($this->type)) {
3088
            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...
3089
                // Get it from the lp_type table in main db.
3090
            } else {
3091
                $res = $this->type;
3092
            }
3093
        }
3094
        if ($this->debug > 2) {
3095
            error_log('New LP - In learnpath::get_type() - Returning ' . ($res ? $res : 'false'), 0);
3096
        }
3097
        return $res;
3098
    }
3099
3100
    /**
3101
     * Gets the learning path type as static method
3102
     * @param	boolean		Return the name? If false, return the ID. Default is false.
3103
     * @return	mixed		Type ID or name, depending on the parameter
3104
     */
3105
    public static function get_type_static($lp_id = 0)
3106
    {
3107
        $course_id = api_get_course_int_id();
3108
        $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
3109
        $lp_id = intval($lp_id);
3110
        $sql = "SELECT lp_type FROM $tbl_lp
3111
                WHERE c_id = $course_id AND id = '" . $lp_id . "'";
3112
        $res = Database::query($sql);
3113
        if ($res === false) {
3114
            return null;
3115
        }
3116
        if (Database :: num_rows($res) <= 0) {
3117
            return null;
3118
        }
3119
        $row = Database :: fetch_array($res);
3120
        return $row['lp_type'];
3121
    }
3122
3123
    /**
3124
     * Gets a flat list of item IDs ordered for display (level by level ordered by order_display)
3125
     * This method can be used as abstract and is recursive
3126
     * @param	integer	Learnpath ID
3127
     * @param	integer	Parent ID of the items to look for
3128
     * @return	mixed	Ordered list of item IDs or false on error
3129
     */
3130
    public static function get_flat_ordered_items_list($lp, $parent = 0, $course_id = null)
3131
    {
3132
        if (empty($course_id)) {
3133
            $course_id = api_get_course_int_id();
3134
        } else {
3135
            $course_id = intval($course_id);
3136
        }
3137
        $list = array();
3138
3139
        if (empty($lp)) {
3140
            return false;
3141
        }
3142
3143
        $lp = intval($lp);
3144
        $parent = intval($parent);
3145
3146
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
3147
        $sql = "SELECT id FROM $tbl_lp_item
3148
                WHERE c_id = $course_id AND lp_id = $lp AND parent_item_id = $parent
3149
                ORDER BY display_order";
3150
3151
        $res = Database::query($sql);
3152
        while ($row = Database :: fetch_array($res)) {
3153
            $sublist = learnpath :: get_flat_ordered_items_list($lp, $row['id'], $course_id);
3154
            $list[] = $row['id'];
3155
            foreach ($sublist as $item) {
3156
                $list[] = $item;
3157
            }
3158
        }
3159
        return $list;
3160
    }
3161
3162
    /**
3163
     * @return array
3164
     */
3165
    public static function getChapterTypes()
3166
    {
3167
        return array(
3168
            'dokeos_chapter',
3169
            'dokeos_module',
3170
            'chapter',
3171
            'dir'
3172
        );
3173
    }
3174
3175
    /**
3176
     * Uses the table generated by get_toc() and returns an HTML-formatted string ready to display
3177
     * @return	string	HTML TOC ready to display
3178
     */
3179
    public function get_html_toc($toc_list = null)
3180
    {
3181
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true, false, false);
3182
3183
        if ($this->debug > 0) {
3184
            error_log('In learnpath::get_html_toc()', 0);
3185
        }
3186
        if (empty($toc_list)) {
3187
            $toc_list = $this->get_toc();
3188
        }
3189
        //$html = '<div id="scorm_title" class="scorm-heading">'.Security::remove_XSS($this->get_name()) . '</div>';
3190
        $html = '<div class="scorm-body">';
3191
3192
3193
        $html .= '<div id="inner_lp_toc" class="inner_lp_toc scrollbar-light">';
3194
        require_once 'resourcelinker.inc.php';
3195
3196
        // Temporary variables.
3197
        $mycurrentitemid = $this->get_current_item_id();
3198
        $color_counter = 0;
3199
        $i = 0;
3200
3201
        foreach ($toc_list as $item) {
3202
3203
            // Style Status
3204
            $class_name = [
3205
                'not attempted' => 'scorm_not_attempted',
3206
                'incomplete' => 'scorm_not_attempted',
3207
                'failed' => 'scorm_failed',
3208
                'completed' => 'scorm_completed',
3209
                'passed' => 'scorm_completed',
3210
                'succeeded' => 'scorm_completed',
3211
                'browsed' => 'scorm_completed',
3212
            ];
3213
3214
            $scorm_color_background = 'row_odd';
3215
            $style_item = '';
3216
3217
            if ($color_counter % 2 == 0) {
3218
                $scorm_color_background = 'row_even';
3219
            }
3220
3221
            $dirTypes = self::getChapterTypes();
3222
3223
            if (in_array($item['type'], $dirTypes)) {
3224
                $scorm_color_background ='scorm_item_section ';
3225
                $style_item = '';
3226
            }
3227
            if ($item['id'] == $this->current) {
3228
                $scorm_color_background = 'scorm_item_normal '.$scorm_color_background.' scorm_highlight';
3229
            } elseif (!in_array($item['type'], $dirTypes)) {
3230
                $scorm_color_background = 'scorm_item_normal '.$scorm_color_background.' ';
3231
            }
3232
3233
            $html .= '<div id="toc_' . $item['id'] . '" class="' . $scorm_color_background . ' '.$class_name[$item['status']].' ">';
3234
3235
            // Learning path title
3236
            $title = $item['title'];
3237
            if (empty ($title)) {
3238
                $title = rl_get_resource_name(api_get_course_id(), $this->get_id(), $item['id']);
3239
            }
3240
            $title = Security::remove_XSS($title);
3241
3242
            // Learning path personalization
3243
            // build the LP tree
3244
            // The anchor atoc_ will let us center the TOC on the currently viewed item &^D
3245
            $description = $item['description'];
3246
            if (empty($description)) {
3247
                $description = $title;
3248
            }
3249
            if (in_array($item['type'], $dirTypes)) {
3250
                // Chapters
3251
                $html .= '<div class="'.$style_item.' scorm_section_level_'.$item['level'].'" title="'.$description.'" >';
3252
            } else {
3253
                $html .= '<div class="'.$style_item.' scorm_item_level_'.$item['level'].' scorm_type_'.learnpath::format_scorm_type_item($item['type']).'" title="'.$description.'" >';
3254
                $html .= '<a name="atoc_'.$item['id'].'"></a>';
3255
            }
3256
3257
            if (in_array($item['type'], $dirTypes)) {
3258
                // Chapter
3259
                // if you want to put an image before, you should use css
3260
                $html .= stripslashes($title);
3261
            } else {
3262
                $this->get_link('http', $item['id'], $toc_list);
3263
                $html .= '<a class="items-list" href="#" onclick="switch_item(' .$mycurrentitemid . ',' .$item['id'] . ');' .'return false;" >' . stripslashes($title) . '</a>';
3264
            }
3265
            $html .= "</div>";
3266
3267
            if ($scorm_color_background != '') {
3268
                $html .= '</div>';
3269
            }
3270
3271
            $color_counter++;
3272
        }
3273
        $html .= "</div>";
3274
        $html .= "</div>";
3275
        return $html;
3276
    }
3277
3278
    /**
3279
     * Returns an HTML-formatted string ready to display with teacher buttons
3280
     * in LP view menu
3281
     * @return	string	HTML TOC ready to display
3282
     */
3283
    public function get_teacher_toc_buttons()
3284
    {
3285
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true, false, false);
3286
        $hide_teacher_icons_lp = api_get_configuration_value('hide_teacher_icons_lp');
3287
        $html = '';
3288
3289
        if ($is_allowed_to_edit && $hide_teacher_icons_lp == false) {
3290
            $gradebook = '';
3291
            if (!empty($_GET['gradebook'])) {
3292
                $gradebook = Security:: remove_XSS($_GET['gradebook']);
3293
            }
3294
            if ($this->get_lp_session_id() == api_get_session_id()) {
3295
                $html .= '<div id="actions_lp" class="actions_lp"><hr>';
3296
                $html .= '<div class="btn-group">';
3297
                $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'>" .
3298
                    Display::returnFontAwesomeIcon('street-view') . get_lang('Overview') . "</a>";
3299
                $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'>" .
3300
                    Display::returnFontAwesomeIcon('pencil') . get_lang('Edit') . "</a>";
3301
                $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">' .
3302
                    Display::returnFontAwesomeIcon('cog') . get_lang('Settings').'</a>';
3303
                $html .= '</div>';
3304
                $html .= '</div>';
3305
            }
3306
        }
3307
3308
        return $html;
3309
3310
    }
3311
    /**
3312
     * Gets the learnpath maker name - generally the editor's name
3313
     * @return	string	Learnpath maker name
3314
     */
3315
    public function get_maker()
3316
    {
3317
        if ($this->debug > 0) {
3318
            error_log('New LP - In learnpath::get_maker()', 0);
3319
        }
3320
        if (!empty ($this->maker)) {
3321
            return $this->maker;
3322
        } else {
3323
            return '';
3324
        }
3325
    }
3326
3327
    /**
3328
     * Gets the learnpath name/title
3329
     * @return	string	Learnpath name/title
3330
     */
3331 View Code Duplication
    public function get_name()
3332
    {
3333
        if ($this->debug > 0) {
3334
            error_log('New LP - In learnpath::get_name()', 0);
3335
        }
3336
        if (!empty ($this->name)) {
3337
            return $this->name;
3338
        } else {
3339
            return 'N/A';
3340
        }
3341
    }
3342
3343
    /**
3344
     * Gets a link to the resource from the present location, depending on item ID.
3345
     * @param	string	$type Type of link expected
3346
     * @param	integer	$item_id Learnpath item ID
3347
     * @return	string	$provided_toc Link to the lp_item resource
3348
     */
3349
    public function get_link($type = 'http', $item_id = null, $provided_toc = false)
3350
    {
3351
        $course_id = $this->get_course_int_id();
3352
3353 View Code Duplication
        if ($this->debug > 0) {
3354
            error_log('New LP - In learnpath::get_link(' . $type . ',' . $item_id . ')', 0);
3355
        }
3356 View Code Duplication
        if (empty($item_id)) {
3357
            if ($this->debug > 2) {
3358
                error_log('New LP - In learnpath::get_link() - no item id given in learnpath::get_link(), using current: ' . $this->get_current_item_id(), 0);
3359
            }
3360
            $item_id = $this->get_current_item_id();
3361
        }
3362
3363 View Code Duplication
        if (empty($item_id)) {
3364
            if ($this->debug > 2) {
3365
                error_log('New LP - In learnpath::get_link() - no current item id found in learnpath object', 0);
3366
            }
3367
            //still empty, this means there was no item_id given and we are not in an object context or
3368
            //the object property is empty, return empty link
3369
            $item_id = $this->first();
3370
            return '';
3371
        }
3372
3373
        $file = '';
3374
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3375
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
3376
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3377
        $item_id = intval($item_id);
3378
3379
        $sql = "SELECT
3380
                    l.lp_type as ltype,
3381
                    l.path as lpath,
3382
                    li.item_type as litype,
3383
                    li.path as lipath,
3384
                    li.parameters as liparams
3385
        		FROM $lp_table l
3386
                INNER JOIN $lp_item_table li
3387
                    ON (li.lp_id = l.id AND l.c_id = $course_id AND li.c_id = $course_id )
3388
        		WHERE li.id = $item_id ";
3389
        if ($this->debug > 2) {
3390
            error_log('New LP - In learnpath::get_link() - selecting item ' . $sql, 0);
3391
        }
3392
        $res = Database::query($sql);
3393
        if (Database :: num_rows($res) > 0) {
3394
            $row = Database :: fetch_array($res);
3395
            $lp_type = $row['ltype'];
3396
            $lp_path = $row['lpath'];
3397
            $lp_item_type = $row['litype'];
3398
            $lp_item_path = $row['lipath'];
3399
            $lp_item_params = $row['liparams'];
3400
3401
            if (empty($lp_item_params) && strpos($lp_item_path, '?') !== false) {
3402
                list($lp_item_path, $lp_item_params) = explode('?', $lp_item_path);
3403
            }
3404
            $sys_course_path = api_get_path(SYS_COURSE_PATH) . api_get_course_path();
3405
            if ($type == 'http') {
3406
                $course_path = api_get_path(WEB_COURSE_PATH) . api_get_course_path(); //web path
3407
            } else {
3408
                $course_path = $sys_course_path; //system path
3409
            }
3410
3411
            // 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.
3412
            if (in_array($lp_item_type, array('quiz', 'document', 'final_item', 'link', 'forum', 'thread', 'student_publication'))) {
3413
                $lp_type = 1;
3414
            }
3415
3416
            if ($this->debug > 2) {
3417
                error_log('New LP - In learnpath::get_link() - $lp_type ' . $lp_type, 0);
3418
                error_log('New LP - In learnpath::get_link() - $lp_item_type ' . $lp_item_type, 0);
3419
            }
3420
3421
            // Now go through the specific cases to get the end of the path
3422
            // @todo Use constants instead of int values.
3423
            switch ($lp_type) {
3424
                case 1 :
3425
                    if ($lp_item_type == 'dokeos_chapter') {
3426
                        $file = 'lp_content.php?type=dir';
3427
                    } else {
3428
                        require_once 'resourcelinker.inc.php';
3429
                        $file = rl_get_resource_link_for_learnpath(
3430
                            $course_id,
3431
                            $this->get_id(),
3432
                            $item_id,
3433
                            $this->get_view_id()
3434
                        );
3435
3436
                        if ($this->debug > 0) {
3437
                            error_log('rl_get_resource_link_for_learnpath - file: ' . $file, 0);
3438
                        }
3439
3440
                        if ($lp_item_type == 'link') {
3441
                            if (Link::is_youtube_link($file)) {
3442
                                $src  = Link::get_youtube_video_id($file);
3443
                                $file = api_get_path(WEB_CODE_PATH).'newscorm/embed.php?type=youtube&source='.$src;
3444
                            } elseif (Link::isVimeoLink($file)) {
3445
                                $src  = Link::getVimeoLinkId($file);
3446
                                $file = api_get_path(WEB_CODE_PATH).'newscorm/embed.php?type=vimeo&source='.$src;
3447
                            } else {
3448
                                // If the current site is HTTPS and the link is
3449
                                // HTTP, browsers will refuse opening the link
3450
                                $urlId = api_get_current_access_url_id();
3451
                                $url = api_get_access_url($urlId, false);
3452
                                $protocol = substr($url['url'], 0, 5);
3453
                                if ($protocol === 'https') {
3454
                                    $linkProtocol = substr($file, 0, 5);
3455
                                    if ($linkProtocol === 'http:') {
3456
                                        //this is the special intervention case
3457
                                        $file = api_get_path(WEB_CODE_PATH).'newscorm/embed.php?type=nonhttps&source=' .  urlencode($file);
3458
                                    }
3459
                                }
3460
                            }
3461
                        } else {
3462
                            // Check how much attempts of a exercise exits in lp
3463
                            $lp_item_id = $this->get_current_item_id();
3464
                            $lp_view_id = $this->get_view_id();
3465
3466
                            $prevent_reinit = null;
3467
                            if (isset($this->items[$this->current])) {
3468
                                $prevent_reinit = $this->items[$this->current]->get_prevent_reinit();
3469
                            }
3470
3471
                            if (empty($provided_toc)) {
3472
                                if ($this->debug > 0) {
3473
                                    error_log('In learnpath::get_link() Loading get_toc ', 0);
3474
                                }
3475
                                $list = $this->get_toc();
3476
                            } else {
3477
                                if ($this->debug > 0) {
3478
                                    error_log('In learnpath::get_link() Loading get_toc from "cache" ', 0);
3479
                                }
3480
                                $list = $provided_toc;
3481
                            }
3482
3483
                            $type_quiz = false;
3484
3485 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...
3486
                                if ($toc['id'] == $lp_item_id && ($toc['type'] == 'quiz')) {
3487
                                    $type_quiz = true;
3488
                                }
3489
                            }
3490
3491
                            if ($type_quiz) {
3492
                                $lp_item_id = intval($lp_item_id);
3493
                                $lp_view_id = intval($lp_view_id);
3494
                                $sql = "SELECT count(*) FROM $lp_item_view_table
3495
                                        WHERE
3496
                                            c_id = $course_id AND
3497
                                            lp_item_id='" . $lp_item_id . "' AND
3498
                                            lp_view_id ='" . $lp_view_id . "' AND
3499
                                            status='completed'";
3500
                                $result = Database::query($sql);
3501
                                $row_count = Database :: fetch_row($result);
3502
                                $count_item_view = (int) $row_count[0];
3503
                                $not_multiple_attempt = 0;
3504
                                if ($prevent_reinit === 1 && $count_item_view > 0) {
3505
                                    $not_multiple_attempt = 1;
3506
                                }
3507
                                $file .= '&not_multiple_attempt=' . $not_multiple_attempt;
3508
                            }
3509
3510
                            $tmp_array = explode('/', $file);
3511
                            $document_name = $tmp_array[count($tmp_array) - 1];
3512
                            if (strpos($document_name, '_DELETED_')) {
3513
                                $file = 'blank.php?error=document_deleted';
3514
                            }
3515
                        }
3516
                    }
3517
                    break;
3518
                case 2 :
3519
                    if ($this->debug > 2) {
3520
                        error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Item type: ' . $lp_item_type, 0);
3521
                    }
3522
3523
                    if ($lp_item_type != 'dir') {
3524
                        // Quite complex here:
3525
                        // We want to make sure 'http://' (and similar) links can
3526
                        // be loaded as is (withouth the Chamilo path in front) but
3527
                        // some contents use this form: resource.htm?resource=http://blablabla
3528
                        // which means we have to find a protocol at the path's start, otherwise
3529
                        // it should not be considered as an external URL.
3530
3531
                        //if ($this->prerequisites_match($item_id)) {
3532
                        if (preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path) != 0) {
3533
                            if ($this->debug > 2) {
3534
                                error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Found match for protocol in ' . $lp_item_path, 0);
3535
                            }
3536
                            // Distant url, return as is.
3537
                            $file = $lp_item_path;
3538
                        } else {
3539
                            if ($this->debug > 2) {
3540
                                error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - No starting protocol in ' . $lp_item_path, 0);
3541
                            }
3542
                            // Prevent getting untranslatable urls.
3543
                            $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path);
3544
                            $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path);
3545
                            // Prepare the path.
3546
                            $file = $course_path . '/scorm/' . $lp_path . '/' . $lp_item_path;
3547
                            // TODO: Fix this for urls with protocol header.
3548
                            $file = str_replace('//', '/', $file);
3549
                            $file = str_replace(':/', '://', $file);
3550
                            if (substr($lp_path, -1) == '/') {
3551
                                $lp_path = substr($lp_path, 0, -1);
3552
                            }
3553
3554
                            if (!is_file(realpath($sys_course_path . '/scorm/' . $lp_path . '/' . $lp_item_path))) {
3555
                                // if file not found.
3556
                                $decoded = html_entity_decode($lp_item_path);
3557
                                list ($decoded) = explode('?', $decoded);
3558
                                if (!is_file(realpath($sys_course_path . '/scorm/' . $lp_path . '/' . $decoded))) {
3559
                                    require_once 'resourcelinker.inc.php';
3560
                                    $file = rl_get_resource_link_for_learnpath(
3561
                                        $course_id,
3562
                                        $this->get_id(),
3563
                                        $item_id,
3564
                                        $this->get_view_id()
3565
                                    );
3566
                                    if (empty($file)) {
3567
                                        $file = 'blank.php?error=document_not_found';
3568
                                    } else {
3569
                                        $tmp_array = explode('/', $file);
3570
                                        $document_name = $tmp_array[count($tmp_array) - 1];
3571
                                        if (strpos($document_name, '_DELETED_')) {
3572
                                            $file = 'blank.php?error=document_deleted';
3573
                                        } else {
3574
                                            $file = 'blank.php?error=document_not_found';
3575
                                        }
3576
                                    }
3577
                                } else {
3578
                                    $file = $course_path . '/scorm/' . $lp_path . '/' . $decoded;
3579
                                }
3580
                            }
3581
                        }
3582
3583
                        // We want to use parameters if they were defined in the imsmanifest
3584
                        if (strpos($file, 'blank.php') === false) {
3585
                            $file .= (strstr($file, '?') === false ? '?' : '') . $lp_item_params;
3586
                        }
3587
                    } else {
3588
                        $file = 'lp_content.php?type=dir';
3589
                    }
3590
                    break;
3591
                case 3 :
3592
                    if ($this->debug > 2) {
3593
                        error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Item type: ' . $lp_item_type, 0);
3594
                    }
3595
                    // Formatting AICC HACP append URL.
3596
                    $aicc_append = '?aicc_sid=' . urlencode(session_id()) . '&aicc_url=' . urlencode(api_get_path(WEB_CODE_PATH) . 'newscorm/aicc_hacp.php') . '&';
3597
                    if (!empty($lp_item_params)) {
3598
                        $aicc_append .= $lp_item_params . '&';
3599
                    }
3600
                    if ($lp_item_type != 'dir') {
3601
                        // Quite complex here:
3602
                        // We want to make sure 'http://' (and similar) links can
3603
                        // be loaded as is (withouth the Chamilo path in front) but
3604
                        // some contents use this form: resource.htm?resource=http://blablabla
3605
                        // which means we have to find a protocol at the path's start, otherwise
3606
                        // it should not be considered as an external URL.
3607
3608
                        if (preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path) != 0) {
3609
                            if ($this->debug > 2) {
3610
                                error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Found match for protocol in ' . $lp_item_path, 0);
3611
                            }
3612
                            // Distant url, return as is.
3613
                            $file = $lp_item_path;
3614
                            // Enabled and modified by Ivan Tcholakov, 16-OCT-2008.
3615
                            /*
3616
                            if (stristr($file,'<servername>') !== false) {
3617
                                $file = str_replace('<servername>', $course_path.'/scorm/'.$lp_path.'/', $lp_item_path);
3618
                            }
3619
                            */
3620
                            if (stripos($file, '<servername>') !== false) {
3621
                                //$file = str_replace('<servername>',$course_path.'/scorm/'.$lp_path.'/',$lp_item_path);
3622
                                $web_course_path = str_replace('https://', '', str_replace('http://', '', $course_path));
3623
                                $file = str_replace('<servername>', $web_course_path . '/scorm/' . $lp_path, $lp_item_path);
3624
                            }
3625
                            //
3626
                            $file .= $aicc_append;
3627
                        } else {
3628
                            if ($this->debug > 2) {
3629
                                error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - No starting protocol in ' . $lp_item_path, 0);
3630
                            }
3631
                            // Prevent getting untranslatable urls.
3632
                            $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path);
3633
                            $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path);
3634
                            // Prepare the path - lp_path might be unusable because it includes the "aicc" subdir name.
3635
                            $file = $course_path . '/scorm/' . $lp_path . '/' . $lp_item_path;
3636
                            // TODO: Fix this for urls with protocol header.
3637
                            $file = str_replace('//', '/', $file);
3638
                            $file = str_replace(':/', '://', $file);
3639
                            $file .= $aicc_append;
3640
                        }
3641
                    } else {
3642
                        $file = 'lp_content.php?type=dir';
3643
                    }
3644
                    break;
3645
                case 4 :
3646
                    break;
3647
                default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
3648
                    break;
3649
            }
3650
            // Replace &amp; by & because &amp; will break URL with params
3651
            $file = !empty($file) ? str_replace('&amp;', '&', $file) : '';
3652
        }
3653
        if ($this->debug > 2) {
3654
            error_log('New LP - In learnpath::get_link() - returning "' . $file . '" from get_link', 0);
3655
        }
3656
        return $file;
3657
    }
3658
3659
    /**
3660
     * Gets the latest usable view or generate a new one
3661
     * @param	integer	Optional attempt number. If none given, takes the highest from the lp_view table
3662
     * @return	integer	DB lp_view id
3663
     */
3664
    public function get_view($attempt_num = 0)
3665
    {
3666
        if ($this->debug > 0) {
3667
            error_log('New LP - In learnpath::get_view()', 0);
3668
        }
3669
        $search = '';
3670
        // Use $attempt_num to enable multi-views management (disabled so far).
3671
        if ($attempt_num != 0 AND intval(strval($attempt_num)) == $attempt_num) {
3672
            $search = 'AND view_count = ' . $attempt_num;
3673
        }
3674
        // When missing $attempt_num, search for a unique lp_view record for this lp and user.
3675
        $lp_view_table = Database :: get_course_table(TABLE_LP_VIEW);
3676
3677
        $course_id = api_get_course_int_id();
3678
        $sessionId = api_get_session_id();
3679
3680
        $sql = "SELECT id, view_count FROM $lp_view_table
3681
        		WHERE
3682
        		    c_id = " . $course_id . " AND
3683
        		    lp_id = " . $this->get_id() . " AND
3684
        		    user_id = " . $this->get_user_id() . " AND
3685
        		    session_id = $sessionId
3686
        		    $search
3687
                ORDER BY view_count DESC";
3688
        $res = Database::query($sql);
3689
        if (Database :: num_rows($res) > 0) {
3690
            $row = Database :: fetch_array($res);
3691
            $this->lp_view_id = $row['id'];
3692
        } else if (!api_is_invitee()) {
3693
            // There is no database record, create one.
3694
            $sql = "INSERT INTO $lp_view_table (c_id, lp_id,user_id, view_count, session_id) VALUES
3695
            		($course_id, " . $this->get_id() . "," . $this->get_user_id() . ", 1, $sessionId)";
3696
            Database::query($sql);
3697
            $id = Database :: insert_id();
3698
            $this->lp_view_id = $id;
3699
3700
            $sql = "UPDATE $lp_view_table SET id = iid WHERE iid = $id";
3701
            Database::query($sql);
3702
        }
3703
3704
        return $this->lp_view_id;
3705
    }
3706
3707
    /**
3708
     * Gets the current view id
3709
     * @return	integer	View ID (from lp_view)
3710
     */
3711
    public function get_view_id()
3712
    {
3713
        if ($this->debug > 0) {
3714
            error_log('New LP - In learnpath::get_view_id()', 0);
3715
        }
3716
        if (!empty ($this->lp_view_id)) {
3717
            return $this->lp_view_id;
3718
        } else {
3719
            return 0;
3720
        }
3721
    }
3722
3723
    /**
3724
     * Gets the update queue
3725
     * @return	array	Array containing IDs of items to be updated by JavaScript
3726
     */
3727
    public function get_update_queue()
3728
    {
3729
        if ($this->debug > 0) {
3730
            error_log('New LP - In learnpath::get_update_queue()', 0);
3731
        }
3732
        return $this->update_queue;
3733
    }
3734
3735
    /**
3736
     * Gets the user ID
3737
     * @return	integer	User ID
3738
     */
3739 View Code Duplication
    public function get_user_id()
3740
    {
3741
        if ($this->debug > 0) {
3742
            error_log('New LP - In learnpath::get_user_id()', 0);
3743
        }
3744
        if (!empty ($this->user_id)) {
3745
            return $this->user_id;
3746
        } else {
3747
            return false;
3748
        }
3749
    }
3750
3751
    /**
3752
     * Checks if any of the items has an audio element attached
3753
     * @return  bool    True or false
3754
     */
3755
    public function has_audio()
3756
    {
3757
        if ($this->debug > 1) {
3758
            error_log('New LP - In learnpath::has_audio()', 0);
3759
        }
3760
        $has = false;
3761
        foreach ($this->items as $i => $item) {
3762
            if (!empty ($this->items[$i]->audio)) {
3763
                $has = true;
3764
                break;
3765
            }
3766
        }
3767
        return $has;
3768
    }
3769
3770
    /**
3771
     * Logs a message into a file
3772
     * @param	string 	Message to log
3773
     * @return	boolean	True on success, false on error or if msg empty
3774
     */
3775
    public function log($msg)
3776
    {
3777
        if ($this->debug > 0) {
3778
            error_log('New LP - In learnpath::log()', 0);
3779
        }
3780
        // TODO
3781
        $this->error .= $msg;
3782
        return true;
3783
    }
3784
3785
    /**
3786
     * Moves an item up and down at its level
3787
     * @param	integer	Item to move up and down
3788
     * @param	string	Direction 'up' or 'down'
3789
     * @return	integer	New display order, or false on error
3790
     */
3791
    public function move_item($id, $direction)
3792
    {
3793
        $course_id = api_get_course_int_id();
3794
        if ($this->debug > 0) {
3795
            error_log('New LP - In learnpath::move_item(' . $id . ',' . $direction . ')', 0);
3796
        }
3797
        if (empty($id) || empty($direction)) {
3798
            return false;
3799
        }
3800
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
3801
        $sql_sel = "SELECT *
3802
                    FROM " . $tbl_lp_item . "
3803
                    WHERE c_id = ".$course_id." AND id = " . $id;
3804
        $res_sel = Database::query($sql_sel);
3805
        // Check if elem exists.
3806
        if (Database :: num_rows($res_sel) < 1) {
3807
            return false;
3808
        }
3809
        // Gather data.
3810
        $row = Database :: fetch_array($res_sel);
3811
        $previous = $row['previous_item_id'];
3812
        $next = $row['next_item_id'];
3813
        $display = $row['display_order'];
3814
        $parent = $row['parent_item_id'];
3815
        $lp = $row['lp_id'];
3816
        // Update the item (switch with previous/next one).
3817
        switch ($direction) {
3818
            case 'up':
3819
                if ($this->debug > 2) {
3820
                    error_log('Movement up detected', 0);
3821
                }
3822
                if ($display <= 1) { /*do nothing*/
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...
3823
                } else {
3824
                    $sql_sel2 = "SELECT * FROM $tbl_lp_item
3825
                                 WHERE c_id = ".$course_id." AND id = $previous";
3826
3827
                    if ($this->debug > 2) {
3828
                        error_log('Selecting previous: ' . $sql_sel2, 0);
3829
                    }
3830
                    $res_sel2 = Database::query($sql_sel2);
3831
                    if (Database :: num_rows($res_sel2) < 1) {
3832
                        $previous_previous = 0;
3833
                    }
3834
                    // Gather data.
3835
                    $row2 = Database :: fetch_array($res_sel2);
3836
                    $previous_previous = $row2['previous_item_id'];
3837
                    // Update previous_previous item (switch "next" with current).
3838 View Code Duplication
                    if ($previous_previous != 0) {
3839
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3840
                                        next_item_id = $id
3841
                                    WHERE c_id = ".$course_id." AND id = $previous_previous";
3842
                        if ($this->debug > 2) {
3843
                            error_log($sql_upd2, 0);
3844
                        }
3845
                        Database::query($sql_upd2);
3846
                    }
3847
                    // Update previous item (switch with current).
3848 View Code Duplication
                    if ($previous != 0) {
3849
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3850
                                    next_item_id = $next,
3851
                                    previous_item_id = $id,
3852
                                    display_order = display_order +1
3853
                                    WHERE c_id = ".$course_id." AND id = $previous";
3854
                        if ($this->debug > 2) {
3855
                            error_log($sql_upd2, 0);
3856
                        }
3857
                        Database::query($sql_upd2);
3858
                    }
3859
3860
                    // Update current item (switch with previous).
3861 View Code Duplication
                    if ($id != 0) {
3862
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3863
                                        next_item_id = $previous,
3864
                                        previous_item_id = $previous_previous,
3865
                                        display_order = display_order-1
3866
                                    WHERE c_id = ".$course_id." AND id = $id";
3867
                        if ($this->debug > 2) {
3868
                            error_log($sql_upd2, 0);
3869
                        }
3870
                        Database::query($sql_upd2);
3871
                    }
3872
                    // Update next item (new previous item).
3873 View Code Duplication
                    if ($next != 0) {
3874
                        $sql_upd2 = "UPDATE $tbl_lp_item SET previous_item_id = $previous
3875
                                     WHERE c_id = ".$course_id." AND id = $next";
3876
                        if ($this->debug > 2) {
3877
                            error_log($sql_upd2, 0);
3878
                        }
3879
                        Database::query($sql_upd2);
3880
                    }
3881
                    $display = $display -1;
3882
                }
3883
                break;
3884
            case 'down':
3885
                if ($this->debug > 2) {
3886
                    error_log('Movement down detected', 0);
3887
                }
3888
                if ($next == 0) { /* Do nothing. */
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...
3889
                } else {
3890
                    $sql_sel2 = "SELECT * FROM $tbl_lp_item WHERE c_id = ".$course_id." AND id = $next";
3891
                    if ($this->debug > 2) {
3892
                        error_log('Selecting next: ' . $sql_sel2, 0);
3893
                    }
3894
                    $res_sel2 = Database::query($sql_sel2);
3895
                    if (Database :: num_rows($res_sel2) < 1) {
3896
                        $next_next = 0;
3897
                    }
3898
                    // Gather data.
3899
                    $row2 = Database :: fetch_array($res_sel2);
3900
                    $next_next = $row2['next_item_id'];
3901
                    // Update previous item (switch with current).
3902 View Code Duplication
                    if ($previous != 0) {
3903
                        $sql_upd2 = "UPDATE $tbl_lp_item SET next_item_id = $next
3904
                                     WHERE c_id = ".$course_id." AND id = $previous";
3905
                        Database::query($sql_upd2);
3906
                    }
3907
                    // Update current item (switch with previous).
3908 View Code Duplication
                    if ($id != 0) {
3909
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3910
                                     previous_item_id = $next, next_item_id = $next_next, display_order = display_order+1
3911
                                     WHERE c_id = ".$course_id." AND id = $id";
3912
                        Database::query($sql_upd2);
3913
                    }
3914
3915
                    // Update next item (new previous item).
3916 View Code Duplication
                    if ($next != 0) {
3917
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3918
                                     previous_item_id = $previous, next_item_id = $id, display_order = display_order-1
3919
                                     WHERE c_id = ".$course_id." AND id = $next";
3920
                        Database::query($sql_upd2);
3921
                    }
3922
3923
                    // Update next_next item (switch "previous" with current).
3924 View Code Duplication
                    if ($next_next != 0) {
3925
                        $sql_upd2 = "UPDATE $tbl_lp_item SET
3926
                                     previous_item_id = $id
3927
                                     WHERE c_id = ".$course_id." AND id = $next_next";
3928
                        Database::query($sql_upd2);
3929
                    }
3930
                    $display = $display +1;
3931
                }
3932
                break;
3933
            default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
3934
                return false;
3935
        }
3936
        return $display;
3937
    }
3938
3939
    /**
3940
     * Move a learnpath up (display_order)
3941
     * @param	integer	$lp_id Learnpath ID
3942
     */
3943
    public static function move_up($lp_id)
3944
    {
3945
        $course_id = api_get_course_int_id();
3946
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
3947
        $sql = "SELECT * FROM $lp_table
3948
                WHERE c_id = ".$course_id."
3949
                ORDER BY display_order";
3950
        $res = Database::query($sql);
3951
        if ($res === false)
3952
            return false;
3953
        $lps = array ();
3954
        $lp_order = array ();
3955
        $num = Database :: num_rows($res);
3956
        // First check the order is correct, globally (might be wrong because
3957
        // of versions < 1.8.4)
3958 View Code Duplication
        if ($num > 0) {
3959
            $i = 1;
3960
            while ($row = Database :: fetch_array($res)) {
3961
                if ($row['display_order'] != $i) { // If we find a gap in the order, we need to fix it.
3962
                    $need_fix = true;
3963
                    $sql_u = "UPDATE $lp_table SET display_order = $i
3964
                              WHERE c_id = ".$course_id." AND id = " . $row['id'];
3965
                    Database::query($sql_u);
3966
                }
3967
                $row['display_order'] = $i;
3968
                $lps[$row['id']] = $row;
3969
                $lp_order[$i] = $row['id'];
3970
                $i++;
3971
            }
3972
        }
3973
        if ($num > 1) { // If there's only one element, no need to sort.
3974
            $order = $lps[$lp_id]['display_order'];
3975
            if ($order > 1) { // If it's the first element, no need to move up.
3976
                $sql_u1 = "UPDATE $lp_table SET display_order = $order
3977
                           WHERE c_id = ".$course_id." AND id = " . $lp_order[$order - 1];
3978
                Database::query($sql_u1);
3979
                $sql_u2 = "UPDATE $lp_table SET display_order = " . ($order - 1) . "
3980
                           WHERE c_id = ".$course_id." AND id = " . $lp_id;
3981
                Database::query($sql_u2);
3982
            }
3983
        }
3984
    }
3985
3986
    /**
3987
     * Move a learnpath down (display_order)
3988
     * @param	integer	$lp_id Learnpath ID
3989
     */
3990
    public static function move_down($lp_id)
3991
    {
3992
        $course_id = api_get_course_int_id();
3993
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
3994
        $sql = "SELECT * FROM $lp_table
3995
                WHERE c_id = ".$course_id."
3996
                ORDER BY display_order";
3997
        $res = Database::query($sql);
3998
        if ($res === false) {
3999
            return false;
4000
        }
4001
        $lps = array ();
4002
        $lp_order = array ();
4003
        $num = Database :: num_rows($res);
4004
        $max = 0;
4005
        // First check the order is correct, globally (might be wrong because
4006
        // of versions < 1.8.4).
4007 View Code Duplication
        if ($num > 0) {
4008
            $i = 1;
4009
            while ($row = Database :: fetch_array($res)) {
4010
                $max = $i;
4011
                if ($row['display_order'] != $i) { // If we find a gap in the order, we need to fix it.
4012
                    $need_fix = true;
4013
                    $sql_u = "UPDATE $lp_table SET display_order = $i
4014
                              WHERE c_id = ".$course_id." AND id = " . $row['id'];
4015
                    Database::query($sql_u);
4016
                }
4017
                $row['display_order'] = $i;
4018
                $lps[$row['id']] = $row;
4019
                $lp_order[$i] = $row['id'];
4020
                $i++;
4021
            }
4022
        }
4023
        if ($num > 1) { // If there's only one element, no need to sort.
4024
            $order = $lps[$lp_id]['display_order'];
4025
            if ($order < $max) { // If it's the first element, no need to move up.
4026
                $sql_u1 = "UPDATE $lp_table SET display_order = $order
4027
                           WHERE c_id = ".$course_id." AND id = " . $lp_order[$order + 1];
4028
                Database::query($sql_u1);
4029
                $sql_u2 = "UPDATE $lp_table SET display_order = " . ($order + 1) . "
4030
                           WHERE c_id = ".$course_id." AND id = " . $lp_id;
4031
                Database::query($sql_u2);
4032
            }
4033
        }
4034
    }
4035
4036
    /**
4037
     * Updates learnpath attributes to point to the next element
4038
     * The last part is similar to set_current_item but processing the other way around
4039
     */
4040
    public function next()
4041
    {
4042
        if ($this->debug > 0) {
4043
            error_log('New LP - In learnpath::next()', 0);
4044
        }
4045
        $this->last = $this->get_current_item_id();
4046
        $this->items[$this->last]->save(false, $this->prerequisites_match($this->last));
4047
        $this->autocomplete_parents($this->last);
4048
        $new_index = $this->get_next_index();
4049
        if ($this->debug > 2) {
4050
            error_log('New LP - New index: ' . $new_index, 0);
4051
        }
4052
        $this->index = $new_index;
4053
        if ($this->debug > 2) {
4054
            error_log('New LP - Now having orderedlist[' . $new_index . '] = ' . $this->ordered_items[$new_index], 0);
4055
        }
4056
        $this->current = $this->ordered_items[$new_index];
4057
        if ($this->debug > 2) {
4058
            error_log('New LP - new item id is ' . $this->current . '-' . $this->get_current_item_id(), 0);
4059
        }
4060
    }
4061
4062
    /**
4063
     * Open a resource = initialise all local variables relative to this resource. Depending on the child
4064
     * class, this might be redefined to allow several behaviours depending on the document type.
4065
     * @param integer Resource ID
4066
     * @return boolean True on success, false otherwise
4067
     */
4068
    public function open($id)
4069
    {
4070
        if ($this->debug > 0) {
4071
            error_log('New LP - In learnpath::open()', 0);
4072
        }
4073
        // TODO:
4074
        // set the current resource attribute to this resource
4075
        // switch on element type (redefine in child class?)
4076
        // set status for this item to "opened"
4077
        // start timer
4078
        // initialise score
4079
        $this->index = 0; //or = the last item seen (see $this->last)
4080
    }
4081
4082
    /**
4083
     * Check that all prerequisites are fulfilled. Returns true and an
4084
     * empty string on success, returns false
4085
     * and the prerequisite string on error.
4086
     * This function is based on the rules for aicc_script language as
4087
     * described in the SCORM 1.2 CAM documentation page 108.
4088
     * @param	integer	$itemId Optional item ID. If none given, uses the current open item.
4089
     * @return	boolean	True if prerequisites are matched, false otherwise -
4090
     * Empty string if true returned, prerequisites string otherwise.
4091
     */
4092
    public function prerequisites_match($itemId = null)
4093
    {
4094
        $debug = $this->debug;
4095
        if ($debug > 0) {
4096
            error_log('In learnpath::prerequisites_match()', 0);
4097
        }
4098
4099
        if (empty($itemId)) {
4100
            $itemId = $this->current;
4101
        }
4102
4103
        $currentItem = $this->getItem($itemId);
4104
4105
        if ($currentItem) {
4106
            if ($this->type == 2) {
4107
                // Getting prereq from scorm
4108
                $prereq_string = $this->get_scorm_prereq_string($itemId);
4109
            } else {
4110
                $prereq_string = $currentItem->get_prereq_string();
4111
            }
4112
4113
            if (empty($prereq_string)) {
4114
                if ($debug > 0) {
4115
                    error_log('Found prereq_string is empty return true');
4116
                }
4117
                return true;
4118
            }
4119
            // Clean spaces.
4120
            $prereq_string = str_replace(' ', '', $prereq_string);
4121
            if ($debug > 0) {
4122
                error_log('Found prereq_string: ' . $prereq_string, 0);
4123
            }
4124
            // Now send to the parse_prereq() function that will check this component's prerequisites.
4125
            $result = $currentItem->parse_prereq(
4126
                $prereq_string,
4127
                $this->items,
4128
                $this->refs_list,
4129
                $this->get_user_id()
4130
            );
4131
4132
            if ($result === false) {
4133
                $this->set_error_msg($currentItem->prereq_alert);
4134
            }
4135
        } else {
4136
            $result = true;
4137
            if ($debug > 1) {
4138
                error_log('$this->items[' . $itemId . '] was not an object', 0);
4139
            }
4140
        }
4141
4142
        if ($debug > 1) {
4143
            error_log('End of prerequisites_match(). Error message is now ' . $this->error, 0);
4144
        }
4145
        return $result;
4146
    }
4147
4148
    /**
4149
     * Updates learnpath attributes to point to the previous element
4150
     * The last part is similar to set_current_item but processing the other way around
4151
     */
4152
    public function previous()
4153
    {
4154
        if ($this->debug > 0) {
4155
            error_log('New LP - In learnpath::previous()', 0);
4156
        }
4157
        $this->last = $this->get_current_item_id();
4158
        $this->items[$this->last]->save(false, $this->prerequisites_match($this->last));
4159
        $this->autocomplete_parents($this->last);
4160
        $new_index = $this->get_previous_index();
4161
        $this->index = $new_index;
4162
        $this->current = $this->ordered_items[$new_index];
4163
    }
4164
4165
    /**
4166
     * Publishes a learnpath. This basically means show or hide the learnpath
4167
     * to normal users.
4168
     * Can be used as abstract
4169
     * @param	integer	Learnpath ID
4170
     * @param	string	New visibility
4171
     */
4172
    public static function toggle_visibility($lp_id, $set_visibility = 1)
4173
    {
4174
        $action = 'visible';
4175
        if ($set_visibility != 1) {
4176
            $action = 'invisible';
4177
            self::toggle_publish($lp_id, 'i');
4178
        }
4179
4180
        return api_item_property_update(
4181
            api_get_course_info(),
4182
            TOOL_LEARNPATH,
4183
            $lp_id,
4184
            $action,
4185
            api_get_user_id()
4186
        );
4187
    }
4188
4189
    /**
4190
     * Publishes a learnpath. This basically means show or hide the learnpath
4191
     * on the course homepage
4192
     * Can be used as abstract
4193
     * @param	integer	$lp_id Learnpath id
4194
     * @param	string	$set_visibility New visibility (v/i - visible/invisible)
4195
     * @return bool
4196
     */
4197
    public static function toggle_publish($lp_id, $set_visibility = 'v')
4198
    {
4199
        $course_id = api_get_course_int_id();
4200
        $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
4201
        $lp_id = intval($lp_id);
4202
        $sql = "SELECT * FROM $tbl_lp
4203
                WHERE c_id = ".$course_id." AND id = $lp_id";
4204
        $result = Database::query($sql);
4205
        if (Database::num_rows($result)) {
4206
            $row = Database :: fetch_array($result);
4207
            $name = domesticate($row['name']);
4208
            if ($set_visibility == 'i') {
4209
                $s = $name . " " . get_lang('LearnpathNotPublished');
4210
                $dialogBox = $s;
4211
                $v = 0;
4212
            }
4213
            if ($set_visibility == 'v') {
4214
                $s = $name . " " . get_lang('LearnpathPublished');
4215
                $dialogBox = $s;
4216
                $v = 1;
4217
            }
4218
        } else {
4219
            return false;
4220
        }
4221
4222
        $session_id = api_get_session_id();
4223
        $session_condition = api_get_session_condition($session_id);
4224
4225
        $tbl_tool = Database :: get_course_table(TABLE_TOOL_LIST);
4226
        $link = 'newscorm/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session='.$session_id;
4227
        $sql = "SELECT * FROM $tbl_tool
4228
                WHERE
4229
                    c_id = ".$course_id." AND
4230
                    link='$link' and
4231
                    image='scormbuilder.gif' and
4232
                    link LIKE '$link%'
4233
                    $session_condition
4234
                ";
4235
        $result = Database::query($sql);
4236
        $num = Database :: num_rows($result);
4237
        if ($set_visibility == 'i' && $num > 0) {
4238
            $sql = "DELETE FROM $tbl_tool
4239
                    WHERE c_id = ".$course_id." AND (link='$link' and image='scormbuilder.gif' $session_condition)";
4240
            Database::query($sql);
4241
4242
        } elseif ($set_visibility == 'v' && $num == 0) {
4243
            $sql = "INSERT INTO $tbl_tool (category, c_id, name, link, image, visibility, admin, address, added_tool, session_id) VALUES
4244
            	    ('authoring', $course_id, '$name', '$link', 'scormbuilder.gif', '$v', '0','pastillegris.gif', 0, $session_id)";
4245
            Database::query($sql);
4246
4247
            $insertId = Database::insert_id();
4248
            if ($insertId) {
4249
                $sql = "UPDATE $tbl_tool SET id = iid WHERE iid = $insertId";
4250
                Database::query($sql);
4251
            }
4252
        } elseif ($set_visibility == 'v' && $num > 0) {
4253
            $sql = "UPDATE $tbl_tool SET
4254
                        c_id = $course_id,
4255
                        name = '$name',
4256
                        link = '$link',
4257
                        image = 'scormbuilder.gif',
4258
                        visibility = '$v',
4259
                        admin = '0',
4260
                        address = 'pastillegris.gif',
4261
                        added_tool = 0,
4262
                        session_id = $session_id
4263
            	    WHERE
4264
            	        c_id = ".$course_id." AND
4265
            	        (link='$link' and image='scormbuilder.gif' $session_condition)
4266
                    ";
4267
            Database::query($sql);
4268
        } else {
4269
            // Parameter and database incompatible, do nothing, exit.
4270
            return false;
4271
        }
4272
4273
    }
4274
4275
    /**
4276
     * Restart the whole learnpath. Return the URL of the first element.
4277
     * Make sure the results are saved with anoter method. This method should probably be
4278
     * redefined in children classes.
4279
     * To use a similar method  statically, use the create_new_attempt() method
4280
     * @return string URL to load in the viewer
4281
     */
4282
    public function restart()
4283
    {
4284
        if ($this->debug > 0) {
4285
            error_log('New LP - In learnpath::restart()', 0);
4286
        }
4287
        // TODO
4288
        // Call autosave method to save the current progress.
4289
        //$this->index = 0;
4290
        if (api_is_invitee()) {
4291
            return false;
4292
        }
4293
        $session_id = api_get_session_id();
4294
        $course_id = api_get_course_int_id();
4295
        $lp_view_table = Database :: get_course_table(TABLE_LP_VIEW);
4296
        $sql = "INSERT INTO $lp_view_table (c_id, lp_id, user_id, view_count, session_id)
4297
                VALUES ($course_id, " . $this->lp_id . "," . $this->get_user_id() . "," . ($this->attempt + 1) . ", $session_id)";
4298
        if ($this->debug > 2) {
4299
            error_log('New LP - Inserting new lp_view for restart: ' . $sql, 0);
4300
        }
4301
        $res = Database::query($sql);
4302
        $view_id = Database::insert_id();
4303
4304
        if ($view_id) {
4305
4306
            $sql = "UPDATE $lp_view_table SET id = iid WHERE iid = $view_id";
4307
            Database::query($sql);
4308
4309
            $this->lp_view_id = $view_id;
4310
            $this->attempt = $this->attempt + 1;
4311
        } else {
4312
            $this->error = 'Could not insert into item_view table...';
4313
            return false;
4314
        }
4315
        $this->autocomplete_parents($this->current);
4316
        foreach ($this->items as $index => $dummy) {
4317
            $this->items[$index]->restart();
4318
            $this->items[$index]->set_lp_view($this->lp_view_id);
4319
        }
4320
        $this->first();
4321
4322
        return true;
4323
    }
4324
4325
    /**
4326
     * Saves the current item
4327
     * @return	boolean
4328
     */
4329
    public function save_current()
4330
    {
4331
        if ($this->debug > 0) {
4332
            error_log('learnpath::save_current()', 0);
4333
        }
4334
        // TODO: Do a better check on the index pointing to the right item (it is supposed to be working
4335
        // on $ordered_items[] but not sure it's always safe to use with $items[]).
4336
        if ($this->debug > 2) {
4337
            error_log('New LP - save_current() saving item ' . $this->current, 0);
4338
        }
4339
        if ($this->debug > 2) {
4340
            error_log('' . print_r($this->items, true), 0);
4341
        }
4342
        if (isset($this->items[$this->current]) &&
4343
            is_object($this->items[$this->current])
4344
        ) {
4345
            $res = $this->items[$this->current]->save(false, $this->prerequisites_match($this->current));
4346
            $this->autocomplete_parents($this->current);
4347
            $status = $this->items[$this->current]->get_status();
4348
            $this->update_queue[$this->current] = $status;
4349
            return $res;
4350
        }
4351
        return false;
4352
    }
4353
4354
    /**
4355
     * Saves the given item
4356
     * @param	integer	$item_id. Optional (will take from $_REQUEST if null)
0 ignored issues
show
Documentation introduced by
There is no parameter named $item_id.. Did you maybe mean $item_id?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

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

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

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

Loading history...
4357
     * @param	boolean	$from_outside Save from url params (true) or from current attributes (false). Optional. Defaults to true
4358
     * @return	boolean
4359
     */
4360
    public function save_item($item_id = null, $from_outside = true)
4361
    {
4362
        $debug = $this->debug;
4363
        if ($debug) {
4364
            error_log('In learnpath::save_item(' . $item_id . ',' . intval($from_outside). ')', 0);
4365
        }
4366
        // TODO: Do a better check on the index pointing to the right item (it is supposed to be working
4367
        // on $ordered_items[] but not sure it's always safe to use with $items[]).
4368
        if (empty($item_id)) {
4369
            $item_id = intval($_REQUEST['id']);
4370
        }
4371
        if (empty($item_id)) {
4372
            $item_id = $this->get_current_item_id();
4373
        }
4374
        if (isset($this->items[$item_id]) && is_object($this->items[$item_id])) {
4375
            if ($debug) {
4376
                error_log('Object exists');
4377
            }
4378
4379
            // Saving the item.
4380
            $res = $this->items[$item_id]->save(
4381
                $from_outside,
4382
                $this->prerequisites_match($item_id)
4383
            );
4384
4385
            if ($debug) {
4386
                error_log('update_queue before:');
4387
                error_log(print_r($this->update_queue,1));
4388
            }
4389
            $this->autocomplete_parents($item_id);
4390
4391
            $status = $this->items[$item_id]->get_status();
4392
            $this->update_queue[$item_id] = $status;
4393
4394
            if ($debug) {
4395
                error_log('get_status(): ' . $status);
4396
                error_log('update_queue after:');
4397
                error_log(print_r($this->update_queue,1));
4398
            }
4399
            return $res;
4400
        }
4401
        return false;
4402
    }
4403
4404
    /**
4405
     * Saves the last item seen's ID only in case
4406
     */
4407
    public function save_last()
4408
    {
4409
        $course_id = api_get_course_int_id();
4410
        if ($this->debug > 0) {
4411
            error_log('New LP - In learnpath::save_last()', 0);
4412
        }
4413
        $session_condition = api_get_session_condition(api_get_session_id(), true, false);
4414
        $table = Database :: get_course_table(TABLE_LP_VIEW);
4415
4416
        if (isset($this->current) && !api_is_invitee()) {
4417
            if ($this->debug > 2) {
4418
                error_log('New LP - Saving current item (' . $this->current . ') for later review', 0);
4419
            }
4420
            $sql = "UPDATE $table SET
4421
                        last_item = " . intval($this->get_current_item_id()). "
4422
                    WHERE
4423
                        c_id = $course_id AND
4424
                        lp_id = " . $this->get_id() . " AND
4425
                        user_id = " . $this->get_user_id()." ".$session_condition;
4426
4427
            if ($this->debug > 2) {
4428
                error_log('New LP - Saving last item seen : ' . $sql, 0);
4429
            }
4430
            Database::query($sql);
4431
        }
4432
4433
        if (!api_is_invitee()) {
4434
            // Save progress.
4435
            list($progress, $text) = $this->get_progress_bar_text('%');
0 ignored issues
show
Unused Code introduced by
The assignment to $text is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
4436
            if ($progress >= 0 && $progress <= 100) {
4437
                $progress = (int) $progress;
4438
                $sql = "UPDATE $table SET
4439
                            progress = $progress
4440
                        WHERE
4441
                            c_id = ".$course_id." AND
4442
                            lp_id = " . $this->get_id() . " AND
4443
                            user_id = " . $this->get_user_id()." ".$session_condition;
4444
                // Ignore errors as some tables might not have the progress field just yet.
4445
                Database::query($sql);
4446
                $this->progress_db = $progress;
0 ignored issues
show
Documentation Bug introduced by
The property $progress_db was declared of type string, but $progress is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
4447
            }
4448
        }
4449
    }
4450
4451
    /**
4452
     * Sets the current item ID (checks if valid and authorized first)
4453
     * @param	integer	$item_id New item ID. If not given or not authorized, defaults to current
4454
     */
4455
    public function set_current_item($item_id = null)
4456
    {
4457
        if ($this->debug > 0) {
4458
            error_log('New LP - In learnpath::set_current_item(' . $item_id . ')', 0);
4459
        }
4460
        if (empty ($item_id)) {
4461
            if ($this->debug > 2) {
4462
                error_log('New LP - No new current item given, ignore...', 0);
4463
            }
4464
            // Do nothing.
4465
        } else {
4466
            if ($this->debug > 2) {
4467
                error_log('New LP - New current item given is ' . $item_id . '...', 0);
4468
            }
4469
            if (is_numeric($item_id)) {
4470
                $item_id = intval($item_id);
4471
                // TODO: Check in database here.
4472
                $this->last = $this->current;
4473
                $this->current = $item_id;
4474
                // TODO: Update $this->index as well.
4475
                foreach ($this->ordered_items as $index => $item) {
4476
                    if ($item == $this->current) {
4477
                        $this->index = $index;
4478
                        break;
4479
                    }
4480
                }
4481 View Code Duplication
                if ($this->debug > 2) {
4482
                    error_log('New LP - set_current_item(' . $item_id . ') done. Index is now : ' . $this->index, 0);
4483
                }
4484
            } else {
4485
                error_log('New LP - set_current_item(' . $item_id . ') failed. Not a numeric value: ', 0);
4486
            }
4487
        }
4488
    }
4489
4490
    /**
4491
     * Sets the encoding
4492
     * @param	string	New encoding
4493
     * TODO (as of Chamilo 1.8.8): Check in the future whether this method is needed.
4494
     */
4495
    public function set_encoding($enc = 'UTF-8')
4496
    {
4497
        if ($this->debug > 0) {
4498
            error_log('New LP - In learnpath::set_encoding()', 0);
4499
        }
4500
4501
        $course_id = api_get_course_int_id();
4502
        $enc = api_refine_encoding_id($enc);
4503
        if (empty($enc)) {
4504
            $enc = api_get_system_encoding();
4505
        }
4506
        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 4502 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...
4507
            $lp = $this->get_id();
4508
            if ($lp != 0) {
4509
                $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
4510
                $sql = "UPDATE $tbl_lp SET default_encoding = '$enc' WHERE c_id = ".$course_id." AND id = " . $lp;
4511
                $res = Database::query($sql);
4512
                return $res;
4513
            }
4514
        }
4515
        return false;
4516
    }
4517
4518
    /**
4519
     * Sets the JS lib setting in the database directly.
4520
     * This is the JavaScript library file this lp needs to load on startup
4521
     * @param	string	Proximity setting
4522
     * @return  boolean True on update success. False otherwise.
4523
     */
4524 View Code Duplication
    public function set_jslib($lib = '')
4525
    {
4526
        if ($this->debug > 0) {
4527
            error_log('New LP - In learnpath::set_jslib()', 0);
4528
        }
4529
        $lp = $this->get_id();
4530
        $course_id = api_get_course_int_id();
4531
4532
        if ($lp != 0) {
4533
            $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
4534
            $sql = "UPDATE $tbl_lp SET js_lib = '$lib' WHERE c_id = ".$course_id." AND id = " . $lp;
4535
            $res = Database::query($sql);
4536
            return $res;
4537
        } else {
4538
            return false;
4539
        }
4540
    }
4541
4542
    /**
4543
     * Sets the name of the LP maker (publisher) (and save)
4544
     * @param	string	Optional string giving the new content_maker of this learnpath
4545
     * @return  boolean True
4546
     */
4547 View Code Duplication
    public function set_maker($name = '')
4548
    {
4549
        if ($this->debug > 0) {
4550
            error_log('New LP - In learnpath::set_maker()', 0);
4551
        }
4552
        if (empty ($name))
4553
            return false;
4554
        $this->maker = $name;
4555
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4556
        $course_id = api_get_course_int_id();
4557
        $lp_id = $this->get_id();
4558
        $sql = "UPDATE $lp_table SET
4559
                content_maker = '" . Database::escape_string($this->maker) . "'
4560
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4561
        if ($this->debug > 2) {
4562
            error_log('New LP - lp updated with new content_maker : ' . $this->maker, 0);
4563
        }
4564
        Database::query($sql);
4565
        return true;
4566
    }
4567
4568
    /**
4569
     * Sets the name of the current learnpath (and save)
4570
     * @param	string	$name Optional string giving the new name of this learnpath
4571
     * @return  boolean True/False
4572
     */
4573
    public function set_name($name = null)
4574
    {
4575
        if ($this->debug > 0) {
4576
            error_log('New LP - In learnpath::set_name()', 0);
4577
        }
4578
        if (empty($name)) {
4579
            return false;
4580
        }
4581
        $this->name = Database::escape_string($name);
4582
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4583
        $lp_id = $this->get_id();
4584
        $course_id = $this->course_info['real_id'];
4585
        $sql = "UPDATE $lp_table SET
4586
                name = '" . Database::escape_string($this->name). "'
4587
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4588
        if ($this->debug > 2) {
4589
            error_log('New LP - lp updated with new name : ' . $this->name, 0);
4590
        }
4591
        $result = Database::query($sql);
4592
        // If the lp is visible on the homepage, change his name there.
4593
        if (Database::affected_rows($result)) {
4594
            $session_id = api_get_session_id();
4595
            $session_condition = api_get_session_condition($session_id);
4596
            $tbl_tool = Database :: get_course_table(TABLE_TOOL_LIST);
4597
            $link = 'newscorm/lp_controller.php?action=view&lp_id=' . $lp_id.'&id_session='.$session_id;
4598
            $sql = "UPDATE $tbl_tool SET name = '$this->name'
4599
            	    WHERE
4600
            	        c_id = $course_id AND
4601
            	        (link='$link' AND image='scormbuilder.gif' $session_condition)";
4602
            Database::query($sql);
4603
            return true;
4604
        } else {
4605
            return false;
4606
        }
4607
    }
4608
4609
    /**
4610
     * Set index specified prefix terms for all items in this path
4611
     * @param   string  Comma-separated list of terms
4612
     * @param   char Xapian term prefix
4613
     * @return  boolean False on error, true otherwise
4614
     */
4615
    public function set_terms_by_prefix($terms_string, $prefix)
4616
    {
4617
        $course_id = api_get_course_int_id();
4618
        if (api_get_setting('search_enabled') !== 'true')
4619
            return false;
4620
4621
        if (!extension_loaded('xapian')) {
4622
            return false;
4623
        }
4624
4625
        $terms_string = trim($terms_string);
4626
        $terms = explode(',', $terms_string);
4627
        array_walk($terms, 'trim_value');
4628
4629
        $stored_terms = $this->get_common_index_terms_by_prefix($prefix);
4630
4631
        // Don't do anything if no change, verify only at DB, not the search engine.
4632 View Code Duplication
        if ((count(array_diff($terms, $stored_terms)) == 0) && (count(array_diff($stored_terms, $terms)) == 0))
4633
            return false;
4634
4635
        require_once 'xapian.php'; // TODO: Try catch every xapian use or make wrappers on API.
4636
        require_once api_get_path(LIBRARY_PATH).'search/ChamiloIndexer.class.php';
4637
        require_once api_get_path(LIBRARY_PATH).'search/xapian/XapianQuery.php';
4638
        require_once api_get_path(LIBRARY_PATH).'search/IndexableChunk.class.php';
4639
4640
        $items_table = Database :: get_course_table(TABLE_LP_ITEM);
4641
        // TODO: Make query secure agains XSS : use member attr instead of post var.
4642
        $lp_id = intval($_POST['lp_id']);
4643
        $sql = "SELECT * FROM $items_table WHERE c_id = $course_id AND lp_id = $lp_id";
4644
        $result = Database::query($sql);
4645
        $di = new ChamiloIndexer();
4646
4647
        while ($lp_item = Database :: fetch_array($result)) {
4648
            // Get search_did.
4649
            $tbl_se_ref = Database :: get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
4650
            $sql = 'SELECT * FROM %s
4651
                    WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d
4652
                    LIMIT 1';
4653
            $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp_id, $lp_item['id']);
4654
4655
            //echo $sql; echo '<br>';
4656
            $res = Database::query($sql);
4657
            if (Database::num_rows($res) > 0) {
4658
                $se_ref = Database :: fetch_array($res);
4659
4660
                // Compare terms.
4661
                $doc = $di->get_document($se_ref['search_did']);
4662
                $xapian_terms = xapian_get_doc_terms($doc, $prefix);
4663
                $xterms = array();
4664
                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...
4665
                    $xterms[] = substr($xapian_term['name'], 1);
4666
                }
4667
4668
                $dterms = $terms;
4669
4670
                $missing_terms = array_diff($dterms, $xterms);
4671
                $deprecated_terms = array_diff($xterms, $dterms);
4672
4673
                // Save it to search engine.
4674
                foreach ($missing_terms as $term) {
4675
                    $doc->add_term($prefix . $term, 1);
4676
                }
4677
                foreach ($deprecated_terms as $term) {
4678
                    $doc->remove_term($prefix . $term);
4679
                }
4680
                $di->getDb()->replace_document((int) $se_ref['search_did'], $doc);
4681
                $di->getDb()->flush();
4682
            } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches 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 else branches can be removed.

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

could be turned into

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

This is much more concise to read.

Loading history...
4683
                //@todo What we should do here?
4684
            }
4685
        }
4686
        return true;
4687
    }
4688
4689
    /**
4690
     * Sets the theme of the LP (local/remote) (and save)
4691
     * @param	string	Optional string giving the new theme of this learnpath
4692
     * @return   bool    Returns true if theme name is not empty
4693
     */
4694 View Code Duplication
    public function set_theme($name = '')
4695
    {
4696
        $course_id = api_get_course_int_id();
4697
        if ($this->debug > 0) {
4698
            error_log('New LP - In learnpath::set_theme()', 0);
4699
        }
4700
        $this->theme = $name;
4701
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4702
        $lp_id = $this->get_id();
4703
        $sql = "UPDATE $lp_table SET theme = '" . Database::escape_string($this->theme). "'
4704
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4705
        if ($this->debug > 2) {
4706
            error_log('New LP - lp updated with new theme : ' . $this->theme, 0);
4707
        }
4708
        Database::query($sql);
4709
4710
        return true;
4711
    }
4712
4713
    /**
4714
     * Sets the image of an LP (and save)
4715
     * @param	 string	Optional string giving the new image of this learnpath
4716
     * @return bool   Returns true if theme name is not empty
4717
     */
4718 View Code Duplication
    public function set_preview_image($name = '')
4719
    {
4720
        $course_id = api_get_course_int_id();
4721
        if ($this->debug > 0) {
4722
            error_log('New LP - In learnpath::set_preview_image()', 0);
4723
        }
4724
4725
        $this->preview_image = $name;
4726
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4727
        $lp_id = $this->get_id();
4728
        $sql = "UPDATE $lp_table SET
4729
                preview_image = '" . Database::escape_string($this->preview_image). "'
4730
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4731
        if ($this->debug > 2) {
4732
            error_log('New LP - lp updated with new preview image : ' . $this->preview_image, 0);
4733
        }
4734
        Database::query($sql);
4735
        return true;
4736
    }
4737
4738
    /**
4739
     * Sets the author of a LP (and save)
4740
     * @param	string	Optional string giving the new author of this learnpath
4741
     * @return   bool    Returns true if author's name is not empty
4742
     */
4743 View Code Duplication
    public function set_author($name = '')
4744
    {
4745
        $course_id = api_get_course_int_id();
4746
        if ($this->debug > 0) {
4747
            error_log('New LP - In learnpath::set_author()', 0);
4748
        }
4749
        $this->author = $name;
4750
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4751
        $lp_id = $this->get_id();
4752
        $sql = "UPDATE $lp_table SET author = '" . Database::escape_string($name). "'
4753
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4754
        if ($this->debug > 2) {
4755
            error_log('New LP - lp updated with new preview author : ' . $this->author, 0);
4756
        }
4757
        Database::query($sql);
4758
4759
        return true;
4760
    }
4761
4762
    /**
4763
     * Sets the hide_toc_frame parameter of a LP (and save)
4764
     * @param	int	1 if frame is hidden 0 then else
4765
     * @return   bool    Returns true if author's name is not empty
4766
     */
4767 View Code Duplication
    public function set_hide_toc_frame($hide)
4768
    {
4769
        $course_id = api_get_course_int_id();
4770
        if ($this->debug > 0) {
4771
            error_log('New LP - In learnpath::set_hide_toc_frame()', 0);
4772
        }
4773
        if (intval($hide) == $hide){
4774
            $this->hide_toc_frame = $hide;
4775
            $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4776
            $lp_id = $this->get_id();
4777
            $sql = "UPDATE $lp_table SET
4778
                    hide_toc_frame = '" . $this->hide_toc_frame . "'
4779
                    WHERE c_id = ".$course_id." AND id = '$lp_id'";
4780
            if ($this->debug > 2) {
4781
                error_log('New LP - lp updated with new preview hide_toc_frame : ' . $this->author, 0);
4782
            }
4783
            Database::query($sql);
4784
4785
            return true;
4786
        } else {
4787
            return false;
4788
        }
4789
    }
4790
4791
    /**
4792
     * Sets the prerequisite of a LP (and save)
4793
     * @param	int		integer giving the new prerequisite of this learnpath
4794
     * @return 	bool 	returns true if prerequisite is not empty
4795
     */
4796 View Code Duplication
    public function set_prerequisite($prerequisite)
4797
    {
4798
        $course_id = api_get_course_int_id();
4799
        if ($this->debug > 0) {
4800
            error_log('New LP - In learnpath::set_prerequisite()', 0);
4801
        }
4802
        $this->prerequisite = intval($prerequisite);
4803
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4804
        $lp_id = $this->get_id();
4805
        $sql = "UPDATE $lp_table SET prerequisite = '".$this->prerequisite."'
4806
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4807
        if ($this->debug > 2) {
4808
            error_log('New LP - lp updated with new preview requisite : ' . $this->requisite, 0);
4809
        }
4810
        Database::query($sql);
4811
        return true;
4812
    }
4813
4814
    /**
4815
     * Sets the location/proximity of the LP (local/remote) (and save)
4816
     * @param	string	Optional string giving the new location of this learnpath
4817
     * @return  boolean True on success / False on error
4818
     */
4819
    public function set_proximity($name = '')
4820
    {
4821
        $course_id = api_get_course_int_id();
4822
        if ($this->debug > 0) {
4823
            error_log('New LP - In learnpath::set_proximity()', 0);
4824
        }
4825
        if (empty ($name))
4826
            return false;
4827
4828
        $this->proximity = $name;
4829
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4830
        $lp_id = $this->get_id();
4831
        $sql = "UPDATE $lp_table SET
4832
                    content_local = '" . Database::escape_string($name) . "'
4833
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4834
        if ($this->debug > 2) {
4835
            error_log('New LP - lp updated with new proximity : ' . $this->proximity, 0);
4836
        }
4837
        Database::query($sql);
4838
        return true;
4839
    }
4840
4841
    /**
4842
     * Sets the previous item ID to a given ID. Generally, this should be set to the previous 'current' item
4843
     * @param	integer	DB ID of the item
4844
     */
4845
    public function set_previous_item($id)
4846
    {
4847
        if ($this->debug > 0) {
4848
            error_log('New LP - In learnpath::set_previous_item()', 0);
4849
        }
4850
        $this->last = $id;
4851
    }
4852
4853
    /**
4854
     * Sets use_max_score
4855
     * @param   string  $use_max_score Optional string giving the new location of this learnpath
4856
     * @return  boolean True on success / False on error
4857
     */
4858
    public function set_use_max_score($use_max_score = 1)
4859
    {
4860
        $course_id = api_get_course_int_id();
4861
        if ($this->debug > 0) {
4862
            error_log('New LP - In learnpath::set_use_max_score()', 0);
4863
        }
4864
        $use_max_score = intval($use_max_score);
4865
        $this->use_max_score = $use_max_score;
4866
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4867
        $lp_id = $this->get_id();
4868
        $sql = "UPDATE $lp_table SET
4869
                    use_max_score = '" . $this->use_max_score . "'
4870
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4871
4872
        if ($this->debug > 2) {
4873
            error_log('New LP - lp updated with new use_max_score : ' . $this->use_max_score, 0);
4874
        }
4875
        Database::query($sql);
4876
4877
        return true;
4878
    }
4879
4880
    /**
4881
     * Sets and saves the expired_on date
4882
     * @param   string  $expired_on Optional string giving the new author of this learnpath
4883
     * @return   bool    Returns true if author's name is not empty
4884
     */
4885 View Code Duplication
    public function set_expired_on($expired_on)
4886
    {
4887
        $course_id = api_get_course_int_id();
4888
        if ($this->debug > 0) {
4889
            error_log('New LP - In learnpath::set_expired_on()', 0);
4890
        }
4891
4892
        if (!empty($expired_on)) {
4893
            $this->expired_on = api_get_utc_datetime($expired_on);
4894
        } else {
4895
            $this->expired_on = '';
4896
        }
4897
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4898
        $lp_id = $this->get_id();
4899
        $sql = "UPDATE $lp_table SET
4900
                expired_on = '" . Database::escape_string($this->expired_on) . "'
4901
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4902
        if ($this->debug > 2) {
4903
            error_log('New LP - lp updated with new expired_on : ' . $this->expired_on, 0);
4904
        }
4905
        Database::query($sql);
4906
4907
        return true;
4908
    }
4909
4910
    /**
4911
     * Sets and saves the publicated_on date
4912
     * @param   string  $publicated_on Optional string giving the new author of this learnpath
4913
     * @return   bool    Returns true if author's name is not empty
4914
     */
4915 View Code Duplication
    public function set_publicated_on($publicated_on)
4916
    {
4917
        $course_id = api_get_course_int_id();
4918
        if ($this->debug > 0) {
4919
            error_log('New LP - In learnpath::set_expired_on()', 0);
4920
        }
4921
        if (!empty($publicated_on)) {
4922
            $this->publicated_on = api_get_utc_datetime($publicated_on);
4923
        } else {
4924
            $this->publicated_on = '';
4925
        }
4926
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4927
        $lp_id = $this->get_id();
4928
        $sql = "UPDATE $lp_table SET
4929
                publicated_on = '" . Database::escape_string($this->publicated_on) . "'
4930
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4931
        if ($this->debug > 2) {
4932
            error_log('New LP - lp updated with new publicated_on : ' . $this->publicated_on, 0);
4933
        }
4934
        Database::query($sql);
4935
4936
        return true;
4937
    }
4938
4939
    /**
4940
     * Sets and saves the expired_on date
4941
     * @return   bool    Returns true if author's name is not empty
4942
     */
4943 View Code Duplication
    public function set_modified_on()
4944
    {
4945
        $course_id = api_get_course_int_id();
4946
        if ($this->debug > 0) {
4947
            error_log('New LP - In learnpath::set_expired_on()', 0);
4948
        }
4949
        $this->modified_on = api_get_utc_datetime();
4950
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
4951
        $lp_id = $this->get_id();
4952
        $sql = "UPDATE $lp_table SET modified_on = '" . $this->modified_on . "'
4953
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
4954
        if ($this->debug > 2) {
4955
            error_log('New LP - lp updated with new expired_on : ' . $this->modified_on, 0);
4956
        }
4957
        Database::query($sql);
4958
        return true;
4959
    }
4960
4961
    /**
4962
     * Sets the object's error message
4963
     * @param	string	Error message. If empty, reinits the error string
4964
     * @return 	void
4965
     */
4966
    public function set_error_msg($error = '')
4967
    {
4968
        if ($this->debug > 0) {
4969
            error_log('New LP - In learnpath::set_error_msg()', 0);
4970
        }
4971
        if (empty ($error)) {
4972
            $this->error = '';
4973
        } else {
4974
            $this->error .= $error;
4975
        }
4976
    }
4977
4978
    /**
4979
     * Launches the current item if not 'sco'
4980
     * (starts timer and make sure there is a record ready in the DB)
4981
     * @param  boolean  $allow_new_attempt Whether to allow a new attempt or not
4982
     * @return boolean
4983
     */
4984
    public function start_current_item($allow_new_attempt = false)
4985
    {
4986
        if ($this->debug > 0) {
4987
            error_log('New LP - In learnpath::start_current_item()', 0);
4988
        }
4989
        if ($this->current != 0 && is_object($this->items[$this->current])) {
4990
            $type = $this->get_type();
4991
            $item_type = $this->items[$this->current]->get_type();
4992
            if (($type == 2 && $item_type != 'sco') ||
4993
                ($type == 3 && $item_type != 'au') ||
4994
                ($type == 1 && $item_type != TOOL_QUIZ && $item_type != TOOL_HOTPOTATOES)
4995
            ) {
4996
                $this->items[$this->current]->open($allow_new_attempt);
4997
                $this->autocomplete_parents($this->current);
4998
                $prereq_check = $this->prerequisites_match($this->current);
4999
                $this->items[$this->current]->save(false, $prereq_check);
5000
                //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
5001
            } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches 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 else branches can be removed.

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

could be turned into

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

This is much more concise to read.

Loading history...
5002
                // If sco, then it is supposed to have been updated by some other call.
5003
            }
5004
            if ($item_type == 'sco') {
5005
                $this->items[$this->current]->restart();
5006
            }
5007
        }
5008
        if ($this->debug > 0) {
5009
            error_log('New LP - End of learnpath::start_current_item()', 0);
5010
        }
5011
        return true;
5012
    }
5013
5014
    /**
5015
     * Stops the processing and counters for the old item (as held in $this->last)
5016
     * @return boolean  True/False
5017
     */
5018
    public function stop_previous_item()
5019
    {
5020
        if ($this->debug > 0) {
5021
            error_log('New LP - In learnpath::stop_previous_item()', 0);
5022
        }
5023
5024
        if ($this->last != 0 && $this->last != $this->current && is_object($this->items[$this->last])) {
5025
            if ($this->debug > 2) {
5026
                error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' is object', 0);
5027
            }
5028
            switch ($this->get_type()) {
5029 View Code Duplication
                case '3' :
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
5030
                    if ($this->items[$this->last]->get_type() != 'au') {
5031
                        if ($this->debug > 2) {
5032
                            error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' in lp_type 3 is <> au', 0);
5033
                        }
5034
                        $this->items[$this->last]->close();
5035
                        //$this->autocomplete_parents($this->last);
5036
                        //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
5037
                    } else {
5038
                        if ($this->debug > 2) {
5039
                            error_log('New LP - In learnpath::stop_previous_item() - Item is an AU, saving is managed by AICC signals', 0);
5040
                        }
5041
                    }
5042 View Code Duplication
                case '2' :
5043
                    if ($this->items[$this->last]->get_type() != 'sco') {
5044
                        if ($this->debug > 2) {
5045
                            error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' in lp_type 2 is <> sco', 0);
5046
                        }
5047
                        $this->items[$this->last]->close();
5048
                        //$this->autocomplete_parents($this->last);
5049
                        //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
5050
                    } else {
5051
                        if ($this->debug > 2) {
5052
                            error_log('New LP - In learnpath::stop_previous_item() - Item is a SCO, saving is managed by SCO signals', 0);
5053
                        }
5054
                    }
5055
                    break;
5056
                case '1' :
5057
                default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
5058
                    if ($this->debug > 2) {
5059
                        error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' in lp_type 1 is asset', 0);
5060
                    }
5061
                    $this->items[$this->last]->close();
5062
                    break;
5063
            }
5064
        } else {
5065
            if ($this->debug > 2) {
5066
                error_log('New LP - In learnpath::stop_previous_item() - No previous element found, ignoring...', 0);
5067
            }
5068
            return false;
5069
        }
5070
        return true;
5071
    }
5072
5073
    /**
5074
     * Updates the default view mode from fullscreen to embedded and inversely
5075
     * @return	string The current default view mode ('fullscreen' or 'embedded')
5076
     */
5077
    public function update_default_view_mode()
5078
    {
5079
        $course_id = api_get_course_int_id();
5080
        if ($this->debug > 0) {
5081
            error_log('New LP - In learnpath::update_default_view_mode()', 0);
5082
        }
5083
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
5084
        $sql = "SELECT * FROM $lp_table
5085
                WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5086
        $res = Database::query($sql);
5087
        if (Database :: num_rows($res) > 0) {
5088
            $row = Database :: fetch_array($res);
5089
            $default_view_mode = $row['default_view_mod'];
5090
            $view_mode = $default_view_mode;
5091
            switch ($default_view_mode) {
5092
                case 'fullscreen': // default with popup
5093
                    $view_mode = 'embedded';
5094
                    break;
5095
                case 'embedded': // default view with left menu
5096
                    $view_mode = 'embedframe';
5097
                    break;
5098
                case 'embedframe': //folded menu
5099
                    $view_mode = 'impress';
5100
                    break;
5101
                case 'impress':
5102
                    $view_mode = 'fullscreen';
5103
                    break;
5104
            }
5105
            $sql = "UPDATE $lp_table SET default_view_mod = '$view_mode'
5106
                    WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5107
            Database::query($sql);
5108
            $this->mode = $view_mode;
5109
5110
            return $view_mode;
5111
        } else {
5112
            if ($this->debug > 2) {
5113
                error_log('New LP - Problem in update_default_view() - could not find LP ' . $this->get_id() . ' in DB', 0);
5114
            }
5115
        }
5116
        return -1;
5117
    }
5118
5119
    /**
5120
     * Updates the default behaviour about auto-commiting SCORM updates
5121
     * @return	boolean	True if auto-commit has been set to 'on', false otherwise
5122
     */
5123
    public function update_default_scorm_commit()
5124
    {
5125
        $course_id = api_get_course_int_id();
5126
        if ($this->debug > 0) {
5127
            error_log('New LP - In learnpath::update_default_scorm_commit()', 0);
5128
        }
5129
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
5130
        $sql = "SELECT * FROM $lp_table
5131
                WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5132
        $res = Database::query($sql);
5133
        if (Database :: num_rows($res) > 0) {
5134
            $row = Database :: fetch_array($res);
5135
            $force = $row['force_commit'];
5136
            if ($force == 1) {
5137
                $force = 0;
5138
                $force_return = false;
5139
            } elseif ($force == 0) {
5140
                $force = 1;
5141
                $force_return = true;
5142
            }
5143
            $sql = "UPDATE $lp_table SET force_commit = $force
5144
                    WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5145
            Database::query($sql);
5146
            $this->force_commit = $force_return;
5147
5148
            return $force_return;
5149
        } else {
5150
            if ($this->debug > 2) {
5151
                error_log('New LP - Problem in update_default_scorm_commit() - could not find LP ' . $this->get_id() . ' in DB', 0);
5152
            }
5153
        }
5154
        return -1;
5155
    }
5156
5157
    /**
5158
     * Updates the order of learning paths (goes through all of them by order and fills the gaps)
5159
     * @return	bool	True on success, false on failure
5160
     */
5161
    public function update_display_order()
5162
    {
5163
        $course_id = api_get_course_int_id();
5164
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
5165
5166
        $sql = "SELECT * FROM $lp_table WHERE c_id = ".$course_id." ORDER BY display_order";
5167
        $res = Database::query($sql);
5168
        if ($res === false)
5169
            return false;
5170
5171
        $num = Database :: num_rows($res);
5172
        // First check the order is correct, globally (might be wrong because
5173
        // of versions < 1.8.4).
5174
        if ($num > 0) {
5175
            $i = 1;
5176
            while ($row = Database :: fetch_array($res)) {
5177
                if ($row['display_order'] != $i) { // If we find a gap in the order, we need to fix it.
5178
                    $need_fix = true;
5179
                    $sql = "UPDATE $lp_table SET display_order = $i
5180
                            WHERE c_id = ".$course_id." AND id = " . $row['id'];
5181
                    Database::query($sql);
5182
                }
5183
                $i++;
5184
            }
5185
        }
5186
        return true;
5187
    }
5188
5189
    /**
5190
     * Updates the "prevent_reinit" value that enables control on reinitialising items on second view
5191
     * @return	boolean	True if prevent_reinit has been set to 'on', false otherwise (or 1 or 0 in this case)
5192
     */
5193 View Code Duplication
    public function update_reinit()
5194
    {
5195
        $course_id = api_get_course_int_id();
5196
        if ($this->debug > 0) {
5197
            error_log('New LP - In learnpath::update_reinit()', 0);
5198
        }
5199
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
5200
        $sql = "SELECT * FROM $lp_table
5201
                WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5202
        $res = Database::query($sql);
5203
        if (Database :: num_rows($res) > 0) {
5204
            $row = Database :: fetch_array($res);
5205
            $force = $row['prevent_reinit'];
5206
            if ($force == 1) {
5207
                $force = 0;
5208
            } elseif ($force == 0) {
5209
                $force = 1;
5210
            }
5211
            $sql = "UPDATE $lp_table SET prevent_reinit = $force
5212
                    WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5213
            Database::query($sql);
5214
            $this->prevent_reinit = $force;
5215
            return $force;
5216
        } else {
5217
            if ($this->debug > 2) {
5218
                error_log('New LP - Problem in update_reinit() - could not find LP ' . $this->get_id() . ' in DB', 0);
5219
            }
5220
        }
5221
        return -1;
5222
    }
5223
5224
    /**
5225
     * Determine the attempt_mode thanks to prevent_reinit and seriousgame_mode db flag
5226
     *
5227
     * @return string 'single', 'multi' or 'seriousgame'
5228
     * @author ndiechburg <[email protected]>
5229
     **/
5230
    public function get_attempt_mode()
5231
    {
5232
        //Set default value for seriousgame_mode
5233
        if (!isset($this->seriousgame_mode)) {
5234
            $this->seriousgame_mode=0;
5235
        }
5236
        // Set default value for prevent_reinit
5237
        if (!isset($this->prevent_reinit)) {
5238
            $this->prevent_reinit =1;
5239
        }
5240
        if ($this->seriousgame_mode == 1 && $this->prevent_reinit == 1) {
5241
            return 'seriousgame';
5242
        }
5243
        if ($this->seriousgame_mode == 0 && $this->prevent_reinit == 1) {
5244
            return 'single';
5245
        }
5246
        if ($this->seriousgame_mode == 0 && $this->prevent_reinit == 0) {
5247
            return 'multiple';
5248
        }
5249
        return 'single';
5250
    }
5251
5252
    /**
5253
     * Register the attempt mode into db thanks to flags prevent_reinit and seriousgame_mode flags
5254
     *
5255
     * @param string 'seriousgame', 'single' or 'multiple'
5256
     * @return boolean
5257
     * @author ndiechburg <[email protected]>
5258
     **/
5259
    public function set_attempt_mode($mode)
5260
    {
5261
        $course_id = api_get_course_int_id();
5262
        switch ($mode) {
5263
            case 'seriousgame' :
5264
                $sg_mode = 1;
5265
                $prevent_reinit = 1;
5266
                break;
5267
            case 'single' :
5268
                $sg_mode = 0;
5269
                $prevent_reinit = 1;
5270
                break;
5271
            case 'multiple' :
5272
                $sg_mode = 0;
5273
                $prevent_reinit = 0;
5274
                break;
5275
            default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
5276
                $sg_mode = 0;
5277
                $prevent_reinit = 0;
5278
                break;
5279
        }
5280
        $this->prevent_reinit = $prevent_reinit;
5281
        $this->seriousgame_mode = $sg_mode;
5282
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
5283
        $sql = "UPDATE $lp_table SET
5284
                prevent_reinit = $prevent_reinit ,
5285
                seriousgame_mode = $sg_mode
5286
                WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5287
        $res = Database::query($sql);
5288
        if ($res) {
5289
            return true;
5290
        } else {
5291
            return false;
5292
        }
5293
    }
5294
5295
    /**
5296
     * Switch between multiple attempt, single attempt or serious_game mode (only for scorm)
5297
     *
5298
     * @return boolean
5299
     * @author ndiechburg <[email protected]>
5300
     **/
5301
    public function switch_attempt_mode()
5302
    {
5303
        if ($this->debug > 0) {
5304
            error_log('New LP - In learnpath::switch_attempt_mode()', 0);
5305
        }
5306
        $mode = $this->get_attempt_mode();
5307
        switch ($mode) {
5308
            case 'single' :
5309
                $next_mode = 'multiple';
5310
                break;
5311
            case 'multiple' :
5312
                $next_mode = 'seriousgame';
5313
                break;
5314
            case 'seriousgame' :
5315
                $next_mode = 'single';
5316
                break;
5317
            default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
5318
                $next_mode = 'single';
5319
                break;
5320
        }
5321
        $this->set_attempt_mode($next_mode);
5322
    }
5323
5324
    /**
5325
     * Switch the lp in ktm mode. This is a special scorm mode with unique attempt
5326
     * but possibility to do again a completed item.
5327
     *
5328
     * @return boolean true if seriousgame_mode has been set to 1, false otherwise
5329
     * @author ndiechburg <[email protected]>
5330
     **/
5331 View Code Duplication
    public function set_seriousgame_mode()
5332
    {
5333
        $course_id = api_get_course_int_id();
5334
        if ($this->debug > 0) {
5335
            error_log('New LP - In learnpath::set_seriousgame_mode()', 0);
5336
        }
5337
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
5338
        $sql = "SELECT * FROM $lp_table WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5339
        $res = Database::query($sql);
5340
        if (Database :: num_rows($res) > 0) {
5341
            $row = Database :: fetch_array($res);
5342
            $force = $row['seriousgame_mode'];
5343
            if ($force == 1) {
5344
                $force = 0;
5345
            } elseif ($force == 0) {
5346
                $force = 1;
5347
            }
5348
            $sql = "UPDATE $lp_table SET seriousgame_mode = $force
5349
			        WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5350
            Database::query($sql);
5351
            $this->seriousgame_mode = $force;
5352
            return $force;
5353
        } else {
5354
            if ($this->debug > 2) {
5355
                error_log('New LP - Problem in set_seriousgame_mode() - could not find LP ' . $this->get_id() . ' in DB', 0);
5356
            }
5357
        }
5358
        return -1;
5359
    }
5360
5361
    /**
5362
     * Updates the "scorm_debug" value that shows or hide the debug window
5363
     * @return	boolean	True if scorm_debug has been set to 'on', false otherwise (or 1 or 0 in this case)
5364
     */
5365 View Code Duplication
    public function update_scorm_debug()
5366
    {
5367
        $course_id = api_get_course_int_id();
5368
        if ($this->debug > 0) {
5369
            error_log('New LP - In learnpath::update_scorm_debug()', 0);
5370
        }
5371
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
5372
        $sql = "SELECT * FROM $lp_table
5373
                WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5374
        $res = Database::query($sql);
5375
        if (Database :: num_rows($res) > 0) {
5376
            $row = Database :: fetch_array($res);
5377
            $force = $row['debug'];
5378
            if ($force == 1) {
5379
                $force = 0;
5380
            } elseif ($force == 0) {
5381
                $force = 1;
5382
            }
5383
            $sql = "UPDATE $lp_table SET debug = $force
5384
                    WHERE c_id = ".$course_id." AND id = " . $this->get_id();
5385
            $res = Database::query($sql);
5386
            $this->scorm_debug = $force;
5387
            return $force;
5388
        } else {
5389
            if ($this->debug > 2) {
5390
                error_log('New LP - Problem in update_scorm_debug() - could not find LP ' . $this->get_id() . ' in DB', 0);
5391
            }
5392
        }
5393
        return -1;
5394
    }
5395
5396
    /**
5397
     * Function that makes a call to the function sort_tree_array and create_tree_array
5398
     * @author Kevin Van Den Haute
5399
     * @param  array
5400
     */
5401
    public function tree_array($array)
5402
    {
5403
        if ($this->debug > 1) {
5404
            error_log('New LP - In learnpath::tree_array()', 0);
5405
        }
5406
        $array = $this->sort_tree_array($array);
5407
        $this->create_tree_array($array);
5408
    }
5409
5410
    /**
5411
     * Creates an array with the elements of the learning path tree in it
5412
     *
5413
     * @author Kevin Van Den Haute
5414
     * @param array $array
5415
     * @param int $parent
5416
     * @param int $depth
5417
     * @param array $tmp
5418
     */
5419
    public function create_tree_array($array, $parent = 0, $depth = -1, $tmp = array ())
5420
    {
5421
        if ($this->debug > 1) {
5422
            error_log('New LP - In learnpath::create_tree_array())', 0);
5423
        }
5424
5425
        if (is_array($array)) {
5426
            for ($i = 0; $i < count($array); $i++) {
5427
                if ($array[$i]['parent_item_id'] == $parent) {
5428
                    if (!in_array($array[$i]['parent_item_id'], $tmp)) {
5429
                        $tmp[] = $array[$i]['parent_item_id'];
5430
                        $depth++;
5431
                    }
5432
                    $preq = (empty($array[$i]['prerequisite']) ? '' : $array[$i]['prerequisite']);
5433
                    $audio = isset($array[$i]['audio']) ? $array[$i]['audio'] : null;
5434
                    $path = isset($array[$i]['path']) ? $array[$i]['path'] : null;
5435
5436
                    $prerequisiteMinScore = isset($array[$i]['prerequisite_min_score']) ? $array[$i]['prerequisite_min_score'] : null;
5437
                    $prerequisiteMaxScore = isset($array[$i]['prerequisite_max_score']) ? $array[$i]['prerequisite_max_score'] : null;
5438
                    $ref = isset($array[$i]['ref']) ? $array[$i]['ref'] : '';
5439
                    $this->arrMenu[] = array(
5440
                        'id' => $array[$i]['id'],
5441
                        'ref' => $ref,
5442
                        'item_type' => $array[$i]['item_type'],
5443
                        'title' => $array[$i]['title'],
5444
                        'path' => $path,
5445
                        'description' => $array[$i]['description'],
5446
                        'parent_item_id' => $array[$i]['parent_item_id'],
5447
                        'previous_item_id' => $array[$i]['previous_item_id'],
5448
                        'next_item_id' => $array[$i]['next_item_id'],
5449
                        'min_score' => $array[$i]['min_score'],
5450
                        'max_score' => $array[$i]['max_score'],
5451
                        'mastery_score' => $array[$i]['mastery_score'],
5452
                        'display_order' => $array[$i]['display_order'],
5453
                        'prerequisite' => $preq,
5454
                        'depth' => $depth,
5455
                        'audio' => $audio,
5456
                        'prerequisite_min_score' => $prerequisiteMinScore,
5457
                        'prerequisite_max_score' => $prerequisiteMaxScore
5458
                    );
5459
5460
                    $this->create_tree_array($array, $array[$i]['id'], $depth, $tmp);
5461
                }
5462
            }
5463
        }
5464
    }
5465
5466
    /**
5467
     * Sorts a multi dimensional array by parent id and display order
5468
     * @author Kevin Van Den Haute
5469
     *
5470
     * @param array $array (array with al the learning path items in it)
5471
     *
5472
     * @return array
5473
     */
5474
    public function sort_tree_array($array) {
5475
        foreach ($array as $key => $row) {
5476
            $parent[$key] = $row['parent_item_id'];
5477
            $position[$key] = $row['display_order'];
5478
        }
5479
5480
        if (count($array) > 0)
5481
            array_multisort($parent, SORT_ASC, $position, SORT_ASC, $array);
5482
5483
        return $array;
5484
    }
5485
5486
    /**
5487
     * Function that creates a html list of learning path items so that we can add audio files to them
5488
     * @author Kevin Van Den Haute
5489
     * @param int $lp_id
0 ignored issues
show
Bug introduced by
There is no parameter named $lp_id. Was it maybe removed?

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

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

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

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

Loading history...
5490
     * @return string
5491
     */
5492
    public function overview()
5493
    {
5494
        if ($this->debug > 0) {
5495
            error_log('New LP - In learnpath::overview()', 0);
5496
        }
5497
5498
        $_SESSION['gradebook'] = isset($_GET['gradebook']) ? Security :: remove_XSS($_GET['gradebook']) : null;
5499
        $return = '';
5500
5501
        $update_audio = isset($_GET['updateaudio']) ? $_GET['updateaudio'] : null;
5502
5503
        // we need to start a form when we want to update all the mp3 files
5504
        if ($update_audio == 'true') {
5505
            $return .= '<form action="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&updateaudio=' . Security :: remove_XSS($_GET['updateaudio']) .'&action=' . Security :: remove_XSS($_GET['action']) . '&lp_id=' . $_SESSION['oLP']->lp_id . '" method="post" enctype="multipart/form-data" name="updatemp3" id="updatemp3">';
5506
        }
5507
        $return .= '<div id="message"></div>';
5508
        if (count($this->items) == 0) {
5509
            $return .= Display::display_normal_message(get_lang('YouShouldAddItemsBeforeAttachAudio'));
5510
        } else {
5511
            $return_audio = '<table class="data_table">';
5512
            $return_audio .= '<tr>';
5513
            $return_audio .= '<th width="40%">' . get_lang('Title') . '</th>';
5514
            $return_audio .= '<th>' . get_lang('Audio') . '</th>';
5515
            $return_audio .= '</tr>';
5516
5517
            if ($update_audio != 'true') {
5518
                $return .= '<div class="col-md-12">';
5519
                $return .= self::return_new_tree($update_audio);
5520
                $return .='</div>';
5521
                $return .= Display::div(Display::url(get_lang('Save'), '#', array('id'=>'listSubmit', 'class'=>'btn btn-primary')), array('style'=>'float:left; margin-top:15px;width:100%'));
5522
            } else {
5523
                $return_audio .= self::return_new_tree($update_audio);
5524
                $return .= $return_audio.'</table>';
5525
            }
5526
5527
            // We need to close the form when we are updating the mp3 files.
5528
            if ($update_audio == 'true') {
5529
                $return .= '<div class="footer-audio">';
5530
                $return .= Display::button('save_audio','<em class="fa fa-file-audio-o"></em> '. get_lang('SaveAudioAndOrganization'),array('class'=>'btn btn-primary','type'=>'submit'));
5531
                $return .= '</div>';
5532
                //$return .= '<div><button class="btn btn-primary" type="submit" name="save_audio" id="save_audio">' . get_lang('SaveAudioAndOrganization') . '</button></div>'; // TODO: What kind of language variable is this?
5533
            }
5534
        }
5535
5536
        // We need to close the form when we are updating the mp3 files.
5537
        if ($update_audio == 'true' && count($this->arrMenu) != 0) {
5538
            $return .= '</form>';
5539
        }
5540
        return $return;
5541
    }
5542
5543
    /**
5544
     * @param string string $update_audio
5545
     * @param bool $drop_element_here
5546
     * @return string
5547
     */
5548
    public function return_new_tree($update_audio = 'false', $drop_element_here = false)
5549
    {
5550
        $return = '';
5551
        $is_allowed_to_edit = api_is_allowed_to_edit(null,true);
5552
5553
        $course_id = api_get_course_int_id();
5554
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
5555
5556
        $sql = "SELECT * FROM $tbl_lp_item
5557
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;
5558
5559
        $result = Database::query($sql);
5560
        $arrLP = array();
5561
        while ($row = Database :: fetch_array($result)) {
5562
5563
            $arrLP[] = array(
5564
                'id' => $row['id'],
5565
                'item_type' => $row['item_type'],
5566
                'title' => Security :: remove_XSS($row['title']),
5567
                'path' => $row['path'],
5568
                'description' => Security::remove_XSS($row['description']),
5569
                'parent_item_id' => $row['parent_item_id'],
5570
                'previous_item_id' => $row['previous_item_id'],
5571
                'next_item_id' => $row['next_item_id'],
5572
                'max_score' => $row['max_score'],
5573
                'min_score' => $row['min_score'],
5574
                'mastery_score' => $row['mastery_score'],
5575
                'prerequisite' => $row['prerequisite'],
5576
                'display_order' => $row['display_order'],
5577
                'audio' => $row['audio'],
5578
                'prerequisite_max_score' => $row['prerequisite_max_score'],
5579
                'prerequisite_min_score' => $row['prerequisite_min_score']
5580
            );
5581
        }
5582
5583
        $this->tree_array($arrLP);
5584
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
5585
        unset ($this->arrMenu);
5586
        $default_data = null;
5587
        $default_content = null;
5588
5589
        $elements = array();
5590
        $return_audio = null;
5591
5592
        for ($i = 0; $i < count($arrLP); $i++) {
5593
            $title = $arrLP[$i]['title'];
5594
5595
            $title_cut = cut($arrLP[$i]['title'], 25);
5596
5597
            // Link for the documents
5598
            if ($arrLP[$i]['item_type'] == 'document') {
5599
                $url = api_get_self() . '?'.api_get_cidreq().'&action=view_item&mode=preview_document&id=' . $arrLP[$i]['id'] . '&lp_id=' . $this->lp_id;
5600
                $title_cut = Display::url(
5601
                    $title_cut,
5602
                    $url,
5603
                    array(
5604
                        'class' => 'ajax moved',
5605
                        'data-title' => $title_cut
5606
                    )
5607
                );
5608
            }
5609
5610
            // Detect if type is FINAL_ITEM to set path_id to SESSION
5611
            if ($arrLP[$i]['item_type'] == TOOL_LP_FINAL_ITEM) {
5612
                $_SESSION['pathItem'] = $arrLP[$i]['path'];
5613
            }
5614
5615
            if (($i % 2) == 0) {
5616
                $oddClass = 'row_odd';
5617
            } else {
5618
                $oddClass = 'row_even';
5619
            }
5620
            $return_audio .= '<tr id ="lp_item_'.$arrLP[$i]['id'] .'" class="' . $oddClass . '">';
5621
5622
            $icon_name = str_replace(' ', '', $arrLP[$i]['item_type']);
5623
5624
            if (file_exists('../img/lp_' . $icon_name . '.png')) {
5625
                $icon = Display::return_icon('lp_'.$icon_name.'.png');
5626
            } else {
5627
                if (file_exists('../img/lp_' . $icon_name . '.gif')) {
5628
                    $icon = Display::return_icon('lp_'.$icon_name.'.gif');
5629
                } else {
5630
                    if ($arrLP[$i]['item_type'] === TOOL_LP_FINAL_ITEM) {
5631
                        $icon = Display::return_icon('certificate.png');
5632
                    } else {
5633
                        $icon = Display::return_icon('folder_document.gif');
5634
                    }
5635
                }
5636
            }
5637
5638
            // The audio column.
5639
            $return_audio  .= '<td align="left" style="padding-left:10px;">';
5640
            $audio = '';
5641
5642
            if (!$update_audio || $update_audio <> 'true') {
5643
                if (!empty($arrLP[$i]['audio'])) {
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...
5644
                } else {
5645
                    $audio .= '';
5646
                }
5647
            } else {
5648
                $types = self::getChapterTypes();
5649
                if (!in_array($arrLP[$i]['item_type'], $types)) {
5650
                    $audio .= '<input type="file" name="mp3file' . $arrLP[$i]['id'] . '" id="mp3file" />';
5651
                    if (!empty ($arrLP[$i]['audio'])) {
5652
                        $audio .= '<br />'.Security::remove_XSS($arrLP[$i]['audio']).'<br />
5653
                        <input type="checkbox" name="removemp3' . $arrLP[$i]['id'] . '" id="checkbox' . $arrLP[$i]['id'] . '" />' . get_lang('RemoveAudio');
5654
                    }
5655
                }
5656
            }
5657
            $return_audio .= Display::span($icon.' '.$title).Display::tag('td', $audio, array('style'=>''));
5658
            $return_audio .= '</td>';
5659
            $move_icon = '';
5660
            $move_item_icon = '';
5661
            $edit_icon = '';
5662
            $delete_icon = '';
5663
            $audio_icon = '';
5664
            $prerequisities_icon = '';
5665
            $forumIcon = '';
5666
5667
            if ($is_allowed_to_edit) {
5668
                if (!$update_audio || $update_audio <> 'true') {
5669 View Code Duplication
                    if ($arrLP[$i]['item_type'] !== TOOL_LP_FINAL_ITEM) {
5670
                        $move_icon .= '<a class="moved" href="#">';
5671
                        $move_icon .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
5672
                        $move_icon .= '</a>';
5673
                    }
5674
                }
5675
5676
                // No edit for this item types
5677
                if (!in_array($arrLP[$i]['item_type'], array('sco', 'asset', 'final_item'))) {
5678
                    if (!in_array($arrLP[$i]['item_type'], array('dokeos_chapter', 'dokeos_module'))) {
5679
                        $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">';
5680
                        $edit_icon .= Display::return_icon('edit.png', get_lang('LearnpathEditModule'), array(), ICON_SIZE_TINY);
5681
                        $edit_icon .= '</a>';
5682
5683
                        if (
5684
                        !in_array($arrLP[$i]['item_type'], ['forum', 'thread'])
5685
                        ) {
5686
                            if (
5687
                            $this->items[$arrLP[$i]['id']]->getForumThread(
5688
                                $this->course_int_id,
5689
                                $this->lp_session_id
5690
                            )
5691
                            ) {
5692
                                $forumIconUrl = api_get_self() . '?' . api_get_cidreq() . '&' . http_build_query([
5693
                                    'action' => 'dissociate_forum',
5694
                                    'id' => $arrLP[$i]['id'],
5695
                                    'lp_id' => $this->lp_id
5696
                                ]);
5697
                                $forumIcon = Display::url(
5698
                                    Display::return_icon('forum.png', get_lang('DissociateForumToLPItem'), [], ICON_SIZE_TINY),
5699
                                    $forumIconUrl,
5700
                                    ['class' => 'btn btn-default lp-btn-dissociate-forum']
5701
                                );
5702
                            } else {
5703
                                $forumIconUrl = api_get_self() . '?' . api_get_cidreq() . '&' . http_build_query([
5704
                                    'action' => 'create_forum',
5705
                                    'id' => $arrLP[$i]['id'],
5706
                                    'lp_id' => $this->lp_id
5707
                                ]);
5708
                                $forumIcon = Display::url(
5709
                                    Display::return_icon('forum.png', get_lang('AssociateForumToLPItem'), [], ICON_SIZE_TINY),
5710
                                    $forumIconUrl,
5711
                                    ['class' => "btn btn-default lp-btn-associate-forum"]
5712
                                );
5713
                            }
5714
                        }
5715
                    } else {
5716
                        $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">';
5717
                        $edit_icon .= Display::return_icon('edit.png', get_lang('LearnpathEditModule'), array(), ICON_SIZE_TINY);
5718
                        $edit_icon .= '</a>';
5719
                    }
5720
                }
5721
5722
                $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">';
5723
                $delete_icon .= Display::return_icon('delete.png', get_lang('LearnpathDeleteModule'), array(), ICON_SIZE_TINY);
5724
                $delete_icon .= '</a>';
5725
5726
                $url = api_get_self() . '?'.api_get_cidreq().'&view=build&id='.$arrLP[$i]['id'] .'&lp_id='.$this->lp_id;
5727
5728
                if (!in_array($arrLP[$i]['item_type'], array('dokeos_chapter', 'dokeos_module', 'dir'))) {
5729
                    $prerequisities_icon = Display::url(Display::return_icon('accept.png', get_lang('LearnpathPrerequisites'), array(), ICON_SIZE_TINY), $url.'&action=edit_item_prereq', ['class' => 'btn btn-default']);
5730
                    $move_item_icon = Display::url(Display::return_icon('move.png', get_lang('Move'), array(), ICON_SIZE_TINY), $url.'&action=move_item', ['class' => 'btn btn-default']);
5731
                    $audio_icon = Display::url(Display::return_icon('audio.png', get_lang('UplUpload'), array(), ICON_SIZE_TINY), $url.'&action=add_audio', ['class' => 'btn btn-default']);
5732
                }
5733
            }
5734
            if ($update_audio != 'true') {
5735
                $row = $move_icon . ' ' . $icon .
5736
                    Display::span($title_cut) .
5737
                    Display::tag(
5738
                        'div',
5739
                        "<div class=\"btn-group btn-group-xs\">$audio $edit_icon $forumIcon $prerequisities_icon $move_item_icon $audio_icon $delete_icon</div>",
5740
                        array('class'=>'btn-toolbar button_actions')
5741
                    );
5742
            } else {
5743
                $row = Display::span($title.$icon).Display::span($audio, array('class'=>'button_actions'));
5744
            }
5745
            $parent_id = $arrLP[$i]['parent_item_id'];
5746
5747
            $default_data[$arrLP[$i]['id']] = $row;
5748
            $default_content[$arrLP[$i]['id']] = $arrLP[$i];
5749
5750
            if (empty($parent_id)) {
5751
                $elements[$arrLP[$i]['id']]['data'] = $row;
5752
                $elements[$arrLP[$i]['id']]['type'] = $arrLP[$i]['item_type'];
5753
            } else {
5754
                $parent_arrays = array();
5755
                if ($arrLP[$i]['depth'] > 1) {
5756
                    //Getting list of parents
5757
                    for($j = 0; $j < $arrLP[$i]['depth']; $j++) {
5758
                        foreach($arrLP as $item) {
5759
                            if ($item['id'] == $parent_id) {
5760
                                if ($item['parent_item_id'] == 0) {
5761
                                    $parent_id = $item['id'];
5762
                                    break;
5763
                                } else {
5764
                                    $parent_id = $item['parent_item_id'];
5765
                                    if (empty($parent_arrays)) {
5766
                                        $parent_arrays[] = intval($item['id']);
5767
                                    }
5768
                                    $parent_arrays[] = $parent_id;
5769
                                    break;
5770
                                }
5771
                            }
5772
                        }
5773
                    }
5774
                }
5775
5776
                if (!empty($parent_arrays)) {
5777
                    $parent_arrays = array_reverse($parent_arrays);
5778
                    $val = '$elements';
5779
                    $x = 0;
5780
                    foreach($parent_arrays as $item) {
5781
                        if ($x != count($parent_arrays) -1) {
5782
                            $val .= '["'.$item.'"]["children"]';
5783
                        } else {
5784
                            $val .= '["'.$item.'"]["children"]';
5785
                        }
5786
                        $x++;
5787
                    }
5788
                    $val .= "";
5789
                    $code_str = $val."[".$arrLP[$i]['id']."][\"load_data\"] = '".$arrLP[$i]['id']."' ; ";
5790
                    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...
5791
                } else {
5792
                    $elements[$parent_id]['children'][$arrLP[$i]['id']]['data'] = $row;
5793
                    $elements[$parent_id]['children'][$arrLP[$i]['id']]['type'] = $arrLP[$i]['item_type'];
5794
                }
5795
            }
5796
        }
5797
5798
        $list = '<ul id="lp_item_list">';
5799
        $tree = self::print_recursive($elements, $default_data, $default_content);
5800
5801
        if (!empty($tree)) {
5802
            $list .= $tree;
5803
        } else {
5804
            if ($drop_element_here) {
5805
                $list .= Display::return_message(get_lang("DragAndDropAnElementHere"));
5806
            }
5807
        }
5808
        $list .= '</ul>';
5809
5810
        //$return .= Display::panel($list, $this->name);
5811
        $return .= Display::panelCollapse($this->name, $list, 'scorm-list', null, 'scorm-list-accordion', 'scorm-list-collapse');
5812
5813
        if ($update_audio == 'true') {
5814
            $return = $return_audio;
5815
        }
5816
5817
        return $return;
5818
    }
5819
5820
    /**
5821
     * @param array $elements
5822
     * @param array $default_data
5823
     * @param array $default_content
5824
     * @return string
5825
     */
5826
    public function print_recursive($elements, $default_data, $default_content)
5827
    {
5828
        $return = '';
5829
        foreach ($elements as $key => $item) {
5830
            if (isset($item['load_data']) || empty($item['data'])) {
5831
                $item['data'] = $default_data[$item['load_data']];
5832
                $item['type'] = $default_content[$item['load_data']]['item_type'];
5833
            }
5834
            $sub_list = '';
5835
            if (isset($item['type']) && $item['type'] == 'dokeos_chapter') {
5836
                $sub_list = Display::tag('li', '', array('class'=>'sub_item empty')); // empty value
5837
            }
5838
            if (empty($item['children'])) {
5839
                $sub_list = Display::tag('ul', $sub_list, array('id'=>'UL_'.$key, 'class'=>'record li_container'));
5840
                $active = null;
5841
                if (isset($_REQUEST['id']) && $key == $_REQUEST['id']) {
5842
                    $active = 'active';
5843
                }
5844
                $return  .= Display::tag('li', Display::div($item['data'], array('class'=>"item_data $active")).$sub_list, array('id'=>$key, 'class'=>'record li_container'));
5845
            } else {
5846
                //sections
5847
                if (isset($item['children'])) {
5848
                    $data = self::print_recursive($item['children'], $default_data, $default_content);
5849
                }
5850
                $sub_list = Display::tag('ul', $sub_list.$data, array('id'=>'UL_'.$key, 'class'=>'record li_container'));
5851
                $return .= Display::tag('li', Display::div($item['data'], array('class'=>'item_data')).$sub_list, array('id'=>$key, 'class'=>'record li_container'));
5852
            }
5853
        }
5854
5855
        return $return;
5856
    }
5857
5858
    /**
5859
     * This function builds the action menu
5860
     * @param bool $returnContent
5861
     * @return void
5862
     */
5863
    public function build_action_menu($returnContent = false)
5864
    {
5865
        $gradebook = isset($_GET['gradebook']) ? Security :: remove_XSS($_GET['gradebook']) : null;
5866
        $return = '<div class="actions">';
5867
        $return .=  '<a href="lp_controller.php?'.api_get_cidreq().'&gradebook=' . $gradebook . '&action=view&lp_id=' . $_SESSION['oLP']->lp_id . '&isStudentView=true">' . Display :: return_icon('preview_view.png', get_lang('Display'),'',ICON_SIZE_MEDIUM).'</a> ';
5868
        $return .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=admin_view&lp_id=' . $_SESSION['oLP']->lp_id . '&updateaudio=true">' . Display :: return_icon('upload_audio.png', get_lang('UpdateAllAudioFragments'),'',ICON_SIZE_MEDIUM).'</a>';
5869
        $return .= '<a href="lp_controller.php?'.api_get_cidreq().'&action=edit&lp_id=' . $_SESSION['oLP']->lp_id . '">' . Display :: return_icon('settings.png', get_lang('CourseSettings'),'',ICON_SIZE_MEDIUM).'</a>';
5870
        $buttons = array(
5871
            array(
5872
                'title' => get_lang('SetPrerequisiteForEachItem'),
5873
                'href' => 'lp_controller.php?'.api_get_cidreq().'&action=set_previous_step_as_prerequisite&lp_id=' . $_SESSION['oLP']->lp_id,
5874
            ),
5875
            array(
5876
                'title' => get_lang('ClearAllPrerequisites'),
5877
                'href' => 'lp_controller.php?'.api_get_cidreq().'&action=clear_prerequisites&lp_id=' . $_SESSION['oLP']->lp_id,
5878
            ),
5879
        );
5880
        $return .= Display::group_button(get_lang('PrerequisitesOptions'), $buttons);
5881
        $return .= '</div>';
5882
5883
        if ($returnContent) {
5884
            return $return;
5885
        }
5886
        echo $return;
5887
    }
5888
5889
    /**
5890
     * Creates the default learning path folder
5891
     * @param array $course
5892
     * @param int $creatorId
5893
     *
5894
     * @return bool
5895
     */
5896
    public static function generate_learning_path_folder($course, $creatorId = 0)
5897
    {
5898
        // Creating learning_path folder
5899
        $dir = '/learning_path';
5900
        $filepath = api_get_path(SYS_COURSE_PATH).$course['path'] . '/document';
5901
        $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId;
5902
5903
        $folder = false;
5904
        if (!is_dir($filepath.'/'.$dir)) {
5905
            $folderData = create_unexisting_directory(
5906
                $course,
5907
                $creatorId,
5908
                api_get_session_id(),
5909
                0,
5910
                0,
5911
                $filepath,
5912
                $dir,
5913
                get_lang('LearningPaths'),
5914
                0
5915
            );
5916
            if (!empty($folderData)) {
5917
                $folder = true;
5918
            }
5919
        } else {
5920
            $folder = true;
5921
        }
5922
5923
        return $folder;
5924
    }
5925
5926
    /**
5927
     * @param array $course
5928
     * @param string $lp_name
5929
     * @param int $creatorId
5930
     *
5931
     * @return array
5932
     */
5933
    public function generate_lp_folder($course, $lp_name = '', $creatorId = 0)
5934
    {
5935
        $filepath = '';
5936
        $dir = '/learning_path/';
5937
5938
        if (empty($lp_name)) {
5939
            $lp_name = $this->name;
5940
        }
5941
        $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId;
5942
5943
        $folder = self::generate_learning_path_folder($course, $creatorId);
5944
        // Creating LP folder
5945
        if ($folder) {
5946
            //Limits title size
5947
            $title = api_substr(api_replace_dangerous_char($lp_name), 0 , 80);
5948
            $dir   = $dir.$title;
5949
            $filepath = api_get_path(SYS_COURSE_PATH) . $course['path'] . '/document';
5950
            if (!is_dir($filepath.'/'.$dir)) {
5951
                $folderData = create_unexisting_directory(
5952
                    $course,
5953
                    $creatorId,
5954
                    0,
5955
                    0,
5956
                    0,
5957
                    $filepath,
5958
                    $dir,
5959
                    $lp_name
5960
                );
5961
                if (!empty($folderData)) {
5962
                    $folder = true;
5963
                }
5964
            } else {
5965
                $folder = true;
5966
            }
5967
            $dir = $dir.'/';
5968
            if ($folder) {
5969
                $filepath = api_get_path(SYS_COURSE_PATH) . $course['path'] . '/document'.$dir;
5970
            }
5971
        }
5972
        $array = array(
5973
            'dir' => $dir,
5974
            'filepath' => $filepath,
5975
            'folder' => $folder
5976
        );
5977
        return $array;
5978
    }
5979
5980
    /**
5981
     * Create a new document //still needs some finetuning
5982
     * @param array $courseInfo
5983
     * @param string $content
5984
     * @param string $title
5985
     * @param string $extension
5986
     * @param int $creatorId creator id
5987
     *
5988
     * @return string
5989
     */
5990
    public function create_document($courseInfo, $content = '', $title = '', $extension = 'html', $creatorId = 0)
5991
    {
5992
        if (!empty($courseInfo)) {
5993
            $course_id = $courseInfo['real_id'];
5994
        } else {
5995
            $course_id = api_get_course_int_id();
5996
        }
5997
        $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId;
5998
        $sessionId = api_get_session_id();
5999
6000
        global $charset;
6001
        $postDir = isset($_POST['dir']) ? $_POST['dir'] : '';
6002
        $dir = isset ($_GET['dir']) ? $_GET['dir'] : $postDir; // Please, do not modify this dirname formatting.
6003
        // Please, do not modify this dirname formatting.
6004
        if (strstr($dir, '..')) {
6005
            $dir = '/';
6006
        }
6007
        if (!empty($dir[0]) && $dir[0] == '.') {
6008
            $dir = substr($dir, 1);
6009
        }
6010
        if (!empty($dir[0]) && $dir[0] != '/') {
6011
            $dir = '/' . $dir;
6012
        }
6013
        if (isset($dir[strlen($dir) - 1]) && $dir[strlen($dir) - 1] != '/') {
6014
            $dir .= '/';
6015
        }
6016
        $filepath = api_get_path(SYS_COURSE_PATH) . $courseInfo['path'] . '/document' . $dir;
6017
6018
        if (empty($_POST['dir']) && empty($_GET['dir'])) {
6019
            //Generates folder
6020
            $result = $this->generate_lp_folder($courseInfo, '', $creatorId);
6021
            $dir = $result['dir'];
6022
            $filepath = $result['filepath'];
6023
        }
6024
6025 View Code Duplication
        if (!is_dir($filepath)) {
6026
            $filepath = api_get_path(SYS_COURSE_PATH) . $courseInfo['path'] . '/document/';
6027
            $dir = '/';
6028
        }
6029
6030
        // stripslashes() before calling api_replace_dangerous_char() because $_POST['title']
6031
        // is already escaped twice when it gets here.
6032
6033
        $originalTitle = !empty($title) ? $title : $_POST['title'];
6034
        if (!empty($title)) {
6035
            $title = api_replace_dangerous_char(stripslashes($title));
6036
        } else {
6037
            $title = api_replace_dangerous_char(stripslashes($_POST['title']));
6038
        }
6039
6040
        $title = disable_dangerous_file($title);
6041
        $filename = $title;
6042
        $content = !empty($content) ? $content : $_POST['content_lp'];
6043
        $tmp_filename = $filename;
6044
6045
        $i = 0;
6046 View Code Duplication
        while (file_exists($filepath . $tmp_filename . '.'.$extension))
6047
            $tmp_filename = $filename . '_' . ++ $i;
6048
6049
        $filename = $tmp_filename . '.'.$extension;
6050
        if ($extension == 'html') {
6051
            $content = stripslashes($content);
6052
            $content = str_replace(
6053
                api_get_path(WEB_COURSE_PATH),
6054
                api_get_path(REL_PATH).'courses/',
6055
                $content
6056
            );
6057
6058
            // Change the path of mp3 to absolute.
6059
6060
            // The first regexp deals with :// urls.
6061
            $content = preg_replace(
6062
                "|(flashvars=\"file=)([^:/]+)/|",
6063
                "$1".api_get_path(
6064
                    REL_COURSE_PATH
6065
                ).$courseInfo['path'].'/document/',
6066
                $content
6067
            );
6068
            // The second regexp deals with audio/ urls.
6069
            $content = preg_replace(
6070
                "|(flashvars=\"file=)([^/]+)/|",
6071
                "$1".api_get_path(
6072
                    REL_COURSE_PATH
6073
                ).$courseInfo['path'].'/document/$2/',
6074
                $content
6075
            );
6076
            // For flv player: To prevent edition problem with firefox, we have to use a strange tip (don't blame me please).
6077
            $content = str_replace(
6078
                '</body>',
6079
                '<style type="text/css">body{}</style></body>',
6080
                $content
6081
            );
6082
        }
6083
6084
        if (!file_exists($filepath . $filename)) {
6085
            if ($fp = @ fopen($filepath . $filename, 'w')) {
6086
                fputs($fp, $content);
6087
                fclose($fp);
6088
6089
                $file_size = filesize($filepath . $filename);
6090
                $save_file_path = $dir.$filename;
6091
6092
                $document_id = add_document(
6093
                    $courseInfo,
6094
                    $save_file_path,
6095
                    'file',
6096
                    $file_size,
6097
                    $tmp_filename,
6098
                    '',
6099
                    0, //readonly
6100
                    true,
6101
                    null,
6102
                    $sessionId,
6103
                    $creatorId
6104
                );
6105
6106
                if ($document_id) {
6107
                    api_item_property_update(
6108
                        $courseInfo,
6109
                        TOOL_DOCUMENT,
6110
                        $document_id,
6111
                        'DocumentAdded',
6112
                        $creatorId,
6113
                        null,
6114
                        null,
6115
                        null,
6116
                        null,
6117
                        $sessionId
6118
                    );
6119
6120
                    $new_comment = isset($_POST['comment']) ? trim($_POST['comment']) : '';
6121
                    $new_title = $originalTitle;
6122
6123
                    if ($new_comment || $new_title) {
6124
                        $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
6125
                        $ct = '';
6126
                        if ($new_comment)
6127
                            $ct .= ", comment='" . Database::escape_string($new_comment). "'";
6128
                        if ($new_title)
6129
                            $ct .= ", title='" . Database::escape_string(htmlspecialchars($new_title, ENT_QUOTES, $charset))."' ";
6130
6131
                        $sql = "UPDATE " . $tbl_doc ." SET " . substr($ct, 1)."
6132
                               WHERE c_id = ".$course_id." AND id = " . $document_id;
6133
                        Database::query($sql);
6134
                    }
6135
                }
6136
                return $document_id;
6137
            }
6138
        }
6139
    }
6140
6141
    /**
6142
     * Edit a document based on $_POST and $_GET parameters 'dir' and 'path'
6143
     * @param 	array $_course array
6144
     * @return 	void
6145
     */
6146
    public function edit_document($_course)
6147
    {
6148
        $course_id = api_get_course_int_id();
6149
        global $_configuration;
6150
        // Please, do not modify this dirname formatting.
6151
        $dir = isset($_GET['dir']) ? $_GET['dir'] : $_POST['dir'];
6152
6153
        if (strstr($dir, '..'))
6154
            $dir = '/';
6155
6156
        if ($dir[0] == '.')
6157
            $dir = substr($dir, 1);
6158
6159
        if ($dir[0] != '/')
6160
            $dir = '/' . $dir;
6161
6162
        if ($dir[strlen($dir) - 1] != '/')
6163
            $dir .= '/';
6164
6165
        $filepath = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document' . $dir;
6166
6167 View Code Duplication
        if (!is_dir($filepath)) {
6168
            $filepath = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document/';
6169
        }
6170
6171
        $table_doc = Database :: get_course_table(TABLE_DOCUMENT);
6172
        if (isset($_POST['path']) && !empty($_POST['path'])) {
6173
            $document_id = intval($_POST['path']);
6174
            $sql = "SELECT path FROM " . $table_doc . "
6175
                    WHERE c_id = $course_id AND id = " . $document_id;
6176
            $res = Database::query($sql);
6177
            $row = Database :: fetch_array($res);
6178
            $content = stripslashes($_POST['content_lp']);
6179
            $file = $filepath . $row['path'];
6180
6181
            if ($fp = @ fopen($file, 'w')) {
6182
                $content = str_replace(api_get_path(WEB_COURSE_PATH), $_configuration['url_append'] . '/courses/', $content);
6183
6184
                // Change the path of mp3 to absolute.
6185
                // The first regexp deals with :// urls.
6186
                $content = preg_replace("|(flashvars=\"file=)([^:/]+)/|", "$1" . api_get_path(REL_COURSE_PATH) . $_course['path'] . '/document/', $content);
6187
                // The second regexp deals with audio/ urls.
6188
                $content = preg_replace("|(flashvars=\"file=)([^:/]+)/|", "$1" . api_get_path(REL_COURSE_PATH) . $_course['path'] . '/document/$2/', $content);
6189
                fputs($fp, $content);
6190
                fclose($fp);
6191
6192
                $sql = "UPDATE " . $table_doc ." SET
6193
                            title='".Database::escape_string($_POST['title'])."'
6194
                        WHERE c_id = ".$course_id." AND id = " . $document_id;
6195
                Database::query($sql);
6196
            }
6197
        }
6198
    }
6199
6200
    /**
6201
     * Displays the selected item, with a panel for manipulating the item
6202
     * @param int $item_id
6203
     * @param string $msg
6204
     * @return string
6205
     */
6206
    public function display_item($item_id, $msg = null, $show_actions = true)
6207
    {
6208
        $course_id = api_get_course_int_id();
6209
        $return = '';
6210
        if (is_numeric($item_id)) {
6211
            $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
6212
            $sql = "SELECT lp.* FROM " . $tbl_lp_item . " as lp
6213
                    WHERE c_id = ".$course_id." AND lp.id = " . intval($item_id);
6214
            $result = Database::query($sql);
6215
            while ($row = Database :: fetch_array($result,'ASSOC')) {
0 ignored issues
show
Bug introduced by
It seems like $result can be null; however, fetch_array() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
6216
                $_SESSION['parent_item_id'] = ($row['item_type'] == 'dokeos_chapter' || $row['item_type'] == 'dokeos_module' || $row['item_type'] == 'dir') ? $item_id : 0;
6217
6218
                // Prevents wrong parent selection for document, see Bug#1251.
6219
                if ($row['item_type'] != 'dokeos_chapter' || $row['item_type'] != 'dokeos_module') {
6220
                    $_SESSION['parent_item_id'] = $row['parent_item_id'];
6221
                }
6222
6223
                if ($show_actions) {
6224
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6225
                }
6226
                $return .= '<div style="padding:10px;">';
6227
6228
                if ($msg != '')
6229
                    $return .= $msg;
6230
6231
                $return .= '<h3>'.$row['title'].'</h3>';
6232
                switch ($row['item_type']) {
6233
                    case TOOL_QUIZ:
6234
                        if (!empty($row['path'])) {
6235
                            $exercise = new Exercise();
6236
                            $exercise->read($row['path']);
6237
                            $return .= $exercise->description.'<br />';
6238
                        }
6239
                        break;
6240
                    case TOOL_DOCUMENT:
6241
                        $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
6242
                        $sql_doc = "SELECT path FROM " . $tbl_doc . "
6243
                                    WHERE c_id = ".$course_id." AND id = " . intval($row['path']);
6244
                        $result = Database::query($sql_doc);
6245
                        $path_file = Database::result($result, 0, 0);
6246
                        $path_parts = pathinfo($path_file);
6247
                        // TODO: Correct the following naive comparisons, also, htm extension is missing.
6248
                        if (in_array($path_parts['extension'], array(
6249
                            'html',
6250
                            'txt',
6251
                            'png',
6252
                            'jpg',
6253
                            'JPG',
6254
                            'jpeg',
6255
                            'JPEG',
6256
                            'gif',
6257
                            'swf'
6258
                        ))) {
6259
                            $return .= $this->display_document($row['path'], true, true);
6260
                        }
6261
                        break;
6262
                }
6263
                $return .= '</div>';
6264
            }
6265
        }
6266
6267
        return $return;
6268
    }
6269
6270
    /**
6271
     * Shows the needed forms for editing a specific item
6272
     * @param int $item_id
6273
     * @return string
6274
     */
6275
    public function display_edit_item($item_id)
6276
    {
6277
        $course_id = api_get_course_int_id();
6278
        $return = '';
6279
        if (is_numeric($item_id)) {
6280
            $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
6281
            $sql = "SELECT * FROM $tbl_lp_item
6282
                    WHERE c_id = ".$course_id." AND id = " . intval($item_id);
6283
            $res = Database::query($sql);
6284
            $row = Database::fetch_array($res);
6285
            switch ($row['item_type']) {
6286
                case 'dokeos_chapter':
6287
                case 'dir':
6288
                case 'asset':
6289 View Code Duplication
                case 'sco':
6290
                    if (isset($_GET['view']) && $_GET['view'] == 'build') {
6291
                        $return .= $this->display_manipulate($item_id, $row['item_type']);
6292
                        $return .= $this->display_item_form($row['item_type'], get_lang('EditCurrentChapter') . ' :', 'edit', $item_id, $row);
6293
                    } else {
6294
                        $return .= $this->display_item_small_form($row['item_type'], get_lang('EditCurrentChapter') . ' :', $row);
6295
                    }
6296
                    break;
6297 View Code Duplication
                case TOOL_DOCUMENT:
6298
                    $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
6299
                    $sql = "SELECT lp.*, doc.path as dir
6300
                            FROM " . $tbl_lp_item . " as lp
6301
                            LEFT JOIN " . $tbl_doc . " as doc
6302
                            ON doc.id = lp.path
6303
                            WHERE
6304
                                lp.c_id = $course_id AND
6305
                                doc.c_id = $course_id AND
6306
                                lp.id = " . intval($item_id);
6307
                    $res_step = Database::query($sql);
6308
                    $row_step = Database :: fetch_array($res_step, 'ASSOC');
6309
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6310
                    $return .= $this->display_document_form('edit', $item_id, $row_step);
6311
                    break;
6312
                case TOOL_LINK:
6313
                    $link_id = (string) $row['path'];
6314
                    if (ctype_digit($link_id)) {
6315
                        $tbl_link = Database :: get_course_table(TABLE_LINK);
6316
                        $sql_select = 'SELECT url FROM ' . $tbl_link . '
6317
                                       WHERE c_id = '.$course_id.' AND id = ' . intval($link_id);
6318
                        $res_link = Database::query($sql_select);
6319
                        $row_link = Database :: fetch_array($res_link);
6320
                        if (is_array($row_link)) {
6321
                            $row['url'] = $row_link['url'];
6322
                        }
6323
                    }
6324
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6325
                    $return .= $this->display_link_form('edit', $item_id, $row);
6326
                    break;
6327 View Code Duplication
                case TOOL_LP_FINAL_ITEM:
6328
                    $_SESSION['finalItem'] = true;
6329
                    $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
6330
                    $sql = "SELECT lp.*, doc.path as dir
6331
                            FROM " . $tbl_lp_item . " as lp
6332
                            LEFT JOIN " . $tbl_doc . " as doc
6333
                            ON doc.id = lp.path
6334
                            WHERE
6335
                                lp.c_id = $course_id AND
6336
                                doc.c_id = $course_id AND
6337
                                lp.id = " . intval($item_id);
6338
                    $res_step = Database::query($sql);
6339
                    $row_step = Database :: fetch_array($res_step, 'ASSOC');
6340
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6341
                    $return .= $this->display_document_form('edit', $item_id, $row_step);
6342
                    break;
6343 View Code Duplication
                case 'dokeos_module':
6344
                    if (isset ($_GET['view']) && $_GET['view'] == 'build') {
6345
                        $return .= $this->display_manipulate($item_id, $row['item_type']);
6346
                        $return .= $this->display_item_form($row['item_type'], get_lang('EditCurrentModule') . ' :', 'edit', $item_id, $row);
6347
                    } else {
6348
                        $return .= $this->display_item_small_form($row['item_type'], get_lang('EditCurrentModule') . ' :', $row);
6349
                    }
6350
                    break;
6351 View Code Duplication
                case TOOL_QUIZ:
6352
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6353
                    $return .= $this->display_quiz_form('edit', $item_id, $row);
6354
                    break;
6355
                case TOOL_HOTPOTATOES:
6356
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6357
                    $return .= $this->display_hotpotatoes_form('edit', $item_id, $row);
6358
                    break;
6359 View Code Duplication
                case TOOL_STUDENTPUBLICATION:
6360
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6361
                    $return .= $this->display_student_publication_form('edit', $item_id, $row);
6362
                    break;
6363
                case TOOL_FORUM:
6364
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6365
                    $return .= $this->display_forum_form('edit', $item_id, $row);
6366
                    break;
6367 View Code Duplication
                case TOOL_THREAD:
6368
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
6369
                    $return .= $this->display_thread_form('edit', $item_id, $row);
6370
                    break;
6371
            }
6372
        }
6373
6374
        return $return;
6375
    }
6376
6377
    /**
6378
     * Function that displays a list with al the resources that
6379
     * could be added to the learning path
6380
     * @return string
6381
     */
6382
    public function display_resources()
6383
    {
6384
        $course_code = api_get_course_id();
6385
6386
        // Get all the docs.
6387
        $documents = $this->get_documents(true);
6388
6389
        // Get all the exercises.
6390
        $exercises = $this->get_exercises();
6391
6392
        // Get all the links.
6393
        $links = $this->get_links();
6394
6395
        // Get all the student publications.
6396
        $works = $this->get_student_publications();
6397
6398
        // Get all the forums.
6399
        $forums = $this->get_forums(null, $course_code);
6400
6401
        // Get the final item form (see BT#11048) .
6402
        $finish = $this->getFinalItemForm();
6403
6404
        $headers = array(
6405
            Display::return_icon('folder_document.png', get_lang('Documents'), array(), ICON_SIZE_BIG),
6406
            Display::return_icon('quiz.png',  get_lang('Quiz'), array(), ICON_SIZE_BIG),
6407
            Display::return_icon('links.png', get_lang('Links'), array(), ICON_SIZE_BIG),
6408
            Display::return_icon('works.png', get_lang('Works'), array(), ICON_SIZE_BIG),
6409
            Display::return_icon('forum.png', get_lang('Forums'), array(), ICON_SIZE_BIG),
6410
            Display::return_icon('add_learnpath_section.png', get_lang('NewChapter'), array(), ICON_SIZE_BIG),
6411
            Display::return_icon('certificate.png', get_lang('Certificate'), [], ICON_SIZE_BIG),
6412
        );
6413
6414
        echo Display::display_normal_message(get_lang('ClickOnTheLearnerViewToSeeYourLearningPath'));
6415
        $chapter = $_SESSION['oLP']->display_item_form('chapter', get_lang('EnterDataNewChapter'), 'add_item');
6416
        echo Display::tabs(
6417
            $headers,
6418
            array($documents, $exercises, $links, $works, $forums, $chapter, $finish), 'resource_tab'
6419
        );
6420
6421
        return true;
6422
    }
6423
6424
    /**
6425
     * Returns the extension of a document
6426
     * @param string filename
6427
     * @return string Extension (part after the last dot)
6428
     */
6429
    public function get_extension($filename)
6430
    {
6431
        $explode = explode('.', $filename);
6432
        return $explode[count($explode) - 1];
6433
    }
6434
6435
    /**
6436
     * Displays a document by id
6437
     *
6438
     * @param int $id
6439
     * @return string
6440
     */
6441
    public function display_document($id, $show_title = false, $iframe = true, $edit_link = false)
6442
    {
6443
        $_course = api_get_course_info();
6444
        $course_id = api_get_course_int_id();
6445
        $return = '';
6446
        $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
6447
        $sql_doc = "SELECT * FROM " . $tbl_doc . "
6448
                    WHERE c_id = ".$course_id." AND id = " . $id;
6449
        $res_doc = Database::query($sql_doc);
6450
        $row_doc = Database :: fetch_array($res_doc);
6451
6452
        // TODO: Add a path filter.
6453
        if ($iframe) {
6454
            $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>';
6455
        } else {
6456
            $return .= file_get_contents(api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document' . $row_doc['path']);
6457
        }
6458
6459
        return $return;
6460
    }
6461
6462
    /**
6463
     * Return HTML form to add/edit a quiz
6464
     * @param	string	Action (add/edit)
6465
     * @param	integer	Item ID if already exists
6466
     * @param	mixed	Extra information (quiz ID if integer)
6467
     * @return	string	HTML form
6468
     */
6469
    public function display_quiz_form($action = 'add', $id = 0, $extra_info = '')
6470
    {
6471
        $course_id = api_get_course_int_id();
6472
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
6473
        $tbl_quiz = Database :: get_course_table(TABLE_QUIZ_TEST);
6474
6475 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
6476
            $item_title = $extra_info['title'];
6477
            $item_description = $extra_info['description'];
6478
        } elseif (is_numeric($extra_info)) {
6479
            $sql = "SELECT title, description
6480
                    FROM " . $tbl_quiz . "
6481
                    WHERE c_id = ".$course_id." AND id = " . $extra_info;
6482
6483
            $result = Database::query($sql);
6484
            $row = Database::fetch_array($result);
6485
            $item_title = $row['title'];
6486
            $item_description = $row['description'];
6487
        } else {
6488
            $item_title = '';
6489
            $item_description = '';
6490
        }
6491
        $item_title			= Security::remove_XSS($item_title);
6492
        $item_description 	= Security::remove_XSS($item_description);
6493
6494 View Code Duplication
        if ($id != 0 && is_array($extra_info))
6495
            $parent = $extra_info['parent_item_id'];
6496
        else
6497
            $parent = 0;
6498
6499
        $sql = "SELECT * FROM " . $tbl_lp_item . "
6500
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
6501
6502
        $result = Database::query($sql);
6503
        $arrLP = array ();
6504 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
6505
            $arrLP[] = array (
6506
                'id' => $row['id'],
6507
                'item_type' => $row['item_type'],
6508
                'title' => $row['title'],
6509
                'path' => $row['path'],
6510
                'description' => $row['description'],
6511
                'parent_item_id' => $row['parent_item_id'],
6512
                'previous_item_id' => $row['previous_item_id'],
6513
                'next_item_id' => $row['next_item_id'],
6514
                'display_order' => $row['display_order'],
6515
                'max_score' => $row['max_score'],
6516
                'min_score' => $row['min_score'],
6517
                'mastery_score' => $row['mastery_score'],
6518
                'prerequisite' => $row['prerequisite'],
6519
                'max_time_allowed' => $row['max_time_allowed']
6520
            );
6521
        }
6522
6523
        $this->tree_array($arrLP);
6524
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
6525
        unset ($this->arrMenu);
6526
6527
        $form = new FormValidator('quiz_form', 'POST', api_get_self() . '?' .$_SERVER['QUERY_STRING']);
6528
        $defaults = [];
6529
6530
        if ($action == 'add') {
6531
            $legend = get_lang('CreateTheExercise');
6532
        } elseif ($action == 'move') {
6533
            $legend = get_lang('MoveTheCurrentExercise');
6534
        } else {
6535
            $legend = get_lang('EditCurrentExecice');
6536
        }
6537
6538 View Code Duplication
        if (isset ($_GET['edit']) && $_GET['edit'] == 'true') {
6539
            $legend .= Display :: return_warning_message(get_lang('Warning') . ' ! ' . get_lang('WarningEditingDocument'));
6540
        }
6541
6542
        $form->addHeader($legend);
6543
6544
        if ($action != 'move') {
6545
            $form->addText('title', get_lang('Title'), true, ['id' => 'idTitle']);
6546
            $defaults['title'] = $item_title;
6547
        }
6548
6549
        // Select for Parent item, root or chapter
6550
        $selectParent = $form->addSelect(
6551
            'parent',
6552
            get_lang('Parent'),
6553
            [],
6554
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);']
6555
        );
6556
        $selectParent->addOption($this->name, 0);
6557
6558
        $arrHide = array (
6559
            $id
6560
        );
6561 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
6562
            if ($action != 'add') {
6563
                if (
6564
                    (
6565
                        $arrLP[$i]['item_type'] == 'dokeos_module' ||
6566
                        $arrLP[$i]['item_type'] == 'dokeos_chapter' ||
6567
                        $arrLP[$i]['item_type'] == 'dir'
6568
                    ) &&
6569
                    !in_array($arrLP[$i]['id'], $arrHide) &&
6570
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
6571
                ) {
6572
                    $selectParent->addOption(
6573
                        $arrLP[$i]['title'],
6574
                        $arrLP[$i]['id'],
6575
                        ['style' => 'padding-left: ' . (20 + $arrLP[$i]['depth'] * 20) . 'px']
6576
                    );
6577
6578
                    if ($parent == $arrLP[$i]['id']) {
6579
                        $selectParent->setSelected($arrLP[$i]['id']);
6580
                    }
6581
                } else {
6582
                    $arrHide[] = $arrLP[$i]['id'];
6583
                }
6584
            } else {
6585
                if (
6586
                    $arrLP[$i]['item_type'] == 'dokeos_module' ||
6587
                    $arrLP[$i]['item_type'] == 'dokeos_chapter' ||
6588
                    $arrLP[$i]['item_type'] == 'dir'
6589
                ) {
6590
                    $selectParent->addOption(
6591
                        $arrLP[$i]['title'],
6592
                        $arrLP[$i]['id'], ['style' => 'padding-left: ' . (20 + $arrLP[$i]['depth'] * 20) . 'px']
6593
                    );
6594
6595
                    if ($parent == $arrLP[$i]['id']) {
6596
                        $selectParent->setSelected($arrLP[$i]['id']);
6597
                    }
6598
                }
6599
            }
6600
        }
6601
        if (is_array($arrLP)) {
6602
            reset($arrLP);
6603
        }
6604
6605
        $selectPrevious = $form->addSelect('previous', get_lang('Position'), [], ['id' => 'previous']);
6606
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
6607
6608 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
6609
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
6610
                $selectPrevious->addOption(get_lang('After') . ' "' . $arrLP[$i]['title'] . '"', $arrLP[$i]['id']);
6611
6612
                if (is_array($extra_info)) {
6613
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
6614
                        $selectPrevious->setSelected($arrLP[$i]['id']);
6615
                    }
6616
                } elseif ($action == 'add') {
6617
                    $selectPrevious->setSelected($arrLP[$i]['id']);
6618
                }
6619
            }
6620
        }
6621
6622 View Code Duplication
        if ($action != 'move') {
6623
            $id_prerequisite = 0;
6624
            if (is_array($arrLP)) {
6625
                foreach ($arrLP as $key => $value) {
6626
                    if ($value['id'] == $id) {
6627
                        $id_prerequisite = $value['prerequisite'];
6628
                        break;
6629
                    }
6630
                }
6631
            }
6632
            $arrHide = array ();
6633
            for ($i = 0; $i < count($arrLP); $i++) {
6634
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
6635
                    if (is_array($extra_info)) {
6636
                        if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
6637
                            $s_selected_position = $arrLP[$i]['id'];
6638
                        }
6639
                    } elseif ($action == 'add') {
6640
                        $s_selected_position = 0;
6641
                    }
6642
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
6643
                }
6644
            }
6645
            /*// Commented the prerequisites, only visible in edit (exercise).
6646
            $return .= '<tr>';
6647
            $return .= '<td class="label"><label for="idPrerequisites">'.get_lang('LearnpathPrerequisites').'</label></td>';
6648
            $return .= '<td class="input"><select name="prerequisites" id="prerequisites" class="learnpath_item_form"><option value="0">'.get_lang('NoPrerequisites').'</option>';
6649
6650
                foreach($arrHide as $key => $value){
6651
                    if($key==$s_selected_position && $action == 'add'){
6652
                        $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
6653
                    }
6654
                    elseif($key==$id_prerequisite && $action == 'edit'){
6655
                        $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
6656
                    }
6657
                    else{
6658
                        $return .= '<option value="'.$key.'">'.$value['value'].'</option>';
6659
                    }
6660
                }
6661
6662
            $return .= "</select></td>";
6663
            */
6664
            /*$return .= '<tr>';
6665
            $return .= '<td class="label"><label for="maxTimeAllowed">' . get_lang('MaxTimeAllowed') . '</label></td>';
6666
            $return .= '<td class="input"><input name="maxTimeAllowed" style="width:98%;" id="maxTimeAllowed" value="' . $extra_info['max_time_allowed'] . '" /></td>';
6667
6668
            // Remove temporarily the test description.
6669
            //$return .= '<td class="label"><label for="idDescription">'.get_lang('Description').' :</label></td>';
6670
            //$return .= '<td class="input"><textarea id="idDescription" name="description" rows="4">' . $item_description . '</textarea></td>';
6671
6672
            $return .= '</tr>'; */
6673
        }
6674
6675
        if ($action == 'add') {
6676
            $form->addButtonSave(get_lang('AddExercise'), 'submit_button');
6677
        } else {
6678
            $form->addButtonSave(get_lang('EditCurrentExecice'), 'submit_button');
6679
        }
6680
6681
        if ($action == 'move') {
6682
            $form->addHidden('title', $item_title);
6683
            $form->addHidden('description', $item_description);
6684
        }
6685
6686 View Code Duplication
        if (is_numeric($extra_info)) {
6687
            $form->addHidden('path', $extra_info);
6688
        } elseif (is_array($extra_info)) {
6689
            $form->addHidden('path', $extra_info['path']);
6690
        }
6691
6692
        $form->addHidden('type', TOOL_QUIZ);
6693
        $form->addHidden('post_time', time());
6694
6695
        $form->setDefaults($defaults);
6696
6697
        return '<div class="sectioncomment">' . $form->returnForm() . '</div>';
6698
    }
6699
6700
    /**
6701
     * Addition of Hotpotatoes tests
6702
     * @param	string	Action
6703
     * @param	integer	Internal ID of the item
6704
     * @param	mixed	Extra information - can be an array with title and description indexes
6705
     * @return  string	HTML structure to display the hotpotatoes addition formular
6706
     */
6707
    public function display_hotpotatoes_form($action = 'add', $id = 0, $extra_info = '')
6708
    {
6709
        $course_id = api_get_course_int_id();
6710
        $uploadPath = DIR_HOTPOTATOES; //defined in main_api
6711
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
6712
6713
        if ($id != 0 && is_array($extra_info)) {
6714
            $item_title = stripslashes($extra_info['title']);
6715
            $item_description = stripslashes($extra_info['description']);
6716
        } elseif (is_numeric($extra_info)) {
6717
            $TBL_DOCUMENT = Database :: get_course_table(TABLE_DOCUMENT);
6718
6719
            $sql = "SELECT * FROM " . $TBL_DOCUMENT . "
6720
                    WHERE
6721
                        c_id = ".$course_id." AND
6722
                        path LIKE '" . $uploadPath . "/%/%htm%' AND
6723
                        id = " . (int) $extra_info . "
6724
                    ORDER BY id ASC";
6725
6726
            $res_hot = Database::query($sql);
6727
            $row = Database::fetch_array($res_hot);
6728
6729
            $item_title = $row['title'];
6730
            $item_description = $row['description'];
6731
6732
            if (!empty ($row['comment'])) {
6733
                $item_title = $row['comment'];
6734
            }
6735
        } else {
6736
            $item_title = '';
6737
            $item_description = '';
6738
        }
6739
6740 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
6741
            $parent = $extra_info['parent_item_id'];
6742
        } else {
6743
            $parent = 0;
6744
        }
6745
6746
        $sql = "SELECT * FROM $tbl_lp_item
6747
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
6748
        $result = Database::query($sql);
6749
        $arrLP = array ();
6750 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
6751
            $arrLP[] = array (
6752
                'id' => $row['id'],
6753
                'item_type' => $row['item_type'],
6754
                'title' => $row['title'],
6755
                'path' => $row['path'],
6756
                'description' => $row['description'],
6757
                'parent_item_id' => $row['parent_item_id'],
6758
                'previous_item_id' => $row['previous_item_id'],
6759
                'next_item_id' => $row['next_item_id'],
6760
                'display_order' => $row['display_order'],
6761
                'max_score' => $row['max_score'],
6762
                'min_score' => $row['min_score'],
6763
                'mastery_score' => $row['mastery_score'],
6764
                'prerequisite' => $row['prerequisite'],
6765
                'max_time_allowed' => $row['max_time_allowed']
6766
            );
6767
        }
6768
6769
        $legend = '<legend>';
6770
        if ($action == 'add')
6771
            $legend .= get_lang('CreateTheExercise');
6772
        elseif ($action == 'move') $legend .= get_lang('MoveTheCurrentExercise');
6773
        else
6774
            $legend .= get_lang('EditCurrentExecice');
6775 View Code Duplication
        if (isset ($_GET['edit']) && $_GET['edit'] == 'true') {
6776
            $legend .= Display :: return_warning_message(get_lang('Warning') . ' ! ' . get_lang('WarningEditingDocument'));
6777
        }
6778
        $legend .= '</legend>';
6779
6780
        $return = '<form method="POST">';
6781
        $return .= $legend;
6782
        $return .= '<table cellpadding="0" cellspacing="0" class="lp_form">';
6783
        $return .= '<tr>';
6784
        $return .= '<td class="label"><label for="idParent">' . get_lang('Parent') . ' :</label></td>';
6785
        $return .= '<td class="input">';
6786
        $return .= '<select id="idParent" name="parent" onChange="javascript: load_cbo(this.value);" size="1">';
6787
        $return .= '<option class="top" value="0">' . $this->name . '</option>';
6788
        $arrHide = array (
6789
            $id
6790
        );
6791
6792
        if (count($arrLP) > 0) {
6793
            for ($i = 0; $i < count($arrLP); $i++) {
6794
                if ($action != 'add') {
6795
                    if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
6796
                        $return .= '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
6797
                    } else {
6798
                        $arrHide[] = $arrLP[$i]['id'];
6799
                    }
6800
                } else {
6801
                    if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir')
6802
                        $return .= '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
6803
                }
6804
            }
6805
6806
            reset($arrLP);
6807
        }
6808
6809
        $return .= '</select>';
6810
        $return .= '</td>';
6811
        $return .= '</tr>';
6812
        $return .= '<tr>';
6813
        $return .= '<td class="label"><label for="previous">' . get_lang('Position') . ' :</label></td>';
6814
        $return .= '<td class="input">';
6815
        $return .= '<select id="previous" name="previous" size="1">';
6816
        $return .= '<option class="top" value="0">' . get_lang('FirstPosition') . '</option>';
6817
6818
        for ($i = 0; $i < count($arrLP); $i++) {
6819
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
6820
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
6821
                    $selected = 'selected="selected" ';
6822
                elseif ($action == 'add') $selected = 'selected="selected" ';
6823
                else
6824
                    $selected = '';
6825
6826
                $return .= '<option ' . $selected . 'value="' . $arrLP[$i]['id'] . '">' . get_lang('After') . ' "' . $arrLP[$i]['title'] . '"</option>';
6827
            }
6828
        }
6829
6830
        $return .= '</select>';
6831
        $return .= '</td>';
6832
        $return .= '</tr>';
6833
6834
        if ($action != 'move') {
6835
            $return .= '<tr>';
6836
            $return .= '<td class="label"><label for="idTitle">' . get_lang('Title') . ' :</label></td>';
6837
            $return .= '<td class="input"><input id="idTitle" name="title" type="text" value="' . $item_title . '" /></td>';
6838
            $return .= '</tr>';
6839
            $id_prerequisite = 0;
6840 View Code Duplication
            if (is_array($arrLP) && count($arrLP) > 0) {
6841
                foreach ($arrLP as $key => $value) {
6842
                    if ($value['id'] == $id) {
6843
                        $id_prerequisite = $value['prerequisite'];
6844
                        break;
6845
                    }
6846
                }
6847
6848
                $arrHide = array ();
6849
                for ($i = 0; $i < count($arrLP); $i++) {
6850
                    if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
6851
                        if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
6852
                            $s_selected_position = $arrLP[$i]['id'];
6853
                        elseif ($action == 'add') $s_selected_position = 0;
6854
                        $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
6855
6856
                    }
6857
                }
6858
            }
6859
        }
6860
6861
        $return .= '<tr>';
6862
        $return .= '<td>&nbsp; </td><td><button class="save" name="submit_button" action="edit" type="submit">' . get_lang('SaveHotpotatoes') . '</button></td>';
6863
        $return .= '</tr>';
6864
        $return .= '</table>';
6865
6866
        if ($action == 'move') {
6867
            $return .= '<input name="title" type="hidden" value="' . $item_title . '" />';
6868
            $return .= '<input name="description" type="hidden" value="' . $item_description . '" />';
6869
        }
6870
6871
        if (is_numeric($extra_info)) {
6872
            $return .= '<input name="path" type="hidden" value="' . $extra_info . '" />';
6873
        } elseif (is_array($extra_info)) {
6874
            $return .= '<input name="path" type="hidden" value="' . $extra_info['path'] . '" />';
6875
        }
6876
        $return .= '<input name="type" type="hidden" value="' . TOOL_HOTPOTATOES . '" />';
6877
        $return .= '<input name="post_time" type="hidden" value="' . time() . '" />';
6878
        $return .= '</form>';
6879
6880
        return $return;
6881
    }
6882
6883
    /**
6884
     * Return the form to display the forum edit/add option
6885
     * @param	string	Action (add/edit)
6886
     * @param	integer	ID of the lp_item if already exists
6887
     * @param	mixed	Forum ID or title
6888
     * @return	string	HTML form
6889
     */
6890
    public function display_forum_form($action = 'add', $id = 0, $extra_info = '')
6891
    {
6892
        $course_id = api_get_course_int_id();
6893
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
6894
        $tbl_forum = Database :: get_course_table(TABLE_FORUM);
6895
6896 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
6897
            $item_title = stripslashes($extra_info['title']);
6898
        } elseif (is_numeric($extra_info)) {
6899
            $sql = "SELECT forum_title as title, forum_comment as comment
6900
                    FROM " . $tbl_forum . "
6901
                    WHERE c_id = ".$course_id." AND forum_id = " . $extra_info;
6902
6903
            $result = Database::query($sql);
6904
            $row = Database :: fetch_array($result);
6905
6906
            $item_title = $row['title'];
6907
            $item_description = $row['comment'];
6908
        } else {
6909
            $item_title = '';
6910
            $item_description = '';
6911
        }
6912
6913 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
6914
            $parent = $extra_info['parent_item_id'];
6915
        } else {
6916
            $parent = 0;
6917
        }
6918
6919
        $sql = "SELECT * FROM " . $tbl_lp_item . "
6920
                WHERE
6921
                    c_id = ".$course_id." AND
6922
                    lp_id = " . $this->lp_id;
6923
        $result = Database::query($sql);
6924
        $arrLP = array();
6925
6926 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
6927
            $arrLP[] = array (
6928
                'id' => $row['id'],
6929
                'item_type' => $row['item_type'],
6930
                'title' => $row['title'],
6931
                'path' => $row['path'],
6932
                'description' => $row['description'],
6933
                'parent_item_id' => $row['parent_item_id'],
6934
                'previous_item_id' => $row['previous_item_id'],
6935
                'next_item_id' => $row['next_item_id'],
6936
                'display_order' => $row['display_order'],
6937
                'max_score' => $row['max_score'],
6938
                'min_score' => $row['min_score'],
6939
                'mastery_score' => $row['mastery_score'],
6940
                'prerequisite' => $row['prerequisite']
6941
            );
6942
        }
6943
6944
        $this->tree_array($arrLP);
6945
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
6946
        unset($this->arrMenu);
6947
6948 View Code Duplication
        if ($action == 'add') {
6949
            $legend = get_lang('CreateTheForum');
6950
        } elseif ($action == 'move') {
6951
            $legend = get_lang('MoveTheCurrentForum');
6952
        } else {
6953
            $legend = get_lang('EditCurrentForum');
6954
        }
6955
6956
        $form = new FormValidator('forum_form', 'POST', api_get_self() . '?' .$_SERVER['QUERY_STRING']);
6957
        $defaults = [];
6958
6959
        $form->addHeader($legend);
6960
6961 View Code Duplication
        if ($action != 'move') {
6962
            $form->addText('title', get_lang('Title'), true, ['id' => 'idTitle', 'class' => 'learnpath_item_form']);
6963
            $defaults['title'] = $item_title;
6964
        }
6965
6966
        $selectParent = $form->addSelect(
6967
            'parent',
6968
            get_lang('Parent'),
6969
            [],
6970
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);', 'class' => 'learnpath_item_form']
6971
        );
6972
        $selectParent->addOption($this->name, 0);
6973
6974
        $arrHide = array(
6975
            $id
6976
        );
6977
6978
        //$parent_item_id = $_SESSION['parent_item_id'];
6979 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
6980
            if ($action != 'add') {
6981
                if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
6982
                    $selectParent->addOption(
6983
                        $arrLP[$i]['title'],
6984
                        $arrLP[$i]['id'],
6985
                        ['style' => 'padding-left: ' . (20 + $arrLP[$i]['depth'] * 20) . 'px']
6986
                    );
6987
6988
                    if ($parent == $arrLP[$i]['id']) {
6989
                        $selectParent->setSelected($arrLP[$i]['id']);
6990
                    }
6991
                } else {
6992
                    $arrHide[] = $arrLP[$i]['id'];
6993
                }
6994
            } else {
6995
                if (
6996
                    $arrLP[$i]['item_type'] == 'dokeos_module' ||
6997
                    $arrLP[$i]['item_type'] == 'dokeos_chapter' ||
6998
                    $arrLP[$i]['item_type'] == 'dir'
6999
                ) {
7000
                    $selectParent->addOption(
7001
                        $arrLP[$i]['title'],
7002
                        $arrLP[$i]['id'],
7003
                        ['style' => 'padding-left: ' . (20 + $arrLP[$i]['depth'] * 20) . 'px']
7004
                    );
7005
7006
                    if ($parent == $arrLP[$i]['id']) {
7007
                        $selectParent->setSelected($arrLP[$i]['id']);
7008
                    }
7009
                }
7010
            }
7011
        }
7012
        if (is_array($arrLP)) {
7013
            reset($arrLP);
7014
        }
7015
7016
        $selectPrevious = $form->addSelect(
7017
            'previous',
7018
            get_lang('Position'),
7019
            [],
7020
            ['id' => 'previous', 'class' => 'learnpath_item_form']
7021
        );
7022
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
7023
7024 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7025
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
7026
                $selectPrevious->addOption(get_lang('After') . ' "' . $arrLP[$i]['title'] . '"', $arrLP[$i]['id']);
7027
7028
                if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7029
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7030
                } elseif ($action == 'add') {
7031
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7032
                }
7033
            }
7034
        }
7035
7036 View Code Duplication
        if ($action != 'move') {
7037
            $id_prerequisite = 0;
7038
            if (is_array($arrLP)) {
7039
                foreach ($arrLP as $key => $value) {
7040
                    if ($value['id'] == $id) {
7041
                        $id_prerequisite = $value['prerequisite'];
7042
                        break;
7043
                    }
7044
                }
7045
            }
7046
7047
            $arrHide = array();
7048
            for ($i = 0; $i < count($arrLP); $i++) {
7049
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
7050
                    if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id'])
7051
                        $s_selected_position = $arrLP[$i]['id'];
7052
                    elseif ($action == 'add') $s_selected_position = 0;
7053
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7054
                }
7055
            }
7056
        }
7057
7058
        if ($action == 'add') {
7059
            $form->addButtonSave(get_lang('AddForumToCourse'), 'submit_button');
7060
        } else {
7061
            $form->addButtonSave(get_lang('EditCurrentForum'), 'submit_button');
7062
        }
7063
7064
        if ($action == 'move') {
7065
            $form->addHidden('title', $item_title);
7066
            $form->addHidden('description', $item_description);
7067
        }
7068
7069 View Code Duplication
        if (is_numeric($extra_info)) {
7070
            $form->addHidden('path', $extra_info);
7071
        } elseif (is_array($extra_info)) {
7072
            $form->addHidden('path', $extra_info['path']);
7073
        }
7074
        $form->addHidden('type', TOOL_FORUM);
7075
        $form->addHidden('post_time', time());
7076
        $form->setDefaults($defaults);
7077
7078
        return '<div class="sectioncomment">' . $form->returnForm() . '</div>';
7079
    }
7080
7081
    /**
7082
     * Return HTML form to add/edit forum threads
7083
     * @param	string	Action (add/edit)
7084
     * @param	integer	Item ID if already exists in learning path
7085
     * @param	mixed	Extra information (thread ID if integer)
7086
     * @return 	string	HTML form
7087
     */
7088
    public function display_thread_form($action = 'add', $id = 0, $extra_info = '')
7089
    {
7090
        $course_id = api_get_course_int_id();
7091
        if (empty($course_id)) {
7092
            return null;
7093
        }
7094
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
7095
        $tbl_forum = Database :: get_course_table(TABLE_FORUM_THREAD);
7096
7097
        if ($id != 0 && is_array($extra_info)) {
7098
            $item_title = stripslashes($extra_info['title']);
7099 View Code Duplication
        } elseif (is_numeric($extra_info)) {
7100
            $sql = "SELECT thread_title as title FROM $tbl_forum
7101
                    WHERE c_id = $course_id AND thread_id = " . $extra_info;
7102
7103
            $result = Database::query($sql);
7104
            $row = Database :: fetch_array($result);
7105
7106
            $item_title = $row['title'];
7107
            $item_description = '';
7108
        } else {
7109
            $item_title = '';
7110
            $item_description = '';
7111
        }
7112
7113 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
7114
            $parent = $extra_info['parent_item_id'];
7115
        } else {
7116
            $parent = 0;
7117
        }
7118
7119
        $sql = "SELECT * FROM " . $tbl_lp_item . "
7120
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
7121
7122
        $result = Database::query($sql);
7123
7124
        $arrLP = array ();
7125
7126 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
7127
            $arrLP[] = array (
7128
                'id' => $row['id'],
7129
                'item_type' => $row['item_type'],
7130
                'title' => $row['title'],
7131
                'path' => $row['path'],
7132
                'description' => $row['description'],
7133
                'parent_item_id' => $row['parent_item_id'],
7134
                'previous_item_id' => $row['previous_item_id'],
7135
                'next_item_id' => $row['next_item_id'],
7136
                'display_order' => $row['display_order'],
7137
                'max_score' => $row['max_score'],
7138
                'min_score' => $row['min_score'],
7139
                'mastery_score' => $row['mastery_score'],
7140
                'prerequisite' => $row['prerequisite']
7141
            );
7142
        }
7143
7144
        $this->tree_array($arrLP);
7145
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
7146
        unset ($this->arrMenu);
7147
7148
        $form = new FormValidator('thread_form', 'POST', api_get_self() . '?' .$_SERVER['QUERY_STRING']);
7149
        $defaults = [];
7150
7151 View Code Duplication
        if ($action == 'add') {
7152
            $legend = get_lang('CreateTheForum');
7153
        } elseif ($action == 'move') {
7154
            $legend = get_lang('MoveTheCurrentForum');
7155
        } else {
7156
            $legend = get_lang('EditCurrentForum');
7157
        }
7158
7159
        $form->addHeader($legend);
7160
        $selectParent = $form->addSelect(
7161
            'parent',
7162
            get_lang('Parent'),
7163
            [],
7164
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);']
7165
        );
7166
        $selectParent->addOption($this->name, 0);
7167
7168
        $arrHide = array (
7169
            $id
7170
        );
7171
7172 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7173
            if ($action != 'add') {
7174
                if (
7175
                    (
7176
                        $arrLP[$i]['item_type'] == 'dokeos_module' ||
7177
                        $arrLP[$i]['item_type'] == 'dokeos_chapter' ||
7178
                        $arrLP[$i]['item_type'] == 'dir'
7179
                    ) &&
7180
                    !in_array($arrLP[$i]['id'], $arrHide) &&
7181
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
7182
                ) {
7183
                    $selectParent->addOption(
7184
                        $arrLP[$i]['title'],
7185
                        $arrLP[$i]['id'],
7186
                        ['style' => 'padding-left: ' . (20 + $arrLP[$i]['depth'] * 20) . 'px']
7187
                    );
7188
7189
                    if ($parent == $arrLP[$i]['id']) {
7190
                        $selectParent->setSelected($arrLP[$i]['id']);
7191
                    }
7192
                } else {
7193
                    $arrHide[] = $arrLP[$i]['id'];
7194
                }
7195
            } else {
7196
                if (
7197
                    $arrLP[$i]['item_type'] == 'dokeos_module' ||
7198
                    $arrLP[$i]['item_type'] == 'dokeos_chapter' ||
7199
                    $arrLP[$i]['item_type'] == 'dir'
7200
                ) {
7201
                    $selectParent->addOption(
7202
                        $arrLP[$i]['title'],
7203
                        $arrLP[$i]['id'],
7204
                        ['style' => 'padding-left: ' . (20 + $arrLP[$i]['depth'] * 20) . 'px']
7205
                    );
7206
7207
                    if ($parent == $arrLP[$i]['id']) {
7208
                        $selectParent->setSelected($arrLP[$i]['id']);
7209
                    }
7210
                }
7211
            }
7212
        }
7213
7214
        if ($arrLP != null) {
7215
            reset($arrLP);
7216
        }
7217
7218
        $selectPrevious = $form->addSelect('previous', get_lang('Position'), [], ['id' => 'previous']);
7219
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
7220
7221 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7222
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
7223
                $selectPrevious->addOption(
7224
                    get_lang('After')  . ' "' . $arrLP[$i]['title'] . '"',
7225
                    $arrLP[$i]['id']
7226
                );
7227
7228
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7229
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7230
                } elseif ($action == 'add') {
7231
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7232
                }
7233
            }
7234
        }
7235
7236
        if ($action != 'move') {
7237
            $form->addText('title', get_lang('Title'), true, ['id' => 'idTitle']);
7238
            $defaults['title'] = $item_title;
7239
7240
            $id_prerequisite = 0;
7241
            if ($arrLP != null) {
7242
                foreach ($arrLP as $key => $value) {
7243
                    if ($value['id'] == $id) {
7244
                        $id_prerequisite = $value['prerequisite'];
7245
                        break;
7246
                    }
7247
                }
7248
            }
7249
7250
            $arrHide = array();
7251
            $s_selected_position = 0;
7252
7253
            for ($i = 0; $i < count($arrLP); $i++) {
7254
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
7255
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
7256
                        $s_selected_position = $arrLP[$i]['id'];
7257
                    elseif ($action == 'add') $s_selected_position = 0;
7258
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7259
7260
                }
7261
            }
7262
7263
            $selectPrerequisites = $form->addSelect(
7264
                'prerequisites',
7265
                get_lang('LearnpathPrerequisites'),
7266
                [],
7267
                ['id' => 'prerequisites']
7268
            );
7269
            $selectPrerequisites->addOption(get_lang('NoPrerequisites'), 0);
7270
7271
            foreach ($arrHide as $key => $value) {
7272
                $selectPrerequisites->addOption($value['value'], $key);
7273
7274
                if ($key == $s_selected_position && $action == 'add') {
7275
                    $selectPrerequisites->setSelected($key);
7276
                } elseif ($key == $id_prerequisite && $action == 'edit') {
7277
                    $selectPrerequisites->setSelected($key);
7278
                }
7279
            }
7280
        }
7281
7282
        $form->addButtonSave(get_lang('Ok'), 'submit_button');
7283
7284
        if ($action == 'move') {
7285
            $form->addHidden('title', $item_title);
7286
            $form->addHidden('description', $item_description);
7287
        }
7288
7289 View Code Duplication
        if (is_numeric($extra_info)) {
7290
            $form->addHidden('path', $extra_info);
7291
        }
7292
        elseif (is_array($extra_info)) {
7293
            $form->addHidden('path', $extra_info['path']);
7294
        }
7295
7296
        $form->addHidden('type', TOOL_THREAD);
7297
        $form->addHidden('post_time', time());
7298
        $form->setDefaults($defaults);
7299
7300
        return $form->returnForm();
7301
    }
7302
7303
    /**
7304
     * Return the HTML form to display an item (generally a section/module item)
7305
     * @param	string	Item type (module/dokeos_module)
7306
     * @param	string	Title (optional, only when creating)
7307
     * @param	string	Action ('add'/'edit')
7308
     * @param	integer	lp_item ID
7309
     * @param	mixed	Extra info
7310
     * @return	string 	HTML form
7311
     */
7312
    public function display_item_form($item_type, $title = '', $action = 'add_item', $id = 0, $extra_info = 'new')
7313
    {
7314
        $course_id = api_get_course_int_id();
7315
        $_course = api_get_course_info();
7316
7317
        global $charset;
7318
7319
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
7320
7321
        if ($id != 0 && is_array($extra_info)) {
7322
            $item_title 		= $extra_info['title'];
7323
            $item_description 	= $extra_info['description'];
7324
            $item_path = api_get_path(WEB_COURSE_PATH) . $_course['path'] . '/scorm/' . $this->path . '/' . stripslashes($extra_info['path']);
7325
            $item_path_fck = '/scorm/' . $this->path . '/' . stripslashes($extra_info['path']);
7326
        } else {
7327
            $item_title = '';
7328
            $item_description = '';
7329
            $item_path_fck = '';
7330
        }
7331
7332 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
7333
            $parent = $extra_info['parent_item_id'];
7334
        } else {
7335
            $parent = 0;
7336
        }
7337
7338
        $id  = intval($id);
7339
        $sql = "SELECT * FROM " . $tbl_lp_item . "
7340
                WHERE
7341
                    c_id = ".$course_id." AND
7342
                    lp_id = " . $this->lp_id . " AND
7343
                    id != $id";
7344
7345
        if ($item_type == 'module')
7346
            $sql .= " AND parent_item_id = 0";
7347
7348
        $result = Database::query($sql);
7349
        $arrLP = array ();
7350
7351 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
7352
            $arrLP[] = array(
7353
                'id' => $row['id'],
7354
                'item_type' => $row['item_type'],
7355
                'title' => $row['title'],
7356
                'path' => $row['path'],
7357
                'description' => $row['description'],
7358
                'parent_item_id' => $row['parent_item_id'],
7359
                'previous_item_id' => $row['previous_item_id'],
7360
                'next_item_id' => $row['next_item_id'],
7361
                'max_score' => $row['max_score'],
7362
                'min_score' => $row['min_score'],
7363
                'mastery_score' => $row['mastery_score'],
7364
                'prerequisite' => $row['prerequisite'],
7365
                'display_order' => $row['display_order']
7366
            );
7367
        }
7368
7369
        $this->tree_array($arrLP);
7370
7371
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
7372
7373
        unset ($this->arrMenu);
7374
7375
        $gradebook = isset($_GET['gradebook']) ? Security :: remove_XSS($_GET['gradebook']) : null;
7376
7377
        $url = api_get_self() . '?' .api_get_cidreq().'&gradeboook='.$gradebook.'&action='.$action.'&type='.$item_type.'&lp_id='.$this->lp_id;
7378
7379
        $form = new FormValidator('form', 'POST',  $url);
7380
7381
        $defaults['title'] = api_html_entity_decode($item_title, ENT_QUOTES, $charset);
7382
        $defaults['description'] = $item_description;
7383
7384
        $form->addElement('header', $title);
7385
7386
        //$arrHide = array($id);
7387
        $arrHide[0]['value'] = Security :: remove_XSS($this->name);
7388
        $arrHide[0]['padding'] = 20;
7389
        $charset = api_get_system_encoding();
7390
7391
        if ($item_type != 'module' && $item_type != 'dokeos_module') {
7392 View Code Duplication
            for ($i = 0; $i < count($arrLP); $i++) {
7393
                if ($action != 'add') {
7394
                    if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
7395
                        $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7396
                        $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
7397
                        if ($parent == $arrLP[$i]['id']) {
7398
                            $s_selected_parent = $arrHide[$arrLP[$i]['id']];
7399
                        }
7400
                    }
7401
                } else {
7402
                    if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') {
7403
                        $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7404
                        $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
7405
                        if ($parent == $arrLP[$i]['id']) {
7406
                            $s_selected_parent = $arrHide[$arrLP[$i]['id']];
7407
                        }
7408
                    }
7409
                }
7410
            }
7411
7412
            if ($action != 'move') {
7413
                $form->addElement('text', 'title', get_lang('Title'));
7414
                $form->applyFilter('title', 'html_filter');
7415
                $form->addRule('title', get_lang('ThisFieldIsRequired'), 'required');
7416
            } else {
7417
                $form->addElement('hidden', 'title');
7418
            }
7419
7420
            $parent_select = $form->addElement('select', 'parent', get_lang('Parent'), '', array('id' => 'idParent', 'onchange' => "javascript: load_cbo(this.value);"));
7421
7422
            foreach ($arrHide as $key => $value) {
7423
                $parent_select->addOption($value['value'], $key, 'style="padding-left:' . $value['padding'] . 'px;"');
7424
            }
7425
            if (!empty($s_selected_parent)) {
7426
                $parent_select->setSelected($s_selected_parent);
7427
            }
7428
        }
7429
        if (is_array($arrLP)) {
7430
            reset($arrLP);
7431
        }
7432
7433
        $arrHide = array();
7434
7435
        // POSITION
7436
        for ($i = 0; $i < count($arrLP); $i++) {
7437
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
7438
                //this is the same!
7439
                if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7440
                    $s_selected_position = $arrLP[$i]['id'];
7441
                } elseif ($action == 'add') {
7442
                    $s_selected_position = $arrLP[$i]['id'];
7443
                }
7444
7445
                $arrHide[$arrLP[$i]['id']]['value'] = get_lang('After') . ' "' . $arrLP[$i]['title'] . '"';
7446
            }
7447
        }
7448
7449
        $position = $form->addElement('select', 'previous', get_lang('Position'), '', array('id' => 'previous'));
7450
        $padding = isset($value['padding']) ? $value['padding'] : 0;
7451
        $position->addOption(get_lang('FirstPosition'), 0, 'style="padding-left:' . $padding . 'px;"');
7452
7453
        foreach ($arrHide as $key => $value) {
7454
            $position->addOption($value['value'] . '"', $key, 'style="padding-left:' . $padding . 'px;"');
7455
        }
7456
7457
        if (!empty ($s_selected_position)) {
7458
            $position->setSelected($s_selected_position);
7459
        }
7460
7461
        if (is_array($arrLP)) {
7462
            reset($arrLP);
7463
        }
7464
7465
        $form->addButtonSave(get_lang('SaveSection'), 'submit_button');
7466
7467
        if ($item_type == 'module' || $item_type == 'dokeos_module') {
7468
            $form->addElement('hidden', 'parent', '0');
7469
        }
7470
        //fix in order to use the tab
7471
        if ($item_type == 'chapter') {
7472
            $form->addElement('hidden', 'type', 'chapter');
7473
        }
7474
7475
        $extension = null;
7476
        if (!empty($item_path)) {
7477
            $extension = pathinfo($item_path, PATHINFO_EXTENSION);
7478
        }
7479
7480
        //assets can't be modified
7481
7482
        //$item_type == 'asset' ||
7483
        if (( $item_type == 'sco') && ($extension == 'html' || $extension == 'htm')) {
7484
7485
            if ($item_type == 'sco') {
7486
                $form->addElement('html', '<script type="text/javascript">alert("' . get_lang('WarningWhenEditingScorm') . '")</script>');
7487
            }
7488
            $renderer = $form->defaultRenderer();
7489
            $renderer->setElementTemplate('<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{label}<br />{element}', 'content_lp');
7490
7491
            $relative_prefix = '';
7492
7493
            $editor_config = array( 'ToolbarSet' 			=> 'LearningPathDocuments',
7494
                'Width' 				=> '100%',
7495
                'Height' 				=> '500',
7496
                'FullPage' 				=> true,
7497
                'CreateDocumentDir' 	=> $relative_prefix,
7498
                'CreateDocumentWebDir' 	=> api_get_path(WEB_COURSE_PATH) . api_get_course_path().'/scorm/',
7499
                'BaseHref' 				=> api_get_path(WEB_COURSE_PATH) . api_get_course_path().$item_path_fck
7500
            );
7501
7502
            $form->addElement('html_editor', 'content_lp', '', null, $editor_config);
7503
            $content_path = (api_get_path(SYS_COURSE_PATH).api_get_course_path().$item_path_fck);
7504
            //$defaults['content_lp'] = file_get_contents($item_path);
7505
            $defaults['content_lp'] = file_get_contents($content_path);
7506
        }
7507
7508
        $form->addElement('hidden', 'type', 'dokeos_' . $item_type);
7509
        $form->addElement('hidden', 'post_time', time());
7510
        $form->setDefaults($defaults);
7511
        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...
7512
    }
7513
7514
    /**
7515
     * Returns the form to update or create a document
7516
     * @param	string	Action (add/edit)
7517
     * @param	integer	ID of the lp_item (if already exists)
7518
     * @param	mixed	Integer if document ID, string if info ('new')
7519
     * @return	string	HTML form
7520
     */
7521
    public function display_document_form($action = 'add', $id = 0, $extra_info = 'new')
7522
    {
7523
        $course_id = api_get_course_int_id();
7524
        $_course = api_get_course_info();
7525
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
7526
        $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
7527
7528
        $no_display_edit_textarea = false;
7529
        $item_description = '';
7530
        //If action==edit document
7531
        //We don't display the document form if it's not an editable document (html or txt file)
7532
        if ($action == "edit") {
7533
            if (is_array($extra_info)) {
7534
                $path_parts = pathinfo($extra_info['dir']);
7535
                if ($path_parts['extension'] != "txt" && $path_parts['extension'] != "html") {
7536
                    $no_display_edit_textarea = true;
7537
                }
7538
            }
7539
        }
7540
        $no_display_add = false;
7541
7542
        // If action==add an existing document
7543
        // We don't display the document form if it's not an editable document (html or txt file).
7544
        if ($action == "add") {
7545
            if (is_numeric($extra_info)) {
7546
                $sql_doc = "SELECT path FROM " . $tbl_doc . "
7547
                            WHERE c_id = ".$course_id." AND id = " . intval($extra_info);
7548
                $result = Database::query($sql_doc);
7549
                $path_file = Database :: result($result, 0, 0);
7550
                $path_parts = pathinfo($path_file);
7551
                if ($path_parts['extension'] != "txt" && $path_parts['extension'] != "html") {
7552
                    $no_display_add = true;
7553
                }
7554
            }
7555
        }
7556
        if ($id != 0 && is_array($extra_info)) {
7557
            $item_title = stripslashes($extra_info['title']);
7558
            $item_description = stripslashes($extra_info['description']);
7559
            $item_terms = stripslashes($extra_info['terms']);
7560 View Code Duplication
            if (empty ($item_title)) {
7561
                $path_parts = pathinfo($extra_info['path']);
7562
                $item_title = stripslashes($path_parts['filename']);
7563
            }
7564
        } elseif (is_numeric($extra_info)) {
7565
            $sql_doc = "SELECT path, title FROM " . $tbl_doc . "
7566
                        WHERE
7567
                            c_id = ".$course_id." AND
7568
                            id = " . intval($extra_info);
7569
7570
            $result = Database::query($sql_doc);
7571
            $row 	= Database::fetch_array($result);
7572
            $item_title = $row['title'];
7573
            $item_title = str_replace('_', ' ', $item_title);
7574 View Code Duplication
            if (empty ($item_title)) {
7575
                $path_parts = pathinfo($row['path']);
7576
                $item_title = stripslashes($path_parts['filename']);
7577
            }
7578
        } else {
7579
            $item_title = '';
7580
            $item_description = '';
7581
        }
7582
        $return = '<legend>';
7583
7584 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
7585
            $parent = $extra_info['parent_item_id'];
7586
        } else {
7587
            $parent = 0;
7588
        }
7589
7590
        $sql = "SELECT * FROM " . $tbl_lp_item . "
7591
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
7592
7593
        $result = Database::query($sql);
7594
        $arrLP = array ();
7595 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
7596
            $arrLP[] = array(
7597
                'id' => $row['id'],
7598
                'item_type' => $row['item_type'],
7599
                'title' => $row['title'],
7600
                'path' => $row['path'],
7601
                'description' => $row['description'],
7602
                'parent_item_id' => $row['parent_item_id'],
7603
                'previous_item_id' => $row['previous_item_id'],
7604
                'next_item_id' => $row['next_item_id'],
7605
                'display_order' => $row['display_order'],
7606
                'max_score' => $row['max_score'],
7607
                'min_score' => $row['min_score'],
7608
                'mastery_score' => $row['mastery_score'],
7609
                'prerequisite' => $row['prerequisite']
7610
            );
7611
        }
7612
7613
        $this->tree_array($arrLP);
7614
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
7615
        unset ($this->arrMenu);
7616
7617
        if ($action == 'add') {
7618
            $return .= get_lang('CreateTheDocument');
7619
        } elseif ($action == 'move') {
7620
            $return .= get_lang('MoveTheCurrentDocument');
7621
        } else {
7622
            $return .= get_lang('EditTheCurrentDocument');
7623
        }
7624
7625
        $return .= '</legend>';
7626
7627 View Code Duplication
        if (isset ($_GET['edit']) && $_GET['edit'] == 'true') {
7628
            $return .= Display :: return_warning_message('<strong>' . get_lang('Warning') . ' !</strong><br />' . get_lang('WarningEditingDocument'), false);
7629
        }
7630
        $form = new FormValidator('form', 'POST', api_get_self() . '?' .$_SERVER['QUERY_STRING'], '', array('enctype'=> "multipart/form-data"));
7631
        $defaults['title'] = Security :: remove_XSS($item_title);
7632
        if (empty($item_title)) {
7633
            $defaults['title'] = Security::remove_XSS($item_title);
7634
        }
7635
        $defaults['description'] = $item_description;
7636
        $form->addElement('html', $return);
7637
        if ($action != 'move') {
7638
            $form->addElement('text', 'title', get_lang('Title'), array('id' => 'idTitle', 'class' => 'col-md-4'));
7639
            $form->applyFilter('title', 'html_filter');
7640
        }
7641
7642
        $arrHide[0]['value'] = $this->name;
7643
        $arrHide[0]['padding'] = 20;
7644
7645 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7646
            if ($action != 'add') {
7647
                if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
7648
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7649
                    $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
7650
                    if ($parent == $arrLP[$i]['id']) {
7651
                        $s_selected_parent = $arrHide[$arrLP[$i]['id']];
7652
                    }
7653
                }
7654
            } else {
7655
                if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') {
7656
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7657
                    $arrHide[$arrLP[$i]['id']]['padding'] = 20 + $arrLP[$i]['depth'] * 20;
7658
                    if ($parent == $arrLP[$i]['id']) {
7659
                        $s_selected_parent = $arrHide[$arrLP[$i]['id']];
7660
                    }
7661
                }
7662
            }
7663
        }
7664
7665
        $parent_select = $form->addSelect('parent', get_lang('Parent'), [], ['id' => 'idParent', 'onchange' => 'javascript: load_cbo(this.value);']);
7666
        $my_count=0;
7667
        foreach ($arrHide as $key => $value) {
7668
            if ($my_count!=0) {
7669
                // The LP name is also the first section and is not in the same charset like the other sections.
7670
                $value['value'] = Security :: remove_XSS($value['value']);
7671
                $parent_select->addOption($value['value'], $key, 'style="padding-left:' . $value['padding'] . 'px;"');
7672
            } else {
7673
                $value['value'] = Security :: remove_XSS($value['value']);
7674
                $parent_select->addOption($value['value'], $key, 'style="padding-left:' . $value['padding'] . 'px;"');
7675
            }
7676
            $my_count++;
7677
        }
7678
7679
        if (!empty($id)) {
7680
            $parent_select->setSelected($parent);
7681
        } else {
7682
            $parent_item_id = isset($_SESSION['parent_item_id']) ? $_SESSION['parent_item_id'] : 0 ;
7683
            $parent_select->setSelected($parent_item_id);
7684
        }
7685
7686
        if (is_array($arrLP)) {
7687
            reset($arrLP);
7688
        }
7689
7690
        $arrHide = array();
7691
        $s_selected_position = null;
7692
7693
        //POSITION
7694
        for ($i = 0; $i < count($arrLP); $i++) {
7695
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id || $arrLP[$i]['item_type'] == TOOL_LP_FINAL_ITEM) {
7696
                if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id'] || $action == 'add') {
7697
                    $s_selected_position = $arrLP[$i]['id'];
7698
                }
7699
                $arrHide[$arrLP[$i]['id']]['value'] = get_lang('After') . ' "' . $arrLP[$i]['title'] . '"';
7700
            }
7701
        }
7702
7703
        $position = $form->addSelect('previous', get_lang('Position'), [], ['id' => 'previous']);
7704
        $position->addOption(get_lang('FirstPosition'), 0);
7705
7706
        foreach ($arrHide as $key => $value) {
7707
            $padding = isset($value['padding']) ? $value['padding']: 20;
7708
            $position->addOption($value['value'], $key, 'style="padding-left:' . $padding . 'px;"');
7709
        }
7710
7711
        $position->setSelected($s_selected_position);
7712
7713
        if (is_array($arrLP)) {
7714
            reset($arrLP);
7715
        }
7716
7717
        if ($action != 'move') {
7718
            $id_prerequisite = 0;
7719
            if (is_array($arrLP)) {
7720
                foreach ($arrLP as $key => $value) {
7721
                    if ($value['id'] == $id) {
7722
                        $id_prerequisite = $value['prerequisite'];
7723
                        break;
7724
                    }
7725
                }
7726
            }
7727
7728
            $arrHide = array();
7729
7730
            for ($i = 0; $i < count($arrLP); $i++) {
7731
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter' && $arrLP[$i]['item_type'] !== TOOL_LP_FINAL_ITEM) {
7732
                    if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id'])
7733
                        $s_selected_position = $arrLP[$i]['id'];
7734
                    elseif ($action == 'add') $s_selected_position = $arrLP[$i]['id'];
7735
7736
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
7737
7738
                }
7739
            }
7740
7741
            if (!$no_display_add) {
7742
                $item_type = isset($extra_info['item_type']) ? $extra_info['item_type'] : null;
7743
                $edit = isset($_GET['edit']) ? $_GET['edit'] : null;
7744
                if (($extra_info == 'new' || $item_type == TOOL_DOCUMENT || $item_type == TOOL_LP_FINAL_ITEM || $edit == 'true')) {
7745
                    if (isset ($_POST['content']))
7746
                        $content = stripslashes($_POST['content']);
7747
                    elseif (is_array($extra_info)) {
7748
                        //If it's an html document or a text file
7749
                        if (!$no_display_edit_textarea) {
7750
                            $content = $this->display_document($extra_info['path'], false, false);
7751
                        }
7752
                    } elseif (is_numeric($extra_info))
7753
                        $content = $this->display_document($extra_info, false, false);
7754
                    else
7755
                        $content = '';
7756
7757
                    if (!$no_display_edit_textarea) {
7758
                        // We need to calculate here some specific settings for the online editor.
7759
                        // The calculated settings work for documents in the Documents tool
7760
                        // (on the root or in subfolders).
7761
                        // For documents in native scorm packages it is unclear whether the
7762
                        // online editor should be activated or not.
7763
7764
                        // A new document, it is in the root of the repository.
7765
                        $relative_path 	 = '';
7766
                        $relative_prefix = '';
7767
7768
                        if (is_array($extra_info) && $extra_info != 'new') {
7769
                            // The document already exists. Whe have to determine its relative path towards the repository root.
7770
                            $relative_path = explode('/', $extra_info['dir']);
7771
                            $cnt = count($relative_path) - 2;
7772
                            if ($cnt < 0) {
7773
                                $cnt = 0;
7774
                            }
7775
                            $relative_prefix = str_repeat('../', $cnt);
7776
                            $relative_path 	 = array_slice($relative_path, 1, $cnt);
7777
                            $relative_path 	 = implode('/', $relative_path);
7778
                            if (strlen($relative_path) > 0) {
7779
                                $relative_path = $relative_path . '/';
7780
                            }
7781
                        } else {
7782
                            $result = $this->generate_lp_folder($_course);
7783
                            $relative_path = api_substr($result['dir'], 1, strlen($result['dir']));
7784
                            $relative_prefix = '../../';
7785
                        }
7786
7787
                        $editor_config = array(
7788
                            'ToolbarSet'=> 'LearningPathDocuments',
7789
                            'Width' 				=> '100%',
7790
                            'Height' 				=> '500',
7791
                            'FullPage' 				=> true,
7792
                            'CreateDocumentDir' 	=> $relative_prefix,
7793
                            'CreateDocumentWebDir' 	=> api_get_path(WEB_COURSE_PATH) . api_get_course_path().'/document/',
7794
                            'BaseHref' 				=> api_get_path(WEB_COURSE_PATH) . api_get_course_path().'/document/'.$relative_path
7795
                        );
7796
7797
                        if ($_GET['action'] == 'add_item') {
7798
                            $class = 'add';
7799
                            $text = get_lang('LPCreateDocument');
7800
                        } else {
7801
                            if ($_GET['action'] == 'edit_item') {
7802
                                $class = 'save';
7803
                                $text = get_lang('SaveDocument');
7804
                            }
7805
                        }
7806
7807
                        $form->addButtonSave($text, 'submit_button');
7808
                        $renderer = $form->defaultRenderer();
7809
                        $renderer->setElementTemplate('&nbsp;{label}{element}', 'content_lp');
7810
                        $form->addElement('html', '<div class="editor-lp">');
7811
                        $form->addHtmlEditor('content_lp', null, null, true, $editor_config, true);
7812
                        $form->addElement('html', '</div>');
7813
                        $defaults['content_lp'] = $content;
7814
                    }
7815
                } elseif (is_numeric($extra_info)) {
7816
                    $form->addButtonSave(get_lang('SaveDocument'), 'submit_button');
7817
7818
                    $return = $this->display_document($extra_info, true, true, true);
7819
                    $form->addElement('html', $return);
7820
                }
7821
            }
7822
        }
7823
7824
        if ($action == 'move') {
7825
            $form->addElement('hidden', 'title', $item_title);
7826
            $form->addElement('hidden', 'description', $item_description);
7827
        }
7828
        if (is_numeric($extra_info)) {
7829
            $form->addButtonSave(get_lang('SaveDocument'), 'submit_button');
7830
            $form->addElement('hidden', 'path', $extra_info);
7831
        } elseif (is_array($extra_info)) {
7832
            $form->addButtonSave(get_lang('SaveDocument'), 'submit_button');
7833
            $form->addElement('hidden', 'path', $extra_info['path']);
7834
        }
7835
        $form->addElement('hidden', 'type', TOOL_DOCUMENT);
7836
        $form->addElement('hidden', 'post_time', time());
7837
        $form->setDefaults($defaults);
7838
7839
        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...
7840
    }
7841
7842
    /**
7843
     * Return HTML form to add/edit a link item
7844
     * @param string	$action (add/edit)
7845
     * @param integer	$id Item ID if exists
7846
     * @param mixed		$extra_info
7847
     * @return	string	HTML form
7848
     */
7849
    public function display_link_form($action = 'add', $id = 0, $extra_info = '')
7850
    {
7851
        $course_id = api_get_course_int_id();
7852
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
7853
        $tbl_link = Database :: get_course_table(TABLE_LINK);
7854
7855
        if ($id != 0 && is_array($extra_info)) {
7856
            $item_title = stripslashes($extra_info['title']);
7857
            $item_description = stripslashes($extra_info['description']);
7858
            $item_url = stripslashes($extra_info['url']);
7859
        } elseif (is_numeric($extra_info)) {
7860
            $extra_info = intval($extra_info);
7861
            $sql = "SELECT title, description, url FROM " . $tbl_link . "
7862
                    WHERE c_id = ".$course_id." AND id = " . $extra_info;
7863
            $result = Database::query($sql);
7864
            $row = Database :: fetch_array($result);
7865
            $item_title       = $row['title'];
7866
            $item_description = $row['description'];
7867
            $item_url = $row['url'];
7868
        } else {
7869
            $item_title = '';
7870
            $item_description = '';
7871
            $item_url = '';
7872
        }
7873
7874
        $form = new FormValidator('edit_link', 'POST', api_get_self() . '?' .$_SERVER['QUERY_STRING']);
7875
        $defaults = [];
7876
7877 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
7878
            $parent = $extra_info['parent_item_id'];
7879
        } else {
7880
            $parent = 0;
7881
        }
7882
7883
        $sql = "SELECT * FROM " . $tbl_lp_item . "
7884
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
7885
        $result = Database::query($sql);
7886
        $arrLP = array();
7887
7888 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
7889
            $arrLP[] = array(
7890
                'id' => $row['id'],
7891
                'item_type' => $row['item_type'],
7892
                'title' => $row['title'],
7893
                'path' => $row['path'],
7894
                'description' => $row['description'],
7895
                'parent_item_id' => $row['parent_item_id'],
7896
                'previous_item_id' => $row['previous_item_id'],
7897
                'next_item_id' => $row['next_item_id'],
7898
                'display_order' => $row['display_order'],
7899
                'max_score' => $row['max_score'],
7900
                'min_score' => $row['min_score'],
7901
                'mastery_score' => $row['mastery_score'],
7902
                'prerequisite' => $row['prerequisite']
7903
            );
7904
        }
7905
7906
        $this->tree_array($arrLP);
7907
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
7908
        unset ($this->arrMenu);
7909
7910
        if ($action == 'add') {
7911
            $legend = get_lang('CreateTheLink');
7912
        } elseif ($action == 'move') {
7913
            $legend = get_lang('MoveCurrentLink');
7914
        } else {
7915
            $legend = get_lang('EditCurrentLink');
7916
        }
7917
7918
        $form->addHeader($legend);
7919
7920 View Code Duplication
        if ($action != 'move') {
7921
            $form->addText('title', get_lang('Title'), true, ['class' => 'learnpath_item_form']);
7922
            $defaults['title'] = $item_title;
7923
        }
7924
7925
        $selectParent = $form->addSelect(
7926
            'parent',
7927
            get_lang('Parent'),
7928
            [],
7929
            ['id' => 'idParent', 'onchange' => 'load_cbo(this.value);', 'class' => 'learnpath_item_form']
7930
        );
7931
        $selectParent->addOption($this->name, 0);
7932
        $arrHide = array(
7933
            $id
7934
        );
7935
7936
        $parent_item_id = isset($_SESSION['parent_item_id']) ? $_SESSION['parent_item_id'] : 0;
7937
7938 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
7939
            if ($action != 'add') {
7940
                if (
7941
                    (
7942
                        $arrLP[$i]['item_type'] == 'dokeos_module' ||
7943
                        $arrLP[$i]['item_type'] == 'dokeos_chapter' ||
7944
                        $arrLP[$i]['item_type'] == 'dir'
7945
                    ) &&
7946
                    !in_array($arrLP[$i]['id'], $arrHide) &&
7947
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
7948
                ) {
7949
                    $selectParent->addOption(
7950
                        $arrLP[$i]['title'],
7951
                        $arrLP[$i]['id'],
7952
                        ['style' => 'padding-left: ' . (20 + $arrLP[$i]['depth'] * 20) . 'px;']
7953
                    );
7954
7955
                    if ($parent == $arrLP[$i]['id']) {
7956
                        $selectParent->setSelected($arrLP[$i]['id']);
7957
                    }
7958
                } else {
7959
                    $arrHide[] = $arrLP[$i]['id'];
7960
                }
7961
            } else {
7962
                if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') {
7963
                    $selectParent->addOption(
7964
                        $arrLP[$i]['title'],
7965
                        $arrLP[$i]['id'],
7966
                        ['style' => 'padding-left: ' . (20 + $arrLP[$i]['depth'] * 20) . 'px']
7967
                    );
7968
7969
                    if ($parent_item_id == $arrLP[$i]['id']) {
7970
                        $selectParent->setSelected($arrLP[$i]['id']);
7971
                    }
7972
                }
7973
            }
7974
        }
7975
7976
        if (is_array($arrLP)) {
7977
            reset($arrLP);
7978
        }
7979
7980
        $selectPrevious = $form->addSelect(
7981
            'previous',
7982
            get_lang('Position'),
7983
            [],
7984
            ['id' => 'previous', 'class' => 'learnpath_item_form']
7985
        );
7986
        $selectPrevious->addOption(get_lang('FirstPosition'), 0);
7987
7988
        for ($i = 0; $i < count($arrLP); $i++) {
7989
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
7990
                $selectPrevious->addOption($arrLP[$i]['title'], $arrLP[$i]['id']);
7991
7992
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
7993
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7994
                } elseif ($action == 'add') {
7995
                    $selectPrevious->setSelected($arrLP[$i]['id']);
7996
                }
7997
            }
7998
        }
7999
8000
        if ($action != 'move') {
8001
            $urlAttributes = ['class' => 'learnpath_item_form'];
8002
8003
            if (is_numeric($extra_info)) {
8004
                $urlAttributes['disabled'] = 'disabled';
8005
            }
8006
8007
            $form->addElement('url', 'url', get_lang('Url'), $urlAttributes);
8008
            $defaults['url'] = $item_url;
8009
8010
            $id_prerequisite = 0;
8011
            if (is_array($arrLP)) {
8012
                foreach ($arrLP as $key => $value) {
8013
                    if ($value['id'] == $id) {
8014
                        $id_prerequisite = $value['prerequisite'];
8015
                        break;
8016
                    }
8017
                }
8018
            }
8019
8020
            $arrHide = array();
8021
            for ($i = 0; $i < count($arrLP); $i++) {
8022
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
8023
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
8024
                        $s_selected_position = $arrLP[$i]['id'];
8025
                    elseif ($action == 'add') $s_selected_position = 0;
8026
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8027
8028
                }
8029
            }
8030
        }
8031
8032
        if ($action == 'add') {
8033
            $form->addButtonSave(get_lang('AddLinkToCourse'), 'submit_button');
8034
        } else {
8035
            $form->addButtonSave(get_lang('EditCurrentLink'), 'submit_button');
8036
        }
8037
8038
        if ($action == 'move') {
8039
            $form->addHidden('title', $item_title);
8040
            $form->addHidden('description', $item_description);
8041
        }
8042
8043 View Code Duplication
        if (is_numeric($extra_info)) {
8044
            $form->addHidden('path', $extra_info);
8045
        } elseif (is_array($extra_info)) {
8046
            $form->addHidden('path', $extra_info['path']);
8047
        }
8048
        $form->addHidden('type', TOOL_LINK);
8049
        $form->addHidden('post_time', time());
8050
8051
        $form->setDefaults($defaults);
8052
8053
        return '<div class="sectioncomment">' . $form->returnForm() . '</div>';
8054
    }
8055
8056
    /**
8057
     * Return HTML form to add/edit a student publication (work)
8058
     * @param	string	Action (add/edit)
8059
     * @param	integer	Item ID if already exists
8060
     * @param	mixed	Extra info (work ID if integer)
8061
     * @return	string	HTML form
8062
     */
8063
    public function display_student_publication_form($action = 'add', $id = 0, $extra_info = '')
8064
    {
8065
        $course_id = api_get_course_int_id();
8066
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
8067
        $tbl_publication = Database :: get_course_table(TABLE_STUDENT_PUBLICATION);
8068
8069
        if ($id != 0 && is_array($extra_info)) {
8070
            $item_title = stripslashes($extra_info['title']);
8071
            $item_description = stripslashes($extra_info['description']);
8072
        } elseif (is_numeric($extra_info)) {
8073
            $extra_info = intval($extra_info);
8074
            $sql = "SELECT title, description
8075
                    FROM " . $tbl_publication . "
8076
                    WHERE c_id = ".$course_id." AND id = " . $extra_info;
8077
8078
            $result = Database::query($sql);
8079
            $row = Database :: fetch_array($result);
8080
8081
            $item_title = $row['title'];
8082
        } else {
8083
            $item_title = get_lang('Student_publication');
8084
        }
8085
8086 View Code Duplication
        if ($id != 0 && is_array($extra_info)) {
8087
            $parent = $extra_info['parent_item_id'];
8088
        } else {
8089
            $parent = 0;
8090
        }
8091
8092
        $sql = "SELECT * FROM " . $tbl_lp_item . "
8093
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
8094
8095
        $result = Database::query($sql);
8096
        $arrLP = array();
8097 View Code Duplication
        while ($row = Database :: fetch_array($result)) {
8098
            $arrLP[] = array (
8099
                'id' => $row['id'],
8100
                'item_type' => $row['item_type'],
8101
                'title' => $row['title'],
8102
                'path' => $row['path'],
8103
                'description' => $row['description'],
8104
                'parent_item_id' => $row['parent_item_id'],
8105
                'previous_item_id' => $row['previous_item_id'],
8106
                'next_item_id' => $row['next_item_id'],
8107
                'display_order' => $row['display_order'],
8108
                'max_score' => $row['max_score'],
8109
                'min_score' => $row['min_score'],
8110
                'mastery_score' => $row['mastery_score'],
8111
                'prerequisite' => $row['prerequisite']
8112
            );
8113
        }
8114
8115
        $this->tree_array($arrLP);
8116
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
8117
        unset ($this->arrMenu);
8118
8119
        $form = new FormValidator('frm_student_publication', 'post', '#');
8120
8121
        if ($action == 'add') {
8122
            $form->addHeader(get_lang('Student_publication'));
8123
        } elseif ($action == 'move') {
8124
            $form->addHeader(get_lang('MoveCurrentStudentPublication'));
8125
        } else {
8126
            $form->addHeader(get_lang('EditCurrentStudentPublication'));
8127
        }
8128
8129 View Code Duplication
        if ($action != 'move') {
8130
            $form->addText('title', get_lang('Title'), true, ['class' => 'learnpath_item_form', 'id' => 'idTitle']);
8131
        }
8132
8133
        $parentSelect = $form->addSelect(
8134
            'parent',
8135
            get_lang('Parent'),
8136
            ['0' => $this->name],
8137
            [
8138
                'onchange' => 'javascript: load_cbo(this.value);',
8139
                'class' => 'learnpath_item_form',
8140
                'id' => 'idParent'
8141
            ]
8142
        );
8143
8144
        $arrHide = array (
8145
            $id
8146
        );
8147
8148 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
8149
            if ($action != 'add') {
8150
                if (
8151
                    (
8152
                        $arrLP[$i]['item_type'] == 'dokeos_module' ||
8153
                        $arrLP[$i]['item_type'] == 'dokeos_chapter' ||
8154
                        $arrLP[$i]['item_type'] == 'dir'
8155
                    ) &&
8156
                    !in_array($arrLP[$i]['id'], $arrHide) &&
8157
                    !in_array($arrLP[$i]['parent_item_id'], $arrHide)
8158
                ) {
8159
                    $parentSelect->addOption(
8160
                        $arrLP[$i]['title'],
8161
                        $arrLP[$i]['id'],
8162
                        ['style' => 'padding-left: ' . (($arrLP[$i]['depth'] * 10) + 20) . 'px;']
8163
                    );
8164
8165
                    if ($parent == $arrLP[$i]['id']) {
8166
                        $parentSelect->setSelected($arrLP[$i]['id']);
8167
                    }
8168
                } else {
8169
                    $arrHide[] = $arrLP[$i]['id'];
8170
                }
8171
            } else {
8172
                if (
8173
                    $arrLP[$i]['item_type'] == 'dokeos_module' ||
8174
                    $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir'
8175
                ) {
8176
                    $parentSelect->addOption(
8177
                        $arrLP[$i]['title'],
8178
                        $arrLP[$i]['id'],
8179
                        ['style' => 'padding-left: ' . (($arrLP[$i]['depth'] * 10) + 20) . 'px;']
8180
                    );
8181
8182
                    if ($parent == $arrLP[$i]['id']) {
8183
                        $parentSelect->setSelected($arrLP[$i]['id']);
8184
                    }
8185
                }
8186
            }
8187
        }
8188
8189
        if (is_array($arrLP)) {
8190
            reset($arrLP);
8191
        }
8192
8193
        $previousSelect = $form->addSelect(
8194
            'previous',
8195
            get_lang('Position'),
8196
            ['0' => get_lang('FirstPosition')],
8197
            ['id' => 'previous', 'class' => 'learnpath_item_form']
8198
        );
8199
8200 View Code Duplication
        for ($i = 0; $i < count($arrLP); $i++) {
8201
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
8202
                $previousSelect->addOption(
8203
                    get_lang('After') . ' "' . $arrLP[$i]['title'] . '"',
8204
                    $arrLP[$i]['id']
8205
                );
8206
8207
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id']) {
8208
                    $previousSelect->setSelected($arrLP[$i]['id']);
8209
                } elseif ($action == 'add') {
8210
                    $previousSelect->setSelected($arrLP[$i]['id']);
8211
                }
8212
            }
8213
        }
8214
8215 View Code Duplication
        if ($action != 'move') {
8216
            $id_prerequisite = 0;
8217
            if (is_array($arrLP)) {
8218
                foreach ($arrLP as $key => $value) {
8219
                    if ($value['id'] == $id) {
8220
                        $id_prerequisite = $value['prerequisite'];
8221
                        break;
8222
                    }
8223
                }
8224
            }
8225
            $arrHide = array ();
8226
            for ($i = 0; $i < count($arrLP); $i++) {
8227
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
8228
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
8229
                        $s_selected_position = $arrLP[$i]['id'];
8230
                    elseif ($action == 'add') $s_selected_position = 0;
8231
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
8232
8233
                }
8234
            }
8235
        }
8236
8237
        if ($action == 'add') {
8238
            $form->addButtonCreate(get_lang('AddAssignmentToCourse'), 'submit_button');
8239
        } else {
8240
            $form->addButtonCreate(get_lang('EditCurrentStudentPublication'), 'submit_button');
8241
        }
8242
8243
        if ($action == 'move') {
8244
            $form->addHidden('title', $item_title);
8245
            $form->addHidden('description', $item_description);
8246
        }
8247
8248 View Code Duplication
        if (is_numeric($extra_info)) {
8249
            $form->addHidden('path', $extra_info);
8250
        } elseif (is_array($extra_info)) {
8251
            $form->addHidden('path', $extra_info['path']);
8252
        }
8253
8254
        $form->addHidden('type', TOOL_STUDENTPUBLICATION);
8255
        $form->addHidden('post_time', time());
8256
        $form->setDefaults(['title' => $item_title]);
8257
8258
        $return = '<div class="sectioncomment">';
8259
        $return .= $form->returnForm();
8260
        $return .= '</div>';
8261
8262
        return $return;
8263
    }
8264
8265
    /**
8266
     * Displays the menu for manipulating a step
8267
     *
8268
     * @param $item_id
8269
     * @param string $item_type
8270
     * @return string
8271
     */
8272
    public function display_manipulate($item_id, $item_type = TOOL_DOCUMENT)
8273
    {
8274
        $_course = api_get_course_info();
8275
        $course_id = api_get_course_int_id();
8276
        $course_code = api_get_course_id();
8277
8278
        $return = '<div class="actions">';
8279
8280
        switch ($item_type) {
8281
            case 'dokeos_chapter' :
8282
            case 'chapter' :
8283
                // Commented the message cause should not show it.
8284
                //$lang = get_lang('TitleManipulateChapter');
8285
                break;
8286
8287
            case 'dokeos_module' :
8288
            case 'module' :
8289
                // Commented the message cause should not show it.
8290
                //$lang = get_lang('TitleManipulateModule');
8291
                break;
8292
            case TOOL_LP_FINAL_ITEM :
8293
            case TOOL_DOCUMENT :
8294
                // Commented the message cause should not show it.
8295
                //$lang = get_lang('TitleManipulateDocument');
8296
                break;
8297
8298
            case TOOL_LINK :
8299
            case 'link' :
8300
                // Commented the message cause should not show it.
8301
                //$lang = get_lang('TitleManipulateLink');
8302
                break;
8303
8304
            case TOOL_QUIZ :
8305
                // Commented the message cause should not show it.
8306
                //$lang = get_lang('TitleManipulateQuiz');
8307
                break;
8308
8309
            case TOOL_STUDENTPUBLICATION :
8310
                // Commented the message cause should not show it.
8311
                //$lang = get_lang('TitleManipulateStudentPublication');
8312
                break;
8313
        }
8314
8315
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
8316
        $item_id = intval($item_id);
8317
        $sql = "SELECT * FROM " . $tbl_lp_item . " as lp
8318
                WHERE lp.c_id = ".$course_id." AND lp.id = " . $item_id;
8319
        $result = Database::query($sql);
8320
        $row = Database::fetch_assoc($result);
8321
8322
        $audio_player = null;
8323
        // We display an audio player if needed.
8324
        if (!empty($row['audio'])) {
8325
            $audio_player .= '<div class="lp_mediaplayer" id="container">
8326
                              <a href="http://www.macromedia.com/go/getflashplayer">Get the Flash Player</a> to see this player.
8327
                              </div>';
8328
            $audio_player .= '<script type="text/javascript" src="../inc/lib/mediaplayer/swfobject.js"></script>';
8329
            $audio_player .= '<script>
8330
                                var s1 = new SWFObject("../inc/lib/mediaplayer/player.swf","ply","250","20","9","#FFFFFF");
8331
                                s1.addParam("allowscriptaccess","always");
8332
                                s1.addParam("flashvars","file=../../courses/' . $_course['path'] . '/document/audio/' . $row['audio'] . '&autostart=true");
8333
                                s1.write("container");
8334
                            </script>';
8335
        }
8336
8337
        $url = api_get_self().'?cidReq='.Security::remove_XSS($_GET['cidReq']).'&view=build&id='.$item_id .'&lp_id='.$this->lp_id;
8338
8339
        $return .= Display::url(
8340
            Display::return_icon('edit.png', get_lang('Edit'), array(), ICON_SIZE_SMALL),
8341
            $url.'&action=edit_item&path_item=' . $row['path']
8342
        );
8343
8344
        $return .= Display::url(
8345
            Display::return_icon('move.png', get_lang('Move'), array(), ICON_SIZE_SMALL),
8346
            $url.'&action=move_item'
8347
        );
8348
8349
        // Commented for now as prerequisites cannot be added to chapters.
8350
        if ($item_type != 'dokeos_chapter' && $item_type != 'chapter') {
8351
            $return .= Display::url(
8352
                Display::return_icon('accept.png', get_lang('LearnpathPrerequisites'), array(), ICON_SIZE_SMALL),
8353
                $url.'&action=edit_item_prereq'
8354
            );
8355
        }
8356
        $return .= Display::url(
8357
            Display::return_icon('delete.png', get_lang('Delete'), array(), ICON_SIZE_SMALL),
8358
            $url.'&action=delete_item'
8359
        );
8360
8361 View Code Duplication
        if ($item_type == TOOL_HOTPOTATOES ) {
8362
            $document_data = DocumentManager::get_document_data_by_id($row['path'], $course_code);
8363
            $return .= get_lang('File').': '.$document_data['absolute_path_from_document'];
8364
        }
8365
8366 View Code Duplication
        if ($item_type == TOOL_DOCUMENT || $item_type == TOOL_LP_FINAL_ITEM) {
8367
            $document_data = DocumentManager::get_document_data_by_id($row['path'], $course_code);
8368
            $return .= get_lang('File').': '.$document_data['absolute_path_from_document'];
8369
        }
8370
8371
        $return .= '</div>';
8372
8373
        if (!empty($audio_player)) {
8374
            $return .= '<br />'.$audio_player;
8375
        }
8376
8377
        return $return;
8378
    }
8379
8380
    /**
8381
     * Creates the javascript needed for filling up the checkboxes without page reload
8382
     * @return string
8383
     */
8384
    public function get_js_dropdown_array()
8385
    {
8386
        $course_id = api_get_course_int_id();
8387
        $return = 'var child_name = new Array();' . "\n";
8388
        $return .= 'var child_value = new Array();' . "\n\n";
8389
        $return .= 'child_name[0] = new Array();' . "\n";
8390
        $return .= 'child_value[0] = new Array();' . "\n\n";
8391
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
8392
        $sql_zero = "SELECT * FROM " . $tbl_lp_item . "
8393
                    WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id . " AND parent_item_id = 0
8394
                    ORDER BY display_order ASC";
8395
        $res_zero = Database::query($sql_zero);
8396
        $i = 0;
8397
8398
        while ($row_zero = Database :: fetch_array($res_zero)) {
8399
            if ($row_zero['item_type'] !== TOOL_LP_FINAL_ITEM) {
8400
                if ($row_zero['item_type'] == TOOL_QUIZ) {
8401
                    $row_zero['title'] = Exercise::get_formated_title_variable($row_zero['title']);
8402
                }
8403
                $js_var = json_encode(get_lang('After').' '.$row_zero['title']);
8404
                $return .= 'child_name[0][' . $i . '] = '.$js_var.' ;' . "\n";
8405
                $return .= 'child_value[0][' . $i++ . '] = "' . $row_zero['id'] . '";' . "\n";
8406
            }
8407
        }
8408
        $return .= "\n";
8409
        $sql = "SELECT * FROM " . $tbl_lp_item . "
8410
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
8411
        $res = Database::query($sql);
8412
        while ($row = Database :: fetch_array($res)) {
8413
            $sql_parent = "SELECT * FROM " . $tbl_lp_item . "
8414
                           WHERE
8415
                                c_id = ".$course_id." AND
8416
                                parent_item_id = " . $row['id'] . "
8417
                           ORDER BY display_order ASC";
8418
            $res_parent = Database::query($sql_parent);
8419
            $i = 0;
8420
            $return .= 'child_name[' . $row['id'] . '] = new Array();' . "\n";
8421
            $return .= 'child_value[' . $row['id'] . '] = new Array();' . "\n\n";
8422
8423
            while ($row_parent = Database :: fetch_array($res_parent)) {
8424
                $js_var = json_encode(get_lang('After').' '.$row_parent['title']);
8425
                $return .= 'child_name[' . $row['id'] . '][' . $i . '] =   '.$js_var.' ;' . "\n";
8426
                $return .= 'child_value[' . $row['id'] . '][' . $i++ . '] = "' . $row_parent['id'] . '";' . "\n";
8427
            }
8428
            $return .= "\n";
8429
        }
8430
8431
        return $return;
8432
    }
8433
8434
    /**
8435
     * Display the form to allow moving an item
8436
     * @param	integer		Item ID
8437
     * @return	string		HTML form
8438
     */
8439
    public function display_move_item($item_id)
8440
    {
8441
        $course_id = api_get_course_int_id();
8442
        $return = '';
8443
8444
        if (is_numeric($item_id)) {
8445
            $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
8446
8447
            $sql = "SELECT * FROM " . $tbl_lp_item . "
8448
                    WHERE c_id = ".$course_id." AND id = " . $item_id;
8449
8450
            $res = Database::query($sql);
8451
            $row = Database :: fetch_array($res);
8452
8453
            switch ($row['item_type']) {
8454
                case 'dokeos_chapter' :
8455
                case 'dir' :
8456
                case 'asset' :
8457
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8458
                    $return .= $this->display_item_form($row['item_type'], get_lang('MoveCurrentChapter'), 'move', $item_id, $row);
8459
                    break;
8460
                case 'dokeos_module' :
8461
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8462
                    $return .= $this->display_item_form($row['item_type'], 'Move th current module:', 'move', $item_id, $row);
8463
                    break;
8464
                case TOOL_DOCUMENT :
8465
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8466
                    $return .= $this->display_document_form('move', $item_id, $row);
8467
                    break;
8468 View Code Duplication
                case TOOL_LINK :
8469
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8470
                    $return .= $this->display_link_form('move', $item_id, $row);
8471
                    break;
8472 View Code Duplication
                case TOOL_HOTPOTATOES :
8473
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8474
                    $return .= $this->display_link_form('move', $item_id, $row);
8475
                    break;
8476 View Code Duplication
                case TOOL_QUIZ :
8477
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8478
                    $return .= $this->display_quiz_form('move', $item_id, $row);
8479
                    break;
8480 View Code Duplication
                case TOOL_STUDENTPUBLICATION :
8481
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8482
                    $return .= $this->display_student_publication_form('move', $item_id, $row);
8483
                    break;
8484
                case TOOL_FORUM :
8485
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8486
                    $return .= $this->display_forum_form('move', $item_id, $row);
8487
                    break;
8488 View Code Duplication
                case TOOL_THREAD :
8489
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
8490
                    $return .= $this->display_forum_form('move', $item_id, $row);
8491
                    break;
8492
            }
8493
        }
8494
8495
        return $return;
8496
    }
8497
8498
    /**
8499
     * Displays a basic form on the overview page for changing the item title and the item description.
8500
     * @param string $item_type
8501
     * @param string $title
8502
     * @param array $data
8503
     * @return string
8504
     */
8505
    public function display_item_small_form($item_type, $title = '', $data = array())
8506
    {
8507
        $url = api_get_self() . '?' .api_get_cidreq().'&action=edit_item&lp_id='.$this->lp_id;
8508
        $form = new FormValidator('small_form', 'post', $url);
8509
        $form->addElement('header', $title);
8510
        $form->addElement('text', 'title', get_lang('Title'));
8511
        $form->addButtonSave(get_lang('Save'), 'submit_button');
8512
        $form->addElement('hidden', 'id', $data['id']);
8513
        $form->addElement('hidden', 'parent', $data['parent_item_id']);
8514
        $form->addElement('hidden', 'previous', $data['previous_item_id']);
8515
        $form->setDefaults(array('title' => $data['title']));
8516
8517
        return $form->toHtml();
8518
    }
8519
8520
    /**
8521
     * Return HTML form to allow prerequisites selection
8522
     * @todo use FormValidator
8523
     * @param	integer Item ID
8524
     * @return	string	HTML form
8525
     */
8526
    public function display_item_prerequisites_form($item_id)
8527
    {
8528
        $course_id = api_get_course_int_id();
8529
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
8530
        $item_id = intval($item_id);
8531
        /* Current prerequisite */
8532
        $sql = "SELECT * FROM $tbl_lp_item
8533
                WHERE c_id = $course_id AND id = " . $item_id;
8534
        $result = Database::query($sql);
8535
        $row    = Database::fetch_array($result);
8536
        $prerequisiteId = $row['prerequisite'];
8537
        $return = '<legend>';
8538
        $return .= get_lang('AddEditPrerequisites');
8539
        $return .= '</legend>';
8540
        $return .= '<form method="POST">';
8541
        $return .= '<table class="data_table">';
8542
        $return .= '<tr>';
8543
        $return .= '<th height="24">' . get_lang('LearnpathPrerequisites') . '</th>';
8544
        $return .= '<th width="70" >' . get_lang('Minimum') . '</th>';
8545
        $return .= '<th width="70">' . get_lang('Maximum') . '</th>';
8546
        $return .= '</tr>';
8547
8548
        // Adding the none option to the prerequisites see http://www.chamilo.org/es/node/146
8549
        $return .= '<tr >';
8550
        $return .= '<td colspan="3" class="radio">';
8551
        $return .= '<input checked="checked" id="idNone" name="prerequisites"  style="margin-left:0px; margin-right:10px;" type="radio" />';
8552
        $return .= '<label for="idNone">' . get_lang('None') . '</label>';
8553
        $return .= '</tr>';
8554
8555
        $sql = "SELECT * FROM $tbl_lp_item
8556
                WHERE c_id = $course_id AND lp_id = " . $this->lp_id;
8557
        $result = Database::query($sql);
8558
        $arrLP = array();
8559
8560
        $selectedMinScore = array();
8561
        $selectedMaxScore = array();
8562
        while ($row = Database :: fetch_array($result)) {
8563
            if ($row['id'] == $item_id) {
8564
                $selectedMinScore[$row['prerequisite']] = $row['prerequisite_min_score'];
8565
                $selectedMaxScore[$row['prerequisite']] = $row['prerequisite_max_score'];
8566
            }
8567
            $arrLP[] = array(
8568
                'id' => $row['id'],
8569
                'item_type' => $row['item_type'],
8570
                'title' => $row['title'],
8571
                'ref' => $row['ref'],
8572
                'description' => $row['description'],
8573
                'parent_item_id' => $row['parent_item_id'],
8574
                'previous_item_id' => $row['previous_item_id'],
8575
                'next_item_id' => $row['next_item_id'],
8576
                'max_score' => $row['max_score'],
8577
                'min_score' => $row['min_score'],
8578
                'mastery_score' => $row['mastery_score'],
8579
                'prerequisite' => $row['prerequisite'],
8580
                'next_item_id' => $row['next_item_id'],
8581
                'display_order' => $row['display_order'],
8582
                'prerequisite_min_score' => $row['prerequisite_min_score'],
8583
                'prerequisite_max_score' => $row['prerequisite_max_score'],
8584
            );
8585
        }
8586
8587
        $this->tree_array($arrLP);
8588
        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;
8589
        unset($this->arrMenu);
8590
8591
        for ($i = 0; $i < count($arrLP); $i++) {
8592
            $item = $arrLP[$i];
8593
8594
            if ($item['id'] == $item_id) {
8595
                break;
8596
            }
8597
8598
            $selectedMaxScoreValue = isset($selectedMaxScore[$item['id']]) ? $selectedMaxScore[$item['id']] : $item['max_score'];
8599
            $selectedMinScoreValue = isset($selectedMinScore[$item['id']]) ? $selectedMinScore[$item['id']]: 0;
8600
8601
            $return .= '<tr>';
8602
            $return .= '<td class="radio"' . (($item['item_type'] != TOOL_QUIZ && $item['item_type'] != TOOL_HOTPOTATOES) ? ' colspan="3"' : '') . '>';
8603
            $return .= '<label for="id' . $item['id'] . '">';
8604
            $return .= '<input' . (in_array($prerequisiteId, array($item['id'], $item['ref'])) ? ' checked="checked" ' : '') . (($item['item_type'] == 'dokeos_module' || $item['item_type'] == 'dokeos_chapter') ? ' disabled="disabled" ' : ' ') . 'id="id' . $item['id'] . '" name="prerequisites" style="margin-left:' . $item['depth'] * 10 . 'px; margin-right:10px;" type="radio" value="' . $item['id'] . '" />';
8605
            $icon_name = str_replace(' ', '', $item['item_type']);
8606
8607
            if (file_exists('../img/lp_' . $icon_name . '.png')) {
8608
                $return .= Display::return_icon('lp_' . $icon_name . '.png');
8609
            } else {
8610
                if (file_exists('../img/lp_' . $icon_name . '.gif')) {
8611
                    $return .= Display::return_icon('lp_' . $icon_name . '.gif');
8612
                } else {
8613
                    $return .= Display::return_icon('folder_document.gif', '', array('style'=>'margin-right:5px;'));
8614
                }
8615
            }
8616
8617
            $return .=  $item['title'] . '</label>';
8618
            $return .= '</td>';
8619
8620
            if ($item['item_type'] == TOOL_QUIZ) {
8621
                // lets update max_score Quiz information depending of the Quiz Advanced properties
8622
                $tmp_obj_lp_item = new LpItem($course_id, $item['id']);
8623
                $tmp_obj_exercice = new Exercise();
8624
                $tmp_obj_exercice->read($tmp_obj_lp_item->path);
8625
                $tmp_obj_lp_item->max_score = $tmp_obj_exercice->get_max_score();
8626
8627
                $tmp_obj_lp_item->update_in_bdd();
8628
                $item['max_score'] = $tmp_obj_lp_item->max_score;
8629
8630
                $return .= '<td class="exercise">';
8631
                $return .= '<input size="4" maxlength="3" name="min_' . $item['id'] . '" type="number" min="0" step="any" max="'.$item['max_score'].'" value="' . $selectedMinScoreValue. '" />';
8632
                $return .= '</td>';
8633
                $return .= '<td class="exercise">';
8634
                $return .= '<input size="4" maxlength="3" name="max_' . $item['id'] . '" type="number" min="0" step="any" max="'.$item['max_score'].'" value="' . $selectedMaxScoreValue . '" />';
8635
                $return .= '</td>';
8636
            }
8637
8638
            if ($item['item_type'] == TOOL_HOTPOTATOES) {
8639
                $return .= '<td class="exercise">';
8640
                $return .= '<center><input size="4" maxlength="3" name="min_' . $item['id'] . '" type="number" min="0" step="any" max="'.$item['max_score'].'" value="' . $selectedMinScoreValue . '" /></center>';
8641
                $return .= '</td>';
8642
                $return .= '<td class="exercise"">';
8643
                $return .= '<center><input size="4" maxlength="3" name="max_' . $item['id'] . '" type="number" min="0" step="any" max="'.$item['max_score'].'"  value="'.$selectedMaxScoreValue . '" /></center>';
8644
                $return .= '</td>';
8645
            }
8646
            $return .= '</tr>';
8647
        }
8648
        $return .= '<tr>';
8649
        $return .= '</tr>';
8650
        $return .= '</table>';
8651
        $return .= '<div style="padding-top:3px;">';
8652
        $return .= '<button class="btn btn-primary" name="submit_button" type="submit">' . get_lang('ModifyPrerequisites') . '</button>';
8653
        $return .= '</form>';
8654
8655
        return $return;
8656
    }
8657
8658
    /**
8659
     * Return HTML list to allow prerequisites selection for lp
8660
     * @param	integer Item ID
8661
     * @return	string	HTML form
8662
     */
8663
    public function display_lp_prerequisites_list()
8664
    {
8665
        $course_id = api_get_course_int_id();
8666
        $lp_id = $this->lp_id;
8667
        $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
8668
8669
        // get current prerequisite
8670
        $sql = "SELECT * FROM $tbl_lp WHERE c_id = $course_id AND id = $lp_id ";
8671
        $result = Database::query($sql);
8672
        $row = Database :: fetch_array($result);
8673
        $prerequisiteId = $row['prerequisite'];
8674
        $session_id = api_get_session_id();
8675
        $session_condition = api_get_session_condition($session_id);
8676
        $sql = "SELECT * FROM $tbl_lp
8677
                WHERE c_id = $course_id $session_condition
8678
                ORDER BY display_order ";
8679
        $rs = Database::query($sql);
8680
        $return = '';
8681
        $return .= '<select name="prerequisites" class="form-control">';
8682
        $return .= '<option value="0">'.get_lang('None').'</option>';
8683
        if (Database::num_rows($rs) > 0) {
8684
            while ($row = Database::fetch_array($rs)) {
8685
                if ($row['id'] == $lp_id) {
8686
                    continue;
8687
                }
8688
                $return .= '<option value="'.$row['id'].'" '.(($row['id']==$prerequisiteId)?' selected ' : '').'>'.$row['name'].'</option>';
8689
            }
8690
        }
8691
        $return .= '</select>';
8692
8693
        return $return;
8694
    }
8695
8696
    /**
8697
     * Creates a list with all the documents in it
8698
     * @param bool $showInvisibleFiles
8699
     * @return string
8700
     */
8701
    public function get_documents($showInvisibleFiles = false)
8702
    {
8703
        $course_info = api_get_course_info();
8704
        $sessionId = api_get_session_id();
8705
8706
        $document_tree = DocumentManager::get_document_preview(
8707
            $course_info,
8708
            $this->lp_id,
8709
            null,
8710
            $sessionId,
8711
            true,
8712
            null,
8713
            null,
8714
            $showInvisibleFiles,
8715
            true
8716
        );
8717
8718
        return $document_tree;
8719
    }
8720
8721
    /**
8722
     * Creates a list with all the exercises (quiz) in it
8723
     * @return string
8724
     */
8725
    public function get_exercises()
8726
    {
8727
        $course_id = api_get_course_int_id();
8728
8729
        // New for hotpotatoes.
8730
        $uploadPath = DIR_HOTPOTATOES; //defined in main_api
8731
        $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
8732
        $tbl_quiz = Database :: get_course_table(TABLE_QUIZ_TEST);
8733
8734
        $session_id = api_get_session_id();
8735
        $condition_session = api_get_session_condition($session_id);
8736
8737
        $setting = api_get_configuration_value('show_invisible_exercise_in_lp_list');
8738
8739
        $activeCondition = " active <> -1 ";
8740
        if ($setting) {
8741
            $activeCondition = " active = 1 ";
8742
        }
8743
8744
        $sql_quiz = "SELECT * FROM $tbl_quiz
8745
                     WHERE c_id = $course_id AND $activeCondition $condition_session
8746
                     ORDER BY title ASC";
8747
8748
        $sql_hot  = "SELECT * FROM $tbl_doc
8749
                     WHERE c_id = $course_id AND path LIKE '" . $uploadPath . "/%/%htm%'  $condition_session
8750
                     ORDER BY id ASC";
8751
8752
        $res_quiz = Database::query($sql_quiz);
8753
        $res_hot  = Database::query($sql_hot);
8754
8755
        $return = '<ul class="lp_resource">';
8756
        $return .= '<li class="lp_resource_element">';
8757
        $return .= Display::return_icon('new_test_small.gif');
8758
        $return .= '<a href="' . api_get_path(WEB_CODE_PATH) . 'exercice/exercise_admin.php?'.api_get_cidreq().'&lp_id=' . $this->lp_id . '">' .
8759
            get_lang('NewExercise') . '</a>';
8760
        $return .= '</li>';
8761
8762
        // Display hotpotatoes
8763
        while ($row_hot = Database :: fetch_array($res_hot)) {
8764
            $return .= '<li class="lp_resource_element" data_id="'.$row_hot['id'].'" data_type="hotpotatoes" title="'.$row_hot['title'].'" >';
8765
8766
            $return .= '<a class="moved" href="#">';
8767
            $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
8768
            $return .= '</a> ';
8769
8770
            $return .= Display::return_icon('hotpotatoes_s.png');
8771
            $return .= '<a href="' . api_get_self() . '?' . api_get_cidreq().'&action=add_item&type=' . TOOL_HOTPOTATOES . '&file=' . $row_hot['id'] . '&lp_id=' . $this->lp_id . '">'.
8772
                ((!empty ($row_hot['comment'])) ? $row_hot['comment'] : Security :: remove_XSS($row_hot['title'])) . '</a>';
8773
            $return .= '</li>';
8774
        }
8775
8776
        while ($row_quiz = Database :: fetch_array($res_quiz)) {
8777
            $return .= '<li class="lp_resource_element" data_id="'.$row_quiz['id'].'" data_type="quiz" title="'.$row_quiz['title'].'" >';
8778
            $return .= '<a class="moved" href="#">';
8779
            $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
8780
            $return .= '</a> ';
8781
            $return .= Display::return_icon('quizz_small.gif', '', array(), ICON_SIZE_TINY);
8782
            $return .= '<a href="' . api_get_self() . '?'.api_get_cidreq().'&action=add_item&type=' . TOOL_QUIZ . '&file=' . $row_quiz['id'] . '&lp_id=' . $this->lp_id . '">' .
8783
                Security :: remove_XSS(cut($row_quiz['title'], 80)).
8784
                '</a>';
8785
            $return .= '</li>';
8786
        }
8787
8788
        $return .= '</ul>';
8789
8790
        return $return;
8791
    }
8792
8793
    /**
8794
     * Creates a list with all the links in it
8795
     * @return string
8796
     */
8797
    public function get_links()
8798
    {
8799
        $selfUrl = api_get_self();
8800
        $courseIdReq = api_get_cidreq();
8801
        $course = api_get_course_info();
8802
        $course_id = $course['real_id'];
8803
        $tbl_link = Database::get_course_table(TABLE_LINK);
8804
        $linkCategoryTable = Database::get_course_table(TABLE_LINK_CATEGORY);
8805
        $moveEverywhereIcon = Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
8806
8807
        $session_id = api_get_session_id();
8808
        $condition_session = api_get_session_condition($session_id, true, null, "link.session_id");
8809
8810
        $sql = "SELECT link.id as link_id,
8811
                    link.title as link_title,
8812
                    link.category_id as category_id,
8813
                    link_category.category_title as category_title
8814
                FROM $tbl_link as link
8815
                LEFT JOIN $linkCategoryTable as link_category
8816
                ON (link.category_id = link_category.id AND link.c_id = link_category.c_id) 
8817
                WHERE link.c_id = ".$course_id." $condition_session
8818
                ORDER BY link_category.category_title ASC, link.title ASC";
8819
        $links = Database::query($sql);
8820
8821
        $categorizedLinks = array();
8822
        $categories = array();
8823
8824
        while ($link = Database :: fetch_array($links)) {
8825
            if (!$link['category_id']) {
8826
                $link['category_title'] = get_lang('Uncategorized');
8827
            }
8828
            $categories[$link['category_id']] = $link['category_title'];
8829
            $categorizedLinks[$link['category_id']][$link['link_id']] = $link['link_title'];
8830
        }
8831
8832
        $linksHtmlCode =
8833
            '<script>
8834
            function toggle_tool(tool, id){
8835
                if(document.getElementById(tool+"_"+id+"_content").style.display == "none"){
8836
                    document.getElementById(tool+"_"+id+"_content").style.display = "block";
8837
                    document.getElementById(tool+"_"+id+"_opener").src = "' . Display::returnIconPath('remove.gif').'";
8838
                } else {
8839
                    document.getElementById(tool+"_"+id+"_content").style.display = "none";
8840
                    document.getElementById(tool+"_"+id+"_opener").src = "'.Display::returnIconPath('add.gif').'";
8841
                }
8842
            }
8843
        </script>
8844
8845
        <ul class="lp_resource">
8846
            <li class="lp_resource_element">
8847
                '.Display::return_icon('linksnew.gif').'
8848
                <a href="'.api_get_path(WEB_CODE_PATH).'link/link.php?'.$courseIdReq.'&action=addlink&lp_id='.$this->lp_id.'" title="'.get_lang('LinkAdd').'">'.
8849
                get_lang('LinkAdd').'
8850
                </a>
8851
            </li>';
8852
8853
        foreach ($categorizedLinks as $categoryId => $links) {
8854
            $linkNodes = null;
8855
            foreach ($links as $key => $title) {
8856
                if (api_get_item_visibility($course, TOOL_LINK, $key, $session_id) != 2)  {
8857
                    $linkNodes .=
8858
                        '<li class="lp_resource_element" data_id="'.$key.'" data_type="'.TOOL_LINK.'" title="'.$title.'" >
8859
                        <a class="moved" href="#">'.
8860
                        $moveEverywhereIcon.
8861
                        '</a>
8862
                        '.Display::return_icon('lp_link.png').'
8863
                        <a href="'.$selfUrl.'?'.$courseIdReq.'&action=add_item&type='.
8864
                        TOOL_LINK.'&file='.$key.'&lp_id='.$this->lp_id.'">'.
8865
                        Security::remove_XSS($title).
8866
                        '</a>
8867
                    </li>';
8868
                }
8869
            }
8870
            $linksHtmlCode .=
8871
                '<li>
8872
                <a style="cursor:hand" onclick="javascript: toggle_tool(\''.TOOL_LINK.'\','.$categoryId.')" style="vertical-align:middle">
8873
                    <img src="'.Display::returnIconPath('add.gif').'" id="'.TOOL_LINK.'_'.$categoryId.'_opener"
8874
                    align="absbottom" />
8875
                </a>
8876
                <span style="vertical-align:middle">'.Security::remove_XSS($categories[$categoryId]).'</span>
8877
            </li>
8878
            <div style="display:none" id="'.TOOL_LINK.'_'.$categoryId.'_content">'.$linkNodes.'</div>';
8879
        }
8880
        $linksHtmlCode .= '</ul>';
8881
8882
        return $linksHtmlCode;
8883
    }
8884
8885
    /**
8886
     * Creates a list with all the student publications in it
8887
     * @return unknown
8888
     */
8889
    public function get_student_publications()
8890
    {
8891
        $return = '<div class="lp_resource" >';
8892
        $return .= '<div class="lp_resource_element">';
8893
        $return .= Display::return_icon('works_small.gif', '', array(), ICON_SIZE_TINY);
8894
        $return .= '<a href="' . api_get_self() . '?' . api_get_cidreq() . '&action=add_item&type=' . TOOL_STUDENTPUBLICATION . '&lp_id=' . $this->lp_id . '">' . get_lang('AddAssignmentPage') . '</a>';
8895
        $return .= '</div>';
8896
        $return .= '</div>';
8897
        return $return;
8898
    }
8899
8900
    /**
8901
     * Creates a list with all the forums in it
8902
     * @return string
8903
     */
8904
    public function get_forums()
8905
    {
8906
        require_once '../forum/forumfunction.inc.php';
8907
        require_once '../forum/forumconfig.inc.php';
8908
8909
        $forumCategories = get_forum_categories();
8910
        $forumsInNoCategory = get_forums_in_category(0);
8911 View Code Duplication
        if (!empty($forumsInNoCategory)) {
8912
            $forumCategories = array_merge(
8913
                $forumCategories,
8914
                array(
8915
                    array(
8916
                        'cat_id' => 0,
8917
                        'session_id' => 0,
8918
                        'visibility' => 1,
8919
                        'cat_comment' => null,
8920
                    ),
8921
                )
8922
            );
8923
        }
8924
8925
        $forumList = get_forums();
8926
        $a_forums = [];
8927
        foreach ($forumCategories as $forumCategory) {
8928
            // The forums in this category.
8929
            $forumsInCategory = get_forums_in_category($forumCategory['cat_id']);
8930
            if (!empty($forumsInCategory)) {
8931
                foreach ($forumList as $forum) {
8932
                    if (isset($forum['forum_category']) && $forum['forum_category'] == $forumCategory['cat_id']) {
8933
                        $a_forums[] = $forum;
8934
                    }
8935
                }
8936
            }
8937
        }
8938
8939
        $return = '<ul class="lp_resource">';
8940
8941
        //First add link
8942
        $return .= '<li class="lp_resource_element">';
8943
        $return .= Display::return_icon('forum_new_small.gif');
8944
        $return .= Display::url(
8945
            get_lang('CreateANewForum'),
8946
            api_get_path(WEB_CODE_PATH) . 'forum/index.php?' . api_get_cidreq() . '&' . http_build_query([
8947
                'action' => 'add',
8948
                'content' => 'forum',
8949
                'lp_id' => $this->lp_id
8950
            ]),
8951
            ['title' => get_lang('CreateANewForum')]
8952
        );
8953
        $return .= '</li>';
8954
8955
        $return .= '<script>
8956
                    function toggle_forum(forum_id){
8957
                        if(document.getElementById("forum_"+forum_id+"_content").style.display == "none"){
8958
                            document.getElementById("forum_"+forum_id+"_content").style.display = "block";
8959
                            document.getElementById("forum_"+forum_id+"_opener").src = "' . Display::returnIconPath('remove.gif').'";
8960
                        } else {
8961
                            document.getElementById("forum_"+forum_id+"_content").style.display = "none";
8962
                            document.getElementById("forum_"+forum_id+"_opener").src = "' . Display::returnIconPath('add.gif').'";
8963
                        }
8964
                    }
8965
                </script>';
8966
8967
        foreach ($a_forums as $forum) {
8968
            if (!empty($forum['forum_id'])) {
8969
                $return .= '<li class="lp_resource_element" data_id="'.$forum['forum_id'].'" data_type="'.TOOL_FORUM.'" title="'.$forum['forum_title'].'" >';
8970
                $return .= '<a class="moved" href="#">';
8971
                $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
8972
                $return .= ' </a>';
8973
                $return .= Display::return_icon('lp_forum.png', '', array(), ICON_SIZE_TINY);
8974
                $return .= '<a style="cursor:hand" onclick="javascript: toggle_forum(' . $forum['forum_id'] . ')" style="vertical-align:middle">
8975
                                <img src="' . Display::returnIconPath('add.gif').'" id="forum_' . $forum['forum_id'] . '_opener" align="absbottom" />
8976
                            </a>
8977
                            <a href="' . api_get_self() . '?'.api_get_cidreq().'&action=add_item&type=' . TOOL_FORUM . '&forum_id=' . $forum['forum_id'] . '&lp_id=' . $this->lp_id . '" style="vertical-align:middle">' .
8978
                    Security :: remove_XSS($forum['forum_title']) . '</a>';
8979
8980
                $return .= '</li>';
8981
8982
                $return .= '<div style="display:none" id="forum_' . $forum['forum_id'] . '_content">';
8983
                $a_threads = get_threads($forum['forum_id']);
8984
                if (is_array($a_threads)) {
8985
                    foreach ($a_threads as $thread) {
8986
                        $return .= '<li class="lp_resource_element" data_id="'.$thread['thread_id'].'" data_type="'.TOOL_THREAD.'" title="'.$thread['thread_title'].'" >';
8987
                        $return .= '&nbsp;<a class="moved" href="#">';
8988
                        $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
8989
                        $return .= ' </a>';
8990
                        $return .= Display::return_icon('forumthread.png', get_lang('Thread'), array(), ICON_SIZE_TINY);
8991
                        $return .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=add_item&type=' . TOOL_THREAD . '&thread_id=' . $thread['thread_id'] . '&lp_id=' . $this->lp_id . '">' .
8992
                            Security :: remove_XSS($thread['thread_title']) . '</a>';
8993
                        $return .= '</li>';
8994
                    }
8995
                }
8996
                $return .= '</div>';
8997
            }
8998
        }
8999
        $return .= '</ul>';
9000
9001
        return $return;
9002
    }
9003
9004
    /**
9005
     * // TODO: The output encoding should be equal to the system encoding.
9006
     *
9007
     * Exports the learning path as a SCORM package. This is the main function that
9008
     * gathers the content, transforms it, writes the imsmanifest.xml file, zips the
9009
     * whole thing and returns the zip.
9010
     *
9011
     * This method needs to be called in PHP5, as it will fail with non-adequate
9012
     * XML package (like the ones for PHP4), and it is *not* a static method, so
9013
     * you need to call it on a learnpath object.
9014
     * @TODO The method might be redefined later on in the scorm class itself to avoid
9015
     * creating a SCORM structure if there is one already. However, if the initial SCORM
9016
     * path has been modified, it should use the generic method here below.
9017
     * @TODO link this function with the export_lp() function in the same class
9018
     * @param	string	Optional name of zip file. If none, title of learnpath is
9019
     * 					domesticated and trailed with ".zip"
9020
     * @return	string	Returns the zip package string, or null if error
9021
     */
9022
    public function scorm_export()
9023
    {
9024
        $_course = api_get_course_info();
9025
        $course_id = $_course['real_id'];
9026
9027
        // Remove memory and time limits as much as possible as this might be a long process...
9028
        if (function_exists('ini_set')) {
9029
            api_set_memory_limit('128M');
9030
            ini_set('max_execution_time', 600);
9031
        }
9032
9033
        // Create the zip handler (this will remain available throughout the method).
9034
        $archive_path = api_get_path(SYS_ARCHIVE_PATH);
9035
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
9036
        $temp_dir_short = uniqid();
9037
        $temp_zip_dir = $archive_path.'/'.$temp_dir_short;
9038
        $temp_zip_file = $temp_zip_dir.'/'.md5(time()).'.zip';
9039
        $zip_folder = new PclZip($temp_zip_file);
9040
        $current_course_path = api_get_path(SYS_COURSE_PATH).api_get_course_path();
9041
        $root_path = $main_path = api_get_path(SYS_PATH);
9042
        $files_cleanup = array();
9043
9044
        // Place to temporarily stash the zip file.
9045
        // create the temp dir if it doesn't exist
9046
        // or do a cleanup before creating the zip file.
9047
        if (!is_dir($temp_zip_dir)) {
9048
            mkdir($temp_zip_dir, api_get_permissions_for_new_directories());
9049
        } else {
9050
            // Cleanup: Check the temp dir for old files and delete them.
9051
            $handle = opendir($temp_zip_dir);
9052
            while (false !== ($file = readdir($handle))) {
9053
                if ($file != '.' && $file != '..') {
9054
                    unlink("$temp_zip_dir/$file");
9055
                }
9056
            }
9057
            closedir($handle);
9058
        }
9059
        $zip_files = $zip_files_abs = $zip_files_dist = array();
9060
        if (is_dir($current_course_path.'/scorm/'.$this->path) && is_file($current_course_path.'/scorm/'.$this->path.'/imsmanifest.xml')) {
9061
            // Remove the possible . at the end of the path.
9062
            $dest_path_to_lp = substr($this->path, -1) == '.' ? substr($this->path, 0, -1) : $this->path;
9063
            $dest_path_to_scorm_folder = str_replace('//','/',$temp_zip_dir.'/scorm/'.$dest_path_to_lp);
9064
            mkdir($dest_path_to_scorm_folder, api_get_permissions_for_new_directories(), true);
9065
            copyr(
9066
                $current_course_path.'/scorm/'.$this->path,
9067
                $dest_path_to_scorm_folder,
9068
                array('imsmanifest'),
9069
                $zip_files
9070
            );
9071
        }
9072
9073
        // Build a dummy imsmanifest structure.
9074
        // Do not add to the zip yet (we still need it).
9075
        // This structure is developed following regulations for SCORM 1.2 packaging in the SCORM 1.2 Content
9076
        // Aggregation Model official document, section "2.3 Content Packaging".
9077
        // We are going to build a UTF-8 encoded manifest. Later we will recode it to the desired (and supported) encoding.
9078
        $xmldoc = new DOMDocument('1.0');
9079
        $root = $xmldoc->createElement('manifest');
9080
        $root->setAttribute('identifier', 'SingleCourseManifest');
9081
        $root->setAttribute('version', '1.1');
9082
        $root->setAttribute('xmlns', 'http://www.imsproject.org/xsd/imscp_rootv1p1p2');
9083
        $root->setAttribute('xmlns:adlcp', 'http://www.adlnet.org/xsd/adlcp_rootv1p2');
9084
        $root->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
9085
        $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');
9086
        // Build mandatory sub-root container elements.
9087
        $metadata = $xmldoc->createElement('metadata');
9088
        $md_schema = $xmldoc->createElement('schema', 'ADL SCORM');
9089
        $metadata->appendChild($md_schema);
9090
        $md_schemaversion = $xmldoc->createElement('schemaversion', '1.2');
9091
        $metadata->appendChild($md_schemaversion);
9092
        $root->appendChild($metadata);
9093
9094
        $organizations = $xmldoc->createElement('organizations');
9095
        $resources = $xmldoc->createElement('resources');
9096
9097
        // Build the only organization we will use in building our learnpaths.
9098
        $organizations->setAttribute('default', 'chamilo_scorm_export');
9099
        $organization = $xmldoc->createElement('organization');
9100
        $organization->setAttribute('identifier', 'chamilo_scorm_export');
9101
        // To set the title of the SCORM entity (=organization), we take the name given
9102
        // in Chamilo and convert it to HTML entities using the Chamilo charset (not the
9103
        // learning path charset) as it is the encoding that defines how it is stored
9104
        // in the database. Then we convert it to HTML entities again as the "&" character
9105
        // alone is not authorized in XML (must be &amp;).
9106
        // The title is then decoded twice when extracting (see scorm::parse_manifest).
9107
        $org_title = $xmldoc->createElement('title', api_utf8_encode($this->get_name()));
9108
        $organization->appendChild($org_title);
9109
9110
        $folder_name = 'document';
9111
9112
        // Removes the learning_path/scorm_folder path when exporting see #4841
9113
        $path_to_remove = null;
9114
        $result = $this->generate_lp_folder($_course);
9115
9116
        if (isset($result['dir']) && strpos($result['dir'], 'learning_path')) {
9117
            $path_to_remove = 'document'.$result['dir'];
9118
            $path_to_replace = $folder_name.'/';
9119
        }
9120
9121
        //Fixes chamilo scorm exports
9122
        if ($this->ref == 'chamilo_scorm_export') {
9123
            $path_to_remove = 'scorm/'.$this->path.'/document/';
9124
        }
9125
9126
        // For each element, add it to the imsmanifest structure, then add it to the zip.
9127
        // Always call the learnpathItem->scorm_export() method to change it to the SCORM format.
9128
        $link_updates = array();
9129
        $links_to_create = array();
9130
        foreach ($this->items as $index => $item) {
9131
            if (!in_array($item->type, array(TOOL_QUIZ, TOOL_FORUM, TOOL_THREAD, TOOL_LINK, TOOL_STUDENTPUBLICATION))) {
9132
                // Get included documents from this item.
9133
                if ($item->type === 'sco') {
9134
                    $inc_docs = $item->get_resources_from_source(
9135
                        null,
9136
                        api_get_path(SYS_COURSE_PATH) . api_get_course_path() . '/' . 'scorm/' . $this->path . '/' . $item->get_path()
9137
                    );
9138
                } else {
9139
                    $inc_docs = $item->get_resources_from_source();
9140
                }
9141
                // Give a child element <item> to the <organization> element.
9142
                $my_item_id = $item->get_id();
9143
                $my_item = $xmldoc->createElement('item');
9144
                $my_item->setAttribute('identifier', 'ITEM_'.$my_item_id);
9145
                $my_item->setAttribute('identifierref', 'RESOURCE_'.$my_item_id);
9146
                $my_item->setAttribute('isvisible', 'true');
9147
                // Give a child element <title> to the <item> element.
9148
                $my_title = $xmldoc->createElement('title', htmlspecialchars(api_utf8_encode($item->get_title()), ENT_QUOTES, 'UTF-8'));
9149
                $my_item->appendChild($my_title);
9150
                // Give a child element <adlcp:prerequisites> to the <item> element.
9151
                $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $this->get_scorm_prereq_string($my_item_id));
9152
                $my_prereqs->setAttribute('type', 'aicc_script');
9153
                $my_item->appendChild($my_prereqs);
9154
                // Give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported.
9155
                //$xmldoc->createElement('adlcp:maxtimeallowed','');
9156
                // Give a child element <adlcp:timelimitaction> to the <item> element - not yet supported.
9157
                //$xmldoc->createElement('adlcp:timelimitaction','');
9158
                // Give a child element <adlcp:datafromlms> to the <item> element - not yet supported.
9159
                //$xmldoc->createElement('adlcp:datafromlms','');
9160
                // Give a child element <adlcp:masteryscore> to the <item> element.
9161
                $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score());
9162
                $my_item->appendChild($my_masteryscore);
9163
9164
                // Attach this item to the organization element or hits parent if there is one.
9165
                if (!empty($item->parent) && $item->parent != 0) {
9166
                    $children = $organization->childNodes;
9167
                    $possible_parent = &$this->get_scorm_xml_node($children, 'ITEM_'.$item->parent);
9168
                    if (is_object($possible_parent)) {
9169
                        $possible_parent->appendChild($my_item);
9170 View Code Duplication
                    } else {
9171
                        if ($this->debug > 0) { error_log('Parent ITEM_'.$item->parent.' of item ITEM_'.$my_item_id.' not found'); }
9172
                    }
9173
                } else {
9174
                    if ($this->debug > 0) { error_log('No parent'); }
9175
                    $organization->appendChild($my_item);
9176
                }
9177
9178
                // Get the path of the file(s) from the course directory root.
9179
                $my_file_path = $item->get_file_path('scorm/'.$this->path.'/');
9180
9181
                if (!empty($path_to_remove)) {
9182
                    //From docs
9183
                    $my_xml_file_path = str_replace($path_to_remove, $path_to_replace, $my_file_path);
9184
9185
                    //From quiz
9186
                    if ($this->ref == 'chamilo_scorm_export') {
9187
                        $path_to_remove = 'scorm/'.$this->path.'/';
9188
                        $my_xml_file_path = str_replace($path_to_remove, '', $my_file_path);
9189
                    }
9190
                } else {
9191
                    $my_xml_file_path = $my_file_path;
9192
                }
9193
9194
                $my_sub_dir = dirname($my_file_path);
9195
                $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
9196
                $my_xml_sub_dir = $my_sub_dir;
9197
                // Give a <resource> child to the <resources> element
9198
                $my_resource = $xmldoc->createElement('resource');
9199
                $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
9200
                $my_resource->setAttribute('type', 'webcontent');
9201
                $my_resource->setAttribute('href', $my_xml_file_path);
9202
                // adlcp:scormtype can be either 'sco' or 'asset'.
9203
                if ($item->type === 'sco') {
9204
                    $my_resource->setAttribute('adlcp:scormtype', 'sco');
9205
                } else {
9206
                    $my_resource->setAttribute('adlcp:scormtype', 'asset');
9207
                }
9208
                // xml:base is the base directory to find the files declared in this resource.
9209
                $my_resource->setAttribute('xml:base', '');
9210
                // Give a <file> child to the <resource> element.
9211
                $my_file = $xmldoc->createElement('file');
9212
                $my_file->setAttribute('href', $my_xml_file_path);
9213
                $my_resource->appendChild($my_file);
9214
9215
                // Dependency to other files - not yet supported.
9216
                $i = 1;
9217
                foreach ($inc_docs as $doc_info) {
9218
                    if (count($doc_info) < 1 || empty($doc_info[0])) {
9219
                        continue;
9220
                    }
9221
                    $my_dep = $xmldoc->createElement('resource');
9222
                    $res_id = 'RESOURCE_'.$item->get_id().'_'.$i;
9223
                    $my_dep->setAttribute('identifier', $res_id);
9224
                    $my_dep->setAttribute('type', 'webcontent');
9225
                    $my_dep->setAttribute('adlcp:scormtype', 'asset');
9226
                    $my_dep_file = $xmldoc->createElement('file');
9227
                    // Check type of URL.
9228
                    //error_log(__LINE__.'Now dealing with '.$doc_info[0].' of type '.$doc_info[1].'-'.$doc_info[2], 0);
9229
                    if ($doc_info[1] == 'remote') {
9230
                        // Remote file. Save url as is.
9231
                        $my_dep_file->setAttribute('href', $doc_info[0]);
9232
                        $my_dep->setAttribute('xml:base', '');
9233
                    } elseif ($doc_info[1] === 'local') {
9234
                        switch ($doc_info[2]) {
9235
                            case 'url': // Local URL - save path as url for now, don't zip file.
9236
                                $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]);
9237
                                $current_dir = dirname($abs_path);
9238
                                $current_dir = str_replace('\\', '/', $current_dir);
9239
                                $file_path = realpath($abs_path);
9240
                                $file_path = str_replace('\\', '/', $file_path);
9241
                                $my_dep_file->setAttribute('href', $file_path);
9242
                                $my_dep->setAttribute('xml:base', '');
9243
                                if (strstr($file_path, $main_path) !== false) {
9244
                                    // The calculated real path is really inside Chamilo's root path.
9245
                                    // Reduce file path to what's under the DocumentRoot.
9246
                                    $file_path = substr($file_path, strlen($root_path) - 1);
9247
                                    //echo $file_path;echo '<br /><br />';
9248
                                    //error_log(__LINE__.'Reduced url path: '.$file_path, 0);
9249
                                    $zip_files_abs[] = $file_path;
9250
                                    $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9251
                                    $my_dep_file->setAttribute('href', $file_path);
9252
                                    $my_dep->setAttribute('xml:base', '');
9253
                                } elseif (empty($file_path)) {
9254
                                    /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH), api_get_path(REL_PATH)));
9255
                                    if (strpos($document_root, -1) == '/') {
9256
                                        $document_root = substr(0, -1, $document_root);
9257
                                    }*/
9258
                                    $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path;
9259
                                    $file_path = str_replace('//', '/', $file_path);
9260
                                    if (file_exists($file_path)) {
9261
                                        $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
9262
                                        $zip_files[] = $my_sub_dir.'/'.$file_path;
9263
                                        $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9264
                                        $my_dep_file->setAttribute('href', $file_path);
9265
                                        $my_dep->setAttribute('xml:base', '');
9266
                                    }
9267
                                }
9268
                                break;
9269
                            case 'abs': // Absolute path from DocumentRoot. Save file and leave path as is in the zip.
9270
                                $my_dep_file->setAttribute('href', $doc_info[0]);
9271
                                $my_dep->setAttribute('xml:base', '');
9272
9273
                                // The next lines fix a bug when using the "subdir" mode of Chamilo, whereas
9274
                                // an image path would be constructed as /var/www/subdir/subdir/img/foo.bar
9275
                                $abs_img_path_without_subdir = $doc_info[0];
9276
                                $relp = api_get_path(REL_PATH); // The url-append config param.
9277
                                $pos = strpos($abs_img_path_without_subdir, $relp);
9278
                                if ($pos === 0) {
9279
                                    $abs_img_path_without_subdir = '/'.substr($abs_img_path_without_subdir, strlen($relp));
9280
                                }
9281
9282
                                //$file_path = realpath(api_get_path(SYS_PATH).$abs_img_path_without_subdir);
9283
                                $file_path = realpath(api_get_path(SYS_APP_PATH).$abs_img_path_without_subdir);
9284
9285
                                $file_path = str_replace('\\', '/', $file_path);
9286
                                $file_path = str_replace('//', '/', $file_path);
9287
9288
                                // Prepare the current directory path (until just under 'document') with a trailing slash.
9289
                                $cur_path = substr($current_course_path, -1) == '/' ? $current_course_path : $current_course_path.'/';
9290
                                // Check if the current document is in that path.
9291
                                if (strstr($file_path, $cur_path) !== false) {
9292
                                    // The document is in that path, now get the relative path
9293
                                    // to the containing document.
9294
                                    $orig_file_path = dirname($cur_path.$my_file_path).'/';
9295
                                    $orig_file_path = str_replace('\\', '/', $orig_file_path);
9296
                                    $relative_path = '';
9297
                                    if (strstr($file_path, $cur_path) !== false) {
9298
                                        //$relative_path = substr($file_path, strlen($orig_file_path));
9299
                                        $relative_path = str_replace($cur_path, '', $file_path);
9300
                                        $file_path = substr($file_path, strlen($cur_path));
9301
                                    } else {
9302
                                        // This case is still a problem as it's difficult to calculate a relative path easily
9303
                                        // might still generate wrong links.
9304
                                        //$file_path = substr($file_path,strlen($cur_path));
9305
                                        // Calculate the directory path to the current file (without trailing slash).
9306
                                        $my_relative_path = dirname($file_path);
9307
                                        $my_relative_path = str_replace('\\', '/', $my_relative_path);
9308
                                        $my_relative_file = basename($file_path);
9309
                                        // Calculate the directory path to the containing file (without trailing slash).
9310
                                        $my_orig_file_path = substr($orig_file_path, 0, -1);
9311
                                        $dotdot = '';
9312
                                        $subdir = '';
9313
                                        while (strstr($my_relative_path, $my_orig_file_path) === false && (strlen($my_orig_file_path) > 1) && (strlen($my_relative_path) > 1)) {
9314
                                            $my_relative_path2 = dirname($my_relative_path);
9315
                                            $my_relative_path2 = str_replace('\\', '/', $my_relative_path2);
9316
                                            $my_orig_file_path = dirname($my_orig_file_path);
9317
                                            $my_orig_file_path = str_replace('\\', '/', $my_orig_file_path);
9318
                                            $subdir = substr($my_relative_path, strlen($my_relative_path2) + 1).'/'.$subdir;
9319
                                            $dotdot += '../';
9320
                                            $my_relative_path = $my_relative_path2;
9321
                                        }
9322
                                        $relative_path = $dotdot.$subdir.$my_relative_file;
9323
                                    }
9324
                                    // Put the current document in the zip (this array is the array
9325
                                    // that will manage documents already in the course folder - relative).
9326
                                    $zip_files[] = $file_path;
9327
                                    // Update the links to the current document in the containing document (make them relative).
9328
                                    $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $relative_path);
9329
                                    $my_dep_file->setAttribute('href', $file_path);
9330
                                    $my_dep->setAttribute('xml:base', '');
9331
                                } elseif (strstr($file_path, $main_path) !== false) {
9332
                                    // The calculated real path is really inside Chamilo's root path.
9333
                                    // Reduce file path to what's under the DocumentRoot.
9334
                                    $file_path = substr($file_path, strlen($root_path));
9335
                                    //echo $file_path;echo '<br /><br />';
9336
                                    //error_log('Reduced path: '.$file_path, 0);
9337
                                    $zip_files_abs[] = $file_path;
9338
                                    $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9339
                                    $my_dep_file->setAttribute('href', 'document/'.$file_path);
9340
                                    $my_dep->setAttribute('xml:base', '');
9341
                                } elseif (empty($file_path)) {
9342
                                    /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH), api_get_path(REL_PATH)));
9343
                                    if(strpos($document_root,-1) == '/') {
9344
                                        $document_root = substr(0, -1, $document_root);
9345
                                    }*/
9346
                                    $file_path = $_SERVER['DOCUMENT_ROOT'].$doc_info[0];
9347
                                    $file_path = str_replace('//', '/', $file_path);
9348
9349
                                    $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]);
9350
                                    $current_dir = dirname($abs_path);
9351
                                    $current_dir = str_replace('\\', '/', $current_dir);
9352
9353
                                    if (file_exists($file_path)) {
9354
                                        $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
9355
                                        $zip_files[] = $my_sub_dir.'/'.$file_path;
9356
                                        $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9357
                                        $my_dep_file->setAttribute('href','document/'.$file_path);
9358
                                        $my_dep->setAttribute('xml:base', '');
9359
                                    }
9360
                                }
9361
                                break;
9362
                            case 'rel':
9363
                                // Path relative to the current document.
9364
                                // Save xml:base as current document's directory and save file in zip as subdir.file_path
9365
                                if (substr($doc_info[0], 0, 2) == '..') {
9366
                                    // Relative path going up.
9367
                                    $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
9368
                                    $current_dir = str_replace('\\', '/', $current_dir);
9369
                                    $file_path = realpath($current_dir.$doc_info[0]);
9370
                                    $file_path = str_replace('\\', '/', $file_path);
9371
9372
                                    //error_log($file_path.' <-> '.$main_path,0);
9373
                                    if (strstr($file_path, $main_path) !== false) {
9374
                                        // The calculated real path is really inside Chamilo's root path.
9375
                                        // Reduce file path to what's under the DocumentRoot.
9376
                                        $file_path = substr($file_path, strlen($root_path));
9377
                                        //error_log('Reduced path: '.$file_path, 0);
9378
                                        $zip_files_abs[] = $file_path;
9379
                                        $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9380
                                        $my_dep_file->setAttribute('href', 'document/'.$file_path);
9381
                                        $my_dep->setAttribute('xml:base', '');
9382
                                    }
9383 View Code Duplication
                                } else {
9384
                                    $zip_files[] = $my_sub_dir.'/'.$doc_info[0];
9385
                                    $my_dep_file->setAttribute('href', $doc_info[0]);
9386
                                    $my_dep->setAttribute('xml:base', $my_xml_sub_dir);
9387
                                }
9388
                                break;
9389
                            default:
9390
                                $my_dep_file->setAttribute('href', $doc_info[0]);
9391
                                $my_dep->setAttribute('xml:base', '');
9392
                                break;
9393
                        }
9394
                    }
9395
                    $my_dep->appendChild($my_dep_file);
9396
                    $resources->appendChild($my_dep);
9397
                    $dependency = $xmldoc->createElement('dependency');
9398
                    $dependency->setAttribute('identifierref', $res_id);
9399
                    $my_resource->appendChild($dependency);
9400
                    $i++;
9401
                }
9402
                $resources->appendChild($my_resource);
9403
                $zip_files[] = $my_file_path;
9404
            } else {
9405
9406
                // If the item is a quiz or a link or whatever non-exportable, we include a step indicating it.
9407
                switch ($item->type) {
9408
                    case TOOL_LINK:
9409
                        $my_item = $xmldoc->createElement('item');
9410
                        $my_item->setAttribute('identifier', 'ITEM_'.$item->get_id());
9411
                        $my_item->setAttribute('identifierref', 'RESOURCE_'.$item->get_id());
9412
                        $my_item->setAttribute('isvisible', 'true');
9413
                        // Give a child element <title> to the <item> element.
9414
                        $my_title = $xmldoc->createElement('title', htmlspecialchars(api_utf8_encode($item->get_title()), ENT_QUOTES, 'UTF-8'));
9415
                        $my_item->appendChild($my_title);
9416
                        // Give a child element <adlcp:prerequisites> to the <item> element.
9417
                        $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $item->get_prereq_string());
9418
                        $my_prereqs->setAttribute('type', 'aicc_script');
9419
                        $my_item->appendChild($my_prereqs);
9420
                        // Give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported.
9421
                        //$xmldoc->createElement('adlcp:maxtimeallowed', '');
9422
                        // Give a child element <adlcp:timelimitaction> to the <item> element - not yet supported.
9423
                        //$xmldoc->createElement('adlcp:timelimitaction', '');
9424
                        // Give a child element <adlcp:datafromlms> to the <item> element - not yet supported.
9425
                        //$xmldoc->createElement('adlcp:datafromlms', '');
9426
                        // Give a child element <adlcp:masteryscore> to the <item> element.
9427
                        $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score());
9428
                        $my_item->appendChild($my_masteryscore);
9429
9430
                        // Attach this item to the organization element or its parent if there is one.
9431 View Code Duplication
                        if (!empty($item->parent) && $item->parent != 0) {
9432
                            $children = $organization->childNodes;
9433
                            for ($i = 0; $i < $children->length; $i++) {
9434
                                $item_temp = $children->item($i);
9435
                                if ($item_temp -> nodeName == 'item') {
9436
                                    if ($item_temp->getAttribute('identifier') == 'ITEM_'.$item->parent) {
9437
                                        $item_temp -> appendChild($my_item);
9438
                                    }
9439
                                }
9440
                            }
9441
                        } else {
9442
                            $organization->appendChild($my_item);
9443
                        }
9444
9445
                        $my_file_path = 'link_'.$item->get_id().'.html';
9446
                        $sql = 'SELECT url, title FROM '.Database :: get_course_table(TABLE_LINK).'
9447
                                WHERE c_id = '.$course_id.' AND id='.$item->path;
9448
                        $rs = Database::query($sql);
9449
                        if ($link = Database :: fetch_array($rs)) {
9450
                            $url = $link['url'];
9451
                            $title = stripslashes($link['title']);
9452
                            $links_to_create[$my_file_path] = array('title' => $title, 'url' => $url);
9453
                            $my_xml_file_path = $my_file_path;
9454
                            $my_sub_dir = dirname($my_file_path);
9455
                            $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
9456
                            $my_xml_sub_dir = $my_sub_dir;
9457
                            // Give a <resource> child to the <resources> element.
9458
                            $my_resource = $xmldoc->createElement('resource');
9459
                            $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
9460
                            $my_resource->setAttribute('type', 'webcontent');
9461
                            $my_resource->setAttribute('href', $my_xml_file_path);
9462
                            // adlcp:scormtype can be either 'sco' or 'asset'.
9463
                            $my_resource->setAttribute('adlcp:scormtype', 'asset');
9464
                            // xml:base is the base directory to find the files declared in this resource.
9465
                            $my_resource->setAttribute('xml:base', '');
9466
                            // give a <file> child to the <resource> element.
9467
                            $my_file = $xmldoc->createElement('file');
9468
                            $my_file->setAttribute('href', $my_xml_file_path);
9469
                            $my_resource->appendChild($my_file);
9470
                            $resources->appendChild($my_resource);
9471
                        }
9472
                        break;
9473
                    case TOOL_QUIZ:
9474
                        $exe_id = $item->path; // Should be using ref when everything will be cleaned up in this regard.
9475
                        $exe = new Exercise();
9476
                        $exe->read($exe_id);
9477
                        $my_item = $xmldoc->createElement('item');
9478
                        $my_item->setAttribute('identifier', 'ITEM_'.$item->get_id());
9479
                        $my_item->setAttribute('identifierref', 'RESOURCE_'.$item->get_id());
9480
                        $my_item->setAttribute('isvisible', 'true');
9481
                        // Give a child element <title> to the <item> element.
9482
                        $my_title = $xmldoc->createElement('title', htmlspecialchars(api_utf8_encode($item->get_title()), ENT_QUOTES, 'UTF-8'));
9483
                        $my_item->appendChild($my_title);
9484
                        $my_max_score = $xmldoc->createElement('max_score', $item->get_max());
9485
                        //$my_item->appendChild($my_max_score);
9486
                        // Give a child element <adlcp:prerequisites> to the <item> element.
9487
                        $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $item->get_prereq_string());
9488
                        $my_prereqs->setAttribute('type','aicc_script');
9489
                        $my_item->appendChild($my_prereqs);
9490
                        // Give a child element <adlcp:masteryscore> to the <item> element.
9491
                        $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score());
9492
                        $my_item->appendChild($my_masteryscore);
9493
9494
                        // Attach this item to the organization element or hits parent if there is one.
9495 View Code Duplication
                        if (!empty($item->parent) && $item->parent != 0) {
9496
                            $children = $organization->childNodes;
9497
                            for ($i = 0; $i < $children->length; $i++) {
9498
                                $item_temp = $children->item($i);
9499
                                if ($item_temp -> nodeName == 'item') {
9500
                                    if ($item_temp->getAttribute('identifier') == 'ITEM_'.$item->parent) {
9501
                                        $item_temp -> appendChild($my_item);
9502
                                    }
9503
                                }
9504
                            }
9505
                        } else {
9506
                            $organization->appendChild($my_item);
9507
                        }
9508
9509
                        // Get the path of the file(s) from the course directory root
9510
                        //$my_file_path = $item->get_file_path('scorm/'.$this->path.'/');
9511
                        $my_file_path = 'quiz_'.$item->get_id().'.html';
9512
                        // Write the contents of the exported exercise into a (big) html file
9513
                        // to later pack it into the exported SCORM. The file will be removed afterwards.
9514
                        $contents = ScormSection::export_exercise_to_scorm($exe_id, true);
9515
                        $tmp_file_path = $archive_path.$temp_dir_short.'/'.$my_file_path;
9516
                        $res = file_put_contents($tmp_file_path, $contents);
9517
                        if ($res === false) { error_log('Could not write into file '.$tmp_file_path.' '.__FILE__.' '.__LINE__, 0); }
9518
                        $files_cleanup[] = $tmp_file_path;
9519
                        //error_log($tmp_path); die();
9520
                        //$my_xml_file_path = api_htmlentities(api_utf8_encode($my_file_path), ENT_QUOTES, 'UTF-8');
9521
                        $my_xml_file_path = $my_file_path;
9522
                        $my_sub_dir = dirname($my_file_path);
9523
                        $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
9524
                        //$my_xml_sub_dir = api_htmlentities(api_utf8_encode($my_sub_dir), ENT_QUOTES, 'UTF-8');
9525
                        $my_xml_sub_dir = $my_sub_dir;
9526
                        // Give a <resource> child to the <resources> element.
9527
                        $my_resource = $xmldoc->createElement('resource');
9528
                        $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
9529
                        $my_resource->setAttribute('type', 'webcontent');
9530
                        $my_resource->setAttribute('href', $my_xml_file_path);
9531
                        // adlcp:scormtype can be either 'sco' or 'asset'.
9532
                        $my_resource->setAttribute('adlcp:scormtype', 'sco');
9533
                        // xml:base is the base directory to find the files declared in this resource.
9534
                        $my_resource->setAttribute('xml:base', '');
9535
                        // Give a <file> child to the <resource> element.
9536
                        $my_file = $xmldoc->createElement('file');
9537
                        $my_file->setAttribute('href', $my_xml_file_path);
9538
                        $my_resource->appendChild($my_file);
9539
9540
                        // Get included docs.
9541
                        $inc_docs = $item->get_resources_from_source(null,$tmp_file_path);
9542
                        // Dependency to other files - not yet supported.
9543
                        $i = 1;
9544
                        foreach ($inc_docs as $doc_info) {
9545
                            if (count($doc_info) < 1 || empty($doc_info[0])) { continue; }
9546
                            $my_dep = $xmldoc->createElement('resource');
9547
                            $res_id = 'RESOURCE_'.$item->get_id().'_'.$i;
9548
                            $my_dep->setAttribute('identifier', $res_id);
9549
                            $my_dep->setAttribute('type', 'webcontent');
9550
                            $my_dep->setAttribute('adlcp:scormtype', 'asset');
9551
                            $my_dep_file = $xmldoc->createElement('file');
9552
                            // Check type of URL.
9553
                            if ($doc_info[1] == 'remote') {
9554
                                // Remote file. Save url as is.
9555
                                $my_dep_file->setAttribute('href', $doc_info[0]);
9556
                                $my_dep->setAttribute('xml:base', '');
9557
                            } elseif ($doc_info[1] == 'local') {
9558
                                switch ($doc_info[2]) {
9559
                                    case 'url': // Local URL - save path as url for now, don't zip file.
9560
                                        // Save file but as local file (retrieve from URL).
9561
                                        $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]);
9562
                                        $current_dir = dirname($abs_path);
9563
                                        $current_dir = str_replace('\\', '/', $current_dir);
9564
                                        $file_path = realpath($abs_path);
9565
                                        $file_path = str_replace('\\', '/', $file_path);
9566
                                        $my_dep_file->setAttribute('href', 'document/'.$file_path);
9567
                                        $my_dep->setAttribute('xml:base', '');
9568 View Code Duplication
                                        if (strstr($file_path, $main_path) !== false) {
9569
                                            // The calculated real path is really inside the chamilo root path.
9570
                                            // Reduce file path to what's under the DocumentRoot.
9571
                                            $file_path = substr($file_path, strlen($root_path));
9572
                                            //echo $file_path;echo '<br /><br />';
9573
                                            //error_log('Reduced path: '.$file_path, 0);
9574
                                            $zip_files_abs[] = $file_path;
9575
                                            $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => 'document/'.$file_path);
9576
                                            $my_dep_file->setAttribute('href', 'document/'.$file_path);
9577
                                            $my_dep->setAttribute('xml:base', '');
9578
                                        } elseif (empty($file_path)) {
9579
                                            /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH),api_get_path(REL_PATH)));
9580
                                            if (strpos($document_root,-1) == '/') {
9581
                                                $document_root = substr(0, -1, $document_root);
9582
                                            }*/
9583
                                            $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path;
9584
                                            $file_path = str_replace('//', '/', $file_path);
9585
                                            if (file_exists($file_path)) {
9586
                                                $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
9587
                                                $zip_files[] = $my_sub_dir.'/'.$file_path;
9588
                                                $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => 'document/'.$file_path);
9589
                                                $my_dep_file->setAttribute('href', 'document/'.$file_path);
9590
                                                $my_dep->setAttribute('xml:base', '');
9591
                                            }
9592
                                        }
9593
                                        break;
9594
                                    case 'abs': // Absolute path from DocumentRoot. Save file and leave path as is in the zip.
9595
                                        $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
9596
                                        $current_dir = str_replace('\\', '/', $current_dir);
9597
                                        $file_path = realpath($doc_info[0]);
9598
                                        $file_path = str_replace('\\', '/', $file_path);
9599
                                        $my_dep_file->setAttribute('href', $file_path);
9600
                                        $my_dep->setAttribute('xml:base', '');
9601
9602 View Code Duplication
                                        if (strstr($file_path,$main_path) !== false) {
9603
                                            // The calculated real path is really inside the chamilo root path.
9604
                                            // Reduce file path to what's under the DocumentRoot.
9605
                                            $file_path = substr($file_path, strlen($root_path));
9606
                                            //echo $file_path;echo '<br /><br />';
9607
                                            //error_log('Reduced path: '.$file_path, 0);
9608
                                            $zip_files_abs[] = $file_path;
9609
                                            $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9610
                                            $my_dep_file->setAttribute('href', 'document/'.$file_path);
9611
                                            $my_dep->setAttribute('xml:base', '');
9612
                                        } elseif (empty($file_path)) {
9613
                                            /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH), api_get_path(REL_PATH)));
9614
                                            if (strpos($document_root,-1) == '/') {
9615
                                                $document_root = substr(0, -1, $document_root);
9616
                                            }*/
9617
                                            $file_path = $_SERVER['DOCUMENT_ROOT'].$doc_info[0];
9618
                                            $file_path = str_replace('//', '/', $file_path);
9619
                                            if (file_exists($file_path)) {
9620
                                                $file_path = substr($file_path,strlen($current_dir)); // We get the relative path.
9621
                                                $zip_files[] = $my_sub_dir.'/'.$file_path;
9622
                                                $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
9623
                                                $my_dep_file->setAttribute('href', 'document/'.$file_path);
9624
                                                $my_dep->setAttribute('xml:base', '');
9625
                                            }
9626
                                        }
9627
                                        break;
9628
                                    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
9629
                                        if (substr($doc_info[0], 0, 2) == '..') {
9630
                                            // Relative path going up.
9631
                                            $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
9632
                                            $current_dir = str_replace('\\', '/', $current_dir);
9633
                                            $file_path = realpath($current_dir.$doc_info[0]);
9634
                                            $file_path = str_replace('\\', '/', $file_path);
9635
                                            //error_log($file_path.' <-> '.$main_path, 0);
9636
                                            if (strstr($file_path, $main_path) !== false) {
9637
                                                // The calculated real path is really inside Chamilo's root path.
9638
                                                // Reduce file path to what's under the DocumentRoot.
9639
9640
                                                $file_path = substr($file_path, strlen($root_path));
9641
                                                $file_path_dest = $file_path;
9642
9643
                                                // File path is courses/CHAMILO/document/....
9644
                                                $info_file_path = explode('/', $file_path);
9645
                                                if ($info_file_path[0] == 'courses') { // Add character "/" in file path.
9646
                                                    $file_path_dest = 'document/'.$file_path;
9647
                                                }
9648
9649
                                                //error_log('Reduced path: '.$file_path, 0);
9650
                                                $zip_files_abs[] = $file_path;
9651
9652
                                                $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path_dest);
9653
                                                $my_dep_file->setAttribute('href', 'document/'.$file_path);
9654
                                                $my_dep->setAttribute('xml:base', '');
9655
                                            }
9656 View Code Duplication
                                        } else {
9657
                                            $zip_files[] = $my_sub_dir.'/'.$doc_info[0];
9658
                                            $my_dep_file->setAttribute('href', $doc_info[0]);
9659
                                            $my_dep->setAttribute('xml:base', $my_xml_sub_dir);
9660
                                        }
9661
                                        break;
9662
                                    default:
9663
                                        $my_dep_file->setAttribute('href', $doc_info[0]); // ../../courses/
9664
                                        $my_dep->setAttribute('xml:base', '');
9665
                                        break;
9666
                                }
9667
                            }
9668
                            $my_dep->appendChild($my_dep_file);
9669
                            $resources->appendChild($my_dep);
9670
                            $dependency = $xmldoc->createElement('dependency');
9671
                            $dependency->setAttribute('identifierref', $res_id);
9672
                            $my_resource->appendChild($dependency);
9673
                            $i++;
9674
                        }
9675
                        $resources->appendChild($my_resource);
9676
                        $zip_files[] = $my_file_path;
9677
                        break;
9678
                    default:
9679
                        // Get the path of the file(s) from the course directory root
9680
                        $my_file_path = 'non_exportable.html';
9681
                        //$my_xml_file_path = api_htmlentities(api_utf8_encode($my_file_path), ENT_COMPAT, 'UTF-8');
9682
                        $my_xml_file_path = $my_file_path;
9683
                        $my_sub_dir = dirname($my_file_path);
9684
                        $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
9685
                        //$my_xml_sub_dir = api_htmlentities(api_utf8_encode($my_sub_dir), ENT_COMPAT, 'UTF-8');
9686
                        $my_xml_sub_dir = $my_sub_dir;
9687
                        // Give a <resource> child to the <resources> element.
9688
                        $my_resource = $xmldoc->createElement('resource');
9689
                        $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
9690
                        $my_resource->setAttribute('type', 'webcontent');
9691
                        $my_resource->setAttribute('href', $folder_name.'/'.$my_xml_file_path);
9692
                        // adlcp:scormtype can be either 'sco' or 'asset'.
9693
                        $my_resource->setAttribute('adlcp:scormtype', 'asset');
9694
                        // xml:base is the base directory to find the files declared in this resource.
9695
                        $my_resource->setAttribute('xml:base', '');
9696
                        // Give a <file> child to the <resource> element.
9697
                        $my_file = $xmldoc->createElement('file');
9698
                        $my_file->setAttribute('href', 'document/'.$my_xml_file_path);
9699
                        $my_resource->appendChild($my_file);
9700
                        $resources->appendChild($my_resource);
9701
                        break;
9702
                }
9703
            }
9704
        }
9705
9706
        $organizations->appendChild($organization);
9707
        $root->appendChild($organizations);
9708
        $root->appendChild($resources);
9709
        $xmldoc->appendChild($root);
9710
9711
        // TODO: Add a readme file here, with a short description and a link to the Reload player
9712
        // then add the file to the zip, then destroy the file (this is done automatically).
9713
        // http://www.reload.ac.uk/scormplayer.html - once done, don't forget to close FS#138
9714
9715
        foreach ($zip_files as $file_path) {
9716
            if (empty($file_path)) {
9717
                continue;
9718
            }
9719
9720
            $dest_file = $archive_path.$temp_dir_short.'/'.$file_path;
9721
            if (!empty($path_to_remove) && !empty($path_to_replace)) {
9722
                $dest_file = str_replace($path_to_remove, $path_to_replace, $dest_file);
9723
            }
9724
            $this->create_path($dest_file);
9725
            @copy($sys_course_path.$_course['path'].'/'.$file_path, $dest_file);
9726
            // Check if the file needs a link update.
9727
            if (in_array($file_path, array_keys($link_updates))) {
9728
                $string = file_get_contents($dest_file);
9729
                unlink($dest_file);
9730
                foreach ($link_updates[$file_path] as $old_new) {
9731
                    // This is an ugly hack that allows .flv files to be found by the flv player that
9732
                    // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs
9733
                    // to find the flv to play in document/main/, so we replace main/ in the flv path by
9734
                    // ../../.. to return from inc/lib/flv_player to the document/main path.
9735
                    if (substr($old_new['dest'], -3) == 'flv' && substr($old_new['dest'], 0, 5) == 'main/') {
9736
                        $old_new['dest'] = str_replace('main/', '../../../', $old_new['dest']);
9737 View Code Duplication
                    } elseif (substr($old_new['dest'], -3) == 'flv' && substr($old_new['dest'], 0, 6) == 'video/') {
9738
                        $old_new['dest'] = str_replace('video/', '../../../../video/', $old_new['dest']);
9739
                    }
9740
                    //Fix to avoid problems with default_course_document
9741
                    if (strpos("main/default_course_document", $old_new['dest'] === false)) {
9742
                        //$newDestination = str_replace('document/', $mult.'document/', $old_new['dest']);
9743
                        $newDestination = $old_new['dest'];
9744
                    } else {
9745
                        $newDestination = str_replace('document/', '', $old_new['dest']);
9746
                    }
9747
                    $string = str_replace($old_new['orig'], $newDestination, $string);
9748
9749
                    //Add files inside the HTMLs
9750
                    $new_path = str_replace('/courses/', '', $old_new['orig']);
9751
                    $destinationFile = $archive_path.$temp_dir_short.'/'.$old_new['dest'];
9752
                    if (file_exists($sys_course_path.$new_path)) {
9753
                        copy($sys_course_path.$new_path, $destinationFile);
9754
                    }
9755
                }
9756
                file_put_contents($dest_file, $string);
9757
            }
9758
        }
9759
9760
        foreach ($zip_files_abs as $file_path) {
9761
            if (empty($file_path)) {
9762
                continue;
9763
            }
9764
            if (!is_file($main_path.$file_path) || !is_readable($main_path.$file_path)) {
9765
                continue;
9766
            }
9767
9768
            $dest_file = $archive_path.$temp_dir_short.'/document/'.$file_path;
9769
            $this->create_path($dest_file);
9770
            copy($main_path.$file_path, $dest_file);
9771
            // Check if the file needs a link update.
9772
            if (in_array($file_path, array_keys($link_updates))) {
9773
                $string = file_get_contents($dest_file);
9774
                unlink($dest_file);
9775
                foreach ($link_updates[$file_path] as $old_new) {
9776
                    // This is an ugly hack that allows .flv files to be found by the flv player that
9777
                    // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs
9778
                    // to find the flv to play in document/main/, so we replace main/ in the flv path by
9779
                    // ../../.. to return from inc/lib/flv_player to the document/main path.
9780 View Code Duplication
                    if (substr($old_new['dest'], -3) == 'flv' && substr($old_new['dest'], 0, 5) == 'main/') {
9781
                        $old_new['dest'] = str_replace('main/', '../../../', $old_new['dest']);
9782
                    }
9783
                    $string = str_replace($old_new['orig'], $old_new['dest'], $string);
9784
                }
9785
                file_put_contents($dest_file, $string);
9786
            }
9787
        }
9788
9789
        if (is_array($links_to_create)) {
9790
            foreach ($links_to_create as $file => $link) {
9791
                $file_content = '<!DOCTYPE html><head>
9792
                                <meta charset="'.api_get_language_isocode().'" />
9793
                                <title>'.$link['title'].'</title>
9794
                                </head>
9795
                                <body dir="'.api_get_text_direction().'">
9796
                                <div style="text-align:center">
9797
                                <a href="'.$link['url'].'">'.$link['title'].'</a></div>
9798
                                </body>
9799
                                </html>';
9800
                file_put_contents($archive_path.$temp_dir_short.'/'.$file, $file_content);
9801
            }
9802
        }
9803
9804
        // Add non exportable message explanation.
9805
        $lang_not_exportable = get_lang('ThisItemIsNotExportable');
9806
        $file_content = '<!DOCTYPE html><head>
9807
                        <meta charset="'.api_get_language_isocode().'" />
9808
                        <title>'.$lang_not_exportable.'</title>
9809
                        <meta http-equiv="Content-Type" content="text/html; charset='.api_get_system_encoding().'" />
9810
                        </head>
9811
                        <body dir="'.api_get_text_direction().'">';
9812
        $file_content .=
9813
            <<<EOD
9814
                    <style>
9815
            .error-message {
9816
                font-family: arial, verdana, helvetica, sans-serif;
9817
                border-width: 1px;
9818
                border-style: solid;
9819
                left: 50%;
9820
                margin: 10px auto;
9821
                min-height: 30px;
9822
                padding: 5px;
9823
                right: 50%;
9824
                width: 500px;
9825
                background-color: #FFD1D1;
9826
                border-color: #FF0000;
9827
                color: #000;
9828
            }
9829
        </style>
9830
    <body>
9831
        <div class="error-message">
9832
            $lang_not_exportable
9833
        </div>
9834
    </body>
9835
</html>
9836
EOD;
9837
        if (!is_dir($archive_path.$temp_dir_short.'/document')) {
9838
            @mkdir($archive_path.$temp_dir_short.'/document', api_get_permissions_for_new_directories());
9839
        }
9840
        file_put_contents($archive_path.$temp_dir_short.'/document/non_exportable.html', $file_content);
9841
9842
        // Add the extra files that go along with a SCORM package.
9843
        $main_code_path = api_get_path(SYS_CODE_PATH).'newscorm/packaging/';
9844
        $extra_files = scandir($main_code_path);
9845
        foreach ($extra_files as $extra_file) {
9846
            if (strpos($extra_file, '.') === 0) {
9847
                continue;
9848
            } else {
9849
                $dest_file = $archive_path.$temp_dir_short.'/'.$extra_file;
9850
                $this->create_path($dest_file);
9851
                copy($main_code_path.$extra_file, $dest_file);
9852
            }
9853
        }
9854
9855
        // Finalize the imsmanifest structure, add to the zip, then return the zip.
9856
9857
        $manifest = @$xmldoc->saveXML();
9858
        $manifest = api_utf8_decode_xml($manifest); // The manifest gets the system encoding now.
9859
        file_put_contents($archive_path.'/'.$temp_dir_short.'/imsmanifest.xml', $manifest);
9860
        $zip_folder->add(
9861
            $archive_path.'/'.$temp_dir_short,
9862
            PCLZIP_OPT_REMOVE_PATH,
9863
            $archive_path.'/'.$temp_dir_short.'/'
9864
        );
9865
9866
        // Clean possible temporary files.
9867
        foreach ($files_cleanup as $file) {
9868
            $res = unlink($file);
9869
            if ($res === false) {
9870
                error_log(
9871
                    'Could not delete temp file '.$file.' '.__FILE__.' '.__LINE__,
9872
                    0
9873
                );
9874
            }
9875
        }
9876
        $name = api_replace_dangerous_char($this->get_name()).'.zip';
9877
        DocumentManager::file_send_for_download($temp_zip_file, true, $name);
9878
    }
9879
9880
    /**
9881
     * @param int $lp_id
9882
     * @return bool
9883
     */
9884
    public function scorm_export_to_pdf($lp_id)
9885
    {
9886
        $lp_id = intval($lp_id);
9887
        $files_to_export = array();
9888
        $course_data = api_get_course_info($this->cc);
9889
        if (!empty($course_data)) {
9890
            $scorm_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/scorm/'.$this->path;
9891
9892
            $list = self::get_flat_ordered_items_list($lp_id);
9893
            if (!empty($list)) {
9894
                foreach ($list as $item_id) {
9895
                    $item = $this->items[$item_id];
9896
                    switch ($item->type) {
9897
                        case 'document':
9898
                            //Getting documents from a LP with chamilo documents
9899
                            $file_data = DocumentManager::get_document_data_by_id($item->path, $this->cc);
9900
                            // Try loading document from the base course.
9901
                            if (empty($file_data) && !empty($sessionId)) {
9902
                                $file_data = DocumentManager::get_document_data_by_id($item->path, $this->cc, false, 0);
9903
                            }
9904
                            $file_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/document'.$file_data['path'];
9905
                            if (file_exists($file_path)) {
9906
                                $files_to_export[] = array('title'=>$item->get_title(),'path'=>$file_path);
9907
                            }
9908
                            break;
9909
                        case 'asset': //commes from a scorm package generated by chamilo
9910
                        case 'sco':
9911
                            $file_path = $scorm_path.'/'.$item->path;
9912
                            if (file_exists($file_path)) {
9913
                                $files_to_export[] = array('title'=>$item->get_title(), 'path' => $file_path);
9914
                            }
9915
                            break;
9916
                        case 'dokeos_chapter':
9917
                        case 'dir':
9918
                        case 'chapter':
9919
                            $files_to_export[] = array('title'=> $item->get_title(), 'path'=>null);
9920
                            break;
9921
                    }
9922
                }
9923
            }
9924
            $pdf = new PDF();
9925
            $result = $pdf->html_to_pdf($files_to_export, $this->name, $this->cc, true);
9926
            return $result;
9927
        }
9928
9929
        return false;
9930
    }
9931
9932
    /**
9933
     * Temp function to be moved in main_api or the best place around for this.
9934
     * Creates a file path if it doesn't exist
9935
     * @param string $path
9936
     */
9937
    public function create_path($path)
9938
    {
9939
        $path_bits = explode('/', dirname($path));
9940
9941
        // IS_WINDOWS_OS has been defined in main_api.lib.php
9942
        $path_built = IS_WINDOWS_OS ? '' : '/';
9943
9944
        foreach ($path_bits as $bit) {
9945
            if (!empty ($bit)) {
9946
                $new_path = $path_built . $bit;
9947
                if (is_dir($new_path)) {
9948
                    $path_built = $new_path . '/';
9949
                } else {
9950
                    mkdir($new_path, api_get_permissions_for_new_directories());
9951
                    $path_built = $new_path . '/';
9952
                }
9953
            }
9954
        }
9955
    }
9956
9957
    /**
9958
     * Delete the image relative to this learning path. No parameter. Only works on instanciated object.
9959
     * @return	boolean	The results of the unlink function, or false if there was no image to start with
9960
     */
9961
    public function delete_lp_image()
9962
    {
9963
        $img = $this->get_preview_image();
9964
        if ($img != '') {
9965
            $del_file = $this->get_preview_image_path(null, 'sys');
9966
            if (isset($del_file) && file_exists($del_file)) {
9967
                $del_file_2 = $this->get_preview_image_path(64, 'sys');
9968
                if (file_exists($del_file_2)) {
9969
                    unlink($del_file_2);
9970
                }
9971
                $this->set_preview_image('');
9972
                return @unlink($del_file);
9973
            }
9974
        }
9975
        return false;
9976
    }
9977
9978
    /**
9979
     * Uploads an author image to the upload/learning_path/images directory
9980
     * @param	array	The image array, coming from the $_FILES superglobal
9981
     * @return	boolean	True on success, false on error
9982
     */
9983
    public function upload_image($image_array)
9984
    {
9985
        $image_moved = false;
9986
        if (!empty ($image_array['name'])) {
9987
            $upload_ok = process_uploaded_file($image_array);
9988
            $has_attachment = true;
9989
        } else {
9990
            $image_moved = true;
9991
        }
9992
9993
        if ($upload_ok) {
9994
            if ($has_attachment) {
9995
                $courseDir = api_get_course_path() . '/upload/learning_path/images';
9996
                $sys_course_path = api_get_path(SYS_COURSE_PATH);
9997
                $updir = $sys_course_path . $courseDir;
9998
                // Try to add an extension to the file if it hasn't one.
9999
                $new_file_name = add_ext_on_mime(stripslashes($image_array['name']), $image_array['type']);
10000
10001
                if (!filter_extension($new_file_name)) {
10002
                    //Display :: display_error_message(get_lang('UplUnableToSaveFileFilteredExtension'));
10003
                    $image_moved = false;
10004
                } else {
10005
                    $file_extension = explode('.', $image_array['name']);
10006
                    $file_extension = strtolower($file_extension[sizeof($file_extension) - 1]);
10007
                    $filename = uniqid('');
10008
                    $new_file_name = $filename.'.'.$file_extension;
10009
                    $new_path = $updir.'/'.$new_file_name;
10010
10011
                    // Resize the image.
10012
                    $temp = new Image($image_array['tmp_name']);
10013
                    $temp->resize(104);
10014
                    $result = $temp->send_image($new_path);
10015
10016
                    // Storing the image filename.
10017
                    if ($result) {
10018
                        $image_moved = true;
10019
                        $this->set_preview_image($new_file_name);
10020
10021
                        //Resize to 64px to use on course homepage
10022
                        $temp->resize(64);
10023
                        $temp->send_image($updir.'/'.$filename.'.64.'.$file_extension);
10024
                        return true;
10025
                    }
10026
                }
10027
            }
10028
        }
10029
10030
        return false;
10031
    }
10032
10033
    /**
10034
     * @param int $lp_id
10035
     * @param string $status
10036
     */
10037
    public function set_autolaunch($lp_id, $status)
10038
    {
10039
        $course_id = api_get_course_int_id();
10040
        $lp_id   = intval($lp_id);
10041
        $status  = intval($status);
10042
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
10043
10044
        // Setting everything to autolaunch = 0
10045
        $attributes['autolaunch'] = 0;
10046
        $where = array('session_id = ? AND c_id = ? '=> array(api_get_session_id(), $course_id));
10047
        Database::update($lp_table, $attributes, $where);
10048
        if ($status == 1) {
10049
            //Setting my lp_id to autolaunch = 1
10050
            $attributes['autolaunch'] = 1;
10051
            $where = array('id = ? AND session_id = ? AND c_id = ?'=> array($lp_id, api_get_session_id(), $course_id));
10052
            Database::update($lp_table, $attributes, $where );
10053
        }
10054
    }
10055
10056
    /**
10057
     * Gets previous_item_id for the next element of the lp_item table
10058
     * @author Isaac flores paz
10059
     * @return	integer	Previous item ID
10060
     */
10061
    public function select_previous_item_id()
10062
    {
10063
        $course_id = api_get_course_int_id();
10064
        if ($this->debug > 0) {
10065
            error_log('New LP - In learnpath::select_previous_item_id()', 0);
10066
        }
10067
        $table_lp_item = Database::get_course_table(TABLE_LP_ITEM);
10068
10069
        // Get the max order of the items
10070
        $sql_max_order = "SELECT max(display_order) AS display_order FROM $table_lp_item
10071
    	                  WHERE c_id = $course_id AND lp_id = '" . $this->lp_id . "'";
10072
        $rs_max_order = Database::query($sql_max_order);
10073
        $row_max_order = Database::fetch_object($rs_max_order);
10074
        $max_order = $row_max_order->display_order;
10075
        // Get the previous item ID
10076
        $sql = "SELECT id as previous FROM $table_lp_item
10077
                WHERE c_id = $course_id AND lp_id = '" . $this->lp_id . "' AND display_order = '".$max_order."' ";
10078
        $rs_max = Database::query($sql);
10079
        $row_max = Database::fetch_object($rs_max);
10080
10081
        // Return the previous item ID
10082
        return $row_max->previous;
10083
    }
10084
10085
    /**
10086
     * Copies an LP
10087
     */
10088
    public function copy()
10089
    {
10090
        $main_path = api_get_path(SYS_CODE_PATH);
10091
        require_once $main_path.'coursecopy/classes/CourseBuilder.class.php';
10092
        require_once $main_path.'coursecopy/classes/CourseArchiver.class.php';
10093
        require_once $main_path.'coursecopy/classes/CourseRestorer.class.php';
10094
        require_once $main_path.'coursecopy/classes/CourseSelectForm.class.php';
10095
10096
        //Course builder
10097
        $cb = new CourseBuilder();
10098
10099
        //Setting tools that will be copied
10100
        $cb->set_tools_to_build(array('learnpaths'));
10101
10102
        //Setting elements that will be copied
10103
        $cb->set_tools_specific_id_list(
10104
            array('learnpaths' => array($this->lp_id))
10105
        );
10106
10107
        $course = $cb->build();
10108
10109
        //Course restorer
10110
        $course_restorer = new CourseRestorer($course);
10111
        $course_restorer->set_add_text_in_items(true);
10112
        $course_restorer->set_tool_copy_settings(array('learnpaths' => array('reset_dates' => true)));
10113
        $course_restorer->restore(api_get_course_id(), api_get_session_id(), false, false);
10114
    }
10115
10116
    public function verify_document_size($s)
10117
    {
10118
        $post_max = ini_get('post_max_size');
10119 View Code Duplication
        if (substr($post_max, -1, 1) == 'M') {
10120
            $post_max = intval(substr($post_max, 0, -1)) * 1024 * 1024;
10121
        } elseif (substr($post_max, -1, 1) == 'G') {
10122
            $post_max = intval(substr($post_max, 0, -1)) * 1024 * 1024 * 1024;
10123
        }
10124
        $upl_max = ini_get('upload_max_filesize');
10125 View Code Duplication
        if (substr($upl_max, -1, 1) == 'M') {
10126
            $upl_max = intval(substr($upl_max, 0, -1)) * 1024 * 1024;
10127
        } elseif (substr($upl_max, -1, 1) == 'G') {
10128
            $upl_max = intval(substr($upl_max, 0, -1)) * 1024 * 1024 * 1024;
10129
        }
10130
        $documents_total_space = DocumentManager::documents_total_space();
10131
        $course_max_space = DocumentManager::get_course_quota();
10132
        $total_size = filesize($s) + $documents_total_space;
10133
        if (filesize($s)>$post_max || filesize($s)>$upl_max  || $total_size>$course_max_space ){
10134
            return true;
10135
        } else{
10136
            return false;
10137
        }
10138
    }
10139
10140
    /**
10141
     * Clear LP prerequisites
10142
     */
10143
    public function clear_prerequisites()
10144
    {
10145
        $course_id = $this->get_course_int_id();
10146
        if ($this->debug > 0) {
10147
            error_log('New LP - In learnpath::clear_prerequisites()', 0);
10148
        }
10149
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
10150
        $lp_id = $this->get_id();
10151
        //Cleaning prerequisites
10152
        $sql = "UPDATE $tbl_lp_item SET prerequisite = ''
10153
                WHERE c_id = ".$course_id." AND lp_id = '$lp_id'";
10154
        Database::query($sql);
10155
10156
        //Cleaning mastery score for exercises
10157
        $sql = "UPDATE $tbl_lp_item SET mastery_score = ''
10158
                WHERE c_id = ".$course_id." AND lp_id = '$lp_id' AND item_type = 'quiz'";
10159
        Database::query($sql);
10160
    }
10161
10162
    public function set_previous_step_as_prerequisite_for_all_items()
10163
    {
10164
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
10165
        $course_id = $this->get_course_int_id();
10166
        $lp_id = $this->get_id();
10167
10168
        if (!empty($this->items)) {
10169
            $previous_item_id = null;
10170
            $previous_item_max = 0;
10171
            $previous_item_type = null;
10172
            $last_item_not_chapter = null;
10173
            $last_item_not_chapter_type = null;
10174
            $last_item_not_chapter_max = null;
10175
            foreach ($this->items as $item) {
10176
                // if there was a previous item... (otherwise jump to set it)
10177
                if (!empty($previous_item_id)) {
10178
                    $current_item_id = $item->get_id(); //save current id
10179
                    if (!in_array($item->get_type(), array('dokeos_chapter', 'chapter'))) {
10180
                        // Current item is not a folder, so it qualifies to get a prerequisites
10181
                        if ($last_item_not_chapter_type == 'quiz') {
10182
                            // if previous is quiz, mark its max score as default score to be achieved
10183
                            $sql = "UPDATE $tbl_lp_item SET mastery_score = '$last_item_not_chapter_max'
10184
                                    WHERE c_id = ".$course_id." AND lp_id = '$lp_id' AND id = '$last_item_not_chapter'";
10185
                            Database::query($sql);
10186
                        }
10187
                        // now simply update the prerequisite to set it to the last non-chapter item
10188
                        $sql = "UPDATE $tbl_lp_item SET prerequisite = '$last_item_not_chapter'
10189
                                WHERE c_id = ".$course_id." AND lp_id = '$lp_id' AND id = '$current_item_id'";
10190
                        Database::query($sql);
10191
                        // record item as 'non-chapter' reference
10192
                        $last_item_not_chapter = $item->get_id();
10193
                        $last_item_not_chapter_type = $item->get_type();
10194
                        $last_item_not_chapter_max = $item->get_max();
10195
                    }
10196
                } else {
10197
                    if (!in_array($item->get_type(), array('dokeos_chapter', 'chapter'))) {
10198
                        // Current item is not a folder (but it is the first item) so record as last "non-chapter" item
10199
                        $last_item_not_chapter = $item->get_id();
10200
                        $last_item_not_chapter_type = $item->get_type();
10201
                        $last_item_not_chapter_max = $item->get_max();
10202
                    }
10203
                }
10204
                // Saving the item as "previous item" for the next loop
10205
                $previous_item_id = $item->get_id();
10206
                $previous_item_max = $item->get_max();
10207
                $previous_item_type = $item->get_type();
10208
            }
10209
        }
10210
    }
10211
10212
    /**
10213
     * @param array $params
10214
     */
10215
    public static function createCategory($params)
10216
    {
10217
        $em = Database::getManager();
10218
        $item = new CLpCategory();
10219
        $item->setName($params['name']);
10220
        $item->setCId($params['c_id']);
10221
        $em->persist($item);
10222
        $em->flush();
10223
    }
10224
    /**
10225
     * @param array $params
10226
     */
10227
    public static function updateCategory($params)
10228
    {
10229
        $em = Database::getManager();
10230
        /** @var CLpCategory $item */
10231
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $params['id']);
10232
        if ($item) {
10233
            $item->setName($params['name']);
10234
            $em->merge($item);
10235
            $em->flush();
10236
        }
10237
    }
10238
10239
    /**
10240
     * @param int $id
10241
     */
10242 View Code Duplication
    public static function moveUpCategory($id)
10243
    {
10244
        $em = Database::getManager();
10245
        /** @var CLpCategory $item */
10246
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
10247
        if ($item) {
10248
            $position = $item->getPosition() - 1;
10249
            $item->setPosition($position);
10250
            $em->persist($item);
10251
            $em->flush();
10252
        }
10253
    }
10254
10255
    /**
10256
     * @param int $id
10257
     */
10258 View Code Duplication
    public static function moveDownCategory($id)
10259
    {
10260
        $em = Database::getManager();
10261
        /** @var CLpCategory $item */
10262
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
10263
        if ($item) {
10264
            $position = $item->getPosition() + 1;
10265
            $item->setPosition($position);
10266
            $em->persist($item);
10267
            $em->flush();
10268
        }
10269
    }
10270
10271
    /**
10272
     * @param int $courseId
10273
     * @return int|mixed
10274
     */
10275
    public static function getCountCategories($courseId)
10276
    {
10277
        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...
10278
            return 0;
10279
        }
10280
        $em = Database::getManager();
10281
        $query = $em->createQuery('SELECT COUNT(u.id) FROM ChamiloCourseBundle:CLpCategory u WHERE u.cId = :id');
10282
        $query->setParameter('id', $courseId);
10283
10284
        return $query->getSingleScalarResult();
10285
    }
10286
10287
    /**
10288
     * @param int $courseId
10289
     *
10290
     * @return mixed
10291
     */
10292 View Code Duplication
    public static function getCategories($courseId)
10293
    {
10294
        $em = Database::getManager();
10295
        //Default behaviour
10296
        /*$items = $em->getRepository('ChamiloCourseBundle:CLpCategory')->findBy(
10297
            array('cId' => $course_id),
10298
            array('name' => 'ASC')
10299
        );*/
10300
10301
        // Using doctrine extensions
10302
        $items = $em->getRepository('ChamiloCourseBundle:CLpCategory')->getBySortableGroupsQuery(
10303
            array('cId' => $courseId)
10304
        )->getResult();
10305
10306
        return $items;
10307
    }
10308
10309
    /**
10310
     * @param int $id
10311
     *
10312
     * @return mixed
10313
     */
10314
    public static function getCategory($id)
10315
    {
10316
        $em = Database::getManager();
10317
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
10318
10319
        return $item;
10320
    }
10321
10322
    /**
10323
     * @param int $courseId
10324
     * @return array
10325
     */
10326 View Code Duplication
    public static function getCategoryByCourse($courseId)
10327
    {
10328
        $em = Database::getManager();
10329
        $items = $em->getRepository('ChamiloCourseBundle:CLpCategory')->findBy(array('cId' => $courseId));
10330
10331
        return $items;
10332
    }
10333
10334
    /**
10335
     * @param int $id
10336
     *
10337
     * @return mixed
10338
     */
10339
    public static function deleteCategory($id)
10340
    {
10341
        $em = Database::getManager();
10342
        $item = $em->find('ChamiloCourseBundle:CLpCategory', $id);
10343
        if ($item) {
10344
10345
            $courseId = $item->getCId();
10346
            $query = $em->createQuery('SELECT u FROM ChamiloCourseBundle:CLp u WHERE u.cId = :id AND u.categoryId = :catId');
10347
            $query->setParameter('id', $courseId);
10348
            $query->setParameter('catId', $item->getId());
10349
            $lps = $query->getResult();
10350
10351
            // Setting category = 0.
10352
            if ($lps) {
10353
                foreach ($lps as $lpItem) {
10354
                    $lpItem->setCategoryId(0);
10355
                }
10356
            }
10357
10358
            // Removing category.
10359
            $em->remove($item);
10360
            $em->flush();
10361
        }
10362
    }
10363
10364
    /**
10365
     * @param int $courseId
10366
     * @param bool $addSelectOption
10367
     *
10368
     * @return mixed
10369
     */
10370
    static function getCategoryFromCourseIntoSelect($courseId, $addSelectOption = false)
10371
    {
10372
        $items = self::getCategoryByCourse($courseId);
10373
        $cats = array();
10374
        if ($addSelectOption) {
10375
            $cats = array(get_lang('SelectACategory'));
10376
        }
10377
10378
        if (!empty($items)) {
10379
            foreach ($items as $cat) {
10380
                $cats[$cat->getId()] = $cat->getName();
10381
            }
10382
        }
10383
10384
        return $cats;
10385
    }
10386
10387
    /**
10388
     * Return the scorm item type object with spaces replaced with _
10389
     * The return result is use to build a css classname like scorm_type_$return
10390
     * @param $in_type
10391
     * @return mixed
10392
     */
10393
    private static function format_scorm_type_item($in_type)
10394
    {
10395
        return str_replace(' ', '_', $in_type);
10396
    }
10397
10398
    /**
10399
     * @return \learnpath
10400
     */
10401
    public static function getLpFromSession($courseCode, $lp_id, $user_id)
10402
    {
10403
        $lpObject = Session::read('lpobject');
10404
        $learnPath = null;
10405
        if (isset($lpObject)) {
10406
            $learnPath = unserialize($lpObject);
10407
        }
10408
10409
        if (!is_object($learnPath)) {
10410
            $learnPath = new learnpath($courseCode, $lp_id, $user_id);
10411
        }
10412
10413
        return $learnPath;
10414
    }
10415
10416
    /**
10417
     * @param int $itemId
10418
     * @return learnpathItem|false
10419
     */
10420
    public function getItem($itemId)
10421
    {
10422
        if (isset($this->items[$itemId]) && is_object($this->items[$itemId])) {
10423
            return $this->items[$itemId];
10424
        }
10425
10426
        return false;
10427
    }
10428
10429
    /**
10430
     * @return int
10431
     */
10432
    public function getCategoryId()
10433
    {
10434
        return $this->categoryId;
10435
    }
10436
10437
    /**
10438
     * @param int $categoryId
10439
     * @return bool
10440
     */
10441
    public function setCategoryId($categoryId)
10442
    {
10443
        $this->categoryId = intval($categoryId);
10444
10445
        $courseId = api_get_course_int_id();
10446
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
10447
        $lp_id = $this->get_id();
10448
        $sql = "UPDATE $lp_table SET category_id = ".$this->categoryId."
10449
                WHERE c_id = $courseId AND id = $lp_id";
10450
        Database::query($sql);
10451
10452
        return true;
10453
    }
10454
10455
    /**
10456
     * Get whether this is a learning path with the possibility to subscribe
10457
     * users or not
10458
     * @return int
10459
     */
10460
    public function getSubscribeUsers()
10461
    {
10462
        return $this->subscribeUsers;
10463
    }
10464
10465
    /**
10466
     * Set whether this is a learning path with the possibility to subscribe
10467
     * users or not
10468
     * @param int $subscribeUsers (0 = false, 1 = true)
0 ignored issues
show
Bug introduced by
There is no parameter named $subscribeUsers. Was it maybe removed?

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

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

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

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

Loading history...
10469
     */
10470
    public function setSubscribeUsers($value)
10471
    {
10472
        if ($this->debug > 0) {
10473
            error_log('New LP - In learnpath::set_subscribe_users()', 0);
10474
        }
10475
        $this->subscribeUsers = intval($value);;
10476
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
10477
        $lp_id = $this->get_id();
10478
        $sql = "UPDATE $lp_table SET subscribe_users = ".$this->subscribeUsers."
10479
                WHERE c_id = ".$this->course_int_id." AND id = $lp_id";
10480
        Database::query($sql);
10481
10482
        return true;
10483
    }
10484
10485
    /**
10486
     * Calculate the count of stars for a user in this LP
10487
     * This calculation is based on the following rules:
10488
     * - the student gets one star when he gets to 50% of the learning path
10489
     * - the student gets a second star when the average score of all tests inside the learning path >= 50%
10490
     * - the student gets a third star when the average score of all tests inside the learning path >= 80%
10491
     * - the student gets the final star when the score for the *last* test is >= 80%
10492
     * @param int $sessionId Optional. The session ID
10493
     * @return int The count of stars
10494
     */
10495
    public function getCalculateStars($sessionId = 0)
10496
    {
10497
        $stars = 0;
10498
10499
        $progress = self::getProgress($this->lp_id, $this->user_id, $this->course_int_id, $sessionId);
10500
10501
        if ($progress >= 50) {
10502
            $stars++;
10503
        }
10504
10505
        // Calculate stars chapters evaluation
10506
        $exercisesItems = $this->getExercisesItems();
10507
10508
        if (!empty($exercisesItems)) {
10509
            $totalResult = 0;
10510
10511
            foreach ($exercisesItems as $exerciseItem) {
10512
                $exerciseResultInfo = Event::getExerciseResultsByUser(
10513
                    $this->user_id,
10514
                    $exerciseItem->path,
10515
                    $this->course_int_id,
10516
                    $sessionId,
10517
                    $this->lp_id,
10518
                    $exerciseItem->db_id
10519
                );
10520
10521
                $exerciseResultInfo = end($exerciseResultInfo);
10522
10523
                if (!$exerciseResultInfo) {
10524
                    continue;
10525
                }
10526
10527
                $exerciseResult = $exerciseResultInfo['exe_result'] * 100 / $exerciseResultInfo['exe_weighting'];
10528
10529
                $totalResult += $exerciseResult;
10530
            }
10531
10532
            $totalExerciseAverage = $totalResult / (count($exercisesItems) > 0 ? count($exercisesItems) : 1);
10533
10534
            if ($totalExerciseAverage >= 50) {
10535
                $stars++;
10536
            }
10537
10538
            if ($totalExerciseAverage >= 80) {
10539
                $stars++;
10540
            }
10541
        }
10542
10543
        // Calculate star for final evaluation
10544
        $finalEvaluationItem = $this->getFinalEvaluationItem();
10545
10546
        if (!empty($finalEvaluationItem)) {
10547
            $evaluationResultInfo = Event::getExerciseResultsByUser(
10548
                $this->user_id,
10549
                $finalEvaluationItem->path,
10550
                $this->course_int_id,
10551
                $sessionId,
10552
                $this->lp_id,
10553
                $finalEvaluationItem->db_id
10554
            );
10555
10556
            $evaluationResultInfo = end($evaluationResultInfo);
10557
10558
            if ($evaluationResultInfo) {
10559
                $evaluationResult = $evaluationResultInfo['exe_result'] * 100 / $evaluationResultInfo['exe_weighting'];
10560
10561
                if ($evaluationResult >= 80) {
10562
                    $stars++;
10563
                }
10564
            }
10565
        }
10566
10567
        return $stars;
10568
    }
10569
10570
    /**
10571
     * Get the items of exercise type
10572
     * @return array The items. Otherwise return false
10573
     */
10574 View Code Duplication
    public function getExercisesItems()
10575
    {
10576
        $exercises = [];
10577
10578
        foreach ($this->items as $item) {
10579
            if ($item->type != 'quiz') {
10580
                continue;
10581
            }
10582
10583
            $exercises[] = $item;
10584
        }
10585
10586
        array_pop($exercises);
10587
10588
        return $exercises;
10589
    }
10590
10591
    /**
10592
     * Get the item of exercise type (evaluation type)
10593
     * @return array The final evaluation. Otherwise return false
10594
     */
10595 View Code Duplication
    public function getFinalEvaluationItem()
10596
    {
10597
        $exercises = [];
10598
10599
        foreach ($this->items as $item) {
10600
            if ($item->type != 'quiz') {
10601
                continue;
10602
            }
10603
10604
            $exercises[] = $item;
10605
        }
10606
10607
        return array_pop($exercises);
10608
    }
10609
10610
    /**
10611
     * Calculate the total points achieved for the current user in this learning path
10612
     * @param int $sessionId Optional. The session Id
10613
     * @return int
10614
     */
10615
    public function getCalculateScore($sessionId = 0)
10616
    {
10617
        // Calculate stars chapters evaluation
10618
        $exercisesItems = $this->getExercisesItems();
10619
        $finalEvaluationItem = $this->getFinalEvaluationItem();
10620
10621
        $totalExercisesResult = 0;
10622
        $totalEvaluationResult = 0;
10623
10624
        if ($exercisesItems !== false) {
10625
            foreach ($exercisesItems as $exerciseItem) {
10626
                $exerciseResultInfo = Event::getExerciseResultsByUser(
10627
                    $this->user_id,
10628
                    $exerciseItem->path,
10629
                    $this->course_int_id,
10630
                    $sessionId,
10631
                    $this->lp_id,
10632
                    $exerciseItem->db_id
10633
                );
10634
10635
                $exerciseResultInfo = end($exerciseResultInfo);
10636
10637
                if (!$exerciseResultInfo) {
10638
                    continue;
10639
                }
10640
10641
                $totalExercisesResult += $exerciseResultInfo['exe_result'];
10642
            }
10643
        }
10644
10645
        if (!empty($finalEvaluationItem)) {
10646
            $evaluationResultInfo = Event::getExerciseResultsByUser(
10647
                $this->user_id,
10648
                $finalEvaluationItem->path,
10649
                $this->course_int_id,
10650
                $sessionId,
10651
                $this->lp_id,
10652
                $finalEvaluationItem->db_id
10653
            );
10654
10655
            $evaluationResultInfo = end($evaluationResultInfo);
10656
10657
            if ($evaluationResultInfo) {
10658
                $totalEvaluationResult += $evaluationResultInfo['exe_result'];
10659
            }
10660
        }
10661
10662
        return $totalExercisesResult + $totalEvaluationResult;
10663
    }
10664
10665
    /**
10666
     * Check if URL is not allowed to be show in a iframe
10667
     * @param string $src
10668
     *
10669
     * @return string
10670
     */
10671
    public function fixBlockedLinks($src)
10672
    {
10673
        $urlInfo = parse_url($src);
10674
        //$platformProtocol = api_get_protocol();
10675
10676
        $platformProtocol = 'https';
10677
        if (strpos(api_get_path(WEB_CODE_PATH), 'https') === false) {
10678
            $platformProtocol = 'http';
10679
        }
10680
10681
        $protocolFixApplied = false;
10682
        //Scheme validation to avoid "Notices" when the lesson doesn't contain a valid scheme
10683
        $scheme = isset($urlInfo['scheme']) ? $urlInfo['scheme'] : null;
10684
        if ($platformProtocol != $scheme) {
10685
            $_SESSION['x_frame_source'] = $src;
10686
            $src = 'blank.php?error=x_frames_options';
10687
            $protocolFixApplied = true;
10688
        }
10689
10690
        if ($protocolFixApplied == false) {
10691
            if (strpos($src, api_get_path(WEB_CODE_PATH)) === false) {
10692
                // Check X-Frame-Options
10693
                $ch = curl_init();
10694
10695
                $options = array(
10696
                    CURLOPT_URL => $src,
10697
                    CURLOPT_RETURNTRANSFER => true,
10698
                    CURLOPT_HEADER => true,
10699
                    CURLOPT_FOLLOWLOCATION => true,
10700
                    CURLOPT_ENCODING => "",
10701
                    CURLOPT_AUTOREFERER => true,
10702
                    CURLOPT_CONNECTTIMEOUT => 120,
10703
                    CURLOPT_TIMEOUT => 120,
10704
                    CURLOPT_MAXREDIRS => 10,
10705
                );
10706
                curl_setopt_array($ch, $options);
10707
                $response = curl_exec($ch);
10708
                $httpCode = curl_getinfo($ch);
10709
                $headers = substr($response, 0, $httpCode['header_size']);
10710
10711
                $error = false;
10712
                if (stripos($headers, 'X-Frame-Options: DENY') > -1
10713
                    //|| stripos($headers, 'X-Frame-Options: SAMEORIGIN') > -1
10714
                ) {
10715
                    $error = true;
10716
                }
10717
10718
                if ($error) {
10719
                    $_SESSION['x_frame_source'] = $src;
10720
                    $src = 'blank.php?error=x_frames_options';
10721
                }
10722
            }
10723
        }
10724
10725
        return $src;
10726
    }
10727
10728
    /**
10729
     * Check if this LP has a created forum in the basis course
10730
     * @return boolean
10731
     */
10732
    public function lpHasForum()
10733
    {
10734
        $forumTable = Database::get_course_table(TABLE_FORUM);
10735
        $itemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
10736
10737
        $fakeFrom = "
10738
            $forumTable f
10739
            INNER JOIN $itemProperty ip
10740
            ON (f.forum_id = ip.ref AND f.c_id = ip.c_id)
10741
        ";
10742
10743
        $resultData = Database::select(
10744
            'COUNT(f.iid) AS qty',
10745
            $fakeFrom,
10746
            [
10747
                'where' => [
10748
                    'ip.visibility != ? AND ' => 2,
10749
                    'ip.tool = ? AND ' => TOOL_FORUM,
10750
                    'f.c_id = ? AND ' => intval($this->course_int_id),
10751
                    'f.lp_id = ?' => intval($this->lp_id)
10752
                ]
10753
            ],
10754
            'first'
10755
        );
10756
10757
        if ($resultData['qty'] > 0) {
10758
            return true;
10759
        }
10760
10761
        return false;
10762
    }
10763
10764
    /**
10765
     * Get the forum for this learning path
10766
     * @return boolean
10767
     */
10768
    public function getForum($sessionId = 0)
10769
    {
10770
        $forumTable = Database::get_course_table(TABLE_FORUM);
10771
        $itemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
10772
10773
        $fakeFrom = "$forumTable f
10774
            INNER JOIN $itemProperty ip ";
10775
10776
        if ($this->lp_session_id == 0) {
10777
            $fakeFrom .= "
10778
                ON (
10779
                    f.forum_id = ip.ref AND f.c_id = ip.c_id AND (
10780
                        f.session_id = ip.session_id OR ip.session_id IS NULL
10781
                    )
10782
                )
10783
            ";
10784
        } else {
10785
            $fakeFrom .= "
10786
                ON (
10787
                    f.forum_id = ip.ref AND f.c_id = ip.c_id AND f.session_id = ip.session_id
10788
                )
10789
            ";
10790
        }
10791
10792
        $resultData = Database::select(
10793
            'f.*',
10794
            $fakeFrom,
10795
            [
10796
                'where' => [
10797
                    'ip.visibility != ? AND ' => 2,
10798
                    'ip.tool = ? AND ' => TOOL_FORUM,
10799
                    'f.session_id = ? AND ' => $sessionId,
10800
                    'f.c_id = ? AND ' => intval($this->course_int_id),
10801
                    'f.lp_id = ?' => intval($this->lp_id)
10802
                ]
10803
            ],
10804
            'first'
10805
        );
10806
10807
        if (empty($resultData)) {
10808
            return false;
10809
        }
10810
10811
        return $resultData;
10812
    }
10813
10814
    /**
10815
     * Create a forum for this learning path
10816
     * @param type $forumCategoryId
10817
     * @return int The forum ID if was created. Otherwise return false
10818
     */
10819
    public function createForum($forumCategoryId)
10820
    {
10821
        require_once api_get_path(SYS_CODE_PATH) . '/forum/forumfunction.inc.php';
10822
10823
        $forumId = store_forum(
10824
            [
10825
                'lp_id' => $this->lp_id,
10826
                'forum_title' => $this->name,
10827
                'forum_comment' => null,
10828
                'forum_category' => intval($forumCategoryId),
10829
                'students_can_edit_group' => ['students_can_edit' => 0],
10830
                'allow_new_threads_group' => ['allow_new_threads' => 0],
10831
                'default_view_type_group' => ['default_view_type' => 'flat'],
10832
                'group_forum' => 0,
10833
                'public_private_group_forum_group' => ['public_private_group_forum' => 'public']
10834
            ],
10835
            [],
10836
            true
10837
        );
10838
10839
        return $forumId;
10840
    }
10841
10842
    /**
10843
     * Check and obtain the lp final item if exist
10844
     *
10845
     * @return array lp items
10846
     */
10847
    private function getFinalItem()
10848
    {
10849
10850
        if (empty($this->items)) {
10851
            return null;
10852
        }
10853
10854
        foreach ($this->items as $item) {
10855
            if ($item->type !== 'final_item') {
10856
                continue;
10857
            }
10858
10859
            return $item;
10860
        }
10861
    }
10862
10863
    /**
10864
     * Get the LP Final Item Template
10865
     *
10866
     * @return html
10867
     */
10868
    private function getFinalItemTemplate()
10869
    {
10870
        return file_get_contents(api_get_path(SYS_CODE_PATH) . 'newscorm/final_item_template/template.html');
10871
    }
10872
10873
    /**
10874
     * Get the LP Final Item Url
10875
     *
10876
     * @return String
10877
     */
10878
    private function getSavedFinalItem()
10879
    {
10880
        $finalItem = $this->getFinalItem();
10881
        $doc = DocumentManager::get_document_data_by_id($finalItem->path, $this->cc);
10882
10883
        return file_get_contents($doc['absolute_path']);
10884
    }
10885
10886
    /**
10887
     * Get the LP Final Item form
10888
     *
10889
     * @return html
10890
     */
10891
    public function getFinalItemForm()
10892
    {
10893
        $finalItem = $this->getFinalItem();
10894
        $title = '';
10895
        $content = '';
10896
10897
        if ($finalItem) {
10898
            $title = $finalItem->title;
10899
            $buttonText = get_lang('Save');
10900
            $content = $this->getSavedFinalItem();
10901
        } else {
10902
            $buttonText = get_lang('LPCreateDocument');
10903
            $content = $this->getFinalItemTemplate();
10904
        }
10905
10906
        $courseInfo = api_get_course_info();
10907
        $result = $this->generate_lp_folder($courseInfo);
10908
        $relative_path = api_substr($result['dir'], 1, strlen($result['dir']));
10909
        $relative_prefix = '../../';
10910
10911
        $editorConfig = [
10912
            'ToolbarSet' => 'LearningPathDocuments',
10913
            'Width' => '100%',
10914
            'Height' => '500',
10915
            'FullPage' => true,
10916
            'CreateDocumentDir' => $relative_prefix,
10917
            'CreateDocumentWebDir' => api_get_path(WEB_COURSE_PATH) . api_get_course_path() . '/document/',
10918
            'BaseHref' => api_get_path(WEB_COURSE_PATH) . api_get_course_path() . '/document/' . $relative_path
10919
        ];
10920
10921
        $url = api_get_self() . '?' . api_get_cidreq() . '&' . http_build_query([
10922
            'type' => 'document',
10923
            'lp_id' => $this->lp_id
10924
        ]);
10925
10926
        $form = new FormValidator('final_item', 'POST', $url);
10927
        $form->addText('title', get_lang('Title'));
10928
        $form->addButtonSave($buttonText);
10929
        $form->addHtml('<div class="alert alert-info">Variables :</br></br> <b>((certificate))</b> </br> <b>((skill))</b></div>');
10930
        $renderer = $form->defaultRenderer();
10931
        $renderer->setElementTemplate('<div class="editor-lp">&nbsp;{label}{element}</div>', 'content_lp');
10932
        $form->addHtmlEditor('content_lp', null, null, true, $editorConfig, true);
10933
        $form->addHidden('action', 'add_final_item');
10934
        $form->addHidden('path', isset($_SESSION['pathItem']) ? $_SESSION['pathItem'] : '');
10935
        $form->addHidden('previous', $this->get_last());
10936
10937
        $form->setDefaults(['title' => $title, 'content_lp' => $content]);
10938
10939
        if ($form->validate()) {
10940
            $values = $form->exportValues();
10941
10942
            $lastItemId = $this->get_last();
10943
10944
            if (!$finalItem) {
10945
                $documentId = $this->create_document($this->course_info, $values['content_lp'], $values['title']);
10946
                $this->add_item(
10947
                    0,
10948
                    $lastItemId,
10949
                    'final_item',
10950
                    $documentId,
10951
                    $values['title'],
10952
                    ''
10953
                );
10954
            } else {
10955
                $this->edit_document($this->course_info);
10956
            }
10957
        }
10958
10959
        return $form->returnForm();
10960
    }
10961
}
10962
10963
if (!function_exists('trim_value')) {
10964
    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 (L90-92) 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...
10965
        $value = trim($value);
10966
    }
10967
}
10968