Passed
Push — master ( 164a3a...6f5f89 )
by Julito
13:30
created

Link::isPdfLink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\Resource\ResourceLink;
6
use Chamilo\CoreBundle\Framework\Container;
7
use Chamilo\CourseBundle\Entity\CLink;
8
use Chamilo\CourseBundle\Entity\CLinkCategory;
9
10
/**
11
 * Function library for the links tool.
12
 *
13
 * This is a complete remake of the original link tool.
14
 * New features:
15
 * - Organize links into categories;
16
 * - favorites/bookmarks interface;
17
 * - move links up/down within a category;
18
 * - move categories up/down;
19
 * - expand/collapse all categories;
20
 * - add link to 'root' category => category-less link is always visible.
21
 *
22
 * @author Patrick Cool (December 2003 - January 2004)
23
 * @author René Haentjens, CSV file import (October 2004)
24
 */
25
class Link extends Model
26
{
27
    public $table;
28
    public $is_course_model = true;
29
    public $columns = [
30
        'id',
31
        'c_id',
32
        'url',
33
        'title',
34
        'description',
35
        'category_id',
36
        'display_order',
37
        'on_homepage',
38
        'target',
39
        'session_id',
40
    ];
41
    public $required = ['url', 'title'];
42
    private $course;
43
44
    /**
45
     * Link constructor.
46
     */
47
    public function __construct()
48
    {
49
        $this->table = Database::get_course_table(TABLE_LINK);
50
    }
51
52
    /**
53
     * @param array $course
54
     */
55
    public function setCourse($course)
56
    {
57
        $this->course = $course;
58
    }
59
60
    /**
61
     * @return array
62
     */
63
    public function getCourse()
64
    {
65
        return !empty($this->course) ? $this->course : api_get_course_info();
66
    }
67
68
    /**
69
     * Organize the saving of a link, using the parent's save method and
70
     * updating the item_property table.
71
     *
72
     * @param array $params
73
     * @param bool  $show_query Whether to show the query in logs when
74
     *                          calling parent's save method
75
     *
76
     * @return bool True if link could be saved, false otherwise
77
     */
78
    public function save($params, $show_query = null)
79
    {
80
        $course_info = $this->getCourse();
81
        $courseId = $course_info['real_id'];
82
83
        $params['session_id'] = api_get_session_id();
84
        $params['category_id'] = isset($params['category_id']) ? $params['category_id'] : 0;
85
86
        $sql = "SELECT MAX(display_order)
87
                FROM  ".$this->table."
88
                WHERE
89
                    c_id = $courseId AND
90
                    category_id = '".intval($params['category_id'])."'";
91
        $result = Database:: query($sql);
92
        list($orderMax) = Database:: fetch_row($result);
93
        $order = $orderMax + 1;
94
        $params['display_order'] = $order;
95
96
        $id = parent::save($params, $show_query);
97
98
        if (!empty($id)) {
99
            // iid
100
            $sql = "UPDATE ".$this->table." SET id = iid WHERE iid = $id";
101
            Database::query($sql);
102
103
            api_item_property_update(
104
                $course_info,
105
                TOOL_LINK,
106
                $id,
107
                'LinkAdded',
108
                api_get_user_id()
109
            );
110
111
            api_set_default_visibility($id, TOOL_LINK);
112
        }
113
114
        return $id;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $id also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
115
    }
116
117
    /**
118
     * Update a link in the database.
119
     *
120
     * @param int    $linkId    The ID of the link to update
121
     * @param string $linkUrl   The new URL to be saved
122
     * @param int    $courseId
123
     * @param int    $sessionId
124
     *
125
     * @return bool
126
     */
127
    public function updateLink(
128
        $linkId,
129
        $linkUrl,
130
        $courseId = null,
131
        $sessionId = null
132
    ) {
133
        $tblLink = Database::get_course_table(TABLE_LINK);
134
        $linkUrl = Database::escape_string($linkUrl);
135
        $linkId = intval($linkId);
136
        if (is_null($courseId)) {
137
            $courseId = api_get_course_int_id();
138
        }
139
        $courseId = intval($courseId);
140
        if (is_null($sessionId)) {
141
            $sessionId = api_get_session_id();
142
        }
143
        $sessionId = intval($sessionId);
144
        if ($linkUrl != '') {
145
            $sql = "UPDATE $tblLink SET
146
                    url = '$linkUrl'
147
                    WHERE id = $linkId AND c_id = $courseId AND session_id = $sessionId";
148
            $resLink = Database::query($sql);
149
150
            return $resLink;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $resLink returns the type Doctrine\DBAL\Driver\Statement which is incompatible with the documented return type boolean.
Loading history...
151
        }
152
153
        return false;
154
    }
155
156
    public static function addCategory()
157
    {
158
        $_course = api_get_course_info();
159
        $course_id = $_course['real_id'];
160
        $tbl_categories = Database::get_course_table(TABLE_LINK_CATEGORY);
161
162
        $category_title = trim($_POST['category_title']);
163
        $description = trim($_POST['description']);
164
165
        if (empty($category_title)) {
166
            echo Display::return_message(get_lang('Please give the category name'), 'error');
167
            return false;
168
        }
169
170
        // Looking for the largest order number for this category.
171
        $result = Database:: query(
172
            "SELECT MAX(display_order) FROM  $tbl_categories
173
            WHERE c_id = $course_id "
174
        );
175
        list($orderMax) = Database:: fetch_row($result);
176
        $order = $orderMax + 1;
177
        $order = (int) $order;
178
        $session_id = api_get_session_id();
179
180
        $repo = Container::getLinkCategoryRepository();
181
        $courseEntity = api_get_course_entity($course_id);
182
        $sessionEntity = api_get_session_entity($session_id);
183
184
        $category = new CLinkCategory();
185
        $category
186
            ->setSessionId($session_id)
187
            ->setCId($course_id)
188
            ->setCategoryTitle($category_title)
189
            ->setDescription($description)
190
            ->setDisplayOrder($order)
191
        ;
192
193
        $repo->addResourceToCourse(
194
            $category,
195
            ResourceLink::VISIBILITY_PUBLISHED,
196
            api_get_user_entity(api_get_user_id()),
197
            $courseEntity,
198
            $sessionEntity,
199
            api_get_group_entity()
200
        );
201
202
        $repo->getEntityManager()->flush();
203
        $linkId = $category->getIid();
204
205
        if ($linkId) {
206
            // iid
207
            $sql = "UPDATE $tbl_categories SET id = iid WHERE iid = $linkId";
208
            Database:: query($sql);
209
210
            // add link_category visibility
211
            // course ID is taken from context in api_set_default_visibility
212
            //api_set_default_visibility($linkId, TOOL_LINK_CATEGORY);
213
            /*api_item_property_update(
214
                $_course,
215
                TOOL_LINK_CATEGORY,
216
                $linkId,
217
                'LinkCategoryAdded',
218
                api_get_user_id()
219
            );
220
            api_set_default_visibility($linkId, TOOL_LINK_CATEGORY);*/
221
        }
222
223
        Display::addFlash(Display::return_message(get_lang('Category added')));
224
225
        return $linkId;
226
    }
227
228
    public static function addLink()
229
    {
230
        $_course = api_get_course_info();
231
        $course_id = $_course['real_id'];
232
        $session_id = api_get_session_id();
233
234
        $title = Security::remove_XSS(stripslashes($_POST['title']));
235
        $urllink = Security::remove_XSS($_POST['url']);
236
        $description = Security::remove_XSS($_POST['description']);
237
        $selectcategory = Security::remove_XSS($_POST['category_id']);
238
239
        $onhomepage = 0;
240
        if (isset($_POST['on_homepage'])) {
241
            $onhomepage = Security::remove_XSS($_POST['on_homepage']);
242
        }
243
244
        $target = '_self'; // Default target.
245
        if (!empty($_POST['target'])) {
246
            $target = Security::remove_XSS($_POST['target']);
247
        }
248
249
        $urllink = trim($urllink);
250
        $title = trim($title);
251
        $description = trim($description);
252
253
        // We ensure URL to be absolute.
254
        if (strpos($urllink, '://') === false) {
255
            $urllink = 'http://'.$urllink;
256
        }
257
258
        // If the title is empty, we use the URL as title.
259
        if ($title == '') {
260
            $title = $urllink;
261
        }
262
263
        // If the URL is invalid, an error occurs.
264
        if (!api_valid_url($urllink, true)) {
265
            // A check against an absolute URL
266
            Display::addFlash(Display::return_message(get_lang('Please give the link URL, it should be valid.'), 'error'));
267
268
            return false;
269
        } else {
270
            // Looking for the largest order number for this category.
271
            $link = new CLink();
272
            $link
273
                ->setCId($course_id)
274
                ->setUrl($urllink)
275
                ->setTitle($title)
276
                ->setDescription($description)
277
                ->setOnHomepage($onhomepage)
278
                ->setTarget($target)
279
                ->setSessionId($session_id)
280
                ->setCategoryId($selectcategory)
281
            ;
282
283
            $repo = Container::getLinkRepository();
284
285
            $courseEntity = api_get_course_entity($course_id);
286
287
            if (empty($selectcategory)) {
288
                $repo->addResourceToCourse(
289
                    $link,
290
                    ResourceLink::VISIBILITY_PUBLISHED,
291
                    api_get_user_entity(api_get_user_id()),
292
                    $courseEntity,
293
                    api_get_session_entity($session_id),
294
                    api_get_group_entity()
295
                );
296
            } else {
297
                $repoCategory = Container::getLinkCategoryRepository();
298
                /** @var CLinkCategory $category */
299
                $category = $repoCategory->find($selectcategory);
300
                $repo->addResourceToCourseWithParent(
301
                    $link,
302
                    $category->getResourceNode(),
303
                    ResourceLink::VISIBILITY_PUBLISHED,
304
                    api_get_user_entity(api_get_user_id()),
305
                    $courseEntity,
306
                    api_get_session_entity($session_id),
307
                    api_get_group_entity()
308
                );
309
            }
310
311
            $repo->getEntityManager()->flush();
312
            $link_id = $link->getIid();
313
314
            if ((api_get_setting('search_enabled') == 'true') &&
315
                $link_id && extension_loaded('xapian')
316
            ) {
317
                require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
318
319
                $course_int_id = $_course['real_id'];
320
                $courseCode = $_course['code'];
321
                $specific_fields = get_specific_field_list();
322
                $ic_slide = new IndexableChunk();
323
324
                // Add all terms to db.
325
                $all_specific_terms = '';
326
                foreach ($specific_fields as $specific_field) {
327
                    if (isset($_REQUEST[$specific_field['code']])) {
328
                        $sterms = trim($_REQUEST[$specific_field['code']]);
329
                        if (!empty($sterms)) {
330
                            $all_specific_terms .= ' '.$sterms;
331
                            $sterms = explode(',', $sterms);
332
                            foreach ($sterms as $sterm) {
333
                                $ic_slide->addTerm(
334
                                    trim($sterm),
335
                                    $specific_field['code']
336
                                );
337
                                add_specific_field_value(
338
                                    $specific_field['id'],
339
                                    $courseCode,
340
                                    TOOL_LINK,
341
                                    $link_id,
342
                                    $sterm
343
                                );
344
                            }
345
                        }
346
                    }
347
                }
348
349
                // Build the chunk to index.
350
                $ic_slide->addValue('title', $title);
351
                $ic_slide->addCourseId($courseCode);
352
                $ic_slide->addToolId(TOOL_LINK);
353
                $xapian_data = [
354
                    SE_COURSE_ID => $courseCode,
355
                    SE_TOOL_ID => TOOL_LINK,
356
                    SE_DATA => [
357
                        'link_id' => (int) $link_id,
358
                    ],
359
                    SE_USER => (int) api_get_user_id(),
360
                ];
361
                $ic_slide->xapian_data = serialize($xapian_data);
362
                $description = $all_specific_terms.' '.$description;
363
                $ic_slide->addValue('content', $description);
364
365
                // Add category name if set.
366
                if (isset($selectcategory) && $selectcategory > 0) {
367
                    $table_link_category = Database::get_course_table(
368
                        TABLE_LINK_CATEGORY
369
                    );
370
                    $sql_cat = 'SELECT * FROM %s WHERE id=%d AND c_id = %d LIMIT 1';
371
                    $sql_cat = sprintf(
372
                        $sql_cat,
373
                        $table_link_category,
374
                        (int) $selectcategory,
375
                        $course_int_id
376
                    );
377
                    $result = Database:: query($sql_cat);
378
                    if (Database:: num_rows($result) == 1) {
379
                        $row = Database:: fetch_array($result);
380
                        $ic_slide->addValue(
381
                            'category',
382
                            $row['category_title']
383
                        );
384
                    }
385
                }
386
387
                $di = new ChamiloIndexer();
388
                isset($_POST['language']) ? $lang = Database:: escape_string(
389
                    $_POST['language']
390
                ) : $lang = 'english';
391
                $di->connectDb(null, null, $lang);
392
                $di->addChunk($ic_slide);
393
394
                // Index and return search engine document id.
395
                $did = $di->index();
396
                if ($did) {
397
                    // Save it to db.
398
                    $tbl_se_ref = Database::get_main_table(
399
                        TABLE_MAIN_SEARCH_ENGINE_REF
400
                    );
401
                    $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
402
                            VALUES (NULL , \'%s\', \'%s\', %s, %s)';
403
                    $sql = sprintf(
404
                        $sql,
405
                        $tbl_se_ref,
406
                        $course_int_id,
407
                        $courseCode,
408
                        TOOL_LINK,
409
                        $link_id,
410
                        $did
411
                    );
412
                    Database:: query($sql);
413
                }
414
            }
415
            Display::addFlash(Display::return_message(get_lang('The link has been added.')));
416
417
            return $link_id;
418
        }
419
420
    }
421
    public static function deleteCategory($id)
422
    {
423
        $repo = Container::getLinkCategoryRepository();
424
        /** @var CLinkCategory $category */
425
        $category = $repo->find($id);
426
        if ($category)  {
0 ignored issues
show
introduced by
$category is of type Chamilo\CourseBundle\Entity\CLinkCategory, thus it always evaluated to true.
Loading history...
427
            $repo->delete($category);
428
            Display::addFlash(Display::return_message(get_lang('The category has been deleted.')));
429
430
            return true;
431
        }
432
433
        return false;
434
435
        // First we delete the category itself and afterwards all the links of this category.
436
        $sql = "DELETE FROM ".$tbl_categories."
0 ignored issues
show
Unused Code introduced by
$sql = 'DELETE FROM ' . ...' AND id='' . $id . ''' is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
437
                        WHERE c_id = $course_id AND id='".$id."'";
438
        Database:: query($sql);
439
440
        $sql = "DELETE FROM ".$tbl_link."
441
                        WHERE c_id = $course_id AND category_id='".$id."'";
442
        Database:: query($sql);
443
444
        api_item_property_update(
445
            $courseInfo,
446
            TOOL_LINK_CATEGORY,
447
            $id,
448
            'delete',
449
            api_get_user_id()
450
        );
451
452
        Display::addFlash(Display::return_message(get_lang('The category has been deleted.')));
453
454
        return true;
455
    }
456
457
    /**
458
     * Used to delete a link.
459
     * @param int    $id
460
     *
461
     * @return bool
462
     */
463
    public static function deleteLink($id)
464
    {
465
        $repo = Container::getLinkRepository();
466
        $link = $repo->find($id);
467
        if ($link) {
468
            $repo->getEntityManager()->remove($link);
469
            $repo->getEntityManager()->flush();
470
            self::delete_link_from_search_engine(api_get_course_id(), $id);
471
            Skill::deleteSkillsFromItem($id, ITEM_TYPE_LINK);
472
            Display::addFlash(Display::return_message(get_lang('The link has been deleted')));
473
474
            return true;
475
        }
476
477
        return false;
478
479
        $courseInfo = api_get_course_info();
0 ignored issues
show
Unused Code introduced by
$courseInfo = api_get_course_info() is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
480
        $tbl_link = Database::get_course_table(TABLE_LINK);
481
482
        $course_id = $courseInfo['real_id'];
483
        $id = intval($id);
484
485
        if (empty($id)) {
486
            return false;
487
        }
488
489
        // -> Items are no longer physically deleted,
490
        // but the visibility is set to 2 (in item_property).
491
        // This will make a restore function possible for the platform administrator.
492
        $sql = "UPDATE $tbl_link SET on_homepage='0'
493
                WHERE c_id = $course_id AND id='".$id."'";
494
        Database:: query($sql);
495
496
        /*api_item_property_update(
497
            $courseInfo,
498
            TOOL_LINK,
499
            $id,
500
            'delete',
501
            api_get_user_id()
502
        );*/
503
504
        return true;
505
    }
506
507
    /**
508
     * Removes a link from search engine database.
509
     *
510
     * @param string $course_id Course code
511
     * @param int    $link_id   Document id to delete
512
     */
513
    public static function delete_link_from_search_engine($course_id, $link_id)
514
    {
515
        // Remove from search engine if enabled.
516
        if (api_get_setting('search_enabled') === 'true') {
517
            $tbl_se_ref = Database::get_main_table(
518
                TABLE_MAIN_SEARCH_ENGINE_REF
519
            );
520
            $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
521
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_LINK, $link_id);
522
            $res = Database:: query($sql);
523
            if (Database:: num_rows($res) > 0) {
524
                $row = Database::fetch_array($res);
525
                $di = new ChamiloIndexer();
526
                $di->remove_document($row['search_did']);
527
            }
528
            $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
529
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_LINK, $link_id);
530
            Database:: query($sql);
531
532
            // Remove terms from db.
533
            require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
534
            delete_all_values_for_item($course_id, TOOL_DOCUMENT, $link_id);
535
        }
536
    }
537
538
    /**
539
     * Get link info.
540
     *
541
     * @param int $id
542
     *
543
     * @return array link info
544
     */
545
    public static function getLinkInfo($id)
546
    {
547
        $tbl_link = Database::get_course_table(TABLE_LINK);
548
        $course_id = api_get_course_int_id();
549
550
        if (empty($id) || empty($course_id)) {
551
            return [];
552
        }
553
554
        $sql = "SELECT * FROM $tbl_link
555
                WHERE c_id = $course_id AND id='".intval($id)."' ";
556
        $result = Database::query($sql);
557
        $data = [];
558
        if (Database::num_rows($result)) {
559
            $data = Database::fetch_array($result);
560
        }
561
562
        return $data;
563
    }
564
565
    /**
566
     * @param int   $id
567
     * @param array $values
568
     */
569
    public static function editLink($id, $values = [])
570
    {
571
        $tbl_link = Database::get_course_table(TABLE_LINK);
572
        $_course = api_get_course_info();
573
        $course_id = $_course['real_id'];
574
        $id = intval($id);
575
576
        $values['url'] = trim($values['url']);
577
        $values['title'] = trim($values['title']);
578
        $values['description'] = trim($values['description']);
579
        $values['target'] = empty($values['target']) ? '_self' : $values['target'];
580
        $values['on_homepage'] = isset($values['on_homepage']) ? $values['on_homepage'] : '';
581
582
        $categoryId = intval($values['category_id']);
583
584
        // We ensure URL to be absolute.
585
        if (strpos($values['url'], '://') === false) {
586
            $values['url'] = 'http://'.$_POST['url'];
587
        }
588
589
        // If the title is empty, we use the URL as title.
590
        if ($values['title'] == '') {
591
            $values['title'] = $values['url'];
592
        }
593
594
        // If the URL is invalid, an error occurs.
595
        if (!api_valid_url($values['url'], true)) {
596
            Display::addFlash(
597
                Display::return_message(get_lang('Please give the link URL, it should be valid.'), 'error')
598
            );
599
600
            return false;
601
        }
602
603
        if (empty($id) || empty($course_id)) {
604
            return false;
605
        }
606
607
        // Finding the old category_id.
608
        $sql = "SELECT * FROM $tbl_link
609
                WHERE c_id = $course_id AND id='".$id."'";
610
        $result = Database:: query($sql);
611
        $row = Database:: fetch_array($result);
612
        $category_id = $row['category_id'];
613
614
        if ($category_id != $values['category_id']) {
615
            $sql = "SELECT MAX(display_order)
616
                    FROM $tbl_link
617
                    WHERE
618
                        c_id = $course_id AND
619
                        category_id='".intval($values['category_id'])."'";
620
            $result = Database:: query($sql);
621
            list($max_display_order) = Database:: fetch_row($result);
622
            $max_display_order++;
623
        } else {
624
            $max_display_order = $row['display_order'];
625
        }
626
        $params = [
627
            'url' => $values['url'],
628
            'title' => $values['title'],
629
            'description' => $values['description'],
630
            'category_id' => $values['category_id'],
631
            'display_order' => $max_display_order,
632
            'on_homepage' => $values['on_homepage'],
633
            'target' => $values['target'],
634
        ];
635
636
        Database::update(
637
            $tbl_link,
638
            $params,
639
            ['c_id = ? AND id = ?' => [$course_id, $id]]
640
        );
641
642
        // Update search enchine and its values table if enabled.
643
        if (api_get_setting('search_enabled') == 'true') {
644
            $course_int_id = api_get_course_int_id();
645
            $course_id = api_get_course_id();
646
            $link_title = Database:: escape_string($values['title']);
647
            $link_description = Database:: escape_string($values['description']);
648
649
            // Actually, it consists on delete terms from db,
650
            // insert new ones, create a new search engine document, and remove the old one.
651
            // Get search_did.
652
            $tbl_se_ref = Database::get_main_table(
653
                TABLE_MAIN_SEARCH_ENGINE_REF
654
            );
655
            $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
656
            $sql = sprintf(
657
                $sql,
658
                $tbl_se_ref,
659
                $course_id,
660
                TOOL_LINK,
661
                $id
662
            );
663
            $res = Database:: query($sql);
664
665
            if (Database:: num_rows($res) > 0) {
666
                require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
667
668
                $se_ref = Database:: fetch_array($res);
669
                $specific_fields = get_specific_field_list();
670
                $ic_slide = new IndexableChunk();
671
672
                $all_specific_terms = '';
673
                foreach ($specific_fields as $specific_field) {
674
                    delete_all_specific_field_value(
675
                        $course_id,
676
                        $specific_field['id'],
677
                        TOOL_LINK,
678
                        $id
679
                    );
680
                    if (isset($_REQUEST[$specific_field['code']])) {
681
                        $sterms = trim(
682
                            $_REQUEST[$specific_field['code']]
683
                        );
684
                        if (!empty($sterms)) {
685
                            $all_specific_terms .= ' '.$sterms;
686
                            $sterms = explode(',', $sterms);
687
                            foreach ($sterms as $sterm) {
688
                                $ic_slide->addTerm(
689
                                    trim($sterm),
690
                                    $specific_field['code']
691
                                );
692
                                add_specific_field_value(
693
                                    $specific_field['id'],
694
                                    $course_id,
695
                                    TOOL_LINK,
696
                                    $id,
697
                                    $sterm
698
                                );
699
                            }
700
                        }
701
                    }
702
                }
703
704
                // Build the chunk to index.
705
                $ic_slide->addValue("title", $link_title);
706
                $ic_slide->addCourseId($course_id);
707
                $ic_slide->addToolId(TOOL_LINK);
708
                $xapian_data = [
709
                    SE_COURSE_ID => $course_id,
710
                    SE_TOOL_ID => TOOL_LINK,
711
                    SE_DATA => [
712
                        'link_id' => (int) $id,
713
                    ],
714
                    SE_USER => (int) api_get_user_id(),
715
                ];
716
                $ic_slide->xapian_data = serialize($xapian_data);
717
                $link_description = $all_specific_terms.' '.$link_description;
718
                $ic_slide->addValue('content', $link_description);
719
720
                // Add category name if set.
721
                if (isset($categoryId) && $categoryId > 0) {
722
                    $table_link_category = Database::get_course_table(
723
                        TABLE_LINK_CATEGORY
724
                    );
725
                    $sql_cat = 'SELECT * FROM %s WHERE id=%d and c_id = %d LIMIT 1';
726
                    $sql_cat = sprintf(
727
                        $sql_cat,
728
                        $table_link_category,
729
                        $categoryId,
730
                        $course_int_id
731
                    );
732
                    $result = Database:: query($sql_cat);
733
                    if (Database:: num_rows($result) == 1) {
734
                        $row = Database:: fetch_array($result);
735
                        $ic_slide->addValue(
736
                            'category',
737
                            $row['category_title']
738
                        );
739
                    }
740
                }
741
742
                $di = new ChamiloIndexer();
743
                isset($_POST['language']) ? $lang = Database:: escape_string($_POST['language']) : $lang = 'english';
744
                $di->connectDb(null, null, $lang);
745
                $di->remove_document($se_ref['search_did']);
746
                $di->addChunk($ic_slide);
747
748
                // Index and return search engine document id.
749
                $did = $di->index();
750
                if ($did) {
751
                    // Save it to db.
752
                    $sql = 'DELETE FROM %s
753
                            WHERE course_code=\'%s\'
754
                            AND tool_id=\'%s\'
755
                            AND ref_id_high_level=\'%s\'';
756
                    $sql = sprintf(
757
                        $sql,
758
                        $tbl_se_ref,
759
                        $course_id,
760
                        TOOL_LINK,
761
                        $id
762
                    );
763
                    Database:: query($sql);
764
                    $sql = 'INSERT INTO %s (c_id, id, course_code, tool_id, ref_id_high_level, search_did)
765
                            VALUES (NULL , \'%s\', \'%s\', %s, %s)';
766
                    $sql = sprintf(
767
                        $sql,
768
                        $tbl_se_ref,
769
                        $course_int_id,
770
                        $course_id,
771
                        TOOL_LINK,
772
                        $id,
773
                        $did
774
                    );
775
                    Database:: query($sql);
776
                }
777
            }
778
        }
779
780
        // "WHAT'S NEW" notification: update table last_toolEdit.
781
        api_item_property_update(
782
            $_course,
783
            TOOL_LINK,
784
            $id,
785
            'LinkUpdated',
786
            api_get_user_id()
787
        );
788
        Display::addFlash(Display::return_message(get_lang('The link has been modified.')));
789
    }
790
791
    /**
792
     * @param int   $id
793
     * @param array $values
794
     *
795
     * @return bool
796
     */
797
    public static function editCategory($id, $values)
798
    {
799
        $table = Database::get_course_table(TABLE_LINK_CATEGORY);
800
        $course_id = api_get_course_int_id();
801
        $id = (int) $id;
802
803
        // This is used to put the modified info of the category-form into the database.
804
        $params = [
805
            'category_title' => $values['category_title'],
806
            'description' => $values['description'],
807
        ];
808
        Database::update(
809
            $table,
810
            $params,
811
            ['c_id = ? AND id = ?' => [$course_id, $id]]
812
        );
813
        Display::addFlash(Display::return_message(get_lang('The category has been modified.')));
814
815
        return true;
816
    }
817
818
    /**
819
     * Changes the visibility of a link.
820
     *
821
     * @todo add the changing of the visibility of a course
822
     *
823
     * @author Patrick Cool <[email protected]>, Ghent University
824
     */
825
    public static function change_visibility_link($id, $scope)
826
    {
827
        $_course = api_get_course_info();
828
        $_user = api_get_user_info();
829
        if ($scope == TOOL_LINK) {
830
            api_item_property_update(
831
                $_course,
832
                TOOL_LINK,
833
                $id,
834
                $_GET['action'],
835
                $_user['user_id']
836
            );
837
            Display::addFlash(Display::return_message(get_lang('The visibility has been changed.')));
838
        } elseif ($scope == TOOL_LINK_CATEGORY) {
839
            api_item_property_update(
840
                $_course,
841
                TOOL_LINK_CATEGORY,
842
                $id,
843
                $_GET['action'],
844
                $_user['user_id']
845
            );
846
            Display::addFlash(Display::return_message(get_lang('The visibility has been changed.')));
847
        }
848
    }
849
850
    /**
851
     * Generate SQL to select all the links categories in the current course and
852
     * session.
853
     *
854
     * @param int  $courseId
855
     * @param int  $sessionId
856
     * @param bool $withBaseContent
857
     *
858
     * @return CLinkCategory[]
859
     */
860
    public static function getLinkCategories($courseId, $sessionId, $withBaseContent = true)
0 ignored issues
show
Unused Code introduced by
The parameter $withBaseContent is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

860
    public static function getLinkCategories($courseId, $sessionId, /** @scrutinizer ignore-unused */ $withBaseContent = true)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
861
    {
862
        $repo = Container::getLinkCategoryRepository();
863
864
        $courseEntity = api_get_course_entity($courseId);
865
        $sessionEntity = api_get_session_entity($sessionId);
866
867
        $qb = $repo->getResourcesByCourse($courseEntity, $sessionEntity);
868
869
        return $qb->getQuery()->getResult();
870
871
        $tblLinkCategory = Database::get_course_table(TABLE_LINK_CATEGORY);
0 ignored issues
show
Unused Code introduced by
$tblLinkCategory = Datab...le(TABLE_LINK_CATEGORY) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
872
        $tblItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
873
        $courseId = (int) $courseId;
874
        $courseInfo = api_get_course_info_by_id($courseId);
875
876
        // Condition for the session.
877
        $sessionCondition = api_get_session_condition(
878
            $sessionId,
879
            true,
880
            $withBaseContent,
881
            'linkcat.session_id'
882
        );
883
884
        // Getting links
885
        $sql = "SELECT *, linkcat.id
886
                FROM $tblLinkCategory linkcat
887
                WHERE
888
                    linkcat.c_id = $courseId
889
                    $sessionCondition
890
                ORDER BY linkcat.display_order DESC";
891
892
        $result = Database::query($sql);
893
        $categories = Database::store_result($result);
894
895
        $sql = "SELECT *, linkcat.id
896
                FROM $tblLinkCategory linkcat
897
                INNER JOIN $tblItemProperty ip
898
                ON (linkcat.id = ip.ref AND linkcat.c_id = ip.c_id)
899
                WHERE
900
                    ip.tool = '".TOOL_LINK_CATEGORY."' AND
901
                    (ip.visibility = '0' OR ip.visibility = '1')
902
                    $sessionCondition AND
903
                    linkcat.c_id = ".$courseId."
904
                ORDER BY linkcat.display_order DESC";
905
906
        $result = Database::query($sql);
907
908
        $categoryInItemProperty = [];
909
        if (Database::num_rows($result)) {
910
            while ($row = Database::fetch_array($result, 'ASSOC')) {
911
                $categoryInItemProperty[$row['id']] = $row;
912
            }
913
        }
914
915
        foreach ($categories as &$category) {
916
            if (!isset($categoryInItemProperty[$category['id']])) {
917
                api_item_property_update(
918
                    $courseInfo,
919
                    TOOL_LINK_CATEGORY,
920
                    $category['id'],
921
                    'LinkCategoryAdded',
922
                    api_get_user_id()
923
                );
924
            }
925
        }
926
927
        $sql = "SELECT DISTINCT linkcat.*, visibility
928
                FROM $tblLinkCategory linkcat
929
                INNER JOIN $tblItemProperty ip
930
                ON (linkcat.id = ip.ref AND linkcat.c_id = ip.c_id)
931
                WHERE
932
                    ip.tool = '".TOOL_LINK_CATEGORY."' AND
933
                    (ip.visibility = '0' OR ip.visibility = '1')
934
                    $sessionCondition AND
935
                    linkcat.c_id = ".$courseId."
936
                ORDER BY linkcat.display_order DESC
937
                ";
938
        $result = Database::query($sql);
939
940
        return Database::store_result($result, 'ASSOC');
941
    }
942
943
    /**
944
     * @param int $categoryId
945
     * @param $courseId
946
     * @param $sessionId
947
     * @param bool $withBaseContent
948
     *
949
     * @return CLink[]|null
950
     */
951
    public static function getLinksPerCategory(
952
        $categoryId,
953
        $courseId,
954
        $sessionId,
955
        $withBaseContent = true
0 ignored issues
show
Unused Code introduced by
The parameter $withBaseContent is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

955
        /** @scrutinizer ignore-unused */ $withBaseContent = true

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
956
    ) {
957
        $courseEntity = api_get_course_entity($courseId);
958
        $sessionEntity = api_get_session_entity($sessionId);
959
        if (empty($categoryId)) {
960
            $repo = Container::getLinkRepository();
961
            $qb = $repo->getResourcesByCourse($courseEntity, $sessionEntity, null, $courseEntity->getResourceNode());
962
963
            return $qb->getQuery()->getResult();
964
        }
965
966
        $repo = Container::getLinkCategoryRepository();
967
        /** @var CLinkCategory $category */
968
        $category = $repo->find($categoryId);
969
        if ($category) {
0 ignored issues
show
introduced by
$category is of type Chamilo\CourseBundle\Entity\CLinkCategory, thus it always evaluated to true.
Loading history...
970
            $repo = Container::getLinkRepository();
971
            $qb = $repo->getResourcesByCourse($courseEntity, $sessionEntity, null, $category->getResourceNode());
972
973
            return $qb->getQuery()->getResult();
974
        }
975
976
        return null;
977
978
        $tbl_link = Database::get_course_table(TABLE_LINK);
0 ignored issues
show
Unused Code introduced by
$tbl_link = Database::get_course_table(TABLE_LINK) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
979
        $TABLE_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
980
        $courseId = (int) $courseId;
981
        $sessionId = (int) $sessionId;
982
        $categoryId = (int) $categoryId;
983
984
        // Condition for the session.
985
        $condition_session = api_get_session_condition(
986
            $sessionId,
987
            true,
988
            false,
989
            'ip.session_id'
990
        );
991
992
        if (!empty($sessionId)) {
993
            $conditionBaseSession = api_get_session_condition(
994
                0,
995
                true,
996
                $withBaseContent,
997
                'ip.session_id'
998
            );
999
            $condition = " AND
1000
                (
1001
                    (ip.visibility = '1' $conditionBaseSession) OR
1002
1003
                    (
1004
                        (ip.visibility = '0' OR ip.visibility = '1')
1005
                        $condition_session
1006
                    )
1007
                )
1008
            ";
1009
        } else {
1010
            $condition = api_get_session_condition(
1011
                0,
1012
                true,
1013
                false,
1014
                'ip.session_id'
1015
            );
1016
            $condition .= " AND (ip.visibility = '0' OR ip.visibility = '1') $condition ";
1017
        }
1018
1019
        $sql = "SELECT
1020
                    link.id,
1021
                    ip.session_id,
1022
                    link.session_id link_session_id,
1023
                    url,
1024
                    category_id,
1025
                    visibility,
1026
                    description,
1027
                    title,
1028
                    target,
1029
                    on_homepage
1030
                FROM $tbl_link link
1031
                INNER JOIN $TABLE_ITEM_PROPERTY ip
1032
                ON (link.id = ip.ref AND link.c_id = ip.c_id)
1033
                WHERE
1034
                    ip.tool = '".TOOL_LINK."' AND
1035
                    link.category_id = '".$categoryId."' AND
1036
                    link.c_id = $courseId AND
1037
                    ip.c_id = $courseId
1038
                    $condition
1039
                ORDER BY link.display_order ASC, ip.session_id DESC";
1040
1041
        $result = Database:: query($sql);
1042
1043
        return Database::store_result($result);
1044
    }
1045
1046
    /**
1047
     * Displays all the links of a given category.
1048
     *
1049
     * @param int  $catid
1050
     * @param int  $courseId
1051
     * @param int  $sessionId
1052
     * @param bool $showActionLinks
1053
     *
1054
     * @return string
1055
     */
1056
    public static function showLinksPerCategory($catid, $courseId, $sessionId, $showActionLinks = true)
1057
    {
1058
        global $token;
1059
        $_user = api_get_user_info();
1060
        $catid = (int) $catid;
1061
1062
        $links = self::getLinksPerCategory($catid, $courseId, $sessionId);
1063
        $content = '';
1064
        $numberOfLinks = count($links);
1065
1066
        $courseEntity = api_get_course_entity($courseId);
1067
        $sessionEntity = api_get_session_entity($sessionId);
1068
        if (!empty($links)) {
1069
            $content .= '<div class="link list-group">';
1070
            $i = 1;
1071
            $linksAdded = [];
1072
            foreach ($links as $link) {
1073
                $linkId = $link->getIid();
1074
1075
                if (in_array($linkId, $linksAdded)) {
1076
                    continue;
1077
                }
1078
1079
                $visibility = (int) $link->isVisible($courseEntity, $sessionEntity);
1080
1081
                $linksAdded[] = $linkId;
1082
                $categoryId = $link->getCategoryId();
1083
1084
                // Validation when belongs to a session.
1085
                $session_img = api_get_session_image(
1086
                    $link->getSessionId(),
1087
                    $_user['status']
1088
                );
1089
1090
                $toolbar = '';
1091
                $link_validator = '';
1092
                if (api_is_allowed_to_edit(null, true)) {
1093
                    $toolbar .= Display::toolbarButton(
1094
                        '',
1095
                        'javascript:void(0);',
1096
                        'check-circle',
1097
                        'secondary btn-sm',
1098
                        [
1099
                            'onclick' => "check_url('".$linkId."', '".addslashes($link->getUrl())."');",
1100
                            'title' => get_lang('Check link'),
1101
                        ]
1102
                    );
1103
1104
                    $link_validator .= Display::span(
1105
                        '',
1106
                        [
1107
                        'id' => 'url_id_'.$linkId,
1108
                        'class' => 'check-link',
1109
                        ]
1110
                    );
1111
1112
                    if ($sessionId == $link->getSessionId()) {
1113
                        $url = api_get_self().'?'.api_get_cidreq().'&action=editlink&id='.$linkId;
1114
                        $title = get_lang('Edit');
1115
                        $toolbar .= Display::toolbarButton(
1116
                            '',
1117
                            $url,
1118
                            'pencil-alt',
1119
                            'secondary btn-sm',
1120
                            [
1121
                                'title' => $title,
1122
                            ]
1123
                        );
1124
                    }
1125
1126
                    $urlVisibility = api_get_self().'?'.api_get_cidreq().
1127
                            '&sec_token='.$token.
1128
                            '&id='.$linkId.
1129
                            '&scope=link&category_id='.$categoryId;
1130
1131
                    switch ($visibility) {
1132
                        case 1:
1133
                            $urlVisibility .= '&action=invisible';
1134
                            $title = get_lang('Make invisible');
1135
                            $toolbar .= Display::toolbarButton(
1136
                                '',
1137
                                $urlVisibility,
1138
                                'eye',
1139
                                'secondary btn-sm',
1140
                                [
1141
                                    'title' => $title,
1142
                                ]
1143
                            );
1144
                            break;
1145
                        case 0:
1146
                            $urlVisibility .= '&action=visible';
1147
                            $title = get_lang('Make Visible');
1148
                            $toolbar .= Display::toolbarButton(
1149
                                '',
1150
                                $urlVisibility,
1151
                                'eye-slash',
1152
                                'secondary btn-sm',
1153
                                [
1154
                                    'title' => $title,
1155
                                ]
1156
                            );
1157
                            break;
1158
                    }
1159
1160
                    if ($sessionId == $link->getSessionId()) {
1161
                        $moveLinkParams = [
1162
                            'id' => $linkId,
1163
                            'scope' => 'category',
1164
                            'category_id' => $categoryId,
1165
                            'action' => 'move_link_up',
1166
                        ];
1167
1168
                        $toolbar .= Display::toolbarButton(
1169
                            get_lang('Move up'),
1170
                            api_get_self().'?'.api_get_cidreq().'&'.http_build_query($moveLinkParams),
1171
                            'level-up-alt',
1172
                            'secondary',
1173
                            ['class' => 'btn-sm '.($i === 1 ? 'disabled' : '')],
1174
                            false
1175
                        );
1176
1177
                        $moveLinkParams['action'] = 'move_link_down';
1178
                        $toolbar .= Display::toolbarButton(
1179
                            get_lang('Move down'),
1180
                            api_get_self().'?'.api_get_cidreq().'&'.http_build_query($moveLinkParams),
1181
                            'level-down-alt',
1182
                            'secondary',
1183
                            ['class' => 'btn-sm '.($i === $numberOfLinks ? 'disabled' : '')],
1184
                            false
1185
                        );
1186
1187
                        $url = api_get_self().'?'.api_get_cidreq().'&sec_token='.$token.'&action=deletelink&id='.$linkId.'&category_id='.$categoryId;
1188
                        $event = "javascript: if(!confirm('".get_lang('Do you want to delete this link?')."'))return false;";
1189
                        $title = get_lang('Delete');
1190
1191
                        $toolbar .= Display::toolbarButton(
1192
                            '',
1193
                            $url,
1194
                            'trash',
1195
                            'secondary btn-sm',
1196
                            [
1197
                                'onclick' => $event,
1198
                                'title' => $title,
1199
                            ]
1200
                        );
1201
                    }
1202
                }
1203
1204
                $showLink = false;
1205
                $titleClass = '';
1206
                if ($visibility) {
1207
                    $showLink = true;
1208
                } else {
1209
                    if (api_is_allowed_to_edit(null, true)) {
1210
                        $showLink = true;
1211
                        $titleClass = 'text-muted';
1212
                    }
1213
                }
1214
1215
                if ($showLink) {
1216
                    $iconLink = Display::return_icon(
1217
                        'url.png',
1218
                        get_lang('Link'),
1219
                        null,
1220
                        ICON_SIZE_SMALL
1221
                    );
1222
                    $url = api_get_path(WEB_CODE_PATH).'link/link_goto.php?'.api_get_cidreq().'&link_id='.$linkId.'&link_url='.urlencode($link->getUrl());
1223
                    $content .= '<div class="list-group-item">';
1224
                    if ($showActionLinks) {
1225
                        $content .= '<div class="pull-right"><div class="btn-group">'.$toolbar.'</div></div>';
1226
                    }
1227
                    $content .= '<h4 class="list-group-item-heading">';
1228
                    $content .= $iconLink;
1229
                    $content .= Display::tag(
1230
                        'a',
1231
                        Security::remove_XSS($link->getTitle()),
1232
                        [
1233
                            'href' => $url,
1234
                            'target' => $link->getTarget(),
1235
                            'class' => $titleClass,
1236
                        ]
1237
                    );
1238
                    $content .= $link_validator;
1239
                    $content .= $session_img;
1240
                    $content .= '</h4>';
1241
                    $content .= '<p class="list-group-item-text">'.$link->getDescription().'</p>';
1242
                    $content .= '</div>';
1243
                }
1244
                $i++;
1245
            }
1246
            $content .= '</div>';
1247
        }
1248
1249
        return $content;
1250
    }
1251
1252
    /**
1253
     * Displays the edit, delete and move icons.
1254
     *
1255
     * @param int   Category ID
1256
     * @param int $currentCategory
1257
     * @param int $countCategories
1258
     *
1259
     * @return string
1260
     *
1261
     * @author Patrick Cool <[email protected]>, Ghent University
1262
     */
1263
    public static function showCategoryAdminTools(CLinkCategory $category, $currentCategory, $countCategories)
1264
    {
1265
        $categoryId = $category->getIid();
1266
        $token = null;
1267
        $tools = '<a href="'.api_get_self().'?'.api_get_cidreq().'&sec_token='.$token.'&action=editcategory&id='.$categoryId.'&category_id='.$categoryId.'" title='.get_lang('Edit').'">'.
1268
            Display:: return_icon(
1269
                'edit.png',
1270
                get_lang('Edit'),
1271
                [],
1272
                ICON_SIZE_SMALL
1273
            ).'</a>';
1274
1275
        // DISPLAY MOVE UP COMMAND only if it is not the top link.
1276
        if ($currentCategory != 0) {
1277
            $tools .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&sec_token='.$token.'&action=up&up='.$categoryId.'&category_id='.$categoryId.'" title="'.get_lang('Up').'">'.
1278
                Display:: return_icon(
1279
                    'up.png',
1280
                    get_lang('Up'),
1281
                    [],
1282
                    ICON_SIZE_SMALL
1283
                ).'</a>';
1284
        } else {
1285
            $tools .= Display:: return_icon(
1286
                'up_na.png',
1287
                get_lang('Up'),
1288
                [],
1289
                ICON_SIZE_SMALL
1290
            ).'</a>';
1291
        }
1292
1293
        // DISPLAY MOVE DOWN COMMAND only if it is not the bottom link.
1294
        if ($currentCategory < $countCategories - 1) {
1295
            $tools .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&sec_token='.$token.'&action=down&down='.$categoryId.'&category_id='.$categoryId.'">'.
1296
                Display:: return_icon(
1297
                    'down.png',
1298
                    get_lang('down'),
1299
                    [],
1300
                    ICON_SIZE_SMALL
1301
                ).'</a>';
1302
        } else {
1303
            $tools .= Display:: return_icon(
1304
                'down_na.png',
1305
                get_lang('down'),
1306
                [],
1307
                ICON_SIZE_SMALL
1308
            ).'</a>';
1309
        }
1310
1311
        $tools .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&sec_token='.$token.'&action=deletecategory&id='.$categoryId."&category_id=$categoryId\"
1312
            onclick=\"javascript: if(!confirm('".get_lang('When deleting a category, all links of this category are also deleted.
1313
Do you really want to delete this category and its links ?')."')) return false;\">".
1314
            Display:: return_icon(
1315
                'delete.png',
1316
                get_lang('Delete'),
1317
                [],
1318
                ICON_SIZE_SMALL
1319
            ).'</a>';
1320
1321
        return $tools;
1322
    }
1323
1324
    /**
1325
     * move a link or a linkcategory up or down.
1326
     *
1327
     * @param   int Category ID
1328
     * @param   int Course ID
1329
     * @param   int Session ID
1330
     *
1331
     * @author Patrick Cool <[email protected]>, Ghent University
1332
     *
1333
     * @todo support sessions
1334
     */
1335
    public static function movecatlink($action, $catlinkid, $courseId = null, $sessionId = null)
1336
    {
1337
        $tbl_categories = Database::get_course_table(TABLE_LINK_CATEGORY);
1338
1339
        if (is_null($courseId)) {
1340
            $courseId = api_get_course_int_id();
1341
        }
1342
        $courseId = intval($courseId);
1343
        if (is_null($sessionId)) {
1344
            $sessionId = api_get_session_id();
1345
        }
1346
        $sessionId = intval($sessionId);
1347
        $thiscatlinkId = intval($catlinkid);
1348
1349
        if ($action == 'down') {
1350
            $sortDirection = 'DESC';
1351
        }
1352
1353
        if ($action == 'up') {
1354
            $sortDirection = 'ASC';
1355
        }
1356
1357
        $movetable = $tbl_categories;
1358
1359
        if (!empty($sortDirection)) {
1360
            if (!in_array(trim(strtoupper($sortDirection)), ['ASC', 'DESC'])) {
1361
                $sortDirection = 'ASC';
1362
            }
1363
1364
            $sql = "SELECT id, display_order FROM $movetable
1365
                    WHERE c_id = $courseId
1366
                    ORDER BY display_order $sortDirection";
1367
            $linkresult = Database:: query($sql);
1368
            $thislinkOrder = 1;
1369
            while ($sortrow = Database:: fetch_array($linkresult)) {
1370
                // STEP 2 : FOUND THE NEXT LINK ID AND ORDER, COMMIT SWAP
1371
                // This part seems unlogic, but it isn't . We first look for the current link with the querystring ID
1372
                // and we know the next iteration of the while loop is the next one. These should be swapped.
1373
                if (isset($thislinkFound) && $thislinkFound) {
1374
                    $nextlinkId = $sortrow['id'];
1375
                    $nextlinkOrder = $sortrow['display_order'];
1376
1377
                    Database:: query(
1378
                        "UPDATE ".$movetable."
1379
                        SET display_order = '$nextlinkOrder'
1380
                        WHERE c_id = $courseId  AND id =  '$thiscatlinkId'"
1381
                    );
1382
                    Database:: query(
1383
                        "UPDATE ".$movetable."
1384
                        SET display_order = '$thislinkOrder'
1385
                        WHERE c_id = $courseId  AND id =  '$nextlinkId'"
1386
                    );
1387
1388
                    break;
1389
                }
1390
                if ($sortrow['id'] == $thiscatlinkId) {
1391
                    $thislinkOrder = $sortrow['display_order'];
1392
                    $thislinkFound = true;
1393
                }
1394
            }
1395
        }
1396
1397
        Display::addFlash(Display::return_message(get_lang('LinksMoved')));
1398
    }
1399
1400
    /**
1401
     * This function checks if the url is a vimeo link.
1402
     *
1403
     * @author Julio Montoya
1404
     *
1405
     * @version 1.0
1406
     */
1407
    public static function isVimeoLink($url)
1408
    {
1409
        $isLink = strrpos($url, 'vimeo.com');
1410
1411
        return $isLink;
1412
    }
1413
1414
    /**
1415
     * Get vimeo id from URL.
1416
     *
1417
     * @param string $url
1418
     *
1419
     * @return bool|mixed
1420
     */
1421
    public static function getVimeoLinkId($url)
1422
    {
1423
        $possibleUrls = [
1424
            'http://www.vimeo.com/',
1425
            'http://vimeo.com/',
1426
            'https://www.vimeo.com/',
1427
            'https://vimeo.com/',
1428
        ];
1429
        $url = str_replace($possibleUrls, '', $url);
1430
1431
        if (is_numeric($url)) {
1432
            return $url;
1433
        }
1434
1435
        return false;
1436
    }
1437
1438
    /**
1439
     * This function checks if the url is a youtube link.
1440
     *
1441
     * @author Jorge Frisancho
1442
     * @author Julio Montoya - Fixing code
1443
     *
1444
     * @version 1.0
1445
     */
1446
    public static function is_youtube_link($url)
1447
    {
1448
        $is_youtube_link = strrpos($url, 'youtube') || strrpos(
1449
            $url,
1450
            'youtu.be'
1451
        );
1452
1453
        return $is_youtube_link;
1454
    }
1455
1456
    /**
1457
     * This function checks if the url is a PDF File link.
1458
     *
1459
     * @author Jorge Frisancho
1460
     * @author Alex Aragón - Fixing code
1461
     *
1462
     * @version 1.0
1463
     */
1464
    public static function isPdfLink($url)
1465
    {
1466
        $isPdfLink = strrpos(strtolower($url), '.pdf');
1467
1468
        return $isPdfLink;
1469
    }
1470
1471
    /**
1472
     * Get youtube id from an URL.
1473
     *
1474
     * @param string $url
1475
     *
1476
     * @return string
1477
     */
1478
    public static function get_youtube_video_id($url)
1479
    {
1480
        // This is the length of YouTube's video IDs
1481
        $len = 11;
1482
1483
        // The ID string starts after "v=", which is usually right after
1484
        // "youtube.com/watch?" in the URL
1485
        $pos = strpos($url, "v=");
1486
        $id = '';
1487
1488
        //If false try other options
1489
        if ($pos === false) {
1490
            $url_parsed = parse_url($url);
1491
1492
            //Youtube shortener
1493
            //http://youtu.be/ID
1494
            $pos = strpos($url, "youtu.be");
1495
1496
            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...
1497
                $id = '';
1498
            } else {
1499
                return substr($url_parsed['path'], 1);
1500
            }
1501
1502
            //if empty try the youtube.com/embed/ID
1503
            if (empty($id)) {
1504
                $pos = strpos($url, "embed");
1505
                if ($pos === false) {
1506
                    return '';
1507
                } else {
1508
                    return substr($url_parsed['path'], 7);
1509
                }
1510
            }
1511
        } else {
1512
            // Offset the start location to match the beginning of the ID string
1513
            $pos += 2;
1514
            // Get the ID string and return it
1515
            $id = substr($url, $pos, $len);
1516
1517
            return $id;
1518
        }
1519
    }
1520
1521
    /**
1522
     * @param int    $course_id
1523
     * @param int    $session_id
1524
     * @param int    $categoryId
1525
     * @param string $show
1526
     * @param null   $token
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $token is correct as it would always require null to be passed?
Loading history...
1527
     * @param bool   $showActionLinks
1528
     * @param bool   $forceOpenCategories
1529
     *
1530
     * @return string
1531
     */
1532
    public static function listLinksAndCategories(
1533
        $course_id,
1534
        $session_id,
1535
        $categoryId,
1536
        $show = 'none',
1537
        $token = null,
1538
        $showActionLinks = true,
1539
        $forceOpenCategories = false
1540
    ) {
1541
        $categoryId = (int) $categoryId;
1542
        $content = '';
1543
        $categories = self::getLinkCategories($course_id, $session_id);
1544
        $countCategories = count($categories);
1545
        $linksPerCategory = self::showLinksPerCategory(0, $course_id, $session_id, $showActionLinks);
1546
1547
        if ($showActionLinks) {
1548
            /*	Action Links */
1549
            $content = '<div class="actions">';
1550
            if (api_is_allowed_to_edit(null, true)) {
1551
                $content .= '<a href="'.api_get_self().'?'.api_get_cidreq(
1552
                    ).'&action=addlink&category_id='.$categoryId.'">'.
1553
                    Display::return_icon('new_link.png', get_lang('Add a link'), '', ICON_SIZE_MEDIUM).'</a>';
1554
                $content .= '<a href="'.api_get_self().'?'.api_get_cidreq(
1555
                    ).'&action=addcategory&category_id='.$categoryId.'">'.
1556
                    Display::return_icon('new_folder.png', get_lang('Add a category'), '', ICON_SIZE_MEDIUM).'</a>';
1557
            }
1558
1559
            if (!empty($countCategories)) {
1560
                $content .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=list&show=none">';
1561
                $content .= Display::return_icon(
1562
                        'forum_listview.png',
1563
                        get_lang('List View'),
1564
                        '',
1565
                        ICON_SIZE_MEDIUM
1566
                    ).' </a>';
1567
1568
                $content .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=list&show=all">';
1569
                $content .= Display::return_icon(
1570
                        'forum_nestedview.png',
1571
                        get_lang('Nested View'),
1572
                        '',
1573
                        ICON_SIZE_MEDIUM
1574
                    ).'</a>';
1575
            }
1576
1577
            $content .= Display::url(
1578
                Display::return_icon('pdf.png', get_lang('Export to PDF'), '', ICON_SIZE_MEDIUM),
1579
                api_get_self().'?'.api_get_cidreq().'&action=export'
1580
            );
1581
            $content .= '</div>';
1582
        }
1583
1584
        if (empty($countCategories)) {
1585
            $content .= $linksPerCategory;
1586
        } else {
1587
            if (!empty($linksPerCategory)) {
1588
                $content .= Display::panel($linksPerCategory, get_lang('General'));
1589
            }
1590
        }
1591
1592
        $counter = 0;
1593
        $courseEntity = api_get_course_entity($course_id);
1594
        $sessionEntity = api_get_session_entity($session_id);
1595
1596
        foreach ($categories as $category) {
1597
            $categoryItemId = $category->getIid();
1598
            $isVisible = $category->isVisible($courseEntity, $sessionEntity);
1599
            // Student don't see invisible categories.
1600
            if (!api_is_allowed_to_edit(null, true)) {
1601
                if (!$isVisible) {
1602
                    continue;
1603
                }
1604
            }
1605
1606
            // Validation when belongs to a session
1607
            $showChildren = $categoryId == $categoryItemId || $show === 'all';
1608
            if ($forceOpenCategories) {
1609
                $showChildren = true;
1610
            }
1611
1612
            $strVisibility = '';
1613
            $visibilityClass = null;
1614
            if ($isVisible) {
1615
                $strVisibility = '<a href="link.php?'.api_get_cidreq().'&sec_token='.$token.'&action=invisible&id='.$categoryItemId.'&scope='.TOOL_LINK_CATEGORY.'" title="'.get_lang('Hide').'">'.
1616
                    Display::return_icon('visible.png', get_lang('Hide'), [], ICON_SIZE_SMALL).'</a>';
1617
            } elseif (!$isVisible) {
1618
                $visibilityClass = 'text-muted';
1619
                $strVisibility = ' <a href="link.php?'.api_get_cidreq().'&sec_token='.$token.'&action=visible&id='.$categoryItemId.'&scope='.TOOL_LINK_CATEGORY.'" title="'.get_lang('Show').'">'.
1620
                    Display::return_icon('invisible.png', get_lang('Show'), [], ICON_SIZE_SMALL).'</a>';
1621
            }
1622
1623
            $header = '';
1624
            if ($showChildren) {
1625
                $header .= '<a class="'.$visibilityClass.'" href="'.api_get_self().'?'.api_get_cidreq().'&category_id=">';
1626
                $header .= Display::return_icon('forum_nestedview.png');
1627
            } else {
1628
                $header .= '<a class="'.$visibilityClass.'" href="'.api_get_self().'?'.api_get_cidreq().'&category_id='.$categoryItemId.'">';
1629
                $header .= Display::return_icon('forum_listview.png');
1630
            }
1631
            $header .= Security::remove_XSS($category->getCategoryTitle()).'</a>';
1632
1633
            if ($showActionLinks) {
1634
                if (api_is_allowed_to_edit(null, true)) {
1635
                    if ($session_id == $category->getSessionId()) {
1636
                        $header .= $strVisibility;
1637
                        $header .= self::showCategoryAdminTools($category, $counter, count($categories));
1638
                    } else {
1639
                        $header .= get_lang('Edition not available from the session, please edit from the basic course.');
1640
                    }
1641
                }
1642
            }
1643
1644
            $childrenContent = '';
1645
            if ($showChildren) {
1646
                $childrenContent = self::showLinksPerCategory(
1647
                    $categoryItemId,
1648
                    api_get_course_int_id(),
1649
                    api_get_session_id()
1650
                );
1651
            }
1652
1653
            $content .= Display::panel($category->getDescription().$childrenContent, $header);
1654
            $counter++;
1655
        }
1656
1657
        return $content;
1658
    }
1659
1660
    /**
1661
     * @param int    $linkId
1662
     * @param string $action
1663
     * @param null   $token
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $token is correct as it would always require null to be passed?
Loading history...
1664
     *
1665
     * @return FormValidator
1666
     */
1667
    public static function getLinkForm($linkId, $action, $token = null)
1668
    {
1669
        $course_id = api_get_course_int_id();
1670
        $session_id = api_get_session_id();
1671
        $linkInfo = self::getLinkInfo($linkId);
1672
        $categoryId = isset($linkInfo['category_id']) ? $linkInfo['category_id'] : '';
1673
        $lpId = isset($_GET['lp_id']) ? Security::remove_XSS($_GET['lp_id']) : null;
1674
1675
        $form = new FormValidator(
1676
            'link',
1677
            'post',
1678
            api_get_self().'?action='.$action.
1679
            '&category_id='.$categoryId.
1680
            '&'.api_get_cidreq().
1681
            '&id='.$linkId.
1682
            '&sec_token='.$token
1683
        );
1684
1685
        if ($action === 'addlink') {
1686
            $form->addHeader(get_lang('LinksAdd'));
1687
        } else {
1688
            $form->addHeader(get_lang('LinksMod'));
1689
        }
1690
1691
        $target_link = '_blank';
1692
        $title = '';
1693
        $category = '';
1694
        $onhomepage = '';
1695
        $description = '';
1696
1697
        if (!empty($linkInfo)) {
1698
            $urllink = $linkInfo['url'];
1699
            $title = $linkInfo['title'];
1700
            $description = $linkInfo['description'];
1701
            $category = $linkInfo['category_id'];
1702
            if ($linkInfo['on_homepage'] != 0) {
1703
                $onhomepage = 1;
1704
            }
1705
            $target_link = $linkInfo['target'];
1706
        }
1707
1708
        $form->addHidden('id', $linkId);
1709
        $form->addText('url', 'URL');
1710
        $form->addRule('url', get_lang('Please give the link URL, it should be valid.'), 'url');
1711
        $form->addText('title', get_lang('LinksName'));
1712
        $form->addHtmlEditor('description', get_lang('Description'), false, false, ['ToolbarSet' => 'Profile', 'Width' => '100%', 'Height' => '130']);
1713
1714
        $resultcategories = self::getLinkCategories($course_id, $session_id);
1715
        $options = ['0' => '--'];
1716
        if (!empty($resultcategories)) {
1717
            foreach ($resultcategories as $myrow) {
1718
                $options[$myrow->getIid()] = $myrow->getCategoryTitle();
1719
            }
1720
        }
1721
1722
        $form->addSelect('category_id', get_lang('Category'), $options);
1723
        $form->addCheckBox('on_homepage', null, get_lang('Show link on course homepage'));
1724
1725
        $targets = [
1726
            '_self' => get_lang('LinksOpenSelf'),
1727
            '_blank' => get_lang('LinksOpenBlank'),
1728
            '_parent' => get_lang('LinksOpenParent'),
1729
            '_top' => get_lang('LinksOpenTop'),
1730
        ];
1731
1732
        $form->addSelect(
1733
            'target',
1734
            [
1735
                get_lang('LinksTarget'),
1736
                get_lang('AddTargetOfLinksShow link on course homepage'),
1737
            ],
1738
            $targets
1739
        );
1740
1741
        $defaults = [
1742
            'url' => empty($urllink) ? 'http://' : Security::remove_XSS($urllink),
1743
            'title' => Security::remove_XSS($title),
1744
            'category_id' => $category,
1745
            'on_homepage' => $onhomepage,
1746
            'description' => $description,
1747
            'target' => $target_link,
1748
        ];
1749
1750
        if (api_get_setting('search_enabled') == 'true') {
1751
            require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
1752
            $specific_fields = get_specific_field_list();
1753
            $form->addCheckBox('index_document', get_lang('Index link title and description?s'), get_lang('Yes'));
1754
1755
            foreach ($specific_fields as $specific_field) {
1756
                $default_values = '';
1757
                if ($action == 'editlink') {
1758
                    $filter = [
1759
                        'field_id' => $specific_field['id'],
1760
                        'ref_id' => intval($_GET['id']),
1761
                        'tool_id' => '\''.TOOL_LINK.'\'',
1762
                    ];
1763
                    $values = get_specific_field_values_list($filter, ['value']);
1764
                    if (!empty($values)) {
1765
                        $arr_str_values = [];
1766
                        foreach ($values as $value) {
1767
                            $arr_str_values[] = $value['value'];
1768
                        }
1769
                        $default_values = implode(', ', $arr_str_values);
1770
                    }
1771
                }
1772
                $form->addText($specific_field['name'], $specific_field['code']);
1773
                $defaults[$specific_field['name']] = $default_values;
1774
            }
1775
        }
1776
1777
        $skillList = Skill::addSkillsToForm($form, ITEM_TYPE_LINK, $linkId);
1778
        $form->addHidden('lp_id', $lpId);
1779
        $form->addButtonSave(get_lang('Save links'), 'submitLink');
1780
        $defaults['skills'] = array_keys($skillList);
1781
        $form->setDefaults($defaults);
1782
1783
        return $form;
1784
    }
1785
1786
    /**
1787
     * @param int    $id
1788
     * @param string $action
1789
     *
1790
     * @return FormValidator
1791
     */
1792
    public static function getCategoryForm($id, $action)
1793
    {
1794
        $id = (int) $id;
1795
        $action = Security::remove_XSS($action);
1796
1797
        $form = new FormValidator(
1798
            'category',
1799
            'post',
1800
            api_get_self().'?action='.$action.'&'.api_get_cidreq()
1801
        );
1802
1803
        $defaults = [];
1804
        if ($action === 'addcategory') {
1805
            $form->addHeader(get_lang('Add a category'));
1806
            $my_cat_title = get_lang('Add a category');
1807
        } else {
1808
            $form->addHeader(get_lang('Edit Category'));
1809
            $my_cat_title = get_lang('Edit Category');
1810
            $defaults = self::getCategory($id);
1811
        }
1812
        $form->addHidden('id', $id);
1813
        $form->addText('category_title', get_lang('Category name'));
1814
        $form->addHtmlEditor('description', get_lang('Description'), false, false, ['ToolbarSet' => 'Profile', 'Width' => '100%', 'Height' => '130']);
1815
        $form->addButtonSave($my_cat_title, 'submitCategory');
1816
        $form->setDefaults($defaults);
1817
1818
        return $form;
1819
    }
1820
1821
    /**
1822
     * @param int $id
1823
     *
1824
     * @return array
1825
     */
1826
    public static function getCategory($id)
1827
    {
1828
        $table = Database::get_course_table(TABLE_LINK_CATEGORY);
1829
        $id = (int) $id;
1830
        $courseId = api_get_course_int_id();
1831
1832
        if (empty($id) || empty($courseId)) {
1833
            return [];
1834
        }
1835
        $sql = "SELECT * FROM $table
1836
                WHERE id = $id AND c_id = $courseId";
1837
        $result = Database::query($sql);
1838
        $category = Database::fetch_array($result, 'ASSOC');
1839
1840
        return $category;
1841
    }
1842
1843
    /**
1844
     * Move a link up in its category.
1845
     *
1846
     * @param int $id
1847
     *
1848
     * @return bool
1849
     */
1850
    public static function moveLinkUp($id)
1851
    {
1852
        return self::moveLinkDisplayOrder($id, 'ASC');
1853
    }
1854
1855
    /**
1856
     * Move a link down in its category.
1857
     *
1858
     * @param int $id
1859
     *
1860
     * @return bool
1861
     */
1862
    public static function moveLinkDown($id)
1863
    {
1864
        return self::moveLinkDisplayOrder($id, 'DESC');
1865
    }
1866
1867
    /**
1868
     * @param string $url
1869
     *
1870
     * @return bool
1871
     */
1872
    public static function checkUrl($url)
1873
    {
1874
        // Check if curl is available.
1875
        if (!in_array('curl', get_loaded_extensions())) {
1876
            return false;
1877
        }
1878
1879
        // set URL and other appropriate options
1880
        $defaults = [
1881
            CURLOPT_URL => $url,
1882
            CURLOPT_FOLLOWLOCATION => true, // follow redirects accept youtube.com
1883
            CURLOPT_HEADER => 0,
1884
            CURLOPT_RETURNTRANSFER => true,
1885
            CURLOPT_TIMEOUT => 4,
1886
        ];
1887
1888
        $proxySettings = api_get_configuration_value('proxy_settings');
1889
1890
        if (!empty($proxySettings) &&
1891
            isset($proxySettings['curl_setopt_array'])
1892
        ) {
1893
            $defaults[CURLOPT_PROXY] = $proxySettings['curl_setopt_array']['CURLOPT_PROXY'];
1894
            $defaults[CURLOPT_PROXYPORT] = $proxySettings['curl_setopt_array']['CURLOPT_PROXYPORT'];
1895
        }
1896
1897
        // Create a new cURL resource
1898
        $ch = curl_init();
1899
        curl_setopt_array($ch, $defaults);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_setopt_array() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1899
        curl_setopt_array(/** @scrutinizer ignore-type */ $ch, $defaults);
Loading history...
1900
1901
        // grab URL and pass it to the browser
1902
        ob_start();
1903
        $result = curl_exec($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_exec() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1903
        $result = curl_exec(/** @scrutinizer ignore-type */ $ch);
Loading history...
1904
        ob_get_clean();
1905
1906
        // close cURL resource, and free up system resources
1907
        curl_close($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_close() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1907
        curl_close(/** @scrutinizer ignore-type */ $ch);
Loading history...
1908
1909
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type string which is incompatible with the documented return type boolean.
Loading history...
1910
    }
1911
1912
    /**
1913
     * Move a link inside its category (display_order field).
1914
     *
1915
     * @param int    $id        The link ID
1916
     * @param string $direction The direction to sort the links
1917
     *
1918
     * @return bool
1919
     */
1920
    private static function moveLinkDisplayOrder($id, $direction)
1921
    {
1922
        $em = Database::getManager();
1923
        /** @var CLink $link */
1924
        $link = $em->find('ChamiloCourseBundle:CLink', $id);
1925
1926
        if (!$link) {
0 ignored issues
show
introduced by
$link is of type Chamilo\CourseBundle\Entity\CLink, thus it always evaluated to true.
Loading history...
1927
            return false;
1928
        }
1929
1930
        $compareLinks = $em
1931
            ->getRepository('ChamiloCourseBundle:CLink')
1932
            ->findBy(
1933
                [
1934
                    'cId' => $link->getCId(),
1935
                    'categoryId' => $link->getCategoryId(),
1936
                ],
1937
                ['displayOrder' => $direction]
1938
            );
1939
1940
        /** @var CLink $prevLink */
1941
        $prevLink = null;
1942
1943
        /** @var CLink $compareLink */
1944
        foreach ($compareLinks as $compareLink) {
1945
            if ($compareLink->getId() !== $link->getId()) {
1946
                $prevLink = $compareLink;
1947
1948
                continue;
1949
            }
1950
1951
            if (!$prevLink) {
1952
                return false;
1953
            }
1954
1955
            $newPrevLinkDisplayOrder = $link->getDisplayOrder();
1956
            $newLinkDisplayOrder = $prevLink->getDisplayOrder();
1957
1958
            $link->setDisplayOrder($newLinkDisplayOrder);
1959
            $prevLink->setDisplayOrder($newPrevLinkDisplayOrder);
1960
1961
            $em->merge($prevLink);
1962
            $em->merge($link);
1963
            break;
1964
        }
1965
1966
        $em->flush();
1967
1968
        return true;
1969
    }
1970
}
1971