Passed
Push — master ( 006e96...fba8cd )
by Julito
09:00
created

Link::getLinkCategories()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 81
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 47
c 0
b 0
f 0
nc 1
nop 3
dl 0
loc 81
rs 8.8452

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
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
        $course_id = $course_info['real_id'];
82
        $session_id = api_get_session_id();
83
84
        $title = stripslashes($params['title']);
85
        $urllink = $params['url'];
86
        $description = $params['description'];
87
        $categoryId = (int) $params['category_id'];
88
89
        $onhomepage = 0;
90
        if (isset($params['on_homepage'])) {
91
            $onhomepage = Security::remove_XSS($params['on_homepage']);
92
        }
93
94
        $target = '_self'; // Default target.
95
        if (!empty($params['target'])) {
96
            $target = Security::remove_XSS($params['target']);
97
        }
98
99
        $urllink = trim($urllink);
100
        $title = trim($title);
101
        $description = trim($description);
102
103
        // We ensure URL to be absolute.
104
        if (strpos($urllink, '://') === false) {
105
            $urllink = 'http://'.$urllink;
106
        }
107
108
        // If the title is empty, we use the URL as title.
109
        if ($title == '') {
110
            $title = $urllink;
111
        }
112
113
        // If the URL is invalid, an error occurs.
114
        if (!api_valid_url($urllink, true)) {
115
            // A check against an absolute URL
116
            Display::addFlash(Display::return_message(get_lang('Please give the link URL, it should be valid.'), 'error'));
117
118
            return false;
119
        } else {
120
            $category = null;
121
            $repoCategory = Container::getLinkCategoryRepository();
122
            if (!empty($categoryId)) {
123
                /** @var CLinkCategory $category */
124
                $category = $repoCategory->find($categoryId);
125
            }
126
127
            // Looking for the largest order number for this category.
128
            $link = new CLink();
129
            $link
130
                ->setCId($course_id)
131
                ->setUrl($urllink)
132
                ->setTitle($title)
133
                ->setDescription($description)
134
                ->setOnHomepage($onhomepage)
135
                ->setTarget($target)
136
                ->setSessionId($session_id)
137
                ->setCategory($category)
138
            ;
139
140
            $repo = Container::getLinkRepository();
141
142
            $courseEntity = api_get_course_entity($course_id);
143
144
            if (empty($category)) {
145
                $repo->addResourceToCourse(
146
                    $link,
147
                    ResourceLink::VISIBILITY_PUBLISHED,
148
                    api_get_user_entity(api_get_user_id()),
149
                    $courseEntity,
150
                    api_get_session_entity($session_id),
151
                    api_get_group_entity()
152
                );
153
            } else {
154
                $repo->addResourceToCourseWithParent(
155
                    $link,
156
                    $category->getResourceNode(),
157
                    ResourceLink::VISIBILITY_PUBLISHED,
158
                    api_get_user_entity(api_get_user_id()),
159
                    $courseEntity,
160
                    api_get_session_entity($session_id),
161
                    api_get_group_entity()
162
                );
163
            }
164
165
            $repo->getEntityManager()->flush();
166
            $link_id = $link->getIid();
167
168
            if ((api_get_setting('search_enabled') == 'true') &&
169
                $link_id && extension_loaded('xapian')
170
            ) {
171
                require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
172
173
                $course_int_id = $_course['real_id'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $_course seems to be never defined.
Loading history...
174
                $courseCode = $_course['code'];
175
                $specific_fields = get_specific_field_list();
176
                $ic_slide = new IndexableChunk();
177
178
                // Add all terms to db.
179
                $all_specific_terms = '';
180
                foreach ($specific_fields as $specific_field) {
181
                    if (isset($_REQUEST[$specific_field['code']])) {
182
                        $sterms = trim($_REQUEST[$specific_field['code']]);
183
                        if (!empty($sterms)) {
184
                            $all_specific_terms .= ' '.$sterms;
185
                            $sterms = explode(',', $sterms);
186
                            foreach ($sterms as $sterm) {
187
                                $ic_slide->addTerm(
188
                                    trim($sterm),
189
                                    $specific_field['code']
190
                                );
191
                                add_specific_field_value(
192
                                    $specific_field['id'],
193
                                    $courseCode,
194
                                    TOOL_LINK,
195
                                    $link_id,
196
                                    $sterm
197
                                );
198
                            }
199
                        }
200
                    }
201
                }
202
203
                // Build the chunk to index.
204
                $ic_slide->addValue('title', $title);
205
                $ic_slide->addCourseId($courseCode);
206
                $ic_slide->addToolId(TOOL_LINK);
207
                $xapian_data = [
208
                    SE_COURSE_ID => $courseCode,
209
                    SE_TOOL_ID => TOOL_LINK,
210
                    SE_DATA => [
211
                        'link_id' => (int) $link_id,
212
                    ],
213
                    SE_USER => (int) api_get_user_id(),
214
                ];
215
                $ic_slide->xapian_data = serialize($xapian_data);
216
                $description = $all_specific_terms.' '.$description;
217
                $ic_slide->addValue('content', $description);
218
219
                // Add category name if set.
220
                if (isset($categoryId) && $categoryId > 0) {
221
                    $table_link_category = Database::get_course_table(
222
                        TABLE_LINK_CATEGORY
223
                    );
224
                    $sql_cat = 'SELECT * FROM %s WHERE id=%d AND c_id = %d LIMIT 1';
225
                    $sql_cat = sprintf(
226
                        $sql_cat,
227
                        $table_link_category,
228
                        (int) $categoryId,
229
                        $course_int_id
230
                    );
231
                    $result = Database:: query($sql_cat);
232
                    if (Database:: num_rows($result) == 1) {
233
                        $row = Database:: fetch_array($result);
234
                        $ic_slide->addValue(
235
                            'category',
236
                            $row['category_title']
237
                        );
238
                    }
239
                }
240
241
                $di = new ChamiloIndexer();
242
                isset($params['language']) ? $lang = Database:: escape_string(
243
                    $params['language']
244
                ) : $lang = 'english';
245
                $di->connectDb(null, null, $lang);
246
                $di->addChunk($ic_slide);
247
248
                // Index and return search engine document id.
249
                $did = $di->index();
250
                if ($did) {
251
                    // Save it to db.
252
                    $tbl_se_ref = Database::get_main_table(
253
                        TABLE_MAIN_SEARCH_ENGINE_REF
254
                    );
255
                    $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
256
                            VALUES (NULL , \'%s\', \'%s\', %s, %s)';
257
                    $sql = sprintf(
258
                        $sql,
259
                        $tbl_se_ref,
260
                        $course_int_id,
261
                        $courseCode,
262
                        TOOL_LINK,
263
                        $link_id,
264
                        $did
265
                    );
266
                    Database:: query($sql);
267
                }
268
            }
269
            Display::addFlash(Display::return_message(get_lang('The link has been added.')));
270
271
            return $link_id;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $link_id returns the type integer which is incompatible with the documented return type boolean.
Loading history...
272
        }
273
    }
274
275
    /**
276
     * Update a link in the database.
277
     *
278
     * @param int    $linkId    The ID of the link to update
279
     * @param string $linkUrl   The new URL to be saved
280
     * @param int    $courseId
281
     * @param int    $sessionId
282
     *
283
     * @return bool
284
     */
285
    public function updateLink(
286
        $linkId,
287
        $linkUrl,
288
        $courseId = null,
289
        $sessionId = null
290
    ) {
291
        $tblLink = Database::get_course_table(TABLE_LINK);
292
        $linkUrl = Database::escape_string($linkUrl);
293
        $linkId = intval($linkId);
294
        if (is_null($courseId)) {
295
            $courseId = api_get_course_int_id();
296
        }
297
        $courseId = intval($courseId);
298
        if (is_null($sessionId)) {
299
            $sessionId = api_get_session_id();
300
        }
301
        $sessionId = intval($sessionId);
302
        if ($linkUrl != '') {
303
            $sql = "UPDATE $tblLink SET
304
                    url = '$linkUrl'
305
                    WHERE id = $linkId AND c_id = $courseId AND session_id = $sessionId";
306
            $resLink = Database::query($sql);
307
308
            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...
309
        }
310
311
        return false;
312
    }
313
314
    public static function addCategory()
315
    {
316
        $_course = api_get_course_info();
317
        $course_id = $_course['real_id'];
318
        $tbl_categories = Database::get_course_table(TABLE_LINK_CATEGORY);
319
320
        $category_title = trim($_POST['category_title']);
321
        $description = trim($_POST['description']);
322
323
        if (empty($category_title)) {
324
            echo Display::return_message(get_lang('Please give the category name'), 'error');
325
            return false;
326
        }
327
328
        // Looking for the largest order number for this category.
329
        $result = Database:: query(
330
            "SELECT MAX(display_order) FROM  $tbl_categories
331
            WHERE c_id = $course_id "
332
        );
333
        list($orderMax) = Database:: fetch_row($result);
334
        $order = $orderMax + 1;
335
        $order = (int) $order;
336
        $session_id = api_get_session_id();
337
338
        $repo = Container::getLinkCategoryRepository();
339
        $courseEntity = api_get_course_entity($course_id);
340
        $sessionEntity = api_get_session_entity($session_id);
341
342
        $category = new CLinkCategory();
343
        $category
344
            ->setSessionId($session_id)
345
            ->setCId($course_id)
346
            ->setCategoryTitle($category_title)
347
            ->setDescription($description)
348
            ->setDisplayOrder($order)
349
        ;
350
351
        $repo->addResourceToCourse(
352
            $category,
353
            ResourceLink::VISIBILITY_PUBLISHED,
354
            api_get_user_entity(api_get_user_id()),
355
            $courseEntity,
356
            $sessionEntity,
357
            api_get_group_entity()
358
        );
359
360
        $repo->getEntityManager()->flush();
361
        $linkId = $category->getIid();
362
363
        if ($linkId) {
364
            // iid
365
            $sql = "UPDATE $tbl_categories SET id = iid WHERE iid = $linkId";
366
            Database:: query($sql);
367
368
            // add link_category visibility
369
            // course ID is taken from context in api_set_default_visibility
370
            //api_set_default_visibility($linkId, TOOL_LINK_CATEGORY);
371
            /*api_item_property_update(
372
                $_course,
373
                TOOL_LINK_CATEGORY,
374
                $linkId,
375
                'LinkCategoryAdded',
376
                api_get_user_id()
377
            );
378
            api_set_default_visibility($linkId, TOOL_LINK_CATEGORY);*/
379
        }
380
381
        Display::addFlash(Display::return_message(get_lang('Category added')));
382
383
        return $linkId;
384
    }
385
386
    public static function deleteCategory($id)
387
    {
388
        $repo = Container::getLinkCategoryRepository();
389
        /** @var CLinkCategory $category */
390
        $category = $repo->find($id);
391
        if ($category)  {
0 ignored issues
show
introduced by
$category is of type Chamilo\CourseBundle\Entity\CLinkCategory, thus it always evaluated to true.
Loading history...
392
            $repo->delete($category);
393
            Display::addFlash(Display::return_message(get_lang('The category has been deleted.')));
394
395
            return true;
396
        }
397
398
        return false;
399
400
        // First we delete the category itself and afterwards all the links of this category.
401
        $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...
402
                        WHERE c_id = $course_id AND id='".$id."'";
403
        Database:: query($sql);
404
405
        $sql = "DELETE FROM ".$tbl_link."
406
                        WHERE c_id = $course_id AND category_id='".$id."'";
407
        Database:: query($sql);
408
409
        api_item_property_update(
410
            $courseInfo,
411
            TOOL_LINK_CATEGORY,
412
            $id,
413
            'delete',
414
            api_get_user_id()
415
        );
416
417
        Display::addFlash(Display::return_message(get_lang('The category has been deleted.')));
418
419
        return true;
420
    }
421
422
    /**
423
     * Used to delete a link.
424
     * @param int    $id
425
     *
426
     * @return bool
427
     */
428
    public static function deleteLink($id)
429
    {
430
        $repo = Container::getLinkRepository();
431
        $link = $repo->find($id);
432
        if ($link) {
0 ignored issues
show
introduced by
$link is of type Chamilo\CoreBundle\Entit...ource\ResourceInterface, thus it always evaluated to true.
Loading history...
433
            $repo->getEntityManager()->remove($link);
434
            $repo->getEntityManager()->flush();
435
            self::delete_link_from_search_engine(api_get_course_id(), $id);
436
            Skill::deleteSkillsFromItem($id, ITEM_TYPE_LINK);
437
            Display::addFlash(Display::return_message(get_lang('The link has been deleted')));
438
439
            return true;
440
        }
441
442
        return false;
443
444
        $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...
445
        $tbl_link = Database::get_course_table(TABLE_LINK);
446
447
        $course_id = $courseInfo['real_id'];
448
        $id = intval($id);
449
450
        if (empty($id)) {
451
            return false;
452
        }
453
454
        // -> Items are no longer physically deleted,
455
        // but the visibility is set to 2 (in item_property).
456
        // This will make a restore function possible for the platform administrator.
457
        $sql = "UPDATE $tbl_link SET on_homepage='0'
458
                WHERE c_id = $course_id AND id='".$id."'";
459
        Database:: query($sql);
460
461
        /*api_item_property_update(
462
            $courseInfo,
463
            TOOL_LINK,
464
            $id,
465
            'delete',
466
            api_get_user_id()
467
        );*/
468
469
        return true;
470
    }
471
472
    /**
473
     * Removes a link from search engine database.
474
     *
475
     * @param string $course_id Course code
476
     * @param int    $link_id   Document id to delete
477
     */
478
    public static function delete_link_from_search_engine($course_id, $link_id)
479
    {
480
        // Remove from search engine if enabled.
481
        if (api_get_setting('search_enabled') === 'true') {
482
            $tbl_se_ref = Database::get_main_table(
483
                TABLE_MAIN_SEARCH_ENGINE_REF
484
            );
485
            $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
486
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_LINK, $link_id);
487
            $res = Database:: query($sql);
488
            if (Database:: num_rows($res) > 0) {
489
                $row = Database::fetch_array($res);
490
                $di = new ChamiloIndexer();
491
                $di->remove_document($row['search_did']);
492
            }
493
            $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
494
            $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_LINK, $link_id);
495
            Database:: query($sql);
496
497
            // Remove terms from db.
498
            require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
499
            delete_all_values_for_item($course_id, TOOL_DOCUMENT, $link_id);
500
        }
501
    }
502
503
    /**
504
     * Get link info.
505
     *
506
     * @param int $id
507
     *
508
     * @return array link info
509
     */
510
    public static function getLinkInfo($id)
511
    {
512
        $tbl_link = Database::get_course_table(TABLE_LINK);
513
        $course_id = api_get_course_int_id();
514
        $id = (int) $id;
515
516
        if (empty($id) || empty($course_id)) {
517
            return [];
518
        }
519
520
        $sql = "SELECT * FROM $tbl_link
521
                WHERE c_id = $course_id AND iid= $id ";
522
        $result = Database::query($sql);
523
        $data = [];
524
        if (Database::num_rows($result)) {
525
            $data = Database::fetch_array($result);
526
        }
527
528
        return $data;
529
    }
530
531
    /**
532
     * @param int   $id
533
     * @param array $values
534
     */
535
    public static function editLink($id, $values = [])
536
    {
537
        $tbl_link = Database::get_course_table(TABLE_LINK);
538
        $_course = api_get_course_info();
539
        $course_id = $_course['real_id'];
540
        $id = intval($id);
541
542
        $values['url'] = trim($values['url']);
543
        $values['title'] = trim($values['title']);
544
        $values['description'] = trim($values['description']);
545
        $values['target'] = empty($values['target']) ? '_self' : $values['target'];
546
        $values['on_homepage'] = isset($values['on_homepage']) ? $values['on_homepage'] : '';
547
548
        $categoryId = intval($values['category_id']);
549
550
        // We ensure URL to be absolute.
551
        if (strpos($values['url'], '://') === false) {
552
            $values['url'] = 'http://'.$_POST['url'];
553
        }
554
555
        // If the title is empty, we use the URL as title.
556
        if ($values['title'] == '') {
557
            $values['title'] = $values['url'];
558
        }
559
560
        // If the URL is invalid, an error occurs.
561
        if (!api_valid_url($values['url'], true)) {
562
            Display::addFlash(
563
                Display::return_message(get_lang('Please give the link URL, it should be valid.'), 'error')
564
            );
565
566
            return false;
567
        }
568
569
        if (empty($id) || empty($course_id)) {
570
            return false;
571
        }
572
573
        // Finding the old category_id.
574
        $sql = "SELECT * FROM $tbl_link
575
                WHERE c_id = $course_id AND id='".$id."'";
576
        $result = Database:: query($sql);
577
        $row = Database:: fetch_array($result);
578
        $category_id = $row['category_id'];
579
580
        if ($category_id != $values['category_id']) {
581
            $sql = "SELECT MAX(display_order)
582
                    FROM $tbl_link
583
                    WHERE
584
                        c_id = $course_id AND
585
                        category_id='".intval($values['category_id'])."'";
586
            $result = Database:: query($sql);
587
            list($max_display_order) = Database:: fetch_row($result);
588
            $max_display_order++;
589
        } else {
590
            $max_display_order = $row['display_order'];
591
        }
592
        $params = [
593
            'url' => $values['url'],
594
            'title' => $values['title'],
595
            'description' => $values['description'],
596
            'category_id' => $values['category_id'],
597
            'display_order' => $max_display_order,
598
            'on_homepage' => $values['on_homepage'],
599
            'target' => $values['target'],
600
        ];
601
602
        Database::update(
603
            $tbl_link,
604
            $params,
605
            ['c_id = ? AND id = ?' => [$course_id, $id]]
606
        );
607
608
        // Update search enchine and its values table if enabled.
609
        if (api_get_setting('search_enabled') == 'true') {
610
            $course_int_id = api_get_course_int_id();
611
            $course_id = api_get_course_id();
612
            $link_title = Database:: escape_string($values['title']);
613
            $link_description = Database:: escape_string($values['description']);
614
615
            // Actually, it consists on delete terms from db,
616
            // insert new ones, create a new search engine document, and remove the old one.
617
            // Get search_did.
618
            $tbl_se_ref = Database::get_main_table(
619
                TABLE_MAIN_SEARCH_ENGINE_REF
620
            );
621
            $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
622
            $sql = sprintf(
623
                $sql,
624
                $tbl_se_ref,
625
                $course_id,
626
                TOOL_LINK,
627
                $id
628
            );
629
            $res = Database:: query($sql);
630
631
            if (Database:: num_rows($res) > 0) {
632
                require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
633
634
                $se_ref = Database:: fetch_array($res);
635
                $specific_fields = get_specific_field_list();
636
                $ic_slide = new IndexableChunk();
637
638
                $all_specific_terms = '';
639
                foreach ($specific_fields as $specific_field) {
640
                    delete_all_specific_field_value(
641
                        $course_id,
642
                        $specific_field['id'],
643
                        TOOL_LINK,
644
                        $id
645
                    );
646
                    if (isset($_REQUEST[$specific_field['code']])) {
647
                        $sterms = trim(
648
                            $_REQUEST[$specific_field['code']]
649
                        );
650
                        if (!empty($sterms)) {
651
                            $all_specific_terms .= ' '.$sterms;
652
                            $sterms = explode(',', $sterms);
653
                            foreach ($sterms as $sterm) {
654
                                $ic_slide->addTerm(
655
                                    trim($sterm),
656
                                    $specific_field['code']
657
                                );
658
                                add_specific_field_value(
659
                                    $specific_field['id'],
660
                                    $course_id,
661
                                    TOOL_LINK,
662
                                    $id,
663
                                    $sterm
664
                                );
665
                            }
666
                        }
667
                    }
668
                }
669
670
                // Build the chunk to index.
671
                $ic_slide->addValue("title", $link_title);
672
                $ic_slide->addCourseId($course_id);
673
                $ic_slide->addToolId(TOOL_LINK);
674
                $xapian_data = [
675
                    SE_COURSE_ID => $course_id,
676
                    SE_TOOL_ID => TOOL_LINK,
677
                    SE_DATA => [
678
                        'link_id' => (int) $id,
679
                    ],
680
                    SE_USER => (int) api_get_user_id(),
681
                ];
682
                $ic_slide->xapian_data = serialize($xapian_data);
683
                $link_description = $all_specific_terms.' '.$link_description;
684
                $ic_slide->addValue('content', $link_description);
685
686
                // Add category name if set.
687
                if (isset($categoryId) && $categoryId > 0) {
688
                    $table_link_category = Database::get_course_table(
689
                        TABLE_LINK_CATEGORY
690
                    );
691
                    $sql_cat = 'SELECT * FROM %s WHERE id=%d and c_id = %d LIMIT 1';
692
                    $sql_cat = sprintf(
693
                        $sql_cat,
694
                        $table_link_category,
695
                        $categoryId,
696
                        $course_int_id
697
                    );
698
                    $result = Database:: query($sql_cat);
699
                    if (Database:: num_rows($result) == 1) {
700
                        $row = Database:: fetch_array($result);
701
                        $ic_slide->addValue(
702
                            'category',
703
                            $row['category_title']
704
                        );
705
                    }
706
                }
707
708
                $di = new ChamiloIndexer();
709
                isset($_POST['language']) ? $lang = Database:: escape_string($_POST['language']) : $lang = 'english';
710
                $di->connectDb(null, null, $lang);
711
                $di->remove_document($se_ref['search_did']);
712
                $di->addChunk($ic_slide);
713
714
                // Index and return search engine document id.
715
                $did = $di->index();
716
                if ($did) {
717
                    // Save it to db.
718
                    $sql = 'DELETE FROM %s
719
                            WHERE course_code=\'%s\'
720
                            AND tool_id=\'%s\'
721
                            AND ref_id_high_level=\'%s\'';
722
                    $sql = sprintf(
723
                        $sql,
724
                        $tbl_se_ref,
725
                        $course_id,
726
                        TOOL_LINK,
727
                        $id
728
                    );
729
                    Database:: query($sql);
730
                    $sql = 'INSERT INTO %s (c_id, id, course_code, tool_id, ref_id_high_level, search_did)
731
                            VALUES (NULL , \'%s\', \'%s\', %s, %s)';
732
                    $sql = sprintf(
733
                        $sql,
734
                        $tbl_se_ref,
735
                        $course_int_id,
736
                        $course_id,
737
                        TOOL_LINK,
738
                        $id,
739
                        $did
740
                    );
741
                    Database:: query($sql);
742
                }
743
            }
744
        }
745
746
        // "WHAT'S NEW" notification: update table last_toolEdit.
747
        api_item_property_update(
748
            $_course,
749
            TOOL_LINK,
750
            $id,
751
            'LinkUpdated',
752
            api_get_user_id()
753
        );
754
        Display::addFlash(Display::return_message(get_lang('The link has been modified.')));
755
    }
756
757
    /**
758
     * @param int   $id
759
     * @param array $values
760
     *
761
     * @return bool
762
     */
763
    public static function editCategory($id, $values)
764
    {
765
        $table = Database::get_course_table(TABLE_LINK_CATEGORY);
766
        $course_id = api_get_course_int_id();
767
        $id = (int) $id;
768
769
        // This is used to put the modified info of the category-form into the database.
770
        $params = [
771
            'category_title' => $values['category_title'],
772
            'description' => $values['description'],
773
        ];
774
        Database::update(
775
            $table,
776
            $params,
777
            ['c_id = ? AND id = ?' => [$course_id, $id]]
778
        );
779
        Display::addFlash(Display::return_message(get_lang('The category has been modified.')));
780
781
        return true;
782
    }
783
784
    /**
785
     * Changes the visibility of a link.
786
     */
787
    public static function setVisible($id, $scope)
788
    {
789
        if ($scope == TOOL_LINK) {
790
            /*api_item_property_update(
791
                $_course,
792
                TOOL_LINK,
793
                $id,
794
                $_GET['action'],
795
                $_user['user_id']
796
            );*/
797
            $repo = Container::getLinkRepository();
798
            /** @var CLink $link */
799
            $link = $repo->find($id);
800
            if ($link) {
0 ignored issues
show
introduced by
$link is of type Chamilo\CourseBundle\Entity\CLink, thus it always evaluated to true.
Loading history...
801
                $repo->setVisibilityPublished($link);
802
            }
803
        } elseif ($scope == TOOL_LINK_CATEGORY) {
804
            $repo = Container::getLinkCategoryRepository();
805
            /** @var CLink $link */
806
            $link = $repo->find($id);
807
            if ($link) {
0 ignored issues
show
introduced by
$link is of type Chamilo\CourseBundle\Entity\CLink, thus it always evaluated to true.
Loading history...
808
                $repo->setVisibilityPublished($link);
809
            }
810
            /*api_item_property_update(
811
                $_course,
812
                TOOL_LINK_CATEGORY,
813
                $id,
814
                $_GET['action'],
815
                $_user['user_id']
816
            );*/
817
        }
818
        Display::addFlash(Display::return_message(get_lang('The visibility has been changed.')));
819
    }
820
    public static function setInvisible($id, $scope)
821
    {
822
        if ($scope == TOOL_LINK) {
823
            $repo = Container::getLinkRepository();
824
            /** @var CLink $link */
825
            $link = $repo->find($id);
826
            if ($link) {
0 ignored issues
show
introduced by
$link is of type Chamilo\CourseBundle\Entity\CLink, thus it always evaluated to true.
Loading history...
827
                $repo->setVisibilityDraft($link);
828
            }
829
        } elseif ($scope == TOOL_LINK_CATEGORY) {
830
            $repo = Container::getLinkCategoryRepository();
831
            /** @var CLinkCategory $link */
832
            $link = $repo->find($id);
833
            if ($link) {
0 ignored issues
show
introduced by
$link is of type Chamilo\CourseBundle\Entity\CLinkCategory, thus it always evaluated to true.
Loading history...
834
                $repo->setVisibilityDraft($link);
835
            }
836
        }
837
        Display::addFlash(Display::return_message(get_lang('The visibility has been changed.')));
838
    }
839
840
    /**
841
     * Generate SQL to select all the links categories in the current course and
842
     * session.
843
     *
844
     * @param int  $courseId
845
     * @param int  $sessionId
846
     * @param bool $withBaseContent
847
     *
848
     * @return CLinkCategory[]
849
     */
850
    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

850
    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...
851
    {
852
        $repo = Container::getLinkCategoryRepository();
853
854
        $courseEntity = api_get_course_entity($courseId);
855
        $sessionEntity = api_get_session_entity($sessionId);
856
857
        $qb = $repo->getResourcesByCourse($courseEntity, $sessionEntity);
858
859
        return $qb->getQuery()->getResult();
860
861
        $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...
862
        $tblItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
863
        $courseId = (int) $courseId;
864
        $courseInfo = api_get_course_info_by_id($courseId);
865
866
        // Condition for the session.
867
        $sessionCondition = api_get_session_condition(
868
            $sessionId,
869
            true,
870
            $withBaseContent,
871
            'linkcat.session_id'
872
        );
873
874
        // Getting links
875
        $sql = "SELECT *, linkcat.id
876
                FROM $tblLinkCategory linkcat
877
                WHERE
878
                    linkcat.c_id = $courseId
879
                    $sessionCondition
880
                ORDER BY linkcat.display_order DESC";
881
882
        $result = Database::query($sql);
883
        $categories = Database::store_result($result);
884
885
        $sql = "SELECT *, linkcat.id
886
                FROM $tblLinkCategory linkcat
887
                INNER JOIN $tblItemProperty ip
888
                ON (linkcat.id = ip.ref AND linkcat.c_id = ip.c_id)
889
                WHERE
890
                    ip.tool = '".TOOL_LINK_CATEGORY."' AND
891
                    (ip.visibility = '0' OR ip.visibility = '1')
892
                    $sessionCondition AND
893
                    linkcat.c_id = ".$courseId."
894
                ORDER BY linkcat.display_order DESC";
895
896
        $result = Database::query($sql);
897
898
        $categoryInItemProperty = [];
899
        if (Database::num_rows($result)) {
900
            while ($row = Database::fetch_array($result, 'ASSOC')) {
901
                $categoryInItemProperty[$row['id']] = $row;
902
            }
903
        }
904
905
        foreach ($categories as &$category) {
906
            if (!isset($categoryInItemProperty[$category['id']])) {
907
                api_item_property_update(
908
                    $courseInfo,
909
                    TOOL_LINK_CATEGORY,
910
                    $category['id'],
911
                    'LinkCategoryAdded',
912
                    api_get_user_id()
913
                );
914
            }
915
        }
916
917
        $sql = "SELECT DISTINCT linkcat.*, visibility
918
                FROM $tblLinkCategory linkcat
919
                INNER JOIN $tblItemProperty ip
920
                ON (linkcat.id = ip.ref AND linkcat.c_id = ip.c_id)
921
                WHERE
922
                    ip.tool = '".TOOL_LINK_CATEGORY."' AND
923
                    (ip.visibility = '0' OR ip.visibility = '1')
924
                    $sessionCondition AND
925
                    linkcat.c_id = ".$courseId."
926
                ORDER BY linkcat.display_order DESC
927
                ";
928
        $result = Database::query($sql);
929
930
        return Database::store_result($result, 'ASSOC');
931
    }
932
933
    /**
934
     * @param int $categoryId
935
     * @param $courseId
936
     * @param $sessionId
937
     * @param bool $withBaseContent
938
     *
939
     * @return CLink[]|null
940
     */
941
    public static function getLinksPerCategory(
942
        $categoryId,
943
        $courseId,
944
        $sessionId,
945
        $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

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

1891
        curl_setopt_array(/** @scrutinizer ignore-type */ $ch, $defaults);
Loading history...
1892
1893
        // grab URL and pass it to the browser
1894
        ob_start();
1895
        $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

1895
        $result = curl_exec(/** @scrutinizer ignore-type */ $ch);
Loading history...
1896
        ob_get_clean();
1897
1898
        // close cURL resource, and free up system resources
1899
        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

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