Completed
Push — master ( 27e209...a08afa )
by Julito
186:04 queued 150:53
created

CourseCategory::deleteNode()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 16
nc 5
nop 1
dl 0
loc 23
rs 8.7972
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
/**
5
 * Class CourseCategory
6
 */
7
class CourseCategory
8
{
9
    /**
10
     * Returns the category fields from the database from an int ID
11
     * @param int $categoryId The category ID
12
     * @return array
13
     */
14
    public static function getCategoryById($categoryId)
15
    {
16
        $table = Database::get_main_table(TABLE_MAIN_CATEGORY);
17
        $categoryId = intval($categoryId);
18
        $sql = "SELECT * FROM $table WHERE id = $categoryId";
19
        $result = Database::query($sql);
20
        if (Database::num_rows($result)) {
21
            return Database::fetch_array($result, 'ASSOC');
22
        }
23
24
        return [];
25
    }
26
27
    /**
28
     * Get category details from a simple category code
29
     * @param string $category The literal category code
30
     * @return array
31
     */
32
    public static function getCategory($category)
33
    {
34
        $table = Database::get_main_table(TABLE_MAIN_CATEGORY);
35
        $category = Database::escape_string($category);
36
        $sql = "SELECT * FROM $table WHERE code ='$category'";
37
        $result = Database::query($sql);
38
        if (Database::num_rows($result)) {
39
            return Database::fetch_array($result, 'ASSOC');
40
        }
41
42
        return [];
43
    }
44
45
    /**
46
     * @param string $category Optional. Parent category code
47
     * @return array
48
     */
49
    public static function getCategories($category = null)
50
    {
51
        $tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY);
52
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
53
        $category = Database::escape_string($category);
54
        $conditions = null;
55
56
        $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
57
        $conditions = " INNER JOIN $table a ON (t1.id = a.course_category_id)";
58
        $whereCondition = " AND a.access_url_id = ".api_get_current_access_url_id();
59
60
        $parentIdCondition = " AND (t1.parent_id IS NULL OR t1.parent_id = '' )";
61
        if (!empty($category)) {
62
            $parentIdCondition = " AND t1.parent_id = '$category' ";
63
        }
64
65
        $sql = "SELECT
66
                t1.name,
67
                t1.code,
68
                t1.parent_id,
69
                t1.tree_pos,
70
                t1.children_count,
71
                COUNT(DISTINCT t3.code) AS nbr_courses
72
                FROM $tbl_category t1
73
                $conditions
74
                LEFT JOIN $tbl_category t2
75
                ON t1.code = t2.parent_id
76
                LEFT JOIN $tbl_course t3
77
                ON t3.category_code=t1.code
78
                WHERE
79
                    1 = 1
80
                    $parentIdCondition
81
                    $whereCondition
82
                GROUP BY t1.name,
83
                         t1.code,
84
                         t1.parent_id,
85
                         t1.tree_pos,
86
                         t1.children_count
87
                ORDER BY t1.tree_pos";
88
89
        $result = Database::query($sql);
90
        $categories = Database::store_result($result);
91
92
        return $categories;
93
    }
94
95
    /**
96
     * @param string $code
97
     * @param string $name
98
     * @param string $canHaveCourses
99
     * @param int $parent_id
100
     *
101
     * @return bool
102
     */
103
    public static function addNode($code, $name, $canHaveCourses, $parent_id)
104
    {
105
        $table = Database::get_main_table(TABLE_MAIN_CATEGORY);
106
        $code = trim($code);
107
        $name = trim($name);
108
        $parent_id = trim($parent_id);
109
110
        $code = CourseManager::generate_course_code($code);
111
        $sql = "SELECT 1 FROM $table
112
                WHERE code = '".Database::escape_string($code)."'";
113
        $result = Database::query($sql);
114
        if (Database::num_rows($result)) {
115
            return false;
116
        }
117
        $result = Database::query("SELECT MAX(tree_pos) AS maxTreePos FROM $table");
118
        $row = Database::fetch_array($result);
119
        $tree_pos = $row['maxTreePos'] + 1;
120
121
        $params = [
122
            'name' => $name,
123
            'code' => $code,
124
            'parent_id' => empty($parent_id) ? null : $parent_id,
125
            'tree_pos' => $tree_pos,
126
            'children_count' => 0,
127
            'auth_course_child' => $canHaveCourses,
128
            'auth_cat_child' => 'TRUE'
129
        ];
130
131
        $categoryId = Database::insert($table, $params);
132
133
        self::updateParentCategoryChildrenCount($parent_id, 1);
134
        self::addToUrl($categoryId);
0 ignored issues
show
Bug introduced by
It seems like $categoryId can also be of type false; however, parameter $id of CourseCategory::addToUrl() does only seem to accept integer, 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

134
        self::addToUrl(/** @scrutinizer ignore-type */ $categoryId);
Loading history...
135
136
        return $categoryId;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $categoryId also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
137
    }
138
139
    /**
140
     * Recursive function that updates the count of children in the parent
141
     * @param string $categoryId Category ID
142
     * @param    int $delta The number to add or delete (1 to add one, -1 to remove one)
143
     */
144
    public static function updateParentCategoryChildrenCount($categoryId, $delta = 1)
145
    {
146
        $table = Database::get_main_table(TABLE_MAIN_CATEGORY);
147
        $categoryId = Database::escape_string($categoryId);
148
        $delta = intval($delta);
149
        // First get to the highest level possible in the tree
150
        $result = Database::query("SELECT parent_id FROM $table WHERE code = '$categoryId'");
151
        $row = Database::fetch_array($result);
152
        if ($row !== false and $row['parent_id'] != 0) {
153
            // if a parent was found, enter there to see if he's got one more parent
154
            self::updateParentCategoryChildrenCount($row['parent_id'], $delta);
155
        }
156
        // Now we're at the top, get back down to update each child
157
        //$children_count = courseCategoryChildrenCount($categoryId);
158
        if ($delta >= 0) {
159
            $sql = "UPDATE $table SET children_count = (children_count + $delta)
160
                WHERE code = '$categoryId'";
161
        } else {
162
            $sql = "UPDATE $table SET children_count = (children_count - ".abs($delta).")
163
                WHERE code = '$categoryId'";
164
        }
165
        Database::query($sql);
166
    }
167
168
    /**
169
     * @param string $node
170
     */
171
    public static function deleteNode($node)
172
    {
173
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
174
        $tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY);
175
        $node = Database::escape_string($node);
176
        $result = Database::query("SELECT parent_id, tree_pos FROM $tbl_category WHERE code='$node'");
177
178
        if ($row = Database::fetch_array($result)) {
179
            if (!empty($row['parent_id'])) {
180
                Database::query(
181
                    "UPDATE $tbl_course SET category_code = '".$row['parent_id']."' WHERE category_code='$node'"
182
                );
183
                Database::query("UPDATE $tbl_category SET parent_id='".$row['parent_id']."' WHERE parent_id='$node'");
184
            } else {
185
                Database::query("UPDATE $tbl_course SET category_code='' WHERE category_code='$node'");
186
                Database::query("UPDATE $tbl_category SET parent_id=NULL WHERE parent_id='$node'");
187
            }
188
189
            Database::query("UPDATE $tbl_category SET tree_pos=tree_pos-1 WHERE tree_pos > '".$row['tree_pos']."'");
190
            Database::query("DELETE FROM $tbl_category WHERE code='$node'");
191
192
            if (!empty($row['parent_id'])) {
193
                self::updateParentCategoryChildrenCount($row['parent_id'], -1);
194
            }
195
        }
196
    }
197
198
    /**
199
     * @param string $code
200
     * @param string $name
201
     * @param string $canHaveCourses
202
     * @param string $old_code
203
     * @return bool
204
     */
205
    public static function editNode($code, $name, $canHaveCourses, $old_code)
206
    {
207
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
208
        $tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY);
209
210
        $code = trim(Database::escape_string($code));
211
        $name = trim(Database::escape_string($name));
212
        $old_code = Database::escape_string($old_code);
213
        $canHaveCourses = Database::escape_string($canHaveCourses);
214
215
        $code = CourseManager::generate_course_code($code);
216
        // Updating category
217
        $sql = "UPDATE $tbl_category SET 
218
                    name='$name', 
219
                    code='$code', 
220
                    auth_course_child = '$canHaveCourses'
221
                WHERE code = '$old_code'";
222
        Database::query($sql);
223
224
        // Updating children
225
        $sql = "UPDATE $tbl_category SET parent_id = '$code'
226
            WHERE parent_id = '$old_code'";
227
        Database::query($sql);
228
229
        // Updating course category
230
        $sql = "UPDATE $tbl_course SET category_code = '$code'
231
            WHERE category_code = '$old_code' ";
232
        Database::query($sql);
233
234
        return true;
235
    }
236
237
    /**
238
     * Move a node up on display
239
     * @param string $code
240
     * @param int $tree_pos
241
     * @param string $parent_id
242
     *
243
     * @return bool
244
     */
245
    public static function moveNodeUp($code, $tree_pos, $parent_id)
246
    {
247
        $table = Database::get_main_table(TABLE_MAIN_CATEGORY);
248
        $code = Database::escape_string($code);
249
        $tree_pos = intval($tree_pos);
250
        $parent_id = Database::escape_string($parent_id);
251
252
        $parentIdCondition = " AND (parent_id IS NULL OR parent_id = '' )";
253
        if (!empty($parent_id)) {
254
            $parentIdCondition = " AND parent_id = '$parent_id' ";
255
        }
256
257
        $sql = "SELECT code,tree_pos
258
                FROM $table
259
                WHERE
260
                    tree_pos < $tree_pos
261
                    $parentIdCondition
262
                ORDER BY tree_pos DESC
263
                LIMIT 0,1";
264
265
        $result = Database::query($sql);
266
        if (!$row = Database::fetch_array($result)) {
267
            $sql = "SELECT code, tree_pos
268
                    FROM $table
269
                    WHERE
270
                        tree_pos > $tree_pos
271
                        $parentIdCondition
272
                    ORDER BY tree_pos DESC
273
                    LIMIT 0,1";
274
            $result2 = Database::query($sql);
275
            if (!$row = Database::fetch_array($result2)) {
276
                return false;
277
            }
278
        }
279
280
        $sql = "UPDATE $table
281
                SET tree_pos ='".$row['tree_pos']."'
282
                WHERE code='$code'";
283
        Database::query($sql);
284
285
        $sql = "UPDATE $table
286
                SET tree_pos = '$tree_pos'
287
                WHERE code= '".$row['code']."'";
288
        Database::query($sql);
289
290
        return true;
291
    }
292
293
    /**
294
     * Counts the number of children categories a category has
295
     * @param int $categoryId The ID of the category of which we want to count the children
296
     *
297
     * @return mixed The number of subcategories this category has
298
     */
299
    public static function courseCategoryChildrenCount($categoryId)
300
    {
301
        $table = Database::get_main_table(TABLE_MAIN_CATEGORY);
302
        $categoryId = intval($categoryId);
303
        $count = 0;
304
        if (empty($categoryId)) {
305
            return 0;
306
        }
307
        $sql = "SELECT id, code FROM $table 
308
                WHERE parent_id = $categoryId";
309
        $result = Database::query($sql);
310
        while ($row = Database::fetch_array($result)) {
311
            $count += self::courseCategoryChildrenCount($row['id']);
312
        }
313
        $sql = "UPDATE $table SET 
314
                    children_count = $count 
315
                WHERE id = $categoryId";
316
        Database::query($sql);
317
318
        return $count + 1;
319
    }
320
321
    /**
322
     * @param string $categoryCode
323
     *
324
     * @return array
325
     */
326
    public static function getChildren($categoryCode)
327
    {
328
        $table = Database::get_main_table(TABLE_MAIN_CATEGORY);
329
        $categoryCode = Database::escape_string($categoryCode);
330
        $sql = "SELECT code, id FROM $table 
331
                WHERE parent_id = '$categoryCode'";
332
        $result = Database::query($sql);
333
        $children = [];
334
        while ($row = Database::fetch_array($result, 'ASSOC')) {
335
            $children[] = $row;
336
            $subChildren = self::getChildren($row['code']);
337
            $children = array_merge($children, $subChildren);
338
        }
339
340
        return $children;
341
    }
342
343
    /**
344
     * @param string $categoryCode
345
     *
346
     * @return array
347
     */
348
    public static function getParents($categoryCode)
349
    {
350
        if (empty($categoryCode)) {
351
            return [];
352
        }
353
354
        $table = Database::get_main_table(TABLE_MAIN_CATEGORY);
355
        $categoryCode = Database::escape_string($categoryCode);
356
        $sql = "SELECT code, parent_id 
357
                FROM $table
358
                WHERE code = '$categoryCode'";
359
360
        $result = Database::query($sql);
361
        $children = [];
362
        while ($row = Database::fetch_array($result, 'ASSOC')) {
363
            $parent = self::getCategory($row['parent_id']);
364
            $children[] = $row;
365
            $subChildren = self::getParents($parent ? $parent['code'] : null);
366
            $children = array_merge($children, $subChildren);
367
        }
368
369
        return $children;
370
    }
371
372
    /**
373
     * @param string $categoryCode
374
     * @return null|string
375
     */
376
    public static function getParentsToString($categoryCode)
377
    {
378
        $parents = self::getParents($categoryCode);
379
380
        if (!empty($parents)) {
381
            $parents = array_reverse($parents);
382
            $categories = [];
383
            foreach ($parents as $category) {
384
                $categories[] = $category['code'];
385
            }
386
            $categoriesInString = implode(' > ', $categories).' > ';
387
388
            return $categoriesInString;
389
        }
390
391
        return null;
392
    }
393
394
    /**
395
     * @param string $categorySource
396
     *
397
     * @return string
398
     */
399
    public static function listCategories($categorySource)
400
    {
401
        $categorySource = isset($categorySource) ? $categorySource : null;
402
        $categories = self::getCategories($categorySource);
403
404
        if (count($categories) > 0) {
405
            $table = new HTML_Table(['class' => 'data_table']);
406
            $column = 0;
407
            $row = 0;
408
            $headers = [
409
                get_lang('Category'),
410
                get_lang('SubCat'),
411
                get_lang('Courses'),
412
                get_lang('Actions')
413
            ];
414
            foreach ($headers as $header) {
415
                $table->setHeaderContents($row, $column, $header);
416
                $column++;
417
            }
418
            $row++;
419
            $mainUrl = api_get_path(WEB_CODE_PATH).'admin/course_category.php?category='.$categorySource;
420
421
            $editIcon = Display::return_icon(
422
                'edit.png',
423
                get_lang('EditNode'),
424
                null,
425
                ICON_SIZE_SMALL
426
            );
427
            $deleteIcon = Display::return_icon(
428
                'delete.png',
429
                get_lang('DeleteNode'),
430
                null,
431
                ICON_SIZE_SMALL
432
            );
433
            $moveIcon = Display::return_icon(
434
                'up.png',
435
                get_lang('UpInSameLevel'),
436
                null,
437
                ICON_SIZE_SMALL
438
            );
439
440
            foreach ($categories as $category) {
441
                $editUrl = $mainUrl.'&id='.$category['code'].'&action=edit';
442
                $moveUrl = $mainUrl.'&id='.$category['code'].'&action=moveUp&tree_pos='.$category['tree_pos'];
443
                $deleteUrl = $mainUrl.'&id='.$category['code'].'&action=delete';
444
445
                $actions = Display::url($editIcon, $editUrl).
446
                    Display::url($moveIcon, $moveUrl).
447
                    Display::url($deleteIcon, $deleteUrl);
448
449
                $url = api_get_path(WEB_CODE_PATH).'admin/course_category.php?category='.$category['code'];
450
                $title = Display::url(
451
                    Display::return_icon(
452
                        'folder_document.gif',
453
                        get_lang('OpenNode'),
454
                        null,
455
                        ICON_SIZE_SMALL
456
                    ).' '.$category['name'].' ('.$category['code'].')',
457
                    $url
458
                );
459
                $content = [
460
                    $title,
461
                    $category['children_count'],
462
                    $category['nbr_courses'],
463
                    $actions
464
                ];
465
                $column = 0;
466
                foreach ($content as $value) {
467
                    $table->setCellContents($row, $column, $value);
468
                    $column++;
469
                }
470
                $row++;
471
            }
472
473
            return $table->toHtml();
474
        } else {
475
            return Display::return_message(get_lang('NoCategories'), 'warning');
476
        }
477
    }
478
479
    /**
480
     * @return array
481
     */
482
    public static function getCategoriesToDisplayInHomePage()
483
    {
484
        $table = Database::get_main_table(TABLE_MAIN_CATEGORY);
485
        $sql = "SELECT name FROM $table
486
                WHERE parent_id IS NULL
487
                ORDER BY tree_pos";
488
489
        return Database::store_result(Database::query($sql));
490
    }
491
492
    /**
493
     * @param int $id
494
     *
495
     * @return bool
496
     */
497
    public static function addToUrl($id)
498
    {
499
        UrlManager::addCourseCategoryListToUrl(
500
            [$id],
501
            [api_get_current_access_url_id()]
502
        );
503
    }
504
505
    /**
506
     * @param string $categoryCode
507
     *
508
     * @return array
509
     */
510
    public static function getCategoriesCanBeAddedInCourse($categoryCode)
511
    {
512
        $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
513
        $conditions = " INNER JOIN $table a ON (c.id = a.course_category_id)";
514
        $whereCondition = " AND a.access_url_id = ".api_get_current_access_url_id();
515
516
        $tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY);
517
        $sql = "SELECT code, name
518
                FROM $tbl_category c
519
                $conditions
520
                WHERE (auth_course_child = 'TRUE' OR code = '".Database::escape_string($categoryCode)."')
521
                $whereCondition
522
                ORDER BY tree_pos";
523
        $res = Database::query($sql);
524
525
        $categories[''] = '-';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$categories was never initialized. Although not strictly required by PHP, it is generally a good practice to add $categories = array(); before regardless.
Loading history...
526
        while ($cat = Database::fetch_array($res)) {
527
            $categories[$cat['code']] = '('.$cat['code'].') '.$cat['name'];
528
            ksort($categories);
529
        }
530
531
        return $categories;
532
    }
533
534
    /**
535
     * @return array
536
     */
537
    public static function browseCourseCategories()
538
    {
539
        $tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY);
540
        $conditions = null;
541
542
        $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
543
        $conditions = " INNER JOIN $table a ON (c.id = a.course_category_id)";
544
        $whereCondition = " WHERE a.access_url_id = ".api_get_current_access_url_id();
545
546
        $sql = "SELECT c.* FROM $tbl_category c
547
                $conditions
548
                $whereCondition
549
                ORDER BY tree_pos ASC";
550
        $result = Database::query($sql);
551
        $url_access_id = 1;
552
        if (api_is_multiple_url_enabled()) {
553
            $url_access_id = api_get_current_access_url_id();
554
        }
555
556
        $countCourses = CourseManager::countAvailableCourses($url_access_id);
557
558
        $categories = [];
559
        $categories[0][0] = [
560
            'id' => 0,
561
            'name' => get_lang('DisplayAll'),
562
            'code' => 'ALL',
563
            'parent_id' => null,
564
            'tree_pos' => 0,
565
            'count_courses' => $countCourses
566
        ];
567
568
        while ($row = Database::fetch_array($result)) {
569
            $count_courses = self::countCoursesInCategory($row['code']);
570
            $row['count_courses'] = $count_courses;
571
            if (!isset($row['parent_id'])) {
572
                $categories[0][$row['tree_pos']] = $row;
573
            } else {
574
                $categories[$row['parent_id']][$row['tree_pos']] = $row;
575
            }
576
        }
577
578
        $count_courses = self::countCoursesInCategory();
579
580
        $categories[0][count($categories[0]) + 1] = [
581
            'id' => 0,
582
            'name' => get_lang('None'),
583
            'code' => 'NONE',
584
            'parent_id' => null,
585
            'tree_pos' => $row['tree_pos'] + 1,
586
            'children_count' => 0,
587
            'auth_course_child' => true,
588
            'auth_cat_child' => true,
589
            'count_courses' => $count_courses
590
        ];
591
592
        return $categories;
593
    }
594
595
    /**
596
     * @param string $category_code
597
     * @param string $searchTerm
598
     * @return int
599
     */
600
    public static function countCoursesInCategory($category_code = '', $searchTerm = '')
601
    {
602
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
603
        $categoryCode = Database::escape_string($category_code);
604
        $searchTerm = Database::escape_string($searchTerm);
605
606
        $specialCourseList = CourseManager::get_special_course_list();
607
        $without_special_courses = '';
608
        if (!empty($specialCourseList)) {
609
            $without_special_courses = ' AND course.id NOT IN ("'.implode('","', $specialCourseList).'")';
610
        }
611
612
        $visibilityCondition = CourseManager::getCourseVisibilitySQLCondition(
613
            'course',
614
            true
615
        );
616
617
        $categoryFilter = '';
618
        if ($categoryCode === 'ALL') {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

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

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

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

could be turned into

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

This is much more concise to read.

Loading history...
619
            // Nothing to do
620
        } elseif ($categoryCode === 'NONE') {
621
            $categoryFilter = ' AND category_code = "" ';
622
        } else {
623
            $categoryFilter = ' AND category_code = "'.$categoryCode.'" ';
624
        }
625
626
        $searchFilter = '';
627
        if (!empty($searchTerm)) {
628
            $searchFilter = ' AND (code LIKE "%'.$searchTerm.'%"
629
            OR title LIKE "%'.$searchTerm.'%"
630
            OR tutor_name LIKE "%'.$searchTerm.'%") ';
631
        }
632
633
        $url_access_id = api_get_current_access_url_id();
634
        $tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
635
        $sql = "SELECT * 
636
                FROM $tbl_course as course
637
                INNER JOIN $tbl_url_rel_course as url_rel_course
638
                ON (url_rel_course.c_id = course.id)
639
                WHERE
640
                    access_url_id = $url_access_id AND
641
                    course.visibility != '0' AND
642
                    course.visibility != '4'
643
                    $categoryFilter
644
                    $searchFilter
645
                    $without_special_courses
646
                    $visibilityCondition
647
            ";
648
        return Database::num_rows(Database::query($sql));
649
    }
650
651
    /**
652
     * @param string $category_code
653
     * @param int $random_value
654
     * @param array $limit will be used if $random_value is not set.
655
     * This array should contains 'start' and 'length' keys
656
     * @return array
657
     */
658
    public static function browseCoursesInCategory($category_code, $random_value = null, $limit = [])
659
    {
660
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
661
        $specialCourseList = CourseManager::get_special_course_list();
662
        $without_special_courses = '';
663
        if (!empty($specialCourseList)) {
664
            $without_special_courses = ' AND course.id NOT IN ("'.implode('","', $specialCourseList).'")';
665
        }
666
        $visibilityCondition = CourseManager::getCourseVisibilitySQLCondition(
667
            'course',
668
            true
669
        );
670
671
        if (!empty($random_value)) {
672
            $random_value = intval($random_value);
673
674
            $sql = "SELECT COUNT(*) FROM $tbl_course";
675
            $result = Database::query($sql);
676
            list($num_records) = Database::fetch_row($result);
677
678
            if (api_is_multiple_url_enabled()) {
679
                $url_access_id = api_get_current_access_url_id();
680
                $tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
681
682
                $sql = "SELECT COUNT(*) FROM $tbl_course course
683
                        INNER JOIN $tbl_url_rel_course as url_rel_course
684
                        ON (url_rel_course.c_id = course.id)
685
                        WHERE access_url_id = $url_access_id ";
686
                $result = Database::query($sql);
687
                list($num_records) = Database::fetch_row($result);
688
689
                $sql = "SELECT course.id, course.id as real_id 
690
                        FROM $tbl_course course
691
                        INNER JOIN $tbl_url_rel_course as url_rel_course
692
                        ON (url_rel_course.c_id = course.id)
693
                        WHERE
694
                            access_url_id = $url_access_id AND
695
                            RAND()*$num_records< $random_value
696
                            $without_special_courses 
697
                            $visibilityCondition
698
                        ORDER BY RAND()
699
                        LIMIT 0, $random_value";
700
            } else {
701
                $sql = "SELECT id, id as real_id FROM $tbl_course course
702
                        WHERE 
703
                            RAND()*$num_records< $random_value 
704
                            $without_special_courses 
705
                            $visibilityCondition
706
                        ORDER BY RAND()
707
                        LIMIT 0, $random_value";
708
            }
709
710
            $result = Database::query($sql);
711
            $id_in = null;
712
            while (list($id) = Database::fetch_row($result)) {
713
                if ($id_in) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id_in of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
714
                    $id_in .= ",$id";
715
                } else {
716
                    $id_in = "$id";
717
                }
718
            }
719
            if ($id_in === null) {
720
                return [];
721
            }
722
            $sql = "SELECT *, id as real_id FROM $tbl_course WHERE id IN($id_in)";
723
        } else {
724
            $limitFilter = self::getLimitFilterFromArray($limit);
725
            $category_code = Database::escape_string($category_code);
726
            if (empty($category_code) || $category_code == "ALL") {
727
                $sql = "SELECT *, id as real_id FROM $tbl_course
728
                    WHERE
729
                        1=1
730
                        $without_special_courses
731
                        $visibilityCondition
732
                    ORDER BY title $limitFilter ";
733
            } else {
734
                if ($category_code == 'NONE') {
735
                    $category_code = '';
736
                }
737
                $sql = "SELECT *, id as real_id FROM $tbl_course
738
                        WHERE
739
                            category_code='$category_code'
740
                            $without_special_courses
741
                            $visibilityCondition
742
                        ORDER BY title $limitFilter ";
743
            }
744
745
            // Showing only the courses of the current Chamilo access_url_id
746
            if (api_is_multiple_url_enabled()) {
747
                $url_access_id = api_get_current_access_url_id();
748
                $tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
749
                if ($category_code != "ALL") {
750
                    $sql = "SELECT *, course.id real_id FROM $tbl_course as course
751
                            INNER JOIN $tbl_url_rel_course as url_rel_course
752
                            ON (url_rel_course.c_id = course.id)
753
                            WHERE
754
                                access_url_id = $url_access_id AND
755
                                category_code='$category_code'
756
                                $without_special_courses
757
                                $visibilityCondition
758
                            ORDER BY title $limitFilter";
759
                } else {
760
                    $sql = "SELECT *, course.id real_id FROM $tbl_course as course
761
                            INNER JOIN $tbl_url_rel_course as url_rel_course
762
                            ON (url_rel_course.c_id = course.id)
763
                            WHERE
764
                                access_url_id = $url_access_id
765
                                $without_special_courses
766
                                $visibilityCondition
767
                            ORDER BY title $limitFilter";
768
                }
769
            }
770
        }
771
772
        $result = Database::query($sql);
773
        $courses = [];
774
        while ($row = Database::fetch_array($result)) {
775
            $row['registration_code'] = !empty($row['registration_code']);
776
            $count_users = CourseManager::get_users_count_in_course($row['code']);
777
            $count_connections_last_month = Tracking::get_course_connections_count(
778
                $row['id'],
779
                0,
780
                api_get_utc_datetime(time() - (30 * 86400))
781
            );
782
783
            if ($row['tutor_name'] == '0') {
784
                $row['tutor_name'] = get_lang('NoManager');
785
            }
786
            $point_info = CourseManager::get_course_ranking($row['id'], 0);
787
            $courses[] = [
788
                'real_id' => $row['real_id'],
789
                'point_info' => $point_info,
790
                'code' => $row['code'],
791
                'directory' => $row['directory'],
792
                'visual_code' => $row['visual_code'],
793
                'title' => $row['title'],
794
                'tutor' => $row['tutor_name'],
795
                'subscribe' => $row['subscribe'],
796
                'unsubscribe' => $row['unsubscribe'],
797
                'registration_code' => $row['registration_code'],
798
                'creation_date' => $row['creation_date'],
799
                'visibility' => $row['visibility'],
800
                'category' => $row['category_code'],
801
                'count_users' => $count_users,
802
                'count_connections' => $count_connections_last_month
803
            ];
804
        }
805
806
        return $courses;
807
    }
808
809
    /**
810
     * create recursively all categories as option of the select passed in parameter.
811
     *
812
     * @param HTML_QuickForm_Element $element
813
     * @param string $defaultCode the option value to select by default (used mainly for edition of courses)
814
     * @param string $parentCode the parent category of the categories added (default=null for root category)
815
     * @param string $padding the indent param (you shouldn't indicate something here)
816
     */
817
    public static function setCategoriesInForm(
818
        $element,
819
        $defaultCode = null,
820
        $parentCode = null,
821
        $padding = null
822
    ) {
823
        $tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY);
824
825
        $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
826
        $conditions = " INNER JOIN $table a ON (c.id = a.course_category_id)";
827
        $whereCondition = " AND a.access_url_id = ".api_get_current_access_url_id();
828
829
        $sql = "SELECT code, name, auth_course_child, auth_cat_child
830
                FROM $tbl_category c
831
                $conditions
832
                WHERE parent_id ".(empty($parentCode) ? "IS NULL" : "='".Database::escape_string($parentCode)."'")."
833
                $whereCondition
834
                ORDER BY name,  code";
835
        $res = Database::query($sql);
836
837
        while ($cat = Database::fetch_array($res, 'ASSOC')) {
838
            $params = $cat['auth_course_child'] == 'TRUE' ? '' : 'disabled';
839
            $params .= ($cat['code'] == $defaultCode) ? ' selected' : '';
840
            $option = $padding.' '.$cat['name'].' ('.$cat['code'].')';
841
842
            $element->addOption($option, $cat['code'], $params);
0 ignored issues
show
Bug introduced by
The method addOption() does not exist on HTML_QuickForm_element. It seems like you code against a sub-type of HTML_QuickForm_element such as HTML_QuickForm_select. ( Ignorable by Annotation )

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

842
            $element->/** @scrutinizer ignore-call */ 
843
                      addOption($option, $cat['code'], $params);
Loading history...
843
            if ($cat['auth_cat_child'] == 'TRUE') {
844
                self::setCategoriesInForm(
845
                    $element,
846
                    $defaultCode,
847
                    $cat['code'],
848
                    $padding.' - '
849
                );
850
            }
851
        }
852
    }
853
854
    /**
855
     * @param array $list
856
     * @return array
857
     */
858
    public static function getCourseCategoryNotInList($list)
859
    {
860
        $table = Database::get_main_table(TABLE_MAIN_CATEGORY);
861
862
        if (empty($list)) {
863
            $sql = "SELECT * FROM $table
864
                    WHERE (parent_id IS NULL) ";
865
            $result = Database::query($sql);
866
867
            return Database::store_result($result, 'ASSOC');
868
        }
869
870
        $list = array_map('intval', $list);
871
        $listToString = implode("','", $list);
872
873
        $sql = "SELECT * FROM $table
874
                WHERE id NOT IN ('$listToString') AND (parent_id IS NULL) ";
875
        $result = Database::query($sql);
876
877
        return Database::store_result($result, 'ASSOC');
878
    }
879
880
    /**
881
     * @param string $keyword
882
     * @return array|null
883
     */
884
    public static function searchCategoryByKeyword($keyword)
885
    {
886
        if (empty($keyword)) {
887
            return null;
888
        }
889
890
        $tableCategory = Database::get_main_table(TABLE_MAIN_CATEGORY);
891
        $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
892
        $conditions = " INNER JOIN $table a ON (c.id = a.course_category_id)";
893
        $whereCondition = " AND a.access_url_id = ".api_get_current_access_url_id();
894
895
        $keyword = Database::escape_string($keyword);
896
897
        $sql = "SELECT c.*, c.name as text
898
                FROM $tableCategory c $conditions
899
                WHERE
900
                (
901
                    c.code LIKE '%$keyword%' OR name LIKE '%$keyword%'
902
                ) AND auth_course_child = 'TRUE'
903
                $whereCondition ";
904
        $result = Database::query($sql);
905
906
        return Database::store_result($result, 'ASSOC');
907
    }
908
909
    /**
910
     * @param array $list
911
     * @return array
912
     */
913
    public static function searchCategoryById($list)
914
    {
915
        if (empty($list)) {
916
            return [];
917
        } else {
918
            $list = array_map('intval', $list);
919
            $list = implode("','", $list);
920
        }
921
922
        $tableCategory = Database::get_main_table(TABLE_MAIN_CATEGORY);
923
924
        $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
925
        $conditions = " INNER JOIN $table a ON (c.id = a.course_category_id)";
926
        $whereCondition = " AND a.access_url_id = ".api_get_current_access_url_id();
927
928
        $sql = "SELECT c.*, c.name as text FROM $tableCategory c $conditions
929
                WHERE c.id IN $list $whereCondition";
930
        $result = Database::query($sql);
931
932
        return Database::store_result($result, 'ASSOC');
933
    }
934
935
    /**
936
     * @return array
937
     */
938
    public static function getLimitArray()
939
    {
940
        $pageCurrent = isset($_REQUEST['pageCurrent']) ? intval($_GET['pageCurrent']) : 1;
941
        $pageLength = isset($_REQUEST['pageLength']) ? intval($_GET['pageLength']) : CoursesAndSessionsCatalog::PAGE_LENGTH;
942
943
        return [
944
            'start' => ($pageCurrent - 1) * $pageLength,
945
            'current' => $pageCurrent,
946
            'length' => $pageLength
947
        ];
948
    }
949
950
    /**
951
     * Return LIMIT to filter SQL query
952
     * @param array $limit
953
     * @return string
954
     */
955
    public static function getLimitFilterFromArray($limit)
956
    {
957
        $limitFilter = '';
958
        if (!empty($limit) && is_array($limit)) {
959
            $limitStart = isset($limit['start']) ? $limit['start'] : 0;
960
            $limitLength = isset($limit['length']) ? $limit['length'] : 12;
961
            $limitFilter = 'LIMIT '.$limitStart.', '.$limitLength;
962
        }
963
964
        return $limitFilter;
965
    }
966
967
    /**
968
     * Get Pagination HTML div
969
     * @param $pageCurrent
970
     * @param $pageLength
971
     * @param $pageTotal
972
     * @return string
973
     */
974
    public static function getCatalogPagination($pageCurrent, $pageLength, $pageTotal)
975
    {
976
        // Start empty html
977
        $pageDiv = '';
978
        $html = '';
979
        $pageBottom = max(1, $pageCurrent - 3);
980
        $pageTop = min($pageTotal, $pageCurrent + 3);
981
982
        if ($pageBottom > 1) {
983
            $pageDiv .= self::getPageNumberItem(1, $pageLength);
984
            if ($pageBottom > 2) {
985
                $pageDiv .= self::getPageNumberItem(
986
                    $pageBottom - 1,
987
                    $pageLength,
988
                    null,
989
                    '...'
990
                );
991
            }
992
        }
993
994
        // For each page add its page button to html
995
        for ($i = $pageBottom; $i <= $pageTop; $i++) {
996
            if ($i === $pageCurrent) {
997
                $pageItemAttributes = ['class' => 'active'];
998
            } else {
999
                $pageItemAttributes = [];
1000
            }
1001
            $pageDiv .= self::getPageNumberItem(
1002
                $i,
1003
                $pageLength,
1004
                $pageItemAttributes
1005
            );
1006
        }
1007
1008
        // Check if current page is the last page
1009
        if ($pageTop < $pageTotal) {
1010
            if ($pageTop < ($pageTotal - 1)) {
1011
                $pageDiv .= self::getPageNumberItem(
1012
                    $pageTop + 1,
1013
                    $pageLength,
1014
                    null,
1015
                    '...'
1016
                );
1017
            }
1018
            $pageDiv .= self::getPageNumberItem($pageTotal, $pageLength);
1019
        }
1020
1021
        // Complete pagination html
1022
        $pageDiv = Display::tag('ul', $pageDiv, ['class' => 'pagination']);
1023
        $html .= '<nav>'.$pageDiv.'</nav>';
1024
1025
        return $html;
1026
    }
1027
1028
    /**
1029
     * Return URL to course catalog
1030
     * @param int $pageCurrent
1031
     * @param int $pageLength
1032
     * @param string $categoryCode
1033
     * @param int $hiddenLinks
1034
     * @param string $action
1035
     * @return string
1036
     */
1037
    public static function getCourseCategoryUrl(
1038
        $pageCurrent,
1039
        $pageLength,
1040
        $categoryCode = null,
1041
        $hiddenLinks = null,
1042
        $action = null
1043
    ) {
1044
        $requestAction = isset($_REQUEST['action']) ? Security::remove_XSS($_REQUEST['action']) : null;
1045
        $action = isset($action) ? Security::remove_XSS($action) : $requestAction;
1046
        $searchTerm = isset($_REQUEST['search_term']) ? Security::remove_XSS($_REQUEST['search_term']) : null;
1047
1048
        if ($action === 'subscribe_user_with_password') {
1049
            $action = 'subscribe';
1050
        }
1051
1052
        $categoryCodeRequest = isset($_REQUEST['category_code']) ? Security::remove_XSS($_REQUEST['category_code']) : null;
1053
        $categoryCode = isset($categoryCode) ? Security::remove_XSS($categoryCode) : $categoryCodeRequest;
1054
        $hiddenLinksRequest = isset($_REQUEST['hidden_links']) ? Security::remove_XSS($_REQUEST['hidden_links']) : null;
1055
        $hiddenLinks = isset($hiddenLinks) ? Security::remove_XSS($hiddenLinksRequest) : $categoryCodeRequest;
1056
1057
        // Start URL with params
1058
        $pageUrl = api_get_self().
1059
            '?action='.$action.
1060
            '&category_code='.$categoryCode.
1061
            '&hidden_links='.$hiddenLinks.
1062
            '&pageCurrent='.$pageCurrent.
1063
            '&pageLength='.$pageLength;
1064
1065
        switch ($action) {
1066
            case 'subscribe':
1067
                // for search
1068
                $pageUrl .=
1069
                    '&search_term='.$searchTerm.
1070
                    '&search_course=1'.
1071
                    '&sec_token='.Security::getTokenFromSession();
1072
                break;
1073
            case 'display_courses':
1074
            default:
1075
                break;
1076
        }
1077
1078
        return $pageUrl;
1079
    }
1080
1081
    /**
1082
     * Get li HTML of page number
1083
     * @param $pageNumber
1084
     * @param $pageLength
1085
     * @param array $liAttributes
1086
     * @param string $content
1087
     * @return string
1088
     */
1089
    public static function getPageNumberItem(
1090
        $pageNumber,
1091
        $pageLength,
1092
        $liAttributes = [],
1093
        $content = ''
1094
    ) {
1095
        // Get page URL
1096
        $url = self::getCourseCategoryUrl(
1097
            $pageNumber,
1098
            $pageLength
1099
        );
1100
1101
        // If is current page ('active' class) clear URL
1102
        if (isset($liAttributes) && is_array($liAttributes) && isset($liAttributes['class'])) {
1103
            if (strpos('active', $liAttributes['class']) !== false) {
1104
                $url = '';
1105
            }
1106
        }
1107
1108
        $content = !empty($content) ? $content : $pageNumber;
1109
1110
        return Display::tag(
1111
            'li',
1112
            Display::url(
1113
                $content,
1114
                $url
1115
            ),
1116
            $liAttributes
1117
        );
1118
    }
1119
1120
    /**
1121
     * Return the name tool by action
1122
     * @param string $action
1123
     * @return string
1124
     */
1125
    public static function getCourseCatalogNameTools($action)
1126
    {
1127
        $nameTools = get_lang('SortMyCourses');
1128
        if (empty($action)) {
1129
            return $nameTools; //should never happen
1130
        }
1131
1132
        switch ($action) {
1133
            case 'createcoursecategory':
1134
                $nameTools = get_lang('CreateCourseCategory');
1135
                break;
1136
            case 'subscribe':
1137
                $nameTools = get_lang('CourseManagement');
1138
                break;
1139
            case 'subscribe_user_with_password':
1140
                $nameTools = get_lang('CourseManagement');
1141
                break;
1142
            case 'display_random_courses':
1143
            case 'display_courses':
1144
                $nameTools = get_lang('CourseManagement');
1145
                break;
1146
            case 'display_sessions':
1147
                $nameTools = get_lang('Sessions');
1148
                break;
1149
            default:
1150
                // Nothing to do
1151
                break;
1152
        }
1153
1154
        return $nameTools;
1155
    }
1156
1157
    /**
1158
     * Save image for a course category
1159
     * @param int $categoryId Course category ID
1160
     * @param array $fileData File data from $_FILES
1161
     */
1162
    public static function saveImage($categoryId, $fileData)
1163
    {
1164
        $categoryInfo = self::getCategoryById($categoryId);
1165
        if (empty($categoryInfo)) {
1166
            return;
1167
        }
1168
1169
        if (!empty($fileData['error'])) {
1170
            return;
1171
        }
1172
1173
        $extension = getextension($fileData['name']);
1174
1175
        $dirName = 'course_category/';
1176
        $fileDir = api_get_path(SYS_UPLOAD_PATH).$dirName;
1177
        $fileName = "cc_$categoryId.{$extension[0]}";
1178
1179
        if (!file_exists($fileDir)) {
1180
            mkdir($fileDir, api_get_permissions_for_new_directories(), true);
1181
        }
1182
1183
        $image = new Image($fileData['tmp_name']);
1184
        $image->send_image($fileDir.$fileName);
1185
1186
        $table = Database::get_main_table(TABLE_MAIN_CATEGORY);
1187
        Database::update(
1188
            $table,
1189
            ['image' => $dirName.$fileName],
1190
            ['id = ?' => $categoryId]
1191
        );
1192
    }
1193
1194
    /**
1195
     * @param $categoryId
1196
     * @param string $description
1197
     *
1198
     * @return string
1199
     */
1200
    public static function saveDescription($categoryId, $description)
1201
    {
1202
        $categoryInfo = self::getCategoryById($categoryId);
1203
        if (empty($categoryInfo)) {
1204
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
1205
        }
1206
        $table = Database::get_main_table(TABLE_MAIN_CATEGORY);
1207
        Database::update(
1208
            $table,
1209
            ['description' => $description],
1210
            ['id = ?' => $categoryId]
1211
        );
1212
        return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type string.
Loading history...
1213
    }
1214
}
1215