Completed
Push — master ( 395485...bbad3a )
by Julito
43:12
created

Link::getLinksPerCategory()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 33
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 21
nc 1
nop 4
dl 0
loc 33
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CourseBundle\Entity\CLink;
5
6
/**
7
 * Function library for the links tool.
8
 *
9
 * This is a complete remake of the original link tool.
10
 * New features:
11
 * - Organize links into categories;
12
 * - favorites/bookmarks interface;
13
 * - move links up/down within a category;
14
 * - move categories up/down;
15
 * - expand/collapse all categories;
16
 * - add link to 'root' category => category-less link is always visible.
17
 *
18
 * @author Patrick Cool, complete remake (December 2003 - January 2004)
19
 * @author René Haentjens, CSV file import (October 2004)
20
 * @package chamilo.link
21
 */
22
class Link extends Model
23
{
24
    public $table;
25
    public $is_course_model = true;
26
    public $columns = array(
27
        'id',
28
        'c_id',
29
        'url',
30
        'title',
31
        'description',
32
        'category_id',
33
        'display_order',
34
        'on_homepage',
35
        'target',
36
        'session_id'
37
    );
38
    public $required = array('url', 'title');
39
    private $course;
40
41
    /**
42
     *
43
     */
44
    public function __construct()
45
    {
46
        $this->table = Database::get_course_table(TABLE_LINK);
47
    }
48
49
    /**
50
     * @param array $course
51
     */
52
    public function setCourse($course)
53
    {
54
        $this->course = $course;
55
    }
56
57
    /**
58
     * @return array
59
     */
60
    public function getCourse()
61
    {
62
        return !empty($this->course) ? $this->course : api_get_course_info();
63
    }
64
65
    /**
66
     * Organize the saving of a link, using the parent's save method and
67
     * updating the item_property table
68
     * @param array $params
69
     * @param boolean $show_query Whether to show the query in logs when
70
     * calling parent's save method
71
     *
72
     * @return bool True if link could be saved, false otherwise
73
     */
74
    public function save($params, $show_query = null)
75
    {
76
        $course_info = $this->getCourse();
77
        $courseId = $course_info['real_id'];
78
79
        $params['session_id'] = api_get_session_id();
80
        $params['category_id'] = isset($params['category_id']) ? $params['category_id'] : 0;
81
82
        $sql =  "SELECT MAX(display_order)
83
                FROM  ".$this->table."
84
                WHERE
85
                    c_id = $courseId AND
86
                    category_id = '" . intval($params['category_id'])."'";
87
        $result = Database:: query($sql);
88
        list ($orderMax) = Database:: fetch_row($result);
0 ignored issues
show
Bug introduced by
It seems like $result can be null; however, fetch_row() 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...
89
        $order = $orderMax + 1;
90
        $params['display_order'] = $order;
91
92
        $id = parent::save($params, $show_query);
0 ignored issues
show
Bug introduced by
It seems like $show_query defined by parameter $show_query on line 74 can also be of type null; however, Model::save() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

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

An additional type check may prevent trouble.

Loading history...
93
94
        if (!empty($id)) {
95
            // iid
96
            $sql = "UPDATE ".$this->table." SET id = iid WHERE iid = $id";
97
            Database:: query($sql);
98
99
            api_set_default_visibility($id, TOOL_LINK);
100
101
            api_item_property_update(
102
                $course_info,
103
                TOOL_LINK,
104
                $id,
105
                'LinkAdded',
106
                api_get_user_id()
107
            );
108
        }
109
110
        return $id;
111
    }
112
113
    /**
114
     * Update a link in the database
115
     * @param int $linkId The ID of the link to update
116
     * @param string $linkUrl The new URL to be saved
117
     * @param int   $courseId
118
     * @param int   $sessionId
119
     * @return bool
120
     */
121
    public function updateLink(
122
        $linkId,
123
        $linkUrl,
124
        $courseId = null,
125
        $sessionId = null
126
    ) {
127
        $tblLink = Database:: get_course_table(TABLE_LINK);
128
        $linkUrl = Database::escape_string($linkUrl);
129
        $linkId = intval($linkId);
130
        if (is_null($courseId)) {
131
            $courseId = api_get_course_int_id();
132
        }
133
        $courseId = intval($courseId);
134
        if (is_null($sessionId)) {
135
            $sessionId = api_get_session_id();
136
        }
137
        $sessionId = intval($sessionId);
138
        if ($linkUrl != '') {
139
            $sql = "UPDATE $tblLink SET 
140
                    url = '$linkUrl'
141
                    WHERE id = $linkId AND c_id = $courseId AND session_id = $sessionId";
142
            $resLink = Database::query($sql);
143
144
            return $resLink;
145
        }
146
147
        return false;
148
    }
149
150
    /**
151
     * Used to add a link or a category
152
     * @param string $type , "link" or "category"
153
     * @todo replace strings by constants
154
     * @author Patrick Cool <[email protected]>, Ghent University
155
     * @return bool True on success, false on failure
156
     */
157
    public static function addlinkcategory($type)
158
    {
159
        $ok = true;
160
        $_course = api_get_course_info();
161
        $course_id = $_course['real_id'];
162
        $session_id = api_get_session_id();
163
164
        if ($type == 'link') {
165
            $title = Security::remove_XSS(stripslashes($_POST['title']));
166
            $urllink = Security::remove_XSS($_POST['url']);
167
            $description = Security::remove_XSS($_POST['description']);
168
            $selectcategory = Security::remove_XSS($_POST['category_id']);
169
170
            if (!isset($_POST['on_homepage'])) {
171
                $onhomepage = 0;
172
            } else {
173
                $onhomepage = Security::remove_XSS($_POST['on_homepage']);
174
            }
175
176
            if (empty($_POST['target'])) {
177
                $target = '_self'; // Default target.
178
            } else {
179
                $target = Security::remove_XSS($_POST['target']);
180
            }
181
182
            $urllink = trim($urllink);
183
            $title = trim($title);
184
            $description = trim($description);
185
186
            // We ensure URL to be absolute.
187
            if (strpos($urllink, '://') === false) {
188
                $urllink = 'http://' . $urllink;
189
            }
190
191
            // If the title is empty, we use the URL as title.
192
            if ($title == '') {
193
                $title = $urllink;
194
            }
195
196
            // If the URL is invalid, an error occurs.
197
            if (!api_valid_url($urllink, true)) {
198
                // A check against an absolute URL
199
                Display::addFlash(Display::return_message(get_lang('GiveURL'), 'error'));
200
201
                return false;
202
            } else {
203
                // Looking for the largest order number for this category.
204
205
                $link = new Link();
206
                $params = [
207
                    'c_id' => $course_id,
208
                    'url' => $urllink,
209
                    'title' => $title,
210
                    'description' => $description ,
211
                    'category_id' => $selectcategory,
212
                    'on_homepage' => $onhomepage,
213
                    'target' => $target,
214
                    'session_id' => $session_id,
215
                ];
216
                $link_id = $link->save($params);
217
218
                if ((api_get_setting('search_enabled') == 'true') &&
219
                    $link_id && extension_loaded('xapian')
220
                ) {
221
                    require_once api_get_path(LIBRARY_PATH) . 'search/ChamiloIndexer.class.php';
222
                    require_once api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php';
223
                    require_once api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php';
224
225
                    $course_int_id = $_course['real_id'];
226
                    $courseCode = $_course['code'];
227
                    $specific_fields = get_specific_field_list();
228
                    $ic_slide = new IndexableChunk();
229
230
                    // Add all terms to db.
231
                    $all_specific_terms = '';
232 View Code Duplication
                    foreach ($specific_fields as $specific_field) {
233
                        if (isset($_REQUEST[$specific_field['code']])) {
234
                            $sterms = trim($_REQUEST[$specific_field['code']]);
235
                            if (!empty($sterms)) {
236
                                $all_specific_terms .= ' ' . $sterms;
237
                                $sterms = explode(',', $sterms);
238
                                foreach ($sterms as $sterm) {
239
                                    $ic_slide->addTerm(
240
                                        trim($sterm),
241
                                        $specific_field['code']
242
                                    );
243
                                    add_specific_field_value(
244
                                        $specific_field['id'],
245
                                        $courseCode,
246
                                        TOOL_LINK,
247
                                        $link_id,
248
                                        $sterm
249
                                    );
250
                                }
251
                            }
252
                        }
253
                    }
254
255
                    // Build the chunk to index.
256
                    $ic_slide->addValue('title', $title);
257
                    $ic_slide->addCourseId($courseCode);
258
                    $ic_slide->addToolId(TOOL_LINK);
259
                    $xapian_data = array(
260
                        SE_COURSE_ID => $courseCode,
261
                        SE_TOOL_ID => TOOL_LINK,
262
                        SE_DATA => array(
263
                            'link_id' => (int)$link_id
264
                        ),
265
                        SE_USER => (int)api_get_user_id(),
266
                    );
267
                    $ic_slide->xapian_data = serialize($xapian_data);
268
                    $description = $all_specific_terms . ' ' . $description;
269
                    $ic_slide->addValue('content', $description);
270
271
                    // Add category name if set.
272 View Code Duplication
                    if (isset($selectcategory) && $selectcategory > 0) {
273
                        $table_link_category = Database:: get_course_table(
274
                            TABLE_LINK_CATEGORY
275
                        );
276
                        $sql_cat = 'SELECT * FROM %s WHERE id=%d AND c_id = %d LIMIT 1';
277
                        $sql_cat = sprintf(
278
                            $sql_cat,
279
                            $table_link_category,
280
                            (int)$selectcategory,
281
                            $course_int_id
282
                        );
283
                        $result = Database:: query($sql_cat);
284
                        if (Database:: num_rows($result) == 1) {
285
                            $row = Database:: fetch_array($result);
286
                            $ic_slide->addValue(
287
                                'category',
288
                                $row['category_title']
289
                            );
290
                        }
291
                    }
292
293
                    $di = new ChamiloIndexer();
294
                    isset($_POST['language']) ? $lang = Database:: escape_string(
295
                        $_POST['language']
296
                    ) : $lang = 'english';
297
                    $di->connectDb(null, null, $lang);
298
                    $di->addChunk($ic_slide);
299
300
                    // Index and return search engine document id.
301
                    $did = $di->index();
302
                    if ($did) {
303
                        // Save it to db.
304
                        $tbl_se_ref = Database:: get_main_table(
305
                            TABLE_MAIN_SEARCH_ENGINE_REF
306
                        );
307
                        $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
308
                                VALUES (NULL , \'%s\', \'%s\', %s, %s)';
309
                        $sql = sprintf(
310
                            $sql,
311
                            $tbl_se_ref,
312
                            $course_int_id,
313
                            $courseCode,
314
                            TOOL_LINK,
315
                            $link_id,
316
                            $did
317
                        );
318
                        Database:: query($sql);
319
                    }
320
                }
321
                Display::addFlash(Display::return_message(get_lang('LinkAdded')));
322
            }
323
        } elseif ($type == 'category') {
324
            $tbl_categories = Database:: get_course_table(TABLE_LINK_CATEGORY);
325
326
            $category_title = trim($_POST['category_title']);
327
            $description = trim($_POST['description']);
328
329
            if (empty($category_title)) {
330
                Display:: display_error_message(get_lang('GiveCategoryName'));
0 ignored issues
show
Deprecated Code introduced by
The method Display::display_error_message() has been deprecated with message: use Display::addFlash with Display::return_message

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...
331
                $ok = false;
332
            } else {
333
                // Looking for the largest order number for this category.
334
                $result = Database:: query(
335
                    "SELECT MAX(display_order) FROM  $tbl_categories
336
                    WHERE c_id = $course_id "
337
                );
338
                list ($orderMax) = Database:: fetch_row($result);
0 ignored issues
show
Bug introduced by
It seems like $result can be null; however, fetch_row() 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...
339
                $order = $orderMax + 1;
340
                $order = intval($order);
341
                $session_id = api_get_session_id();
342
343
                $params = [
344
                    'c_id' => $course_id,
345
                    'category_title' => $category_title,
346
                    'description' => $description,
347
                    'display_order' => $order,
348
                    'session_id' => $session_id
349
                ];
350
                $linkId = Database::insert($tbl_categories, $params);
351
352
                if ($linkId) {
353
                    // iid
354
                    $sql = "UPDATE $tbl_categories SET id = iid WHERE iid = $linkId";
355
                    Database:: query($sql);
356
357
                    // add link_category visibility
358
                    // course ID is taken from context in api_set_default_visibility
359
                    api_set_default_visibility($linkId, TOOL_LINK_CATEGORY);
360
                }
361
362
                Display::addFlash(Display::return_message(get_lang('CategoryAdded')));
363
            }
364
        }
365
366
        return $ok;
367
    }
368
369
    /**
370
     * Used to delete a link or a category
371
     * @author Patrick Cool <[email protected]>, Ghent University
372
     * @param int $id
373
     * @param string $type The type of item to delete
374
     * @return bool
375
     */
376
    public static function deletelinkcategory($id, $type)
377
    {
378
        $courseInfo = api_get_course_info();
379
        $tbl_link = Database:: get_course_table(TABLE_LINK);
380
        $tbl_categories = Database:: get_course_table(TABLE_LINK_CATEGORY);
381
382
        $course_id = $courseInfo['real_id'];
383
        $id = intval($id);
384
385
        if (empty($id)) {
386
            return false;
387
        }
388
389
        $result = false;
390
391
        switch ($type) {
392
            case 'link':
393
                // -> Items are no longer physically deleted,
394
                // but the visibility is set to 2 (in item_property).
395
                // This will make a restore function possible for the platform administrator.
396
                $sql = "UPDATE $tbl_link SET on_homepage='0'
397
                        WHERE c_id = $course_id AND id='" . $id . "'";
398
                Database:: query($sql);
399
400
                api_item_property_update(
401
                    $courseInfo,
402
                    TOOL_LINK,
403
                    $id,
404
                    'delete',
405
                    api_get_user_id()
406
                );
407
                self::delete_link_from_search_engine(api_get_course_id(), $id);
408
                Display:: display_confirmation_message(get_lang('LinkDeleted'));
0 ignored issues
show
Deprecated Code introduced by
The method Display::display_confirmation_message() has been deprecated with message: use Display::addFlash with Display::return_message

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...
409
                $result = true;
410
                break;
411
            case 'category':
412
                // First we delete the category itself and afterwards all the links of this category.
413
                $sql = "DELETE FROM " . $tbl_categories . "
414
                        WHERE c_id = $course_id AND id='" . $id . "'";
415
                Database:: query($sql);
416
417
                $sql = "DELETE FROM " . $tbl_link . "
418
                        WHERE c_id = $course_id AND category_id='" . $id . "'";
419
                Database:: query($sql);
420
421
                api_item_property_update(
422
                    $courseInfo,
423
                    TOOL_LINK_CATEGORY,
424
                    $id,
425
                    'delete',
426
                    api_get_user_id()
427
                );
428
429
                Display::addFlash(Display::return_message(get_lang('CategoryDeleted')));
430
                $result = true;
431
                break;
432
        }
433
434
        return $result;
435
    }
436
437
    /**
438
     * Removes a link from search engine database
439
     * @param string $course_id Course code
440
     * @param int $link_id Document id to delete
441
     * @return void
442
     */
443 View Code Duplication
    public static function delete_link_from_search_engine($course_id, $link_id)
444
    {
445
        // Remove from search engine if enabled.
446
        if (api_get_setting('search_enabled') === 'true') {
447
            $tbl_se_ref = Database:: get_main_table(
448
                TABLE_MAIN_SEARCH_ENGINE_REF
449
            );
450
            $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
451
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_LINK, $link_id);
452
            $res = Database:: query($sql);
453
            if (Database:: num_rows($res) > 0) {
454
                $row = Database:: fetch_array($res);
455
                require_once api_get_path(LIBRARY_PATH) . 'search/ChamiloIndexer.class.php';
456
                $di = new ChamiloIndexer();
457
                $di->remove_document((int)$row['search_did']);
458
            }
459
            $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
460
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_LINK, $link_id);
461
            Database:: query($sql);
462
463
            // Remove terms from db.
464
            require_once api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php';
465
            delete_all_values_for_item($course_id, TOOL_DOCUMENT, $link_id);
466
        }
467
    }
468
469
    /**
470
     *
471
     * Get link info
472
     * @param int link id
473
     * @return array link info
474
     *
475
     **/
476 View Code Duplication
    public static function get_link_info($id)
477
    {
478
        $tbl_link = Database:: get_course_table(TABLE_LINK);
479
        $course_id = api_get_course_int_id();
480
481
        if (empty($id) || empty($course_id)) {
482
            return [];
483
        }
484
485
        $sql = "SELECT * FROM $tbl_link
486
                WHERE c_id = $course_id AND id='" . intval($id) . "' ";
487
        $result = Database::query($sql);
488
        $data = array();
489
        if (Database::num_rows($result)) {
490
            $data = Database::fetch_array($result);
491
        }
492
        return $data;
493
    }
494
495
    /**
496
     * @param int $id
497
     * @param array $values
498
     */
499
    public static function editLink($id, $values = array())
500
    {
501
        $tbl_link = Database:: get_course_table(TABLE_LINK);
502
        $_course = api_get_course_info();
503
        $course_id = $_course['real_id'];
504
        $id = intval($id);
505
506
        $values['url'] = trim($values['url']);
507
        $values['title'] = trim($values['title']);
508
        $values['description'] = trim($values['description']);
509
        $values['target'] = empty($values['target']) ? '_self' : $values['target'];
510
        $values['on_homepage'] = isset($values['on_homepage']) ? $values['on_homepage'] : '';
511
512
        $categoryId = intval($values['category_id']);
513
514
        // We ensure URL to be absolute.
515
        if (strpos($values['url'], '://') === false) {
516
            $values['url'] = 'http://' . $_POST['url'];
517
        }
518
519
        // If the title is empty, we use the URL as title.
520
        if ($values['title'] == '') {
521
            $values['title'] = $values['url'];
522
        }
523
524
        // If the URL is invalid, an error occurs.
525
        if (!api_valid_url($values['url'], true)) {
526
            Display::addFlash(
527
                Display::return_message(get_lang('GiveURL'), 'error')
528
            );
529
530
            return false;
531
        }
532
533
        if (empty($id) || empty($course_id)) {
534
            return false;
535
        }
536
537
        // Finding the old category_id.
538
        $sql = "SELECT * FROM $tbl_link
539
                WHERE c_id = $course_id AND id='" . $id . "'";
540
        $result = Database:: query($sql);
541
        $row = Database:: fetch_array($result);
542
        $category_id = $row['category_id'];
543
544
        if ($category_id != $values['category_id']) {
545
            $sql = "SELECT MAX(display_order)
546
                    FROM $tbl_link 
547
                    WHERE
548
                        c_id = $course_id AND
549
                        category_id='" . intval($values['category_id']) . "'";
550
            $result = Database:: query($sql);
551
            list ($max_display_order) = Database:: fetch_row($result);
0 ignored issues
show
Bug introduced by
It seems like $result can be null; however, fetch_row() 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...
552
            $max_display_order++;
553
        } else {
554
            $max_display_order = $row['display_order'];
555
        }
556
        $params = [
557
            'url' => $values['url'],
558
            'title' => $values['title'],
559
            'description' => $values['description'],
560
            'category_id' => $values['category_id'],
561
            'display_order' => $max_display_order,
562
            'on_homepage' => $values['on_homepage'],
563
            'target' => $values['target'],
564
            'category_id' => $values['category_id']
565
        ];
566
567
        Database::update(
568
            $tbl_link,
569
            $params,
570
            ['c_id = ? AND id = ?' => [$course_id, $id]]
571
        );
572
573
        // Update search enchine and its values table if enabled.
574
        if (api_get_setting('search_enabled') == 'true') {
575
            $course_int_id = api_get_course_int_id();
576
            $course_id = api_get_course_id();
577
            $link_title = Database:: escape_string($values['title']);
578
            $link_description = Database:: escape_string($values['description']);
579
580
            // Actually, it consists on delete terms from db,
581
            // insert new ones, create a new search engine document, and remove the old one.
582
            // Get search_did.
583
            $tbl_se_ref = Database:: get_main_table(
584
                TABLE_MAIN_SEARCH_ENGINE_REF
585
            );
586
            $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
587
            $sql = sprintf(
588
                $sql,
589
                $tbl_se_ref,
590
                $course_id,
591
                TOOL_LINK,
592
                $id
593
            );
594
            $res = Database:: query($sql);
595
596
            if (Database:: num_rows($res) > 0) {
597
                require_once api_get_path(LIBRARY_PATH).'search/ChamiloIndexer.class.php';
598
                require_once api_get_path(LIBRARY_PATH).'search/IndexableChunk.class.php';
599
                require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
600
601
                $se_ref = Database:: fetch_array($res);
602
                $specific_fields = get_specific_field_list();
603
                $ic_slide = new IndexableChunk();
604
605
                $all_specific_terms = '';
606
                foreach ($specific_fields as $specific_field) {
607
                    delete_all_specific_field_value(
608
                        $course_id,
609
                        $specific_field['id'],
610
                        TOOL_LINK,
611
                        $id
612
                    );
613
                    if (isset($_REQUEST[$specific_field['code']])) {
614
                        $sterms = trim(
615
                            $_REQUEST[$specific_field['code']]
616
                        );
617
                        if (!empty($sterms)) {
618
                            $all_specific_terms .= ' ' . $sterms;
619
                            $sterms = explode(',', $sterms);
620
                            foreach ($sterms as $sterm) {
621
                                $ic_slide->addTerm(
622
                                    trim($sterm),
623
                                    $specific_field['code']
624
                                );
625
                                add_specific_field_value(
626
                                    $specific_field['id'],
627
                                    $course_id,
628
                                    TOOL_LINK,
629
                                    $id,
630
                                    $sterm
631
                                );
632
                            }
633
                        }
634
                    }
635
                }
636
637
                // Build the chunk to index.
638
                $ic_slide->addValue("title", $link_title);
639
                $ic_slide->addCourseId($course_id);
640
                $ic_slide->addToolId(TOOL_LINK);
641
                $xapian_data = array(
642
                    SE_COURSE_ID => $course_id,
643
                    SE_TOOL_ID => TOOL_LINK,
644
                    SE_DATA => array(
645
                        'link_id' => (int)$id
646
                    ),
647
                    SE_USER => (int)api_get_user_id(),
648
649
                );
650
                $ic_slide->xapian_data = serialize($xapian_data);
651
                $link_description = $all_specific_terms . ' ' . $link_description;
652
                $ic_slide->addValue('content', $link_description);
653
654
                // Add category name if set.
655 View Code Duplication
                if (isset($categoryId) && $categoryId > 0) {
656
                    $table_link_category = Database:: get_course_table(
657
                        TABLE_LINK_CATEGORY
658
                    );
659
                    $sql_cat = 'SELECT * FROM %s WHERE id=%d and c_id = %d LIMIT 1';
660
                    $sql_cat = sprintf(
661
                        $sql_cat,
662
                        $table_link_category,
663
                        $categoryId,
664
                        $course_int_id
665
                    );
666
                    $result = Database:: query($sql_cat);
667
                    if (Database:: num_rows($result) == 1) {
668
                        $row = Database:: fetch_array($result);
669
                        $ic_slide->addValue(
670
                            'category',
671
                            $row['category_title']
672
                        );
673
                    }
674
                }
675
676
                $di = new ChamiloIndexer();
677
                isset ($_POST['language']) ? $lang = Database:: escape_string($_POST['language']) : $lang = 'english';
678
                $di->connectDb(null, null, $lang);
679
                $di->remove_document((int)$se_ref['search_did']);
680
                $di->addChunk($ic_slide);
681
682
                // Index and return search engine document id.
683
                $did = $di->index();
684
                if ($did) {
685
                    // Save it to db.
686
                    $sql = 'DELETE FROM %s
687
                            WHERE course_code=\'%s\'
688
                            AND tool_id=\'%s\'
689
                            AND ref_id_high_level=\'%s\'';
690
                    $sql = sprintf(
691
                        $sql,
692
                        $tbl_se_ref,
693
                        $course_id,
694
                        TOOL_LINK,
695
                        $id
696
                    );
697
                    Database:: query($sql);
698
                    $sql = 'INSERT INTO %s (c_id, id, course_code, tool_id, ref_id_high_level, search_did)
699
                            VALUES (NULL , \'%s\', \'%s\', %s, %s)';
700
                    $sql = sprintf(
701
                        $sql,
702
                        $tbl_se_ref,
703
                        $course_int_id,
704
                        $course_id,
705
                        TOOL_LINK,
706
                        $id,
707
                        $did
708
                    );
709
                    Database:: query($sql);
710
                }
711
            }
712
        }
713
714
        // "WHAT'S NEW" notification: update table last_toolEdit.
715
        api_item_property_update(
716
            $_course,
717
            TOOL_LINK,
718
            $id,
719
            'LinkUpdated',
720
            api_get_user_id()
721
        );
722
        Display::addFlash(Display::return_message(get_lang('LinkModded')));
723
    }
724
725
    /**
726
     * @param int $id
727
     * @return bool
728
     */
729
    public static function editCategory($id, $values)
730
    {
731
        $tbl_categories = Database:: get_course_table(TABLE_LINK_CATEGORY);
732
        $course_id = api_get_course_int_id();
733
        $id = intval($id);
734
735
        // This is used to put the modified info of the category-form into the database.
736
        $params = [
737
            'category_title' => $values['category_title'],
738
            'description' => $values['description']
739
        ];
740
        Database::update(
741
            $tbl_categories,
742
            $params,
743
            ['c_id = ? AND id = ?' => [$course_id, $id]]
744
        );
745
        Display::addFlash(Display::return_message(get_lang('CategoryModded')));
746
747
        return true;
748
    }
749
750
    /**
751
     * Changes the visibility of a link
752
     * @todo add the changing of the visibility of a course
753
     * @author Patrick Cool <[email protected]>, Ghent University
754
     */
755
    public static function change_visibility_link($id, $scope)
756
    {
757
        $_course = api_get_course_info();
758
        $_user = api_get_user_info();
759
        if ($scope == TOOL_LINK) {
760
            api_item_property_update(
761
                $_course,
762
                TOOL_LINK,
763
                $id,
764
                $_GET['action'],
765
                $_user['user_id']
766
            );
767
            Display::addFlash(Display::return_message(get_lang('VisibilityChanged')));
768
769
        } elseif ($scope == TOOL_LINK_CATEGORY) {
770
            api_item_property_update(
771
                $_course,
772
                TOOL_LINK_CATEGORY,
773
                $id,
774
                $_GET['action'],
775
                $_user['user_id']
776
            );
777
            Display::addFlash(Display::return_message(get_lang('VisibilityChanged')));
778
        }
779
    }
780
781
    /**
782
     * Generate SQL to select all the links categories in the current course and
783
     * session
784
     * @param   int $courseId
785
     * @param   int $sessionId
786
     * @param   bool $withBaseContent
787
     *
788
     * @return array
789
     */
790
    public static function getLinkCategories($courseId, $sessionId, $withBaseContent = true)
791
    {
792
        $tblLinkCategory = Database:: get_course_table(TABLE_LINK_CATEGORY);
793
        $tblItemProperty = Database:: get_course_table(TABLE_ITEM_PROPERTY);
794
        $courseId = intval($courseId);
795
796
        // Condition for the session.
797
        $sessionCondition = api_get_session_condition(
798
            $sessionId,
799
            true,
800
            $withBaseContent,
801
            'linkcat.session_id'
802
        );
803
804
        // Getting links
805
        $sql = "SELECT *, linkcat.id
806
                FROM $tblLinkCategory linkcat
807
                WHERE
808
                    linkcat.c_id = " . $courseId . "
809
                    $sessionCondition
810
                ORDER BY linkcat.display_order DESC";
811
812
        $result = Database::query($sql);
813
        $categories = Database::store_result($result);
814
815
        $sql = "SELECT *, linkcat.id
816
                FROM $tblLinkCategory linkcat
817
                INNER JOIN $tblItemProperty ip
818
                ON (linkcat.id = ip.ref AND linkcat.c_id = ip.c_id)
819
                WHERE
820
                    ip.tool = '" . TOOL_LINK_CATEGORY . "' AND
821
                    (ip.visibility = '0' OR ip.visibility = '1')
822
                    $sessionCondition AND
823
                    linkcat.c_id = " . $courseId . "
824
                ORDER BY linkcat.display_order DESC";
825
826
        $result = Database::query($sql);
827
828
        $categoryInItemProperty = array();
829
        if (Database::num_rows($result)) {
830
            while ($row = Database::fetch_array($result, 'ASSOC')) {
831
                $categoryInItemProperty[$row['id']] = $row;
832
            }
833
        }
834
835
        foreach ($categories as & $category) {
836
            if (!isset($categoryInItemProperty[$category['id']])) {
837
                api_set_default_visibility($category['id'], TOOL_LINK_CATEGORY);
838
            }
839
        }
840
841
        $sql = "SELECT DISTINCT linkcat.*, visibility
842
                FROM $tblLinkCategory linkcat
843
                INNER JOIN $tblItemProperty ip
844
                ON (linkcat.id = ip.ref AND linkcat.c_id = ip.c_id)
845
                WHERE
846
                    ip.tool = '" . TOOL_LINK_CATEGORY . "' AND
847
                    (ip.visibility = '0' OR ip.visibility = '1')
848
                    $sessionCondition AND
849
                    linkcat.c_id = " . $courseId . "
850
                ORDER BY linkcat.display_order DESC
851
                ";
852
        $result = Database::query($sql);
853
854
        return Database::store_result($result, 'ASSOC');
855
    }
856
857
    /**
858
     * @param $categoryId
859
     * @param $courseId
860
     * @param $sessionId
861
     * @param bool $withBaseContent
862
     *
863
     * @return array
864
     */
865
    public static function getLinksPerCategory($categoryId, $courseId, $sessionId, $withBaseContent = true)
866
    {
867
        $tbl_link = Database:: get_course_table(TABLE_LINK);
868
        $TABLE_ITEM_PROPERTY = Database:: get_course_table(TABLE_ITEM_PROPERTY);
869
        $courseId = (int) $courseId;
870
        $sessionId = (int) $sessionId;
871
        $categoryId = (int) $categoryId;
872
873
        // Condition for the session.
874
        $condition_session = api_get_session_condition(
875
            $sessionId,
876
            true,
877
            $withBaseContent,
878
            'link.session_id'
879
        );
880
881
        $sql = "SELECT *, link.id 
882
                FROM $tbl_link link
883
                INNER JOIN $TABLE_ITEM_PROPERTY ip
884
                ON (link.id = ip.ref AND link.c_id = ip.c_id)
885
                WHERE
886
                    ip.tool = '" . TOOL_LINK . "' AND
887
                    link.category_id = '" . $categoryId . "' AND
888
                    (ip.visibility = '0' OR ip.visibility = '1')
889
                    $condition_session AND
890
                    link.c_id = $courseId AND
891
                    ip.c_id = $courseId 
892
                ORDER BY link.display_order ASC";
893
894
        $result = Database:: query($sql);
895
896
        return Database::store_result($result);
897
    }
898
899
    /**
900
     * Displays all the links of a given category.
901
     * @author Patrick Cool <[email protected]>, Ghent University
902
     * @author Julio Montoya
903
     *
904
     * @param $catid
905
     * @param $courseId
906
     * @param $session_id
907
     * @return string
908
     */
909
    public static function showLinksPerCategory($catid, $courseId, $session_id)
910
    {
911
        global $token;
912
        $_user = api_get_user_info();
913
        $catid = intval($catid);
914
915
        $links = self::getLinksPerCategory($catid, $courseId, $session_id);
916
        $content = '';
917
        $numberoflinks = count($links);
918
919
        if (!empty($links)) {
920
            $content .= '<div class="link list-group">';
921
            $i = 1;
922
            foreach ($links as $myrow) {
923
                // Validation when belongs to a session.
924
                $session_img = api_get_session_image(
925
                    $myrow['session_id'],
926
                    $_user['status']
927
                );
928
929
                $toolbar = '';
930
                $link_validator = '';
931
                if (api_is_allowed_to_edit(null, true)) {
932
                    $toolbar .= Display::toolbarButton(
933
                        '',
934
                        'javascript:void(0);',
935
                        'check-circle-o',
936
                        'default btn-sm',
937
                        array(
938
                            'onclick' => "check_url('" . $myrow['id'] . "', '" . addslashes($myrow['url']) . "');",
939
                            'title' => get_lang('CheckURL')
940
                        )
941
                    );
942
                    $link_validator .= Display::span(
943
                        '',
944
                        array(
945
                        'id' => 'url_id_' . $myrow['id'],
946
                        'class' => 'check-link'
947
                        )
948
                    );
949
950
                    if ($session_id == $myrow['session_id']) {
951
                        $url = api_get_self().'?' .api_get_cidreq().'&action=editlink&category=' .(!empty ($category) ? $category : '').
952
                            '&id=' . $myrow['id'] .
953
                            '&category_id=' . $myrow['id'];
954
                        $title = get_lang('Edit');
955
                        $toolbar .= Display::toolbarButton(
956
                            '',
957
                            $url,
958
                            'pencil',
959
                            'default btn-sm',
960
                            array(
961
                                'title' => $title
962
                            )
963
                        );
964
965 View Code Duplication
                        if ($myrow['visibility'] == '1') {
966
                            $url .= 'link.php?' . api_get_cidreq() .
967
                                '&sec_token=' . $token .
968
                                '&action=invisible&id=' . $myrow['id'] .
969
                                '&scope=link&category_id=' . $myrow['category_id'];
970
                            $title = get_lang('MakeInvisible');
971
                            $toolbar .= Display::toolbarButton(
972
                                '',
973
                                $url,
974
                                'eye',
975
                                'default btn-sm',
976
                                array(
977
                                    'title' => $title
978
                                )
979
                            );
980
                        }
981
982 View Code Duplication
                        if ($myrow['visibility'] == '0') {
983
                            $url .= 'link.php?' . api_get_cidreq() .'&sec_token=' . $token .'&action=visible&id=' . $myrow['id'] .'&scope=link&category_id=' . $myrow['category_id'];
984
                            $title = get_lang('MakeVisible');
985
                            $toolbar .= Display::toolbarButton(
986
                                '',
987
                                $url,
988
                                'eye-slash',
989
                                'primary btn-sm',
990
                                array(
991
                                    'title' => $title
992
                                )
993
                            );
994
                        }
995
996
                        $moveLinkParams = [
997
                            'id' => $myrow['id'],
998
                            'scope' => 'category',
999
                            'category_id' => $myrow['category_id'],
1000
                            'action' => 'move_link_up'
1001
                        ];
1002
1003
                        $toolbar .= Display::toolbarButton(
1004
                            get_lang('MoveUp'),
1005
                            'link.php?' . api_get_cidreq() . '&' . http_build_query($moveLinkParams),
1006
                            'level-up',
1007
                            'default',
1008
                            ['class' => 'btn-sm ' . ($i === 1 ? 'disabled' : '')],
1009
                            false
1010
                        );
1011
1012
                        $moveLinkParams['action'] = 'move_link_down';
1013
                        $toolbar .= Display::toolbarButton(
1014
                            get_lang('MoveDown'),
1015
                            'link.php?' . api_get_cidreq() . '&' . http_build_query($moveLinkParams),
1016
                            'level-down',
1017
                            'default',
1018
                            ['class' => 'btn-sm ' . ($i === $numberoflinks ? 'disabled' : '')],
1019
                            false
1020
                        );
1021
1022
                        $url .= api_get_self() . '?' . api_get_cidreq() .'&sec_token=' . $token .'&action=deletelink&id=' . $myrow['id'] .'&category_id=' . $myrow['category_id'];
1023
                        $event = "javascript: if(!confirm('" . get_lang('LinkDelconfirm') . "'))return false;";
1024
                        $title = get_lang('Delete');
1025
1026
                        $toolbar .= Display::toolbarButton(
1027
                            '',
1028
                            $url,
1029
                            'trash',
1030
                            'default btn-sm',
1031
                            array(
1032
                                'onclick' => $event,
1033
                                'title' => $title
1034
                            )
1035
                        );
1036
                    } else {
1037
                        $title = get_lang('EditionNotAvailableFromSession');
1038
                        $toolbar .= Display::toolbarButton(
1039
                            '',
1040
                            '#',
1041
                            'trash-o',
1042
                            'default btn-sm',
1043
                            array(
1044
                                'title' => $title
1045
                            )
1046
                        );
1047
                    }
1048
                }
1049
                $iconLink = Display::return_icon(
1050
                    'url.png',
1051
                    get_lang('Link'),
1052
                    null,
1053
                    ICON_SIZE_SMALL
1054
                );
1055
1056
                if ($myrow['visibility'] == '1') {
1057
                    $content .= '<div class="list-group-item">';
1058
                    $content .= '<div class="pull-right"><div class="btn-group">'.$toolbar.'</div></div>';
1059
                    $content .= '<h4 class="list-group-item-heading">';
1060
                    $content .= $iconLink;
1061
                    $url =  'link_goto.php?' . api_get_cidreq() .'&link_id=' . $myrow['id'] .'&link_url=' . urlencode($myrow['url']);
1062
                    $content .= Display::tag(
1063
                        'a',
1064
                        Security:: remove_XSS($myrow['title']),
1065
                        array(
1066
                            'href' => $url,
1067
                            'target' => $myrow['target']
1068
                        )
1069
                    );
1070
                    $content .= $link_validator;
1071
                    $content .= $session_img;
1072
                    $content .= '</h4>';
1073
1074
                    $content .= '<p class="list-group-item-text">' . $myrow['description'] . '</p>';
1075
                    $content .= '</div>';
1076
                } else {
1077
                    if (api_is_allowed_to_edit(null, true)) {
1078
                        $content .= '<div class="list-group-item">';
1079
                        $content .= '<div class="pull-right"><div class="btn-group">'.$toolbar.'</div></div>';
1080
                        $content .= '<h4 class="list-group-item-heading">';
1081
                        $content .= $iconLink;
1082
                        $url = 'link_goto.php?' . api_get_cidreq() .'&link_id=' . $myrow['id'] . "&link_url=" . urlencode($myrow['url']);
1083
                        $content .= Display::tag(
1084
                            'a',
1085
                            Security:: remove_XSS($myrow['title']),
1086
                            array(
1087
                                'href' => $url,
1088
                                'target' => '_blank',
1089
                                'class' => 'text-muted'
1090
                            )
1091
                        );
1092
                        $content .= $link_validator;
1093
                        $content .= $session_img;
1094
                        $content .= '</h4>';
1095
                        $content .= '<p class="list-group-item-text">' . $myrow['description'] . '</p>';
1096
                        $content .= '</div>';
1097
                    }
1098
                }
1099
                $i++;
1100
            }
1101
            $content .= '</div>';
1102
        }
1103
1104
        return $content;
1105
    }
1106
1107
    /**
1108
     * Displays the edit, delete and move icons
1109
     * @param int   Category ID
1110
     * @return string
1111
     *
1112
     * @author Patrick Cool <[email protected]>, Ghent University
1113
     */
1114
    public static function showCategoryAdminTools($category, $currentCategory, $countCategories)
1115
    {
1116
        $categoryId = $category['id'];
1117
        $token = null;
1118
        $tools = '<a href="' . api_get_self() . '?' . api_get_cidreq() . '&sec_token=' . $token . '&action=editcategory&id=' . $categoryId . '&category_id=' . $categoryId . '" title=' . get_lang('Modify') . '">' .
1119
            Display:: return_icon(
1120
                'edit.png',
1121
                get_lang('Modify'),
1122
                array(),
1123
                ICON_SIZE_SMALL
1124
            ) . '</a>';
1125
1126
        // DISPLAY MOVE UP COMMAND only if it is not the top link.
1127
        if ($currentCategory != 0) {
1128
            $tools .= '<a href="' . api_get_self() . '?' . api_get_cidreq() . '&sec_token=' . $token . '&action=up&up='.$categoryId.'&category_id='.$categoryId.'" title="'.get_lang('Up').'">'.
1129
                Display:: return_icon(
1130
                    'up.png',
1131
                    get_lang('Up'),
1132
                    array(),
1133
                    ICON_SIZE_SMALL
1134
                ) . '</a>';
1135
        } else {
1136
            $tools .= Display:: return_icon(
1137
                'up_na.png',
1138
                get_lang('Up'),
1139
                array(),
1140
                ICON_SIZE_SMALL
1141
            ) . '</a>';
1142
        }
1143
1144
        // DISPLAY MOVE DOWN COMMAND only if it is not the bottom link.
1145 View Code Duplication
        if ($currentCategory < $countCategories-1) {
1146
            $tools .= '<a href="' . api_get_self() . '?' . api_get_cidreq() .'&sec_token=' . $token .'&action=down&down=' . $categoryId .'&category_id=' . $categoryId . '">'.
1147
                Display:: return_icon(
1148
                    'down.png',
1149
                    get_lang('Down'),
1150
                    array(),
1151
                    ICON_SIZE_SMALL
1152
                ) . '</a>';
1153
        } else {
1154
            $tools .= Display:: return_icon(
1155
                    'down_na.png',
1156
                    get_lang('Down'),
1157
                    array(),
1158
                    ICON_SIZE_SMALL
1159
                ) . '</a>';
1160
        }
1161
1162
        $tools .= '<a href="' . api_get_self() . '?' . api_get_cidreq() .'&sec_token=' . $token .'&action=deletecategory&id='.$categoryId.            "&category_id=$categoryId\"
1163
            onclick=\"javascript: if(!confirm('" . get_lang('CategoryDelconfirm') . "')) return false;\">".
1164
            Display:: return_icon(
1165
                'delete.png',
1166
                get_lang('Delete'),
1167
                array(),
1168
                ICON_SIZE_SMALL
1169
            ) . '</a>';
1170
1171
        return $tools;
1172
    }
1173
1174
    /**
1175
     * move a link or a linkcategory up or down
1176
     * @param   int Category ID
1177
     * @param   int Course ID
1178
     * @param   int Session ID
1179
     * @author Patrick Cool <[email protected]>, Ghent University
1180
     * @todo support sessions
1181
     */
1182
    public static function movecatlink($action, $catlinkid, $courseId = null, $sessionId = null)
1183
    {
1184
        $tbl_categories = Database:: get_course_table(TABLE_LINK_CATEGORY);
1185
1186
        if (is_null($courseId)) {
1187
            $courseId = api_get_course_int_id();
1188
        }
1189
        $courseId = intval($courseId);
1190
        if (is_null($sessionId)) {
1191
            $sessionId = api_get_session_id();
1192
        }
1193
        $sessionId = intval($sessionId);
1194
        $thiscatlinkId = intval($catlinkid);
1195
1196
        if ($action == 'down') {
1197
            $sortDirection = 'DESC';
1198
        }
1199
1200
        if ($action == 'up') {
1201
            $sortDirection = 'ASC';
1202
        }
1203
1204
        $movetable = $tbl_categories;
1205
1206
        if (!empty($sortDirection)) {
1207 View Code Duplication
            if (!in_array(trim(strtoupper($sortDirection)), array('ASC', 'DESC'))) {
1208
                $sortDirection = 'ASC';
1209
            }
1210
1211
            $sql = "SELECT id, display_order FROM $movetable
1212
                    WHERE c_id = $courseId
1213
                    ORDER BY display_order $sortDirection";
1214
            $linkresult = Database:: query($sql);
1215
            $thislinkOrder = 1;
1216
            while ($sortrow = Database:: fetch_array($linkresult)) {
1217
                // STEP 2 : FOUND THE NEXT LINK ID AND ORDER, COMMIT SWAP
1218
                // This part seems unlogic, but it isn't . We first look for the current link with the querystring ID
1219
                // and we know the next iteration of the while loop is the next one. These should be swapped.
1220
                if (isset($thislinkFound) && $thislinkFound) {
1221
                    $nextlinkId = $sortrow['id'];
1222
                    $nextlinkOrder = $sortrow['display_order'];
1223
1224
                    Database:: query(
1225
                        "UPDATE " . $movetable . "
1226
                        SET display_order = '$nextlinkOrder'
1227
                        WHERE c_id = $courseId  AND id =  '$thiscatlinkId'"
1228
                    );
1229
                    Database:: query(
1230
                        "UPDATE " . $movetable . "
1231
                        SET display_order = '$thislinkOrder'
1232
                        WHERE c_id = $courseId  AND id =  '$nextlinkId'"
1233
                    );
1234
1235
                    break;
1236
                }
1237
                if ($sortrow['id'] == $thiscatlinkId) {
1238
                    $thislinkOrder = $sortrow['display_order'];
1239
                    $thislinkFound = true;
1240
                }
1241
            }
1242
        }
1243
1244
        Display::addFlash(Display::return_message(get_lang('LinkMoved')));
1245
    }
1246
1247
    /**
1248
     * CSV file import functions
1249
     * @author René Haentjens , Ghent University
1250
     */
1251
    public static function get_cat($catname)
1252
    {
1253
        // Get category id (existing or make new).
1254
        $tbl_categories = Database:: get_course_table(TABLE_LINK_CATEGORY);
1255
        $course_id = api_get_course_int_id();
1256
1257
        $result = Database:: query(
1258
            "SELECT id FROM " . $tbl_categories . "
1259
            WHERE c_id = $course_id AND category_title='" . Database::escape_string($catname) . "'"
1260
        );
1261
        if (Database:: num_rows($result) >= 1 && ($row = Database:: fetch_array($result))) {
1262
            return $row['id']; // Several categories with same name: take the first.
1263
        }
1264
1265
        $result = Database:: query(
1266
            "SELECT MAX(display_order) FROM " . $tbl_categories . " WHERE c_id = $course_id "
1267
        );
1268
        list ($max_order) = Database:: fetch_row($result);
0 ignored issues
show
Bug introduced by
It seems like $result can be null; however, fetch_row() 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...
1269
1270
        $params = [
1271
            'c_id' => $course_id,
1272
            'category_title' => $catname,
1273
            'description' => '',
1274
            'display_order' => $max_order + 1
1275
        ];
1276
        $id = Database::insert($tbl_categories, $params);
1277
1278
        return $id;
1279
    }
1280
1281
    /**
1282
     * CSV file import functions
1283
     * @author René Haentjens , Ghent University
1284
     */
1285
    public static function put_link($url, $cat, $title, $description, $on_homepage, $hidden)
1286
    {
1287
        $_course = api_get_course_info();
1288
        $_user = api_get_user_info();
1289
1290
        $tbl_link = Database:: get_course_table(TABLE_LINK);
1291
        $course_id = api_get_course_int_id();
1292
1293
        $urleq = "url='" . Database:: escape_string($url) . "'";
1294
        $cateq = "category_id=" . intval($cat);
1295
1296
        $result = Database:: query("
1297
            SELECT id FROM $tbl_link
1298
            WHERE c_id = $course_id AND " . $urleq . ' AND ' . $cateq
1299
        );
1300
1301
        if (Database:: num_rows($result) >= 1 && ($row = Database:: fetch_array($result))) {
1302
            $sql = "UPDATE $tbl_link SET 
1303
                        title = '" . Database:: escape_string($title) . "', 
1304
                        description = '" . Database:: escape_string($description) . "'
1305
                    WHERE c_id = $course_id AND  id='" . Database:: escape_string($row['id']) . "'";
1306
            Database:: query($sql);
1307
1308
            $ipu = 'LinkUpdated';
1309
            $rv = 1; // 1 = upd
1310
        } else {
1311
            // Add new link
1312
            $result = Database:: query(
1313
                "SELECT MAX(display_order) FROM  $tbl_link
1314
                WHERE c_id = $course_id AND category_id='" . intval($cat) . "'"
1315
            );
1316
            list ($max_order) = Database:: fetch_row($result);
0 ignored issues
show
Bug introduced by
It seems like $result can be null; however, fetch_row() 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...
1317
1318
            Database:: query(
1319
                "INSERT INTO $tbl_link (c_id, url, title, description, category_id, display_order, on_homepage)
1320
                VALUES (" . api_get_course_int_id() . ",
1321
                '" . Database:: escape_string($url) . "',
1322
                '" . Database:: escape_string($title) . "',
1323
                '" . Database:: escape_string($description) . "',
1324
                '" . intval($cat) . "','" . (intval($max_order) + 1) . "',
1325
                '" . intval($on_homepage) .
1326
                "')"
1327
            );
1328
1329
            $id = Database:: insert_id();
1330
            $ipu = 'LinkAdded';
1331
            $rv = 2; // 2 = new
1332
        }
1333
1334
        api_item_property_update(
1335
            $_course,
1336
            TOOL_LINK,
1337
            $id,
1338
            $ipu,
1339
            $_user['user_id']
1340
        );
1341
1342
        if ($hidden && $ipu == 'LinkAdded') {
1343
            api_item_property_update(
1344
                $_course,
1345
                TOOL_LINK,
1346
                $id,
1347
                'invisible',
1348
                $_user['user_id']
1349
            );
1350
        }
1351
1352
        return $rv;
1353
    }
1354
1355
    /**
1356
     * CSV file import functions
1357
     * @author René Haentjens , Ghent University
1358
     */
1359
    public static function import_link($linkdata)
1360
    {
1361
        // url, category_id, title, description, ...
1362
1363
        // Field names used in the uploaded file
1364
        $known_fields = array(
1365
            'url',
1366
            'category',
1367
            'title',
1368
            'description',
1369
            'on_homepage',
1370
            'hidden'
1371
        );
1372
1373
        $hide_fields = array(
1374
            'kw',
1375
            'kwd',
1376
            'kwds',
1377
            'keyword',
1378
            'keywords'
1379
        );
1380
1381
        // All other fields are added to description, as "name:value".
1382
1383
        // Only one hide_field is assumed to be present, <> is removed from value.
1384
1385
        if (!($url = trim($linkdata['url'])) || !($title = trim($linkdata['title']))) {
1386
            return 0; // 0 = fail
1387
        }
1388
1389
        $cat = ($catname = trim($linkdata['category'])) ? Link::get_cat($catname) : 0;
1390
1391
        $regs = array(); // Will be passed to ereg()
1392
        $d = '';
1393
        foreach ($linkdata as $key => $value) {
1394
            if (!in_array($key, $known_fields)) {
1395
                if (in_array($key, $hide_fields) && ereg(
1396
                        '^<?([^>]*)>?$',
1397
                        $value,
1398
                        $regs
1399
                    )
1400
                ) { // possibly in <...>
1401
                    if (($kwlist = trim($regs[1])) != '') {
1402
                        $kw = '<i kw="' . htmlspecialchars($kwlist) . '">';
1403
                    } else {
1404
                        $kw = '';
1405
                    }
1406
                    // i.e. assume only one of the $hide_fields will be present
1407
                    // and if found, hide the value as expando property of an <i> tag
1408
                } elseif (trim($value)) {
1409
                    $d .= ', ' . $key . ':' . $value;
1410
                }
1411
            }
1412
        }
1413
        if (!empty($d)) {
1414
            $d = substr($d, 2) . ' - ';
1415
        }
1416
1417
        return Link::put_link(
1418
            $url,
1419
            $cat,
1420
            $title,
1421
            $kw . ereg_replace(
1422
                '\[((/?(b|big|i|small|sub|sup|u))|br/)\]',
1423
                '<\\1>',
1424
                htmlspecialchars($d . $linkdata['description'])
1425
            ) . ($kw ? '</i>' : ''),
1426
            $linkdata['on_homepage'] ? '1' : '0',
1427
            $linkdata['hidden'] ? '1' : '0'
1428
        );
1429
        // i.e. allow some BBcode tags, e.g. [b]...[/b]
1430
    }
1431
1432
    /**
1433
     * This function checks if the url is a vimeo link
1434
     * @author Julio Montoya
1435
     * @version 1.0
1436
     */
1437
    public static function isVimeoLink($url)
1438
    {
1439
        $isLink = strrpos($url, "vimeo.com");
1440
1441
        return $isLink;
1442
    }
1443
1444
    /**
1445
     * Get vimeo id from URL
1446
     * @param string $url
1447
     * @return bool|mixed
1448
     */
1449
    public static function getVimeoLinkId($url)
1450
    {
1451
        $possibleUrls = array(
1452
            'http://www.vimeo.com/',
1453
            'http://vimeo.com/',
1454
            'https://www.vimeo.com/',
1455
            'https://vimeo.com/'
1456
        );
1457
        $url = str_replace($possibleUrls, '', $url);
1458
1459
        if (is_numeric($url)) {
1460
            return $url;
1461
        }
1462
        return false;
1463
    }
1464
1465
    /**
1466
     * This function checks if the url is a youtube link
1467
     * @author Jorge Frisancho
1468
     * @author Julio Montoya - Fixing code
1469
     * @version 1.0
1470
     */
1471
    public static function is_youtube_link($url)
1472
    {
1473
        $is_youtube_link = strrpos($url, "youtube") || strrpos(
1474
            $url,
1475
            "youtu.be"
1476
        );
1477
        return $is_youtube_link;
1478
    }
1479
1480
    /**
1481
     * Get youtube id from an URL
1482
     * @param string $url
1483
     * @return string
1484
     */
1485
    public static function get_youtube_video_id($url)
1486
    {
1487
        // This is the length of YouTube's video IDs
1488
        $len = 11;
1489
1490
        // The ID string starts after "v=", which is usually right after
1491
        // "youtube.com/watch?" in the URL
1492
        $pos = strpos($url, "v=");
1493
        $id = '';
1494
1495
        //If false try other options
1496
        if ($pos === false) {
1497
            $url_parsed = parse_url($url);
1498
1499
            //Youtube shortener
1500
            //http://youtu.be/ID
1501
            $pos = strpos($url, "youtu.be");
1502
1503
            if ($pos == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $pos of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
1504
                $id = '';
1505
            } else {
1506
                return substr($url_parsed['path'], 1);
1507
            }
1508
1509
            //if empty try the youtube.com/embed/ID
1510
            if (empty($id)) {
1511
                $pos = strpos($url, "embed");
1512
                if ($pos === false) {
1513
                    return '';
1514
                } else {
1515
                    return substr($url_parsed['path'], 7);
1516
                }
1517
            }
1518
        } else {
1519
            // Offset the start location to match the beginning of the ID string
1520
            $pos += 2;
1521
            // Get the ID string and return it
1522
            $id = substr($url, $pos, $len);
1523
            return $id;
1524
        }
1525
    }
1526
1527
    /**
1528
     * @param int $course_id
1529
     * @param int $session_id
1530
     * @param int $categoryId
1531
     * @param string $show
1532
     * @param null $token
1533
     */
1534
    public static function listLinksAndCategories($course_id, $session_id, $categoryId, $show = 'none', $token = null)
1535
    {
1536
        $tbl_link = Database::get_course_table(TABLE_LINK);
1537
        $categoryId = intval($categoryId);
1538
1539
        /*	Action Links */
1540
        echo '<div class="actions">';
1541
        if (api_is_allowed_to_edit(null, true)) {
1542
            echo '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=addlink&category_id='.$categoryId.'">'.
1543
                Display::return_icon('new_link.png', get_lang('LinkAdd'),'',ICON_SIZE_MEDIUM).'</a>';
1544
            echo '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=addcategory&category_id='.$categoryId.'">'.
1545
                Display::return_icon('new_folder.png', get_lang('CategoryAdd'),'',ICON_SIZE_MEDIUM).'</a>';
1546
        }
1547
1548
        $categories = Link::getLinkCategories($course_id, $session_id);
1549
        $count = count($categories);
1550
        if (!empty($count)) {
1551
            echo '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=list&show=none">';
1552
            echo Display::return_icon('forum_listview.png', get_lang('FlatView'), '', ICON_SIZE_MEDIUM) . ' </a>';
1553
1554
            echo '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=list&show=all">';
1555
            echo Display::return_icon('forum_nestedview.png', get_lang('NestedView'), '', ICON_SIZE_MEDIUM) . '</a>';
1556
        }
1557
        echo '</div>';
1558
1559
        // Displaying the links which have no category (thus category = 0 or NULL),
1560
        // if none present this will not be displayed
1561
        $sql = "SELECT * FROM $tbl_link
1562
                WHERE c_id = $course_id AND (category_id=0 OR category_id IS NULL)";
1563
        $result = Database::query($sql);
1564
        $count = Database::num_rows($result);
1565
1566
        if ($count !== 0) {
1567
            echo Display::panel(
1568
                Link::showLinksPerCategory(
1569
                    0,
1570
                    api_get_course_int_id(),
1571
                    api_get_session_id()
1572
                ),
1573
                get_lang('General')
1574
            );
1575
        }
1576
1577
        $counter = 0;
1578
        foreach ($categories as $myrow) {
1579
            // Student don't see invisible categories.
1580
            if (!api_is_allowed_to_edit(null, true)) {
1581
                if ($myrow['visibility'] == 0) {
1582
                    continue;
1583
                }
1584
            }
1585
1586
            // Validation when belongs to a session
1587
            $showChildren = $categoryId == $myrow['id'] || $show == 'all';
1588
            $myrow['description'] = $myrow['description'];
1589
1590
            $strVisibility = '';
1591
            $visibilityClass = null;
1592
            if ($myrow['visibility'] == '1') {
1593
                $strVisibility =  '<a href="link.php?' . api_get_cidreq() .  '&sec_token='.$token.'&action=invisible&id=' . $myrow['id'] . '&scope=' . TOOL_LINK_CATEGORY . '" title="' . get_lang('Hide') . '">' .
1594
                    Display :: return_icon('visible.png', get_lang('Hide'), array (), ICON_SIZE_SMALL) . '</a>';
1595
            } elseif ($myrow['visibility'] == '0') {
1596
                $visibilityClass = 'text-muted';
1597
                $strVisibility =  ' <a href="link.php?' . api_get_cidreq() .  '&sec_token='.$token.'&action=visible&id=' . $myrow['id'] . '&scope=' . TOOL_LINK_CATEGORY . '" title="' . get_lang('Show') . '">' .
1598
                    Display :: return_icon('invisible.png', get_lang('Show'), array (), ICON_SIZE_SMALL) . '</a>';
1599
            }
1600
1601
            $header = '';
1602
            if ($showChildren) {
1603
                $header .= '<a class="'.$visibilityClass.'" href="'.api_get_self().'?'.api_get_cidreq().'&category_id=">';
1604
                $header .= Display::return_icon('forum_nestedview.png');
1605
            } else {
1606
                $header .= '<a class="'.$visibilityClass.'" href="'.api_get_self().'?'.api_get_cidreq().'&category_id='.$myrow['id'].'">';
1607
                $header .= Display::return_icon('forum_listview.png');
1608
            }
1609
1610
            $header .= Security::remove_XSS($myrow['category_title']).'</a>';
1611
            $header .= '<div class="pull-right">';
1612
            if (api_is_allowed_to_edit(null, true)) {
1613
                if ($session_id == $myrow['session_id']) {
1614
                    $header .= $strVisibility;
1615
                    $header .= Link::showCategoryAdminTools($myrow, $counter, count($categories));
1616
                } else {
1617
                    $header .= get_lang('EditionNotAvailableFromSession');
1618
                }
1619
            }
1620
1621
            $childrenContent = '';
1622
            if ($showChildren) {
1623
                $childrenContent = Link::showLinksPerCategory(
1624
                    $myrow['id'],
1625
                    api_get_course_int_id(),
1626
                    api_get_session_id()
1627
                );
1628
            }
1629
1630
            echo Display::panel($myrow['description'].$childrenContent, $header);
1631
1632
            $counter++;
1633
        }
1634
    }
1635
1636
    /**
1637
     * @param int $linkId
1638
     * @param $action
1639
     * @param null $token
1640
     *
1641
     * @return FormValidator
1642
     */
1643
    public static function getLinkForm($linkId, $action, $token = null)
1644
    {
1645
        $course_id = api_get_course_int_id();
1646
        $session_id = api_get_session_id();
1647
        $linkInfo = Link::get_link_info($linkId);
1648
        $categoryId = isset($linkInfo['category_id']) ? $linkInfo['category_id'] : '';
1649
        $lpId = isset($_GET['lp_id']) ? Security::remove_XSS($_GET['lp_id']) : null;
1650
1651
        $form = new FormValidator(
1652
            'link',
1653
            'post',
1654
            api_get_self().'?action='.$action.
1655
            '&category_id='.$categoryId.
1656
            '&'.api_get_cidreq().
1657
            '&id='.$linkId.
1658
            '&sec_token='.$token
1659
        );
1660
1661
        if ($action == 'addlink') {
1662
            $form->addHeader(get_lang('LinkAdd'));
1663
        } else {
1664
            $form->addHeader(get_lang('LinkMod'));
1665
        }
1666
1667
        $target_link = '_blank';
1668
        $title = '';
1669
        $category = '';
1670
        $onhomepage = '';
1671
        $description = '';
1672
1673
        if (!empty($linkInfo)) {
1674
            $urllink = $linkInfo['url'];
1675
            $title = $linkInfo['title'];
1676
            $description = $linkInfo['description'];
1677
            $category = $linkInfo['category_id'];
1678
            if ($linkInfo['on_homepage'] != 0) {
1679
                $onhomepage = 1;
1680
            }
1681
            $target_link = $linkInfo['target'];
1682
        }
1683
1684
        $form->addHidden('id', $linkId);
1685
        $form->addText('url', 'URL');
1686
        $form->addRule('url', get_lang('GiveURL'), 'url');
1687
        $form->addText('title', get_lang('LinkName'));
1688
        $form->addTextarea('description', get_lang('Description'));
1689
1690
        $resultcategories = Link::getLinkCategories($course_id, $session_id);
1691
        $options = ['0' => '--'];
1692
        if (!empty($resultcategories)) {
1693
            foreach ($resultcategories as $myrow) {
1694
                $options[$myrow['id']] = $myrow['category_title'];
1695
            }
1696
        }
1697
1698
        $form->addSelect('category_id', get_lang('Category'), $options);
1699
        $form->addCheckBox('on_homepage', null, get_lang('OnHomepage'));
1700
1701
        $targets = array(
1702
            '_self' => get_lang('LinkOpenSelf'),
1703
            '_blank' => get_lang('LinkOpenBlank'),
1704
            '_parent' => get_lang('LinkOpenParent'),
1705
            '_top' => get_lang('LinkOpenTop')
1706
        );
1707
1708
        $form->addSelect(
1709
            'target',
1710
            array(
1711
                get_lang('LinkTarget'),
1712
                get_lang('AddTargetOfLinkOnHomepage')
1713
            ),
1714
            $targets
1715
        );
1716
1717
        $defaults = array(
1718
            'url' => empty($urllink) ? 'http://' : Security::remove_XSS($urllink),
1719
            'title' => Security::remove_XSS($title),
1720
            'category_id' => $category,
1721
            'on_homepage' => $onhomepage,
1722
            'description' => $description,
1723
            'target' => $target_link
1724
        );
1725
1726
        if (api_get_setting('search_enabled') == 'true') {
1727
            require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1728
            $specific_fields = get_specific_field_list();
1729
            $form->addCheckBox('index_document', get_lang('SearchFeatureDoIndexLink'), get_lang('Yes'));
1730
1731
            foreach ($specific_fields as $specific_field) {
1732
                $default_values = '';
1733
                if ($action == 'editlink') {
1734
                    $filter = array(
1735
                        'field_id' => $specific_field['id'],
1736
                        'ref_id' => intval($_GET['id']),
1737
                        'tool_id' => '\''.TOOL_LINK.'\''
1738
                    );
1739
                    $values = get_specific_field_values_list($filter, array('value'));
1740
                    if (!empty($values)) {
1741
                        $arr_str_values = array();
1742
                        foreach ($values as $value) {
1743
                            $arr_str_values[] = $value['value'];
1744
                        }
1745
                        $default_values = implode(', ', $arr_str_values);
1746
                    }
1747
                }
1748
                $form->addText($specific_field['name'], $specific_field['code']);
1749
                $defaults[$specific_field['name']] = $default_values;
1750
            }
1751
        }
1752
1753
        $form->addHidden('lp_id', $lpId);
1754
        $form->addButtonSave(get_lang('SaveLink'), 'submitLink');
1755
        $form->setDefaults($defaults);
1756
1757
        return $form;
1758
    }
1759
1760
    /**
1761
     * @param int $id
1762
     * @param string $action
1763
     *
1764
     * @return FormValidator
1765
     */
1766
    public static function getCategoryForm($id, $action)
1767
    {
1768
        $form = new FormValidator(
1769
            'category',
1770
            'post',
1771
            api_get_self().'?action='.$action.'&'.api_get_cidreq()
1772
        );
1773
1774
        $defaults = [];
1775
        if ($action == 'addcategory') {
1776
            $form->addHeader(get_lang('CategoryAdd'));
1777
            $my_cat_title = get_lang('CategoryAdd');
1778
        } else {
1779
            $form->addHeader(get_lang('CategoryMod'));
1780
            $my_cat_title = get_lang('CategoryMod');
1781
            $defaults = self::getCategory($id);
1782
        }
1783
        $form->addHidden('id', $id);
1784
        $form->addText('category_title', get_lang('CategoryName'));
1785
        $form->addTextarea('description', get_lang('Description'));
1786
        $form->addButtonSave($my_cat_title, 'submitCategory');
1787
        $form->setDefaults($defaults);
1788
1789
        return $form;
1790
    }
1791
1792
    /**
1793
     * @param int $id
1794
     * @return array
1795
     */
1796 View Code Duplication
    public static function getCategory($id)
1797
    {
1798
        $table = Database::get_course_table(TABLE_LINK_CATEGORY);
1799
        $id = intval($id);
1800
        $courseId = api_get_course_int_id();
1801
1802
        if (empty($id) || empty($courseId)) {
1803
            return [];
1804
        }
1805
        $sql = "SELECT * FROM $table 
1806
                WHERE id = $id AND c_id = $courseId";
1807
        $result = Database::query($sql);
1808
        $category = Database::fetch_array($result, 'ASSOC');
1809
1810
        return $category;
1811
    }
1812
1813
    /**
1814
     * Move a link inside its category (display_order field)
1815
     * @param int $id The link ID
1816
     * @param string $direction The direction to sort the links
1817
     * @return bool
1818
     */
1819
    private static function moveLinkDisplayOrder($id, $direction)
1820
    {
1821
        $em = Database::getManager();
1822
        /** @var CLink $link */
1823
        $link = $em->find('ChamiloCourseBundle:CLink', $id);
1824
1825
        if (!$link) {
1826
            return false;
1827
        }
1828
1829
        $compareLinks = $em
1830
            ->getRepository('ChamiloCourseBundle:CLink')
1831
            ->findBy(
1832
                [
1833
                    'cId' => $link->getCId(),
1834
                    'categoryId' => $link->getCategoryId()
1835
                ],
1836
                ['displayOrder' => $direction]
1837
            );
1838
1839
        /** @var CLink $prevLink */
1840
        $prevLink = null;
1841
1842
        /** @var CLink $compareLink */
1843
        foreach ($compareLinks as $compareLink) {
1844
            if ($compareLink->getId() !== $link->getId()) {
1845
                $prevLink = $compareLink;
1846
1847
                continue;
1848
            }
1849
1850
            if (!$prevLink) {
1851
                return false;
1852
            }
1853
1854
            $newPrevLinkDisplayOrder = $link->getDisplayOrder();
1855
            $newLinkDisplayOrder = $prevLink->getDisplayOrder();
1856
1857
            $link->setDisplayOrder($newLinkDisplayOrder);
1858
            $prevLink->setDisplayOrder($newPrevLinkDisplayOrder);
1859
1860
            $em->merge($prevLink);
1861
            $em->merge($link);
1862
            break;
1863
        }
1864
1865
        $em->flush();
1866
1867
        return true;
1868
    }
1869
1870
    /**
1871
     * Move a link up in its category
1872
     * @param int $id
1873
     * @return bool
1874
     */
1875
    public static function moveLinkUp($id)
1876
    {
1877
        return self::moveLinkDisplayOrder($id, 'ASC');
1878
    }
1879
1880
    /**
1881
     * Move a link down in its category
1882
     * @param int $id
1883
     * @return bool
1884
     */
1885
    public static function moveLinkDown($id)
1886
    {
1887
        return self::moveLinkDisplayOrder($id, 'DESC');
1888
    }
1889
}
1890