Completed
Push — master ( 9f183a...8cd85a )
by Julito
13:16
created

CourseManager::getCoursesFollowedByUser()   F

Complexity

Conditions 14
Paths 480

Size

Total Lines 107
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 64
nc 480
nop 10
dl 0
loc 107
rs 2.8222
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/* For licensing terms, see /license.txt*/
3
4
use Chamilo\CoreBundle\Entity\Course;
5
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
6
use Chamilo\CoreBundle\Framework\Container;
7
use Chamilo\CoreBundle\Hook\HookCreateCourse;
8
use Chamilo\CoreBundle\Repository\CourseRepository;
9
use Chamilo\CoreBundle\ToolChain;
10
use Chamilo\CourseBundle\Component\CourseCopy\CourseBuilder;
11
use Chamilo\CourseBundle\Component\CourseCopy\CourseRestorer;
12
use Chamilo\CourseBundle\Manager\SettingsManager;
13
use ChamiloSession as Session;
14
use Doctrine\ORM\EntityManager;
15
16
/**
17
 * Class CourseManager.
18
 *
19
 * This is the course library for Chamilo.
20
 *
21
 * All main course functions should be placed here.
22
 *
23
 * There are probably some places left with the wrong code.
24
 */
25
class CourseManager
26
{
27
    public const MAX_COURSE_LENGTH_CODE = 40;
28
    /** This constant is used to show separate user names in the course
29
     * list (userportal), footer, etc */
30
    public const USER_SEPARATOR = ' |';
31
    public $columns = [];
32
    public static $em;
33
    public static $toolList;
34
    public static $courseSettingsManager;
35
    private static $manager;
36
37
    /**
38
     * @param EntityManager
39
     */
40
    public static function setEntityManager($em)
41
    {
42
        self::$em = $em;
43
    }
44
45
    /**
46
     * @return EntityManager
47
     */
48
    public static function getEntityManager()
49
    {
50
        return self::$em;
51
    }
52
53
    /**
54
     * @return SettingsManager
55
     */
56
    public static function getCourseSettingsManager()
57
    {
58
        return self::$courseSettingsManager;
59
    }
60
61
    /**
62
     * @param SettingsManager $courseSettingsManager
63
     */
64
    public static function setCourseSettingsManager($courseSettingsManager)
65
    {
66
        self::$courseSettingsManager = $courseSettingsManager;
67
    }
68
69
    /**
70
     * @deprecated
71
     *
72
     * @return CourseRepository
73
     */
74
    public static function getManager()
75
    {
76
        return Container::getCourseRepository();
77
    }
78
79
    /**
80
     * Creates a course.
81
     *
82
     * @param array $params      Columns in the main.course table.
83
     * @param int   $authorId    Optional.
84
     * @param int   $accessUrlId Optional.
85
     *
86
     * @return mixed false if the course was not created, array with the course info
87
     */
88
    public static function create_course($params, $authorId = 0, $accessUrlId = 0)
89
    {
90
        global $_configuration;
91
92
        $hook = Container::instantiateHook(HookCreateCourse::class);
93
94
        // Check portal limits
95
        $accessUrlId = !empty($accessUrlId) ? (int) $accessUrlId : api_get_current_access_url_id();
96
        $authorId = empty($authorId) ? api_get_user_id() : (int) $authorId;
97
98
        if (isset($_configuration[$accessUrlId]) && is_array($_configuration[$accessUrlId])) {
99
            $return = self::checkCreateCourseAccessUrlParam(
100
                $_configuration,
101
                $accessUrlId,
102
                'hosting_limit_courses',
103
                'PortalCoursesLimitReached'
104
            );
105
            if ($return != false) {
106
                return $return;
107
            }
108
            $return = self::checkCreateCourseAccessUrlParam(
109
                $_configuration,
110
                $accessUrlId,
111
                'hosting_limit_active_courses',
112
                'PortalActiveCoursesLimitReached'
113
            );
114
            if ($return != false) {
115
                return $return;
116
            }
117
        }
118
119
        if (empty($params['title'])) {
120
            return false;
121
        }
122
123
        if (empty($params['wanted_code'])) {
124
            $params['wanted_code'] = $params['title'];
125
            // Check whether the requested course code has already been occupied.
126
            $substring = api_substr($params['title'], 0, self::MAX_COURSE_LENGTH_CODE);
127
            if ($substring === false || empty($substring)) {
128
                return false;
129
            } else {
130
                $params['wanted_code'] = self::generate_course_code($substring);
131
            }
132
        }
133
134
        // Create the course keys
135
        $keys = AddCourse::define_course_keys($params['wanted_code']);
136
        $params['exemplary_content'] = isset($params['exemplary_content']) ? $params['exemplary_content'] : false;
137
138
        if (count($keys)) {
139
            $params['code'] = $keys['currentCourseCode'];
140
            $params['visual_code'] = $keys['currentCourseId'];
141
            $params['directory'] = $keys['currentCourseRepository'];
142
            $courseInfo = api_get_course_info($params['code']);
143
            if (empty($courseInfo)) {
144
                $courseId = AddCourse::register_course($params, $accessUrlId);
145
                $courseInfo = api_get_course_info_by_id($courseId);
146
147
                if ($hook) {
148
                    $hook->setEventData(['course_info' => $courseInfo]);
149
                    $hook->notifyCreateCourse(HOOK_EVENT_TYPE_POST);
150
                }
151
152
                if (!empty($courseInfo)) {
153
                    self::fillCourse($courseInfo, $params, $authorId);
154
155
                    return $courseInfo;
156
                }
157
            }
158
        }
159
160
        return false;
161
    }
162
163
    /**
164
     * Returns all the information of a given course code.
165
     *
166
     * @param string $course_code , the course code
167
     *
168
     * @return array with all the fields of the course table
169
     *
170
     * @deprecated Use api_get_course_info() instead
171
     *
172
     * @author Patrick Cool <[email protected]>, Ghent University
173
     * @assert ('') === false
174
     */
175
    public static function get_course_information($course_code)
176
    {
177
        return Database::fetch_array(
178
            Database::query(
179
                "SELECT *, id as real_id FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
180
                WHERE code = '".Database::escape_string($course_code)."'"
181
            ),
182
            'ASSOC'
183
        );
184
    }
185
186
    /**
187
     * Returns a list of courses. Should work with quickform syntax.
188
     *
189
     * @param int    $from               Offset (from the 7th = '6'). Optional.
190
     * @param int    $howmany            Number of results we want. Optional.
191
     * @param int    $orderby            The column we want to order it by. Optional, defaults to first column.
192
     * @param string $orderdirection     The direction of the order (ASC or DESC). Optional, defaults to ASC.
193
     * @param int    $visibility         the visibility of the course, or all by default
194
     * @param string $startwith          If defined, only return results for which the course *title* begins with this string
195
     * @param string $urlId              The Access URL ID, if using multiple URLs
196
     * @param bool   $alsoSearchCode     An extension option to indicate that we also want to search for course codes (not *only* titles)
197
     * @param array  $conditionsLike
198
     * @param array  $onlyThisCourseList
199
     *
200
     * @return array
201
     */
202
    public static function get_courses_list(
203
        $from = 0,
204
        $howmany = 0,
205
        $orderby = 1,
206
        $orderdirection = 'ASC',
207
        $visibility = -1,
208
        $startwith = '',
209
        $urlId = null,
210
        $alsoSearchCode = false,
211
        $conditionsLike = [],
212
        $onlyThisCourseList = []
213
    ) {
214
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
215
        $sql = "SELECT course.*, course.id as real_id 
216
                FROM $courseTable course ";
217
218
        if (!empty($urlId)) {
219
            $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
220
            $sql .= " INNER JOIN $table url ON (url.c_id = course.id) ";
221
        }
222
223
        $visibility = (int) $visibility;
224
225
        if (!empty($startwith)) {
226
            $sql .= "WHERE (title LIKE '".Database::escape_string($startwith)."%' ";
227
            if ($alsoSearchCode) {
228
                $sql .= "OR code LIKE '".Database::escape_string($startwith)."%' ";
229
            }
230
            $sql .= ') ';
231
            if ($visibility !== -1) {
232
                $sql .= " AND visibility = $visibility ";
233
            }
234
        } else {
235
            $sql .= 'WHERE 1 ';
236
            if ($visibility !== -1) {
237
                $sql .= " AND visibility = $visibility ";
238
            }
239
        }
240
241
        if (!empty($urlId)) {
242
            $urlId = (int) $urlId;
243
            $sql .= " AND access_url_id = $urlId";
244
        }
245
246
        if (!empty($onlyThisCourseList)) {
247
            $onlyThisCourseList = array_map('intval', $onlyThisCourseList);
248
            $onlyThisCourseList = implode("','", $onlyThisCourseList);
249
            $sql .= " AND course.id IN ('$onlyThisCourseList') ";
250
        }
251
252
        $allowedFields = [
253
            'title',
254
            'code',
255
        ];
256
257
        if (count($conditionsLike) > 0) {
258
            $sql .= ' AND ';
259
            $temp_conditions = [];
260
            foreach ($conditionsLike as $field => $value) {
261
                if (!in_array($field, $allowedFields)) {
262
                    continue;
263
                }
264
                $field = Database::escape_string($field);
265
                $value = Database::escape_string($value);
266
                $simple_like = false;
267
                if ($simple_like) {
268
                    $temp_conditions[] = $field." LIKE '$value%'";
269
                } else {
270
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
271
                }
272
            }
273
            $condition = ' AND ';
274
            if (!empty($temp_conditions)) {
275
                $sql .= implode(' '.$condition.' ', $temp_conditions);
276
            }
277
        }
278
279
        if (!empty($orderby)) {
280
            $sql .= " ORDER BY ".Database::escape_string($orderby)." ";
281
        } else {
282
            $sql .= ' ORDER BY 1 ';
283
        }
284
285
        if (!in_array($orderdirection, ['ASC', 'DESC'])) {
286
            $sql .= 'ASC';
287
        } else {
288
            $sql .= ($orderdirection == 'ASC' ? 'ASC' : 'DESC');
289
        }
290
291
        if (!empty($howmany) && is_int($howmany) and $howmany > 0) {
292
            $sql .= ' LIMIT '.Database::escape_string($howmany);
293
        } else {
294
            $sql .= ' LIMIT 1000000'; //virtually no limit
295
        }
296
        if (!empty($from)) {
297
            $from = intval($from);
298
            $sql .= ' OFFSET '.intval($from);
299
        } else {
300
            $sql .= ' OFFSET 0';
301
        }
302
303
        $data = [];
304
        $res = Database::query($sql);
305
        if (Database::num_rows($res) > 0) {
306
            while ($row = Database::fetch_array($res, 'ASSOC')) {
307
                $data[] = $row;
308
            }
309
        }
310
311
        return $data;
312
    }
313
314
    /**
315
     * Returns the status of a user in a course, which is COURSEMANAGER or STUDENT.
316
     *
317
     * @param int $userId
318
     * @param int $courseId
319
     *
320
     * @return int|bool the status of the user in that course (or false if the user is not in that course)
321
     */
322
    public static function getUserInCourseStatus($userId, $courseId)
323
    {
324
        $courseId = (int) $courseId;
325
        $userId = (int) $userId;
326
        if (empty($courseId)) {
327
            return false;
328
        }
329
330
        $result = Database::fetch_array(
331
            Database::query(
332
                "SELECT status FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
333
                WHERE
334
                    c_id  = $courseId AND
335
                    user_id = $userId"
336
            )
337
        );
338
339
        return $result['status'];
340
    }
341
342
    /**
343
     * @param int $userId
344
     * @param int $courseId
345
     *
346
     * @return mixed
347
     */
348
    public static function getUserCourseInfo($userId, $courseId)
349
    {
350
        $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
351
                WHERE
352
                    c_id  = ".intval($courseId)." AND
353
                    user_id = ".intval($userId);
354
        $result = Database::fetch_array(Database::query($sql));
355
356
        return $result;
357
    }
358
359
    /**
360
     * @param int  $userId
361
     * @param int  $courseId
362
     * @param bool $isTutor
363
     *
364
     * @return bool
365
     */
366
    public static function updateUserCourseTutor($userId, $courseId, $isTutor)
367
    {
368
        $table = Database::escape_string(TABLE_MAIN_COURSE_USER);
369
370
        $courseId = intval($courseId);
371
        $isTutor = intval($isTutor);
372
373
        $sql = "UPDATE $table SET is_tutor = '".$isTutor."'
374
			    WHERE
375
				    user_id = ".$userId." AND
376
				    c_id = ".$courseId;
377
378
        $result = Database::query($sql);
379
380
        if (Database::affected_rows($result) > 0) {
381
            return true;
382
        } else {
383
            return false;
384
        }
385
    }
386
387
    /**
388
     * @param int $userId
389
     * @param int $courseId
390
     *
391
     * @return mixed
392
     */
393
    public static function get_tutor_in_course_status($userId, $courseId)
394
    {
395
        $userId = intval($userId);
396
        $courseId = intval($courseId);
397
        $result = Database::fetch_array(
398
            Database::query(
399
                "SELECT is_tutor
400
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
401
                WHERE
402
                    c_id = $courseId AND
403
                    user_id = $userId"
404
            )
405
        );
406
407
        return $result['is_tutor'];
408
    }
409
410
    /**
411
     * Unsubscribe one or more users from a course.
412
     *
413
     * @param   mixed   user_id or an array with user ids
414
     * @param   string  course code
415
     * @param   int     session id
416
     *
417
     * @return bool
418
     *
419
     * @assert ('', '') === false
420
     */
421
    public static function unsubscribe_user($user_id, $course_code, $session_id = 0)
422
    {
423
        if (empty($user_id)) {
424
            return false;
425
        }
426
        if (!is_array($user_id)) {
427
            $user_id = [$user_id];
428
        }
429
430
        if (count($user_id) == 0) {
431
            return false;
432
        }
433
434
        if (!empty($session_id)) {
435
            $session_id = (int) $session_id;
436
        } else {
437
            $session_id = api_get_session_id();
438
        }
439
440
        if (empty($course_code)) {
441
            return false;
442
        }
443
444
        $userList = [];
445
        // Cleaning the $user_id variable
446
        if (is_array($user_id)) {
447
            $new_user_id_list = [];
448
            foreach ($user_id as $my_user_id) {
449
                $new_user_id_list[] = (int) $my_user_id;
450
            }
451
            $new_user_id_list = array_filter($new_user_id_list);
452
            $userList = $new_user_id_list;
453
            $user_ids = implode(',', $new_user_id_list);
454
        } else {
455
            $user_ids = (int) $user_id;
456
            $userList[] = $user_id;
457
        }
458
459
        $course_info = api_get_course_info($course_code);
460
        $course_id = $course_info['real_id'];
461
462
        // Unsubscribe user from all groups in the course.
463
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_GROUP_USER)."
464
                WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
465
        Database::query($sql);
466
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_GROUP_TUTOR)."
467
                WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
468
        Database::query($sql);
469
470
        // Erase user student publications (works) in the course - by André Boivin
471
        if (!empty($userList)) {
472
            require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
473
            foreach ($userList as $userId) {
474
                // Getting all work from user
475
                $workList = getWorkPerUser($userId);
476
                if (!empty($workList)) {
477
                    foreach ($workList as $work) {
478
                        $work = $work['work'];
479
                        // Getting user results
480
                        if (!empty($work->user_results)) {
481
                            foreach ($work->user_results as $workSent) {
482
                                deleteWorkItem($workSent['id'], $course_info);
483
                            }
484
                        }
485
                    }
486
                }
487
            }
488
        }
489
490
        // Unsubscribe user from all blogs in the course.
491
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_BLOGS_REL_USER)." 
492
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
493
        Database::query($sql);
494
495
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_BLOGS_TASKS_REL_USER)." 
496
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
497
        Database::query($sql);
498
499
        // Deleting users in forum_notification and mailqueue course tables
500
        $sql = "DELETE FROM  ".Database::get_course_table(TABLE_FORUM_NOTIFICATION)."
501
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
502
        Database::query($sql);
503
504
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_FORUM_MAIL_QUEUE)."
505
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
506
        Database::query($sql);
507
508
        // Unsubscribe user from the course.
509
        if (!empty($session_id)) {
510
            foreach ($userList as $uid) {
511
                SessionManager::unSubscribeUserFromCourseSession($uid, $course_id, $session_id);
512
513
                // check if a user is register in the session with other course
514
                $sql = "SELECT user_id FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
515
                        WHERE session_id = $session_id AND user_id = $uid";
516
                $rs = Database::query($sql);
517
518
                if (Database::num_rows($rs) == 0) {
519
                    SessionManager::unsubscribe_user_from_session($uid, $session_id);
520
                }
521
            }
522
523
            Event::addEvent(
524
                LOG_UNSUBSCRIBE_USER_FROM_COURSE,
525
                LOG_COURSE_CODE,
526
                $course_code,
527
                api_get_utc_datetime(),
528
                $user_id,
529
                $course_id,
530
                $session_id
531
            );
532
        } else {
533
            $sql = "DELETE FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
534
                    WHERE
535
                        user_id IN ($user_ids) AND
536
                        relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
537
                        c_id = $course_id";
538
            Database::query($sql);
539
540
            // add event to system log
541
            $user_id = api_get_user_id();
542
543
            Event::addEvent(
544
                LOG_UNSUBSCRIBE_USER_FROM_COURSE,
545
                LOG_COURSE_CODE,
546
                $course_code,
547
                api_get_utc_datetime(),
548
                $user_id,
549
                $course_id
550
            );
551
552
            foreach ($userList as $userId) {
553
                $userInfo = api_get_user_info($userId);
554
                Event::addEvent(
555
                    LOG_UNSUBSCRIBE_USER_FROM_COURSE,
556
                    LOG_USER_OBJECT,
557
                    $userInfo,
558
                    api_get_utc_datetime(),
559
                    $user_id,
560
                    $course_id
561
                );
562
            }
563
        }
564
    }
565
566
    /**
567
     * @param string $courseCode
568
     * @param int    $status
569
     *
570
     * @return bool
571
     */
572
    public static function autoSubscribeToCourse($courseCode, $status = STUDENT)
573
    {
574
        $courseInfo = api_get_course_info($courseCode);
575
576
        if (empty($courseInfo)) {
577
            return false;
578
        }
579
580
        if (api_is_anonymous()) {
581
            return false;
582
        }
583
584
        if (in_array(
585
            $courseInfo['visibility'],
586
            [
587
                COURSE_VISIBILITY_CLOSED,
588
                COURSE_VISIBILITY_REGISTERED,
589
                COURSE_VISIBILITY_HIDDEN,
590
            ]
591
        )
592
        ) {
593
            Display::addFlash(
594
                Display::return_message(
595
                    get_lang('Subscribing not allowed'),
596
                    'warning'
597
                )
598
            );
599
600
            return false;
601
        }
602
603
        return self::subscribeUser(api_get_user_id(), $courseInfo['code'], $status);
604
    }
605
606
    /**
607
     * Subscribe a user to a course. No checks are performed here to see if
608
     * course subscription is allowed.
609
     *
610
     * @param int    $userId
611
     * @param string $courseCode
612
     * @param int    $status                 (STUDENT, COURSEMANAGER, COURSE_ADMIN, NORMAL_COURSE_MEMBER)
613
     * @param int    $sessionId
614
     * @param int    $userCourseCategoryId
615
     * @param bool   $checkTeacherPermission
616
     *
617
     * @return bool True on success, false on failure
618
     *
619
     * @assert ('', '') === false
620
     */
621
    public static function subscribeUser(
622
        $userId,
623
        $courseCode,
624
        $status = STUDENT,
625
        $sessionId = 0,
626
        $userCourseCategoryId = 0,
627
        $checkTeacherPermission = true
628
    ) {
629
        $userId = (int) $userId;
630
        $status = (int) $status;
631
632
        if (empty($userId) || empty($courseCode)) {
633
            return false;
634
        }
635
636
        $courseInfo = api_get_course_info($courseCode);
637
638
        if (empty($courseInfo)) {
639
            Display::addFlash(Display::return_message(get_lang('This course doesn\'t exist'), 'warning'));
640
641
            return false;
642
        }
643
644
        $userInfo = api_get_user_info($userId);
645
646
        if (empty($userInfo)) {
647
            Display::addFlash(Display::return_message(get_lang('This user doesn\'t exist'), 'warning'));
648
649
            return false;
650
        }
651
652
        $courseId = $courseInfo['real_id'];
653
        $courseCode = $courseInfo['code'];
654
        $userCourseCategoryId = (int) $userCourseCategoryId;
655
656
        $sessionId = empty($sessionId) ? api_get_session_id() : (int) $sessionId;
657
        $status = $status === STUDENT || $status === COURSEMANAGER ? $status : STUDENT;
658
        $courseUserTable = Database::get_main_table(TABLE_MAIN_COURSE_USER);
659
660
        if (!empty($sessionId)) {
661
            SessionManager::subscribe_users_to_session_course(
662
                [$userId],
663
                $sessionId,
664
                $courseCode
665
            );
666
667
            // Add event to the system log
668
            Event::addEvent(
669
                LOG_SUBSCRIBE_USER_TO_COURSE,
670
                LOG_COURSE_CODE,
671
                $courseCode,
672
                api_get_utc_datetime(),
673
                api_get_user_id(),
674
                $courseId,
675
                $sessionId
676
            );
677
            Event::addEvent(
678
                LOG_SUBSCRIBE_USER_TO_COURSE,
679
                LOG_USER_OBJECT,
680
                $userInfo,
681
                api_get_utc_datetime(),
682
                api_get_user_id(),
683
                $courseId,
684
                $sessionId
685
            );
686
687
            return true;
688
        } else {
689
            // Check whether the user has not been already subscribed to the course.
690
            $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."                    
691
                    WHERE 
692
                        user_id = $userId AND 
693
                        relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND 
694
                        c_id = $courseId
695
                    ";
696
            if (Database::num_rows(Database::query($sql)) > 0) {
697
                Display::addFlash(Display::return_message(get_lang('Already registered in course'), 'warning'));
698
699
                return false;
700
            }
701
702
            if ($checkTeacherPermission && !api_is_course_admin()) {
703
                // Check in advance whether subscription is allowed or not for this course.
704
                if ((int) $courseInfo['subscribe'] === SUBSCRIBE_NOT_ALLOWED) {
705
                    Display::addFlash(Display::return_message(get_lang('SubscriptionNotAllowed'), 'warning'));
706
707
                    return false;
708
                }
709
            }
710
711
            if ($status === STUDENT) {
712
                // Check if max students per course extra field is set
713
                $extraFieldValue = new ExtraFieldValue('course');
714
                $value = $extraFieldValue->get_values_by_handler_and_field_variable($courseId, 'max_subscribed_students');
715
                if (!empty($value) && isset($value['value'])) {
716
                    $maxStudents = $value['value'];
717
                    if ($maxStudents !== '') {
718
                        $maxStudents = (int) $maxStudents;
719
                        $count = self::get_user_list_from_course_code(
720
                            $courseCode,
721
                            0,
722
                            null,
723
                            null,
724
                            STUDENT,
725
                            true,
726
                            false
727
                        );
728
729
                        if ($count >= $maxStudents) {
730
                            Display::addFlash(Display::return_message(get_lang('The maximum number of student has already been reached, it is not possible to subscribe more student.'), 'warning'));
731
732
                            return false;
733
                        }
734
                    }
735
                }
736
            }
737
738
            $maxSort = api_max_sort_value('0', $userId);
739
            $params = [
740
                'c_id' => $courseId,
741
                'user_id' => $userId,
742
                'status' => $status,
743
                'sort' => $maxSort + 1,
744
                'relation_type' => 0,
745
                'user_course_cat' => (int) $userCourseCategoryId,
746
            ];
747
            $insertId = Database::insert($courseUserTable, $params);
748
749
            if ($insertId) {
750
                Display::addFlash(
751
                    Display::return_message(
752
                        sprintf(
753
                            get_lang('User %s has been registered to course %s'),
754
                            $userInfo['complete_name_with_username'],
755
                            $courseInfo['title']
756
                        )
757
                    )
758
                );
759
760
                $send = api_get_course_setting('email_alert_to_teacher_on_new_user_in_course', $courseInfo);
761
762
                if ($send == 1) {
763
                    self::email_to_tutor(
764
                        $userId,
765
                        $courseInfo['real_id'],
766
                        false
767
                    );
768
                } elseif ($send == 2) {
769
                    self::email_to_tutor(
770
                        $userId,
771
                        $courseInfo['real_id'],
772
                        true
773
                    );
774
                }
775
776
                $subscribe = (int) api_get_course_setting('subscribe_users_to_forum_notifications', $courseInfo);
777
                if ($subscribe === 1) {
778
                    require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
779
                    $forums = get_forums(0, $courseCode, true, $sessionId);
780
                    foreach ($forums as $forum) {
781
                        $forumId = $forum['iid'];
782
                        set_notification('forum', $forumId, false, $userInfo, $courseInfo);
783
                    }
784
                }
785
786
                // Add event to the system log
787
                Event::addEvent(
788
                    LOG_SUBSCRIBE_USER_TO_COURSE,
789
                    LOG_COURSE_CODE,
790
                    $courseCode,
791
                    api_get_utc_datetime(),
792
                    api_get_user_id(),
793
                    $courseId
794
                );
795
796
                Event::addEvent(
797
                    LOG_SUBSCRIBE_USER_TO_COURSE,
798
                    LOG_USER_OBJECT,
799
                    $userInfo,
800
                    api_get_utc_datetime(),
801
                    api_get_user_id(),
802
                    $courseId
803
                );
804
805
                return true;
806
            }
807
808
            return false;
809
        }
810
    }
811
812
    /**
813
     * Get the course id based on the original id and field name in the
814
     * extra fields. Returns 0 if course was not found.
815
     *
816
     * @param string $original_course_id_value
817
     * @param string $original_course_id_name
818
     *
819
     * @return int Course id
820
     *
821
     * @assert ('', '') === false
822
     */
823
    public static function get_course_code_from_original_id(
824
        $original_course_id_value,
825
        $original_course_id_name
826
    ) {
827
        $t_cfv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
828
        $table_field = Database::get_main_table(TABLE_EXTRA_FIELD);
829
        $extraFieldType = EntityExtraField::COURSE_FIELD_TYPE;
830
        $original_course_id_value = Database::escape_string($original_course_id_value);
831
        $original_course_id_name = Database::escape_string($original_course_id_name);
832
833
        $sql = "SELECT item_id
834
                FROM $table_field cf
835
                INNER JOIN $t_cfv cfv
836
                ON cfv.field_id=cf.id
837
                WHERE
838
                    variable = '$original_course_id_name' AND
839
                    value = '$original_course_id_value' AND
840
                    cf.extra_field_type = $extraFieldType
841
                ";
842
        $res = Database::query($sql);
843
        $row = Database::fetch_object($res);
844
        if ($row) {
845
            return $row->item_id;
846
        } else {
847
            return 0;
848
        }
849
    }
850
851
    /**
852
     * Gets the course code from the course id. Returns null if course id was not found.
853
     *
854
     * @param int $id Course id
855
     *
856
     * @return string Course code
857
     * @assert ('') === false
858
     */
859
    public static function get_course_code_from_course_id($id)
860
    {
861
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
862
        $id = intval($id);
863
        $sql = "SELECT code FROM $table WHERE id = $id ";
864
        $res = Database::query($sql);
865
        $row = Database::fetch_object($res);
866
        if ($row) {
867
            return $row->code;
868
        } else {
869
            return null;
870
        }
871
    }
872
873
    /**
874
     * Add the user $userId visibility to the course $courseCode in the catalogue.
875
     *
876
     * @author David Nos (https://github.com/dnos)
877
     *
878
     * @param int    $userId     the id of the user
879
     * @param string $courseCode the course code
880
     * @param int    $visible    (optional) The course visibility in the catalogue to the user (1=visible, 0=invisible)
881
     *
882
     * @return bool true if added succesfully, false otherwise
883
     */
884
    public static function addUserVisibilityToCourseInCatalogue(
885
        $userId,
886
        $courseCode,
887
        $visible = 1
888
    ) {
889
        $debug = false;
890
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
891
        $courseUserTable = Database::get_main_table(TABLE_MAIN_COURSE_CATALOGUE_USER);
892
        $visible = (int) $visible;
893
        if (empty($userId) || empty($courseCode) || ($userId != strval(intval($userId)))) {
894
            return false;
895
        }
896
897
        $courseCode = Database::escape_string($courseCode);
898
        $courseInfo = api_get_course_info($courseCode);
899
        $courseId = $courseInfo['real_id'];
900
901
        // Check in advance whether the user has already been registered on the platform.
902
        $sql = "SELECT status FROM ".$userTable." WHERE user_id = $userId ";
903
        if (Database::num_rows(Database::query($sql)) == 0) {
904
            if ($debug) {
905
                error_log('The user has not been registered to the platform');
906
            }
907
908
            return false; // The user has not been registered to the platform.
909
        }
910
911
        // Check whether the user has already been registered to the course visibility in the catalogue.
912
        $sql = "SELECT * FROM $courseUserTable
913
                WHERE
914
                    user_id = $userId AND
915
                    visible = $visible AND
916
                    c_id = $courseId";
917
        if (Database::num_rows(Database::query($sql)) > 0) {
918
            if ($debug) {
919
                error_log('The user has been already registered to the course visibility in the catalogue');
920
            }
921
922
            return true; // The visibility of the user to the course in the catalogue does already exist.
923
        }
924
925
        // Register the user visibility to course in catalogue.
926
        $params = [
927
            'user_id' => $userId,
928
            'c_id' => $courseId,
929
            'visible' => $visible,
930
        ];
931
        $insertId = Database::insert($courseUserTable, $params);
932
933
        return $insertId;
934
    }
935
936
    /**
937
     * Remove the user $userId visibility to the course $courseCode in the catalogue.
938
     *
939
     * @author David Nos (https://github.com/dnos)
940
     *
941
     * @param int    $userId     the id of the user
942
     * @param string $courseCode the course code
943
     * @param int    $visible    (optional) The course visibility in the catalogue to the user (1=visible, 0=invisible)
944
     *
945
     * @return bool true if removed succesfully or register not found, false otherwise
946
     */
947
    public static function removeUserVisibilityToCourseInCatalogue(
948
        $userId,
949
        $courseCode,
950
        $visible = 1
951
    ) {
952
        $courseUserTable = Database::get_main_table(TABLE_MAIN_COURSE_CATALOGUE_USER);
953
954
        if (empty($userId) || empty($courseCode) || ($userId != strval(intval($userId)))) {
955
            return false;
956
        }
957
958
        $courseCode = Database::escape_string($courseCode);
959
        $courseInfo = api_get_course_info($courseCode);
960
        $courseId = $courseInfo['real_id'];
961
962
        // Check whether the user has already been registered to the course visibility in the catalogue.
963
        $sql = "SELECT * FROM $courseUserTable
964
                WHERE
965
                    user_id = $userId AND
966
                    visible = $visible AND
967
                    c_id = $courseId";
968
        if (Database::num_rows(Database::query($sql)) > 0) {
969
            $cond = [
970
                'user_id = ? AND c_id = ? AND visible = ? ' => [
971
                    $userId,
972
                    $courseId,
973
                    $visible,
974
                ],
975
            ];
976
977
            return Database::delete($courseUserTable, $cond);
978
        } else {
979
            return true; // Register does not exist
980
        }
981
    }
982
983
    /**
984
     * @param string $code
985
     *
986
     * @return bool if there already are one or more courses
987
     *              with the same code OR visual_code (visualcode), false otherwise
988
     */
989
    public static function course_code_exists($code)
990
    {
991
        $code = Database::escape_string($code);
992
        $sql = "SELECT COUNT(*) as number
993
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
994
                WHERE code = '$code' OR visual_code = '$code'";
995
        $result = Database::fetch_array(Database::query($sql));
996
997
        return $result['number'] > 0;
998
    }
999
1000
    /**
1001
     * @param int    $user_id
1002
     * @param string $startsWith Optional
1003
     *
1004
     * @return array an array with the course info of all the courses (real and virtual)
1005
     *               of which the current user is course admin
1006
     */
1007
    public static function get_course_list_of_user_as_course_admin($user_id, $startsWith = '')
1008
    {
1009
        if ($user_id != strval(intval($user_id))) {
1010
            return [];
1011
        }
1012
1013
        // Definitions database tables and variables
1014
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
1015
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1016
        $user_id = intval($user_id);
1017
        $data = [];
1018
1019
        $sql = "SELECT
1020
                    course.code,
1021
                    course.title,
1022
                    course.id,
1023
                    course.id as real_id,
1024
                    course.category_code
1025
                FROM $tbl_course_user as course_rel_user
1026
                INNER JOIN $tbl_course as course
1027
                ON course.id = course_rel_user.c_id
1028
                WHERE
1029
                    course_rel_user.user_id = $user_id AND
1030
                    course_rel_user.status = 1
1031
        ";
1032
1033
        if (api_get_multiple_access_url()) {
1034
            $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
1035
            $access_url_id = api_get_current_access_url_id();
1036
            if ($access_url_id != -1) {
1037
                $sql = "
1038
                    SELECT
1039
                        course.code,
1040
                        course.title,
1041
                        course.id,
1042
                        course.id as real_id
1043
                    FROM $tbl_course_user as course_rel_user
1044
                    INNER JOIN $tbl_course as course
1045
                    ON course.id = course_rel_user.c_id
1046
                    INNER JOIN $tbl_course_rel_access_url course_rel_url
1047
                    ON (course_rel_url.c_id = course.id)
1048
                    WHERE
1049
                        access_url_id = $access_url_id  AND
1050
                        course_rel_user.user_id = $user_id AND
1051
                        course_rel_user.status = 1
1052
                ";
1053
            }
1054
        }
1055
1056
        if (!empty($startsWith)) {
1057
            $startsWith = Database::escape_string($startsWith);
1058
1059
            $sql .= " AND (course.title LIKE '$startsWith%' OR course.code LIKE '$startsWith%')";
1060
        }
1061
1062
        $sql .= ' ORDER BY course.title';
1063
1064
        $result_nb_cours = Database::query($sql);
1065
        if (Database::num_rows($result_nb_cours) > 0) {
1066
            while ($row = Database::fetch_array($result_nb_cours, 'ASSOC')) {
1067
                $data[$row['id']] = $row;
1068
            }
1069
        }
1070
1071
        return $data;
1072
    }
1073
1074
    /**
1075
     * @param int   $userId
1076
     * @param array $courseInfo
1077
     *
1078
     * @return bool|null
1079
     */
1080
    public static function isUserSubscribedInCourseAsDrh($userId, $courseInfo)
1081
    {
1082
        $userId = intval($userId);
1083
1084
        if (!api_is_drh()) {
1085
            return false;
1086
        }
1087
1088
        if (empty($courseInfo) || empty($userId)) {
1089
            return false;
1090
        }
1091
1092
        $courseId = intval($courseInfo['real_id']);
1093
        $table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1094
1095
        $sql = "SELECT * FROM $table
1096
                WHERE
1097
                    user_id = $userId AND
1098
                    relation_type = ".COURSE_RELATION_TYPE_RRHH." AND
1099
                    c_id = $courseId";
1100
1101
        $result = Database::fetch_array(Database::query($sql));
1102
1103
        if (!empty($result)) {
1104
            // The user has been registered in this course.
1105
            return true;
1106
        }
1107
    }
1108
1109
    /**
1110
     * Check if user is subscribed inside a course.
1111
     *
1112
     * @param int    $user_id
1113
     * @param string $course_code  , if this parameter is null, it'll check for all courses
1114
     * @param bool   $in_a_session True for checking inside sessions too, by default is not checked
1115
     * @param int    $session_id
1116
     *
1117
     * @return bool $session_id true if the user is registered in the course, false otherwise
1118
     */
1119
    public static function is_user_subscribed_in_course(
1120
        $user_id,
1121
        $course_code = null,
1122
        $in_a_session = false,
1123
        $session_id = 0
1124
    ) {
1125
        $user_id = (int) $user_id;
1126
        $session_id = (int) $session_id;
1127
1128
        if (empty($session_id)) {
1129
            $session_id = api_get_session_id();
1130
        }
1131
1132
        $condition_course = '';
1133
        if (isset($course_code)) {
1134
            $courseInfo = api_get_course_info($course_code);
1135
            if (empty($courseInfo)) {
1136
                return false;
1137
            }
1138
            $courseId = $courseInfo['real_id'];
1139
            $condition_course = " AND c_id = $courseId";
1140
        }
1141
1142
        $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
1143
                WHERE
1144
                    user_id = $user_id AND
1145
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
1146
                    $condition_course ";
1147
1148
        $result = Database::fetch_array(Database::query($sql));
1149
1150
        if (!empty($result)) {
1151
            // The user has been registered in this course.
1152
            return true;
1153
        }
1154
1155
        if (!$in_a_session) {
1156
            // The user has not been registered in this course.
1157
            return false;
1158
        }
1159
1160
        $tableSessionCourseUser = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1161
        $sql = "SELECT 1 FROM $tableSessionCourseUser
1162
                WHERE user_id = $user_id AND session_id = $session_id $condition_course";
1163
1164
        if (Database::num_rows(Database::query($sql)) > 0) {
1165
            return true;
1166
        }
1167
1168
        $sql = "SELECT 1 FROM $tableSessionCourseUser
1169
                WHERE user_id = $user_id AND session_id = $session_id AND status = 2 $condition_course";
1170
1171
        if (Database::num_rows(Database::query($sql)) > 0) {
1172
            return true;
1173
        }
1174
1175
        $sql = 'SELECT 1 FROM '.Database::get_main_table(TABLE_MAIN_SESSION).
1176
              " WHERE id = $session_id AND id_coach = $user_id";
1177
1178
        if (Database::num_rows(Database::query($sql)) > 0) {
1179
            return true;
1180
        }
1181
1182
        return false;
1183
    }
1184
1185
    /**
1186
     * Is the user a teacher in the given course?
1187
     *
1188
     * @param int    $user_id     , the id (int) of the user
1189
     * @param string $course_code , the course code
1190
     *
1191
     * @return bool if the user is a teacher in the course, false otherwise
1192
     */
1193
    public static function is_course_teacher($user_id, $course_code)
1194
    {
1195
        if ($user_id != strval(intval($user_id))) {
1196
            return false;
1197
        }
1198
1199
        $courseInfo = api_get_course_info($course_code);
1200
        if (empty($courseInfo)) {
1201
            return false;
1202
        }
1203
        $courseId = $courseInfo['real_id'];
1204
        $sql = "SELECT status FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
1205
                WHERE c_id = $courseId AND user_id = $user_id ";
1206
        $result = Database::query($sql);
1207
1208
        if (Database::num_rows($result) > 0) {
1209
            return Database::result($result, 0, 'status') == 1;
1210
        }
1211
1212
        return false;
1213
    }
1214
1215
    /**
1216
     *    Is the user subscribed in the real course or linked courses?
1217
     *
1218
     * @param int the id of the user
1219
     * @param int $courseId
1220
     *
1221
     * @deprecated linked_courses definition doesn't exists
1222
     *
1223
     * @return bool if the user is registered in the real course or linked courses, false otherwise
1224
     */
1225
    public static function is_user_subscribed_in_real_or_linked_course($user_id, $courseId, $session_id = 0)
1226
    {
1227
        if ($user_id != strval(intval($user_id))) {
1228
            return false;
1229
        }
1230
1231
        $courseId = intval($courseId);
1232
        $session_id = intval($session_id);
1233
1234
        if (empty($session_id)) {
1235
            $result = Database::fetch_array(
1236
                Database::query(
1237
                    "SELECT *
1238
                    FROM ".Database::get_main_table(TABLE_MAIN_COURSE)." course
1239
                    LEFT JOIN ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." course_user
1240
                    ON course.id = course_user.c_id
1241
                    WHERE
1242
                        course_user.user_id = $user_id AND
1243
                        course_user.relation_type<>".COURSE_RELATION_TYPE_RRHH." AND
1244
                        ( course.id = $courseId)"
1245
                )
1246
            );
1247
1248
            return !empty($result);
1249
        }
1250
1251
        // From here we trust session id.
1252
        // Is he/she subscribed to the session's course?
1253
        // A user?
1254
        if (Database::num_rows(Database::query("SELECT user_id
1255
                FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
1256
                WHERE session_id = $session_id
1257
                AND user_id = $user_id"))
1258
        ) {
1259
            return true;
1260
        }
1261
1262
        // A course coach?
1263
        if (Database::num_rows(Database::query("SELECT user_id
1264
                FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
1265
                WHERE session_id = $session_id
1266
                AND user_id = $user_id AND status = 2
1267
                AND c_id = $courseId"))
1268
        ) {
1269
            return true;
1270
        }
1271
1272
        // A session coach?
1273
        if (Database::num_rows(Database::query("SELECT id_coach
1274
                FROM ".Database::get_main_table(TABLE_MAIN_SESSION)." AS session
1275
                WHERE session.id = $session_id
1276
                AND id_coach = $user_id"))
1277
        ) {
1278
            return true;
1279
        }
1280
1281
        return false;
1282
    }
1283
1284
    /**
1285
     * Return user info array of all users registered in a course
1286
     * This only returns the users that are registered in this actual course, not linked courses.
1287
     *
1288
     * @param string    $course_code
1289
     * @param int       $session_id
1290
     * @param string    $limit
1291
     * @param string    $order_by         the field to order the users by.
1292
     *                                    Valid values are 'lastname', 'firstname', 'username', 'email', 'official_code' OR a part of a SQL statement
1293
     *                                    that starts with ORDER BY ...
1294
     * @param int|null  $filter_by_status if using the session_id: 0 or 2 (student, coach),
1295
     *                                    if using session_id = 0 STUDENT or COURSEMANAGER
1296
     * @param bool|null $return_count
1297
     * @param bool      $add_reports
1298
     * @param bool      $resumed_report
1299
     * @param array     $extra_field
1300
     * @param array     $courseCodeList
1301
     * @param array     $userIdList
1302
     * @param string    $filterByActive
1303
     * @param array     $sessionIdList
1304
     * @param string    $searchByKeyword
1305
     *
1306
     * @return array|int
1307
     */
1308
    public static function get_user_list_from_course_code(
1309
        $course_code = null,
1310
        $session_id = 0,
1311
        $limit = null,
1312
        $order_by = null,
1313
        $filter_by_status = null,
1314
        $return_count = null,
1315
        $add_reports = false,
1316
        $resumed_report = false,
1317
        $extra_field = [],
1318
        $courseCodeList = [],
1319
        $userIdList = [],
1320
        $filterByActive = null,
1321
        $sessionIdList = [],
1322
        $searchByKeyword = ''
1323
    ) {
1324
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
1325
        $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
1326
1327
        $session_id = (int) $session_id;
1328
        $course_code = Database::escape_string($course_code);
1329
        $courseInfo = api_get_course_info($course_code);
1330
        $courseId = 0;
1331
        if (!empty($courseInfo)) {
1332
            $courseId = $courseInfo['real_id'];
1333
        }
1334
1335
        $where = [];
1336
        if (empty($order_by)) {
1337
            $order_by = 'user.lastname, user.firstname';
1338
            if (api_is_western_name_order()) {
1339
                $order_by = 'user.firstname, user.lastname';
1340
            }
1341
        }
1342
1343
        // if the $order_by does not contain 'ORDER BY'
1344
        // we have to check if it is a valid field that can be sorted on
1345
        if (!strstr($order_by, 'ORDER BY')) {
1346
            if (!empty($order_by)) {
1347
                $order_by = "ORDER BY $order_by";
1348
            } else {
1349
                $order_by = '';
1350
            }
1351
        }
1352
1353
        $filter_by_status_condition = null;
1354
1355
        if (!empty($session_id) || !empty($sessionIdList)) {
1356
            $sql = 'SELECT DISTINCT
1357
                        user.user_id,
1358
                        user.email,
1359
                        session_course_user.status as status_session,
1360
                        session_id,
1361
                        user.*,
1362
                        course.*,
1363
                        course.id AS c_id,
1364
                        session.name as session_name
1365
                    ';
1366
            if ($return_count) {
1367
                $sql = " SELECT COUNT(user.user_id) as count";
1368
            }
1369
1370
            $sessionCondition = " session_course_user.session_id = $session_id";
1371
            if (!empty($sessionIdList)) {
1372
                $sessionIdListTostring = implode("','", array_map('intval', $sessionIdList));
1373
                $sessionCondition = " session_course_user.session_id IN ('$sessionIdListTostring') ";
1374
            }
1375
1376
            $courseCondition = " course.id = $courseId";
1377
            if (!empty($courseCodeList)) {
1378
                $courseCodeListForSession = array_map(['Database', 'escape_string'], $courseCodeList);
1379
                $courseCodeListForSession = implode('","', $courseCodeListForSession);
1380
                $courseCondition = " course.code IN ('$courseCodeListForSession')  ";
1381
            }
1382
1383
            $sql .= ' FROM '.Database::get_main_table(TABLE_MAIN_USER).' as user ';
1384
            $sql .= " LEFT JOIN ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)." as session_course_user
1385
                      ON
1386
                        user.id = session_course_user.user_id AND
1387
                        $sessionCondition
1388
                        INNER JOIN $course_table course 
1389
                        ON session_course_user.c_id = course.id AND
1390
                        $courseCondition
1391
                        INNER JOIN $sessionTable session 
1392
                        ON session_course_user.session_id = session.id
1393
                   ";
1394
            $where[] = ' session_course_user.c_id IS NOT NULL ';
1395
1396
            // 2 = coach
1397
            // 0 = student
1398
            if (isset($filter_by_status)) {
1399
                $filter_by_status = intval($filter_by_status);
1400
                $filter_by_status_condition = " session_course_user.status = $filter_by_status AND ";
1401
            }
1402
        } else {
1403
            if ($return_count) {
1404
                $sql = " SELECT COUNT(*) as count";
1405
            } else {
1406
                if (empty($course_code)) {
1407
                    $sql = 'SELECT DISTINCT
1408
                                course.title,
1409
                                course.code,
1410
                                course.id AS c_id,
1411
                                course_rel_user.status as status_rel,
1412
                                user.id as user_id,
1413
                                user.email,
1414
                                course_rel_user.is_tutor,
1415
                                user.*  ';
1416
                } else {
1417
                    $sql = 'SELECT DISTINCT
1418
                                course_rel_user.status as status_rel,
1419
                                user.id as user_id,
1420
                                user.email,
1421
                                course_rel_user.is_tutor,
1422
                                user.*  ';
1423
                }
1424
            }
1425
1426
            $sql .= " FROM ".Database::get_main_table(TABLE_MAIN_USER)." as user 
1427
                      LEFT JOIN ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." as course_rel_user
1428
                      ON 
1429
                        user.id = course_rel_user.user_id AND
1430
                        course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
1431
                       INNER JOIN $course_table course 
1432
                       ON course_rel_user.c_id = course.id ";
1433
1434
            if (!empty($course_code)) {
1435
                $sql .= " AND course_rel_user.c_id = $courseId";
1436
            }
1437
            $where[] = ' course_rel_user.c_id IS NOT NULL ';
1438
1439
            if (isset($filter_by_status) && is_numeric($filter_by_status)) {
1440
                $filter_by_status = (int) $filter_by_status;
1441
                $filter_by_status_condition = " course_rel_user.status = $filter_by_status AND ";
1442
            }
1443
        }
1444
1445
        $multiple_access_url = api_get_multiple_access_url();
1446
        if ($multiple_access_url) {
1447
            $sql .= ' LEFT JOIN '.Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER).' au
1448
                      ON (au.user_id = user.id) ';
1449
        }
1450
1451
        $extraFieldWasAdded = false;
1452
        if ($return_count && $resumed_report) {
1453
            foreach ($extra_field as $extraField) {
1454
                $extraFieldInfo = UserManager::get_extra_field_information_by_name($extraField);
1455
                if (!empty($extraFieldInfo)) {
1456
                    $fieldValuesTable = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1457
                    $sql .= " LEFT JOIN $fieldValuesTable as ufv
1458
                            ON (
1459
                                user.id = ufv.item_id AND
1460
                                (field_id = ".$extraFieldInfo['id']." OR field_id IS NULL)
1461
                            )";
1462
                    $extraFieldWasAdded = true;
1463
                }
1464
            }
1465
        }
1466
1467
        $sql .= " WHERE $filter_by_status_condition ".implode(' OR ', $where);
1468
1469
        if ($multiple_access_url) {
1470
            $current_access_url_id = api_get_current_access_url_id();
1471
            $sql .= " AND (access_url_id =  $current_access_url_id ) ";
1472
        }
1473
1474
        if ($return_count && $resumed_report && $extraFieldWasAdded) {
1475
            $sql .= ' AND field_id IS NOT NULL GROUP BY value ';
1476
        }
1477
1478
        if (!empty($courseCodeList)) {
1479
            $courseCodeList = array_map(['Database', 'escape_string'], $courseCodeList);
1480
            $courseCodeList = implode('","', $courseCodeList);
1481
            if (empty($sessionIdList)) {
1482
                $sql .= ' AND course.code IN ("'.$courseCodeList.'")';
1483
            }
1484
        }
1485
1486
        if (!empty($userIdList)) {
1487
            $userIdList = array_map('intval', $userIdList);
1488
            $userIdList = implode('","', $userIdList);
1489
            $sql .= ' AND user.id IN ("'.$userIdList.'")';
1490
        }
1491
1492
        if (isset($filterByActive)) {
1493
            $filterByActive = (int) $filterByActive;
1494
            $sql .= " AND user.active = $filterByActive";
1495
        }
1496
1497
        if (!empty($searchByKeyword)) {
1498
            $searchByKeyword = Database::escape_string($searchByKeyword);
1499
            $sql .= " AND (
1500
                        user.firstname LIKE '$searchByKeyword' OR 
1501
                        user.username LIKE '$searchByKeyword' OR 
1502
                        user.lastname LIKE '$searchByKeyword'
1503
                    ) ";
1504
        }
1505
1506
        $sql .= " $order_by $limit";
1507
1508
        $rs = Database::query($sql);
1509
        $users = [];
1510
1511
        $extra_fields = UserManager::get_extra_fields(
1512
            0,
1513
            100,
1514
            null,
1515
            null,
1516
            true,
1517
            true
1518
        );
1519
1520
        $counter = 1;
1521
        $count_rows = Database::num_rows($rs);
1522
1523
        if ($return_count && $resumed_report) {
1524
            return $count_rows;
1525
        }
1526
1527
        $table_user_field_value = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1528
        $tableExtraField = Database::get_main_table(TABLE_EXTRA_FIELD);
1529
        if ($count_rows) {
1530
            while ($user = Database::fetch_array($rs)) {
1531
                if ($return_count) {
1532
                    return $user['count'];
1533
                }
1534
                $report_info = [];
1535
                $user_info = $user;
1536
                $user_info['status'] = $user['status'];
1537
                if (isset($user['is_tutor'])) {
1538
                    $user_info['is_tutor'] = $user['is_tutor'];
1539
                }
1540
                if (!empty($session_id)) {
1541
                    $user_info['status_session'] = $user['status_session'];
1542
                }
1543
1544
                $sessionId = isset($user['session_id']) ? $user['session_id'] : 0;
1545
                $course_code = isset($user['code']) ? $user['code'] : null;
1546
1547
                if ($add_reports) {
1548
                    if ($resumed_report) {
1549
                        $extra = [];
1550
1551
                        if (!empty($extra_fields)) {
1552
                            foreach ($extra_fields as $extra) {
1553
                                if (in_array($extra['1'], $extra_field)) {
1554
                                    $user_data = UserManager::get_extra_user_data_by_field(
1555
                                        $user['user_id'],
1556
                                        $extra['1']
1557
                                    );
1558
                                    break;
1559
                                }
1560
                            }
1561
                        }
1562
1563
                        $row_key = '-1';
1564
                        $name = '-';
1565
1566
                        if (!empty($extra)) {
1567
                            if (!empty($user_data[$extra['1']])) {
1568
                                $row_key = $user_data[$extra['1']];
1569
                                $name = $user_data[$extra['1']];
1570
                                $users[$row_key]['extra_'.$extra['1']] = $name;
1571
                            }
1572
                        }
1573
1574
                        if (empty($users[$row_key])) {
1575
                            $users[$row_key] = [];
1576
                        }
1577
1578
                        if (!array_key_exists('training_hours', $users[$row_key])) {
1579
                            $users[$row_key]['training_hours'] = 0;
1580
                        }
1581
1582
                        $users[$row_key]['training_hours'] += Tracking::get_time_spent_on_the_course(
1583
                            $user['user_id'],
1584
                            $courseId,
1585
                            $sessionId
1586
                        );
1587
1588
                        if (!array_key_exists('count_users', $users[$row_key])) {
1589
                            $users[$row_key]['count_users'] = 0;
1590
                        }
1591
1592
                        $users[$row_key]['count_users'] += $counter;
1593
1594
                        $registered_users_with_extra_field = self::getCountRegisteredUsersWithCourseExtraField(
1595
                            $name,
1596
                            $tableExtraField,
1597
                            $table_user_field_value
1598
                        );
1599
1600
                        $users[$row_key]['count_users_registered'] = $registered_users_with_extra_field;
1601
                        $users[$row_key]['average_hours_per_user'] = $users[$row_key]['training_hours'] / $users[$row_key]['count_users'];
1602
1603
                        $category = Category:: load(
1604
                            null,
1605
                            null,
1606
                            $course_code,
1607
                            null,
1608
                            null,
1609
                            $sessionId
1610
                        );
1611
1612
                        if (!isset($users[$row_key]['count_certificates'])) {
1613
                            $users[$row_key]['count_certificates'] = 0;
1614
                        }
1615
1616
                        if (isset($category[0]) && $category[0]->is_certificate_available($user['user_id'])) {
1617
                            $users[$row_key]['count_certificates']++;
1618
                        }
1619
1620
                        foreach ($extra_fields as $extra) {
1621
                            if ($extra['1'] == 'ruc') {
1622
                                continue;
1623
                            }
1624
1625
                            if (!isset($users[$row_key][$extra['1']])) {
1626
                                $user_data = UserManager::get_extra_user_data_by_field($user['user_id'], $extra['1']);
1627
                                if (!empty($user_data[$extra['1']])) {
1628
                                    $users[$row_key][$extra['1']] = $user_data[$extra['1']];
1629
                                }
1630
                            }
1631
                        }
1632
                    } else {
1633
                        $sessionName = !empty($sessionId) ? ' - '.$user['session_name'] : '';
1634
                        $report_info['course'] = $user['title'].$sessionName;
1635
                        $report_info['user'] = api_get_person_name($user['firstname'], $user['lastname']);
1636
                        $report_info['email'] = $user['email'];
1637
                        $report_info['time'] = api_time_to_hms(
1638
                            Tracking::get_time_spent_on_the_course(
1639
                                $user['user_id'],
1640
                                empty($user['c_id']) ? $courseId : $user['c_id'],
1641
                                $sessionId
1642
                            )
1643
                        );
1644
1645
                        $category = Category:: load(
1646
                            null,
1647
                            null,
1648
                            $course_code,
1649
                            null,
1650
                            null,
1651
                            $sessionId
1652
                        );
1653
1654
                        $report_info['certificate'] = Display::label(get_lang('No'));
1655
                        if (isset($category[0]) && $category[0]->is_certificate_available($user['user_id'])) {
1656
                            $report_info['certificate'] = Display::label(get_lang('Yes'), 'success');
1657
                        }
1658
1659
                        $progress = intval(
1660
                            Tracking::get_avg_student_progress(
1661
                                $user['user_id'],
1662
                                $course_code,
1663
                                [],
1664
                                $sessionId
1665
                            )
1666
                        );
1667
1668
                        $report_info['progress_100'] = $progress == 100 ? Display::label(get_lang('Yes'), 'success') : Display::label(get_lang('No'));
1669
                        $report_info['progress'] = $progress."%";
1670
1671
                        foreach ($extra_fields as $extra) {
1672
                            $user_data = UserManager::get_extra_user_data_by_field($user['user_id'], $extra['1']);
1673
                            $report_info[$extra['1']] = $user_data[$extra['1']];
1674
                        }
1675
                        $report_info['user_id'] = $user['user_id'];
1676
                        $users[] = $report_info;
1677
                    }
1678
                } else {
1679
                    $users[$user['user_id']] = $user_info;
1680
                }
1681
            }
1682
        }
1683
1684
        return $users;
1685
    }
1686
1687
    /**
1688
     * @param bool  $resumed_report
1689
     * @param array $extra_field
1690
     * @param array $courseCodeList
1691
     * @param array $userIdList
1692
     * @param array $sessionIdList
1693
     *
1694
     * @return array|int
1695
     */
1696
    public static function get_count_user_list_from_course_code(
1697
        $resumed_report = false,
1698
        $extra_field = [],
1699
        $courseCodeList = [],
1700
        $userIdList = [],
1701
        $sessionIdList = []
1702
    ) {
1703
        return self::get_user_list_from_course_code(
1704
            null,
1705
            0,
1706
            null,
1707
            null,
1708
            null,
1709
            true,
1710
            false,
1711
            $resumed_report,
1712
            $extra_field,
1713
            $courseCodeList,
1714
            $userIdList,
1715
            null,
1716
            $sessionIdList
1717
        );
1718
    }
1719
1720
    /**
1721
     * Gets subscribed users in a course or in a course/session.
1722
     *
1723
     * @param string $course_code
1724
     * @param int    $session_id
1725
     *
1726
     * @return int
1727
     */
1728
    public static function get_users_count_in_course(
1729
        $course_code,
1730
        $session_id = 0,
1731
        $status = null
1732
    ) {
1733
        // variable initialisation
1734
        $session_id = (int) $session_id;
1735
        $course_code = Database::escape_string($course_code);
1736
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
1737
        $tblSessionCourseUser = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1738
        $tblCourseUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1739
        $tblUrlUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1740
1741
        $courseInfo = api_get_course_info($course_code);
1742
        $courseId = $courseInfo['real_id'];
1743
1744
        $sql = "
1745
            SELECT DISTINCT count(user.id) as count  
1746
            FROM $tblUser as user
1747
        ";
1748
        $where = [];
1749
        if (!empty($session_id)) {
1750
            $sql .= "
1751
                LEFT JOIN $tblSessionCourseUser as session_course_user
1752
                    ON user.user_id = session_course_user.user_id
1753
                    AND session_course_user.c_id = $courseId
1754
                    AND session_course_user.session_id = $session_id
1755
            ";
1756
1757
            $where[] = ' session_course_user.c_id IS NOT NULL ';
1758
        } else {
1759
            $sql .= "
1760
                LEFT JOIN $tblCourseUser as course_rel_user
1761
                    ON user.user_id = course_rel_user.user_id
1762
                    AND course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
1763
                    AND course_rel_user.c_id = $courseId
1764
            ";
1765
            $where[] = ' course_rel_user.c_id IS NOT NULL ';
1766
        }
1767
1768
        $multiple_access_url = api_get_multiple_access_url();
1769
        if ($multiple_access_url) {
1770
            $sql .= " LEFT JOIN $tblUrlUser au ON (au.user_id = user.user_id) ";
1771
        }
1772
1773
        $sql .= ' WHERE '.implode(' OR ', $where);
1774
1775
        if ($multiple_access_url) {
1776
            $current_access_url_id = api_get_current_access_url_id();
1777
            $sql .= " AND (access_url_id =  $current_access_url_id ) ";
1778
        }
1779
        $rs = Database::query($sql);
1780
        $count = 0;
1781
        if (Database::num_rows($rs)) {
1782
            $user = Database::fetch_array($rs);
1783
            $count = $user['count'];
1784
        }
1785
1786
        return $count;
1787
    }
1788
1789
    /**
1790
     * Get a list of coaches of a course and a session.
1791
     *
1792
     * @param string $course_code
1793
     * @param int    $session_id
1794
     * @param bool   $addGeneralCoach
1795
     *
1796
     * @return array List of users
1797
     */
1798
    public static function get_coach_list_from_course_code(
1799
        $course_code,
1800
        $session_id,
1801
        $addGeneralCoach = true
1802
    ) {
1803
        if (empty($course_code) || empty($session_id)) {
1804
            return [];
1805
        }
1806
1807
        $course_code = Database::escape_string($course_code);
1808
        $courseInfo = api_get_course_info($course_code);
1809
        $courseId = $courseInfo['real_id'];
1810
        $session_id = (int) $session_id;
1811
        $users = [];
1812
1813
        // We get the coach for the given course in a given session.
1814
        $sql = 'SELECT user_id FROM '.Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER).
1815
               " WHERE session_id = $session_id AND c_id = $courseId AND status = 2";
1816
        $rs = Database::query($sql);
1817
        while ($user = Database::fetch_array($rs)) {
1818
            $userInfo = api_get_user_info($user['user_id']);
1819
            if ($userInfo) {
1820
                $users[$user['user_id']] = $userInfo;
1821
            }
1822
        }
1823
1824
        if ($addGeneralCoach) {
1825
            $table = Database::get_main_table(TABLE_MAIN_SESSION);
1826
            // We get the session coach.
1827
            $sql = "SELECT id_coach FROM $table WHERE id = $session_id";
1828
            $rs = Database::query($sql);
1829
            $session_id_coach = Database::result($rs, 0, 'id_coach');
1830
            if (is_int($session_id_coach)) {
1831
                $userInfo = api_get_user_info($session_id_coach);
1832
                if ($userInfo) {
1833
                    $users[$session_id_coach] = $userInfo;
1834
                }
1835
            }
1836
        }
1837
1838
        return $users;
1839
    }
1840
1841
    /**
1842
     *  Return user info array of all users registered in a course
1843
     *  This only returns the users that are registered in this actual course, not linked courses.
1844
     *
1845
     * @param string $course_code
1846
     * @param bool   $with_session
1847
     * @param int    $session_id
1848
     * @param string $date_from
1849
     * @param string $date_to
1850
     * @param bool   $includeInvitedUsers Whether include the invited users
1851
     * @param int    $groupId
1852
     *
1853
     * @return array with user id
1854
     */
1855
    public static function get_student_list_from_course_code(
1856
        $course_code,
1857
        $with_session = false,
1858
        $session_id = 0,
1859
        $date_from = null,
1860
        $date_to = null,
1861
        $includeInvitedUsers = true,
1862
        $groupId = 0
1863
    ) {
1864
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1865
        $session_id = (int) $session_id;
1866
        $courseInfo = api_get_course_info($course_code);
1867
        if (empty($courseInfo)) {
1868
            return [];
1869
        }
1870
        $courseId = $courseInfo['real_id'];
1871
        $students = [];
1872
1873
        if ($session_id == 0) {
1874
            if (empty($groupId)) {
1875
                // students directly subscribed to the course
1876
                $sql = "SELECT *
1877
                        FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." cu
1878
                        INNER JOIN $userTable u
1879
                        ON cu.user_id = u.user_id
1880
                        WHERE c_id = $courseId AND cu.status = ".STUDENT;
1881
1882
                if (!$includeInvitedUsers) {
1883
                    $sql .= " AND u.status != ".INVITEE;
1884
                }
1885
                $rs = Database::query($sql);
1886
                while ($student = Database::fetch_array($rs)) {
1887
                    $students[$student['user_id']] = $student;
1888
                }
1889
            } else {
1890
                $students = GroupManager::get_users(
1891
                    $groupId,
1892
                    false,
1893
                    null,
1894
                    null,
1895
                    false,
1896
                    $courseInfo['real_id']
1897
                );
1898
                $students = array_flip($students);
1899
            }
1900
        }
1901
1902
        // students subscribed to the course through a session
1903
        if ($with_session) {
1904
            $joinSession = '';
1905
            //Session creation date
1906
            if (!empty($date_from) && !empty($date_to)) {
1907
                $joinSession = "INNER JOIN ".Database::get_main_table(TABLE_MAIN_SESSION)." s";
1908
            }
1909
1910
            $sql_query = "SELECT *
1911
                          FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)." scu
1912
                          $joinSession
1913
                          INNER JOIN $userTable u ON scu.user_id = u.user_id
1914
                          WHERE scu.c_id = $courseId AND scu.status <> 2";
1915
1916
            if (!empty($date_from) && !empty($date_to)) {
1917
                $date_from = Database::escape_string($date_from);
1918
                $date_to = Database::escape_string($date_to);
1919
                $sql_query .= " AND s.access_start_date >= '$date_from' AND s.access_end_date <= '$date_to'";
1920
            }
1921
1922
            if ($session_id != 0) {
1923
                $sql_query .= " AND scu.session_id = $session_id";
1924
            }
1925
1926
            if (!$includeInvitedUsers) {
1927
                $sql_query .= " AND u.status != ".INVITEE;
1928
            }
1929
1930
            $rs = Database::query($sql_query);
1931
            while ($student = Database::fetch_array($rs)) {
1932
                $students[$student['user_id']] = $student;
1933
            }
1934
        }
1935
1936
        return $students;
1937
    }
1938
1939
    /**
1940
     * Return user info array of all teacher-users registered in a course
1941
     * This only returns the users that are registered in this actual course, not linked courses.
1942
     *
1943
     * @param string $course_code
1944
     *
1945
     * @return array with user id
1946
     */
1947
    public static function get_teacher_list_from_course_code($course_code)
1948
    {
1949
        $courseInfo = api_get_course_info($course_code);
1950
        $courseId = $courseInfo['real_id'];
1951
        if (empty($courseId)) {
1952
            return false;
1953
        }
1954
1955
        $sql = "SELECT DISTINCT
1956
                    u.id as user_id,
1957
                    u.lastname,
1958
                    u.firstname,
1959
                    u.email,
1960
                    u.username,
1961
                    u.status
1962
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." cu
1963
                INNER JOIN ".Database::get_main_table(TABLE_MAIN_USER)." u
1964
                ON (cu.user_id = u.id)
1965
                WHERE
1966
                    cu.c_id = $courseId AND
1967
                    cu.status = 1 ";
1968
        $rs = Database::query($sql);
1969
        $teachers = [];
1970
        while ($teacher = Database::fetch_array($rs)) {
1971
            $teachers[$teacher['user_id']] = $teacher;
1972
        }
1973
1974
        return $teachers;
1975
    }
1976
1977
    /**
1978
     * Return user info array of all teacher-users registered in a course
1979
     * This only returns the users that are registered in this actual course, not linked courses.
1980
     *
1981
     * @param int  $courseId
1982
     * @param bool $loadAvatars
1983
     *
1984
     * @return array with user id
1985
     */
1986
    public static function getTeachersFromCourse($courseId, $loadAvatars = true)
1987
    {
1988
        $courseId = (int) $courseId;
1989
1990
        if (empty($courseId)) {
1991
            return false;
1992
        }
1993
1994
        $sql = "SELECT DISTINCT
1995
                    u.id as user_id,
1996
                    u.lastname,
1997
                    u.firstname,
1998
                    u.email,
1999
                    u.username,
2000
                    u.status
2001
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." cu
2002
                INNER JOIN ".Database::get_main_table(TABLE_MAIN_USER)." u
2003
                ON (cu.user_id = u.id)
2004
                WHERE
2005
                    cu.c_id = $courseId AND
2006
                    cu.status = 1 ";
2007
        $rs = Database::query($sql);
2008
        $listTeachers = [];
2009
        $teachers = [];
2010
        $url = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&course_id='.$courseId;
2011
        while ($teacher = Database::fetch_array($rs)) {
2012
            $teachers['id'] = $teacher['user_id'];
2013
            $teachers['lastname'] = $teacher['lastname'];
2014
            $teachers['firstname'] = $teacher['firstname'];
2015
            $teachers['email'] = $teacher['email'];
2016
            $teachers['username'] = $teacher['username'];
2017
            $teachers['status'] = $teacher['status'];
2018
            $teachers['fullname'] = api_get_person_name($teacher['firstname'], $teacher['lastname']);
2019
            $teachers['avatar'] = '';
2020
            if ($loadAvatars) {
2021
                $userPicture = UserManager::getUserPicture($teacher['user_id'], USER_IMAGE_SIZE_SMALL);
2022
                $teachers['avatar'] = $userPicture;
2023
            }
2024
            $teachers['url'] = $url.'&user_id='.$teacher['user_id'];
2025
            $listTeachers[] = $teachers;
2026
        }
2027
2028
        return $listTeachers;
2029
    }
2030
2031
    /**
2032
     * Returns a string list of teachers assigned to the given course.
2033
     *
2034
     * @param string $course_code
2035
     * @param string $separator           between teachers names
2036
     * @param bool   $add_link_to_profile Whether to add a link to the teacher's profile
2037
     * @param bool   $orderList
2038
     *
2039
     * @return string List of teachers teaching the course
2040
     */
2041
    public static function getTeacherListFromCourseCodeToString(
2042
        $course_code,
2043
        $separator = self::USER_SEPARATOR,
2044
        $add_link_to_profile = false,
2045
        $orderList = false
2046
    ) {
2047
        $teacher_list = self::get_teacher_list_from_course_code($course_code);
2048
        $html = '';
2049
        $list = [];
2050
        if (!empty($teacher_list)) {
2051
            foreach ($teacher_list as $teacher) {
2052
                $teacher_name = api_get_person_name(
2053
                    $teacher['firstname'],
2054
                    $teacher['lastname']
2055
                );
2056
                if ($add_link_to_profile) {
2057
                    $url = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$teacher['user_id'];
2058
                    $teacher_name = Display::url(
2059
                        $teacher_name,
2060
                        $url,
2061
                        [
2062
                            'class' => 'ajax',
2063
                            'data-title' => $teacher_name,
2064
                        ]
2065
                    );
2066
                }
2067
                $list[] = $teacher_name;
2068
            }
2069
2070
            if (!empty($list)) {
2071
                if ($orderList === true) {
2072
                    $html .= '<ul class="user-teacher">';
2073
                    foreach ($list as $teacher) {
2074
                        $html .= '<li>';
2075
                        $html .= Display::return_icon('teacher.png', '', null, ICON_SIZE_TINY);
2076
                        $html .= ' '.$teacher;
2077
                        $html .= '</li>';
2078
                    }
2079
                    $html .= '</ul>';
2080
                } else {
2081
                    $html .= array_to_string($list, $separator);
2082
                }
2083
            }
2084
        }
2085
2086
        return $html;
2087
    }
2088
2089
    /**
2090
     * This function returns information about coachs from a course in session.
2091
     *
2092
     * @param int $session_id
2093
     * @param int $courseId
2094
     *
2095
     * @return array containing user_id, lastname, firstname, username
2096
     */
2097
    public static function get_coachs_from_course($session_id = 0, $courseId = 0)
2098
    {
2099
        if (!empty($session_id)) {
2100
            $session_id = intval($session_id);
2101
        } else {
2102
            $session_id = api_get_session_id();
2103
        }
2104
2105
        if (!empty($courseId)) {
2106
            $courseId = intval($courseId);
2107
        } else {
2108
            $courseId = api_get_course_int_id();
2109
        }
2110
2111
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2112
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2113
2114
        $sql = "SELECT DISTINCT 
2115
                    u.user_id,
2116
                    u.lastname,
2117
                    u.firstname,
2118
                    u.username
2119
                FROM $tbl_user u 
2120
                INNER JOIN $tbl_session_course_user scu
2121
                ON (u.user_id = scu.user_id)
2122
                WHERE
2123
                    scu.session_id = $session_id AND
2124
                    scu.c_id = $courseId AND
2125
                    scu.status = 2";
2126
        $rs = Database::query($sql);
2127
2128
        $coaches = [];
2129
        if (Database::num_rows($rs) > 0) {
2130
            while ($row = Database::fetch_array($rs)) {
2131
                $completeName = api_get_person_name($row['firstname'], $row['lastname']);
2132
                $coaches[] = $row + ['full_name' => $completeName];
2133
            }
2134
        }
2135
2136
        return $coaches;
2137
    }
2138
2139
    /**
2140
     * @param int    $session_id
2141
     * @param int    $courseId
2142
     * @param string $separator
2143
     * @param bool   $add_link_to_profile
2144
     * @param bool   $orderList
2145
     *
2146
     * @return string
2147
     */
2148
    public static function get_coachs_from_course_to_string(
2149
        $session_id = 0,
2150
        $courseId = 0,
2151
        $separator = self::USER_SEPARATOR,
2152
        $add_link_to_profile = false,
2153
        $orderList = false
2154
    ) {
2155
        $coachList = self::get_coachs_from_course($session_id, $courseId);
2156
        $course_coachs = [];
2157
        if (!empty($coachList)) {
2158
            foreach ($coachList as $coach_course) {
2159
                $coach_name = $coach_course['full_name'];
2160
                if ($add_link_to_profile) {
2161
                    $url = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$coach_course['user_id'].'&course_id='.$courseId.'&session_id='.$session_id;
2162
                    $coach_name = Display::url(
2163
                        $coach_name,
2164
                        $url,
2165
                        [
2166
                            'class' => 'ajax',
2167
                            'data-title' => $coach_name,
2168
                        ]
2169
                    );
2170
                }
2171
                $course_coachs[] = $coach_name;
2172
            }
2173
        }
2174
2175
        $html = '';
2176
        if (!empty($course_coachs)) {
2177
            if ($orderList === true) {
2178
                $html .= '<ul class="user-coachs">';
2179
                foreach ($course_coachs as $coachs) {
2180
                    $html .= Display::tag(
2181
                        'li',
2182
                        Display::return_icon(
2183
                            'teacher.png',
2184
                            get_lang('Coach'),
2185
                            null,
2186
                            ICON_SIZE_TINY
2187
                        ).' '.$coachs
2188
                    );
2189
                }
2190
                $html .= '</ul>';
2191
            } else {
2192
                $html = array_to_string($course_coachs, $separator);
2193
            }
2194
        }
2195
2196
        return $html;
2197
    }
2198
2199
    /**
2200
     * Get the list of groups from the course.
2201
     *
2202
     * @param string $course_code
2203
     * @param int    $session_id         Session ID (optional)
2204
     * @param int    $in_get_empty_group get empty groups (optional)
2205
     *
2206
     * @return array List of groups info
2207
     */
2208
    public static function get_group_list_of_course(
2209
        $course_code,
2210
        $session_id = 0,
2211
        $in_get_empty_group = 0
2212
    ) {
2213
        $course_info = api_get_course_info($course_code);
2214
2215
        if (empty($course_info)) {
2216
            return [];
2217
        }
2218
        $course_id = $course_info['real_id'];
2219
2220
        if (empty($course_id)) {
2221
            return [];
2222
        }
2223
2224
        $session_id != 0 ? $session_condition = ' WHERE g.session_id IN(1,'.intval($session_id).')' : $session_condition = ' WHERE g.session_id = 0';
2225
        if ($in_get_empty_group == 0) {
2226
            // get only groups that are not empty
2227
            $sql = "SELECT DISTINCT g.id, g.iid, g.name
2228
                    FROM ".Database::get_course_table(TABLE_GROUP)." AS g
2229
                    INNER JOIN ".Database::get_course_table(TABLE_GROUP_USER)." gu
2230
                    ON (g.id = gu.group_id AND g.c_id = $course_id AND gu.c_id = $course_id)
2231
                    $session_condition
2232
                    ORDER BY g.name";
2233
        } else {
2234
            // get all groups even if they are empty
2235
            $sql = "SELECT g.id, g.name, g.iid 
2236
                    FROM ".Database::get_course_table(TABLE_GROUP)." AS g
2237
                    $session_condition
2238
                    AND c_id = $course_id";
2239
        }
2240
2241
        $result = Database::query($sql);
2242
        $groupList = [];
2243
        while ($groupData = Database::fetch_array($result)) {
2244
            $groupData['userNb'] = GroupManager::number_of_students($groupData['id'], $course_id);
2245
            $groupList[$groupData['iid']] = $groupData;
2246
        }
2247
2248
        return $groupList;
2249
    }
2250
2251
    /**
2252
     * Delete a course
2253
     * This function deletes a whole course-area from the platform. When the
2254
     * given course is a virtual course, the database and directory will not be
2255
     * deleted.
2256
     * When the given course is a real course, also all virtual courses refering
2257
     * to the given course will be deleted.
2258
     * Considering the fact that we remove all traces of the course in the main
2259
     * database, it makes sense to remove all tracking as well (if stats databases exist)
2260
     * so that a new course created with this code would not use the remains of an older
2261
     * course.
2262
     *
2263
     * @param string $code The code of the course to delete
2264
     *
2265
     * @todo When deleting a virtual course: unsubscribe users from that virtual
2266
     * course from the groups in the real course if they are not subscribed in
2267
     * that real course.
2268
     */
2269
    public static function delete_course($code)
2270
    {
2271
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2272
        $table_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
2273
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2274
        $table_course_survey = Database::get_main_table(TABLE_MAIN_SHARED_SURVEY);
2275
        $table_course_survey_question = Database::get_main_table(TABLE_MAIN_SHARED_SURVEY_QUESTION);
2276
        $table_course_survey_question_option = Database::get_main_table(TABLE_MAIN_SHARED_SURVEY_QUESTION_OPTION);
2277
2278
        $table_stats_hotpots = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTPOTATOES);
2279
        $table_stats_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2280
        $table_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2281
        $table_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
2282
        $table_stats_lastaccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
2283
        $table_stats_course_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2284
        $table_stats_online = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
2285
        $table_stats_default = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
2286
        $table_stats_downloads = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
2287
        $table_stats_links = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
2288
        $table_stats_uploads = Database::get_main_table(TABLE_STATISTIC_TRACK_E_UPLOADS);
2289
2290
        if (empty($code)) {
2291
            return false;
2292
        }
2293
2294
        $course = api_get_course_info($code);
2295
2296
        if (empty($course)) {
2297
            return false;
2298
        }
2299
2300
        $codeFiltered = $course['code'];
2301
        $courseId = $course['real_id'];
2302
        $courseEntity = api_get_course_entity($courseId);
2303
2304
        $count = 0;
2305
        if (api_is_multiple_url_enabled()) {
2306
            $url_id = 1;
2307
            if (api_get_current_access_url_id() != -1) {
2308
                $url_id = api_get_current_access_url_id();
2309
            }
2310
            UrlManager::delete_url_rel_course($courseId, $url_id);
2311
            $count = UrlManager::getCountUrlRelCourse($courseId);
2312
        }
2313
2314
        if ($count === 0) {
2315
            self::create_database_dump($code);
2316
2317
            // Cleaning group categories
2318
            $groupCategories = GroupManager::get_categories($course['code']);
2319
            if (!empty($groupCategories)) {
2320
                foreach ($groupCategories as $category) {
2321
                    GroupManager::delete_category($category['id'], $course['code']);
2322
                }
2323
            }
2324
2325
            // Cleaning groups
2326
            $groups = GroupManager::get_groups($courseId);
2327
            if (!empty($groups)) {
2328
                foreach ($groups as $group) {
2329
                    GroupManager::deleteGroup($group, $course['code']);
2330
                }
2331
            }
2332
2333
            $course_tables = AddCourse::get_course_tables();
2334
            // Cleaning c_x tables
2335
            if (!empty($courseId)) {
2336
                foreach ($course_tables as $table) {
2337
                    if ($table === 'document') {
2338
                        // Table document will be deleted by Doctrine.
2339
                        continue;
2340
                    }
2341
                    $table = Database::get_course_table($table);
2342
                    $sql = "DELETE FROM $table WHERE c_id = $courseId ";
2343
                    Database::query($sql);
2344
                }
2345
            }
2346
2347
            /*$course_dir = api_get_path(SYS_COURSE_PATH).$course['directory'];
2348
            $archive_dir = api_get_path(SYS_ARCHIVE_PATH).$course['directory'].'_'.time();
2349
            if (is_dir($course_dir)) {
2350
                rename($course_dir, $archive_dir);
2351
            }*/
2352
2353
            Category::deleteFromCourse($courseEntity);
2354
2355
            // Unsubscribe all users from the course
2356
            $sql = "DELETE FROM $table_course_user WHERE c_id = $courseId";
2357
            Database::query($sql);
2358
            // Delete the course from the sessions tables
2359
            $sql = "DELETE FROM $table_session_course WHERE c_id = $courseId";
2360
            Database::query($sql);
2361
            $sql = "DELETE FROM $table_session_course_user WHERE c_id = $courseId";
2362
            Database::query($sql);
2363
2364
            // Delete from Course - URL
2365
            // Already deleted because of entities.
2366
            //$sql = "DELETE FROM $table_course_rel_url WHERE c_id = $courseId";
2367
            //Database::query($sql);
2368
2369
            $sql = "SELECT survey_id FROM $table_course_survey WHERE course_code = '$codeFiltered'";
2370
            $result_surveys = Database::query($sql);
2371
            while ($surveys = Database::fetch_array($result_surveys)) {
2372
                $survey_id = $surveys[0]; //int
2373
                $sql = "DELETE FROM $table_course_survey_question WHERE survey_id = $survey_id";
2374
                Database::query($sql);
2375
                $sql = "DELETE FROM $table_course_survey_question_option WHERE survey_id = $survey_id";
2376
                Database::query($sql);
2377
                $sql = "DELETE FROM $table_course_survey WHERE survey_id = $survey_id";
2378
                Database::query($sql);
2379
            }
2380
2381
            // Delete the course from the stats tables
2382
            $sql = "DELETE FROM $table_stats_hotpots WHERE c_id = $courseId";
2383
            Database::query($sql);
2384
            $sql = "DELETE FROM $table_stats_attempt WHERE c_id = $courseId";
2385
            Database::query($sql);
2386
            $sql = "DELETE FROM $table_stats_exercises WHERE c_id = $courseId";
2387
            Database::query($sql);
2388
            $sql = "DELETE FROM $table_stats_access WHERE c_id = $courseId";
2389
            Database::query($sql);
2390
            $sql = "DELETE FROM $table_stats_lastaccess WHERE c_id = $courseId";
2391
            Database::query($sql);
2392
            $sql = "DELETE FROM $table_stats_course_access WHERE c_id = $courseId";
2393
            Database::query($sql);
2394
            $sql = "DELETE FROM $table_stats_online WHERE c_id = $courseId";
2395
            Database::query($sql);
2396
            // Do not delete rows from track_e_default as these include course
2397
            // creation and other important things that do not take much space
2398
            // but give information on the course history
2399
            //$sql = "DELETE FROM $table_stats_default WHERE c_id = $courseId";
2400
            //Database::query($sql);
2401
            $sql = "DELETE FROM $table_stats_downloads WHERE c_id = $courseId";
2402
            Database::query($sql);
2403
            $sql = "DELETE FROM $table_stats_links WHERE c_id = $courseId";
2404
            Database::query($sql);
2405
            $sql = "DELETE FROM $table_stats_uploads WHERE c_id = $courseId";
2406
            Database::query($sql);
2407
2408
            // Update ticket
2409
            $table = Database::get_main_table(TABLE_TICKET_TICKET);
2410
            $sql = "UPDATE $table SET course_id = NULL WHERE course_id = $courseId";
2411
            Database::query($sql);
2412
2413
            // Class
2414
            $table = Database::get_main_table(TABLE_USERGROUP_REL_COURSE);
2415
            $sql = "DELETE FROM $table
2416
                    WHERE course_id = $courseId";
2417
            Database::query($sql);
2418
2419
            // Skills
2420
            $table = Database::get_main_table(TABLE_MAIN_SKILL_REL_USER);
2421
            $argumentation = Database::escape_string(sprintf(get_lang('This skill was obtained through course %s which has been removed since then.'), $course['code']));
2422
            $sql = "UPDATE $table SET course_id = NULL, session_id = NULL, argumentation = '$argumentation' 
2423
                    WHERE course_id = $courseId";
2424
            Database::query($sql);
2425
2426
            $sql = "DELETE FROM skill_rel_course WHERE c_id = $courseId";
2427
            Database::query($sql);
2428
2429
            // Deletes all groups, group-users, group-tutors information
2430
            // To prevent fK mix up on some tables
2431
            GroupManager::deleteAllGroupsFromCourse($courseId);
2432
2433
            $appPlugin = new AppPlugin();
2434
            $appPlugin->performActionsWhenDeletingItem('course', $courseId);
2435
2436
            // Delete the course from the database
2437
            $repo = Container::getCourseRepository();
2438
            $repo->deleteCourse($courseEntity);
2439
2440
            // delete extra course fields
2441
            $extraFieldValues = new ExtraFieldValue('course');
2442
            $extraFieldValues->deleteValuesByItem($courseId);
2443
2444
            // Add event to system log
2445
            Event::addEvent(
2446
                LOG_COURSE_DELETE,
2447
                LOG_COURSE_CODE,
2448
                $code,
2449
                api_get_utc_datetime(),
2450
                api_get_user_id(),
2451
                $courseId
2452
            );
2453
        }
2454
    }
2455
2456
    /**
2457
     * Creates a file called mysql_dump.sql in the course folder.
2458
     *
2459
     * @param string $course_code The code of the course
2460
     *
2461
     * @todo Implementation for single database
2462
     */
2463
    public static function create_database_dump($course_code)
2464
    {
2465
        $sql_dump = '';
2466
        $course_code = Database::escape_string($course_code);
2467
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
2468
        $sql = "SELECT * FROM $table_course WHERE code = '$course_code'";
2469
        $res = Database::query($sql);
2470
        $course = Database::fetch_array($res);
2471
2472
        $course_tables = AddCourse::get_course_tables();
2473
2474
        if (!empty($course['id'])) {
2475
            //Cleaning c_x tables
2476
            foreach ($course_tables as $table) {
2477
                $table = Database::get_course_table($table);
2478
                $sql = "SELECT * FROM $table WHERE c_id = {$course['id']} ";
2479
                $res_table = Database::query($sql);
2480
2481
                while ($row = Database::fetch_array($res_table, 'ASSOC')) {
2482
                    $row_to_save = [];
2483
                    foreach ($row as $key => $value) {
2484
                        $row_to_save[$key] = $key."='".Database::escape_string($row[$key])."'";
2485
                    }
2486
                    $sql_dump .= "\nINSERT INTO $table SET ".implode(', ', $row_to_save).';';
2487
                }
2488
            }
2489
        }
2490
2491
        if (is_dir(api_get_path(SYS_COURSE_PATH).$course['directory'])) {
2492
            $file_name = api_get_path(SYS_COURSE_PATH).$course['directory'].'/mysql_dump.sql';
2493
            $handle = fopen($file_name, 'a+');
2494
            if ($handle !== false) {
2495
                fwrite($handle, $sql_dump);
2496
                fclose($handle);
2497
            } else {
2498
                //TODO trigger exception in a try-catch
2499
            }
2500
        }
2501
    }
2502
2503
    /**
2504
     * Sort courses for a specific user ??
2505
     *
2506
     * @param int    $user_id     User ID
2507
     * @param string $course_code Course code
2508
     *
2509
     * @return int Minimum course order
2510
     *
2511
     * @todo Review documentation
2512
     */
2513
    public static function userCourseSort($user_id, $course_code)
2514
    {
2515
        if ($user_id != strval(intval($user_id))) {
2516
            return false;
2517
        }
2518
2519
        $course_code = Database::escape_string($course_code);
2520
        $TABLECOURSE = Database::get_main_table(TABLE_MAIN_COURSE);
2521
        $TABLECOURSUSER = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2522
2523
        $course_title = Database::result(
2524
            Database::query(
2525
                "SELECT title FROM $TABLECOURSE WHERE code = '$course_code'"
2526
            ),
2527
            0,
2528
            0
2529
        );
2530
        if ($course_title === false) {
2531
            $course_title = '';
2532
        }
2533
2534
        $sql = "SELECT course.code as code, course.title as title, cu.sort as sort
2535
                FROM $TABLECOURSUSER as cu, $TABLECOURSE as course
2536
                WHERE   course.id = cu.c_id AND user_id = $user_id AND
2537
                        cu.relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
2538
                        user_course_cat = 0
2539
                ORDER BY cu.sort";
2540
        $result = Database::query($sql);
2541
2542
        $course_title_precedent = '';
2543
        $counter = 0;
2544
        $course_found = false;
2545
        $course_sort = 1;
2546
2547
        if (Database::num_rows($result) > 0) {
2548
            while ($courses = Database::fetch_array($result)) {
2549
                if ($course_title_precedent == '') {
2550
                    $course_title_precedent = $courses['title'];
2551
                }
2552
                if (api_strcasecmp($course_title_precedent, $course_title) < 0) {
2553
                    $course_found = true;
2554
                    $course_sort = $courses['sort'];
2555
                    if ($counter == 0) {
2556
                        $sql = "UPDATE $TABLECOURSUSER
2557
                                SET sort = sort+1
2558
                                WHERE
2559
                                    user_id= $user_id AND
2560
                                    relation_type <> ".COURSE_RELATION_TYPE_RRHH."
2561
                                    AND user_course_cat = 0
2562
                                    AND sort > $course_sort";
2563
                        $course_sort++;
2564
                    } else {
2565
                        $sql = "UPDATE $TABLECOURSUSER SET sort = sort+1
2566
                                WHERE
2567
                                    user_id= $user_id AND
2568
                                    relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
2569
                                    user_course_cat = 0 AND
2570
                                    sort >= $course_sort";
2571
                    }
2572
                    Database::query($sql);
2573
                    break;
2574
                } else {
2575
                    $course_title_precedent = $courses['title'];
2576
                }
2577
                $counter++;
2578
            }
2579
2580
            // We must register the course in the beginning of the list
2581
            if (!$course_found) {
2582
                $course_sort = Database::result(
2583
                    Database::query(
2584
                        'SELECT min(sort) as min_sort FROM '.$TABLECOURSUSER.' WHERE user_id = "'.$user_id.'" AND user_course_cat="0"'
2585
                    ),
2586
                    0,
2587
                    0
2588
                );
2589
                Database::query("UPDATE $TABLECOURSUSER SET sort = sort+1 WHERE user_id = $user_id AND user_course_cat = 0");
2590
            }
2591
        }
2592
2593
        return $course_sort;
2594
    }
2595
2596
    /**
2597
     * check if course exists.
2598
     *
2599
     * @param string $courseCode
2600
     *
2601
     * @return int if exists, false else
2602
     */
2603
    public static function course_exists($courseCode)
2604
    {
2605
        $courseCode = Database::escape_string($courseCode);
2606
        $sql = "SELECT 1 FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
2607
                WHERE code = '$courseCode'";
2608
2609
        return Database::num_rows(Database::query($sql));
2610
    }
2611
2612
    /**
2613
     * Send an email to tutor after the auth-suscription of a student in your course.
2614
     *
2615
     * @author Carlos Vargas <[email protected]>, Dokeos Latino
2616
     *
2617
     * @param int    $user_id            the id of the user
2618
     * @param string $courseId           the course code
2619
     * @param bool   $send_to_tutor_also
2620
     *
2621
     * @return false|null we return the message that is displayed when the action is successful
2622
     */
2623
    public static function email_to_tutor($user_id, $courseId, $send_to_tutor_also = false)
2624
    {
2625
        $user_id = (int) $user_id;
2626
        $courseId = (int) $courseId;
2627
        $information = api_get_course_info_by_id($courseId);
2628
        $course_code = $information['code'];
2629
        $student = api_get_user_info($user_id);
2630
2631
        $name_course = $information['title'];
2632
        $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." 
2633
                WHERE c_id = $courseId";
2634
2635
        // TODO: Ivan: This is a mistake, please, have a look at it. Intention here is diffcult to be guessed.
2636
        //if ($send_to_tutor_also = true)
2637
        // Proposed change:
2638
        if ($send_to_tutor_also) {
2639
            $sql .= ' AND is_tutor = 1';
2640
        } else {
2641
            $sql .= ' AND status = 1';
2642
        }
2643
2644
        $result = Database::query($sql);
2645
        while ($row = Database::fetch_array($result)) {
2646
            $tutor = api_get_user_info($row['user_id']);
2647
            $emailto = $tutor['email'];
2648
            $emailsubject = get_lang('New user in the course').': '.$name_course;
2649
            $emailbody = get_lang('Dear').': '.api_get_person_name($tutor['firstname'], $tutor['lastname'])."\n";
2650
            $emailbody .= get_lang('MessageNew user in the course').': '.$name_course."\n";
2651
            $emailbody .= get_lang('Username').': '.$student['username']."\n";
2652
            if (api_is_western_name_order()) {
2653
                $emailbody .= get_lang('First name').': '.$student['firstname']."\n";
2654
                $emailbody .= get_lang('Last name').': '.$student['lastname']."\n";
2655
            } else {
2656
                $emailbody .= get_lang('Last name').': '.$student['lastname']."\n";
2657
                $emailbody .= get_lang('First name').': '.$student['firstname']."\n";
2658
            }
2659
            $emailbody .= get_lang('e-mail').': <a href="mailto:'.$student['email'].'">'.$student['email']."</a>\n\n";
2660
            $recipient_name = api_get_person_name(
2661
                $tutor['firstname'],
2662
                $tutor['lastname'],
2663
                null,
2664
                PERSON_NAME_EMAIL_ADDRESS
2665
            );
2666
            $sender_name = api_get_person_name(
2667
                api_get_setting('administratorName'),
2668
                api_get_setting('administratorSurname'),
2669
                null,
2670
                PERSON_NAME_EMAIL_ADDRESS
2671
            );
2672
            $email_admin = api_get_setting('emailAdministrator');
2673
2674
            $additionalParameters = [
2675
                'smsType' => SmsPlugin::NEW_USER_SUBSCRIBED_COURSE,
2676
                'userId' => $tutor['user_id'],
2677
                'userUsername' => $student['username'],
2678
                'courseCode' => $course_code,
2679
            ];
2680
            api_mail_html(
2681
                $recipient_name,
2682
                $emailto,
2683
                $emailsubject,
2684
                $emailbody,
2685
                $sender_name,
2686
                $email_admin,
2687
                null,
2688
                null,
2689
                null,
2690
                $additionalParameters
2691
            );
2692
        }
2693
    }
2694
2695
    /**
2696
     * @return array
2697
     */
2698
    public static function get_special_course_list()
2699
    {
2700
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
2701
        $tbl_course_field = Database::get_main_table(TABLE_EXTRA_FIELD);
2702
        $tbl_course_field_value = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2703
2704
        //we filter the courses from the URL
2705
        $join_access_url = $where_access_url = '';
2706
        if (api_get_multiple_access_url()) {
2707
            $access_url_id = api_get_current_access_url_id();
2708
            if ($access_url_id != -1) {
2709
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2710
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course
2711
                                    ON url_rel_course.c_id = tcfv.item_id ";
2712
                $where_access_url = " AND access_url_id = $access_url_id ";
2713
            }
2714
        }
2715
2716
        $extraFieldType = EntityExtraField::COURSE_FIELD_TYPE;
2717
2718
        // get course list auto-register
2719
        $sql = "SELECT DISTINCT(c.id)
2720
                FROM $tbl_course_field_value tcfv
2721
                INNER JOIN $tbl_course_field tcf
2722
                ON tcfv.field_id =  tcf.id $join_access_url
2723
                INNER JOIN $courseTable c
2724
                ON (c.id = tcfv.item_id)
2725
                WHERE
2726
                    tcf.extra_field_type = $extraFieldType AND
2727
                    tcf.variable = 'special_course' AND
2728
                    tcfv.value = 1 $where_access_url";
2729
2730
        $result = Database::query($sql);
2731
        $courseList = [];
2732
2733
        if (Database::num_rows($result) > 0) {
2734
            while ($row = Database::fetch_array($result)) {
2735
                $courseList[] = $row['id'];
2736
            }
2737
        }
2738
2739
        return $courseList;
2740
    }
2741
2742
    /**
2743
     * Get the course codes that have been restricted in the catalogue, and if byUserId is set
2744
     * then the courses that the user is allowed or not to see in catalogue.
2745
     *
2746
     * @param bool $allowed  Either if the courses have some users that are or are not allowed to see in catalogue
2747
     * @param int  $byUserId if the courses are or are not allowed to see to the user
2748
     *
2749
     * @return array Course codes allowed or not to see in catalogue by some user or the user
2750
     */
2751
    public static function getCatalogueCourseList($allowed = true, $byUserId = -1)
2752
    {
2753
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
2754
        $tblCourseRelUserCatalogue = Database::get_main_table(TABLE_MAIN_COURSE_CATALOGUE_USER);
2755
        $visibility = $allowed ? 1 : 0;
2756
2757
        // Restriction by user id
2758
        $currentUserRestriction = '';
2759
        if ($byUserId > 0) {
2760
            $byUserId = (int) $byUserId;
2761
            $currentUserRestriction = " AND tcruc.user_id = $byUserId ";
2762
        }
2763
2764
        //we filter the courses from the URL
2765
        $joinAccessUrl = '';
2766
        $whereAccessUrl = '';
2767
        if (api_get_multiple_access_url()) {
2768
            $accessUrlId = api_get_current_access_url_id();
2769
            if ($accessUrlId != -1) {
2770
                $tblUrlCourse = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2771
                $joinAccessUrl = "LEFT JOIN $tblUrlCourse url_rel_course
2772
                                  ON url_rel_course.c_id = c.id ";
2773
                $whereAccessUrl = " AND access_url_id = $accessUrlId ";
2774
            }
2775
        }
2776
2777
        // get course list auto-register
2778
        $sql = "SELECT DISTINCT(c.code)
2779
                FROM $tblCourseRelUserCatalogue tcruc
2780
                INNER JOIN $courseTable c
2781
                ON (c.id = tcruc.c_id) $joinAccessUrl
2782
                WHERE tcruc.visible = $visibility $currentUserRestriction $whereAccessUrl";
2783
2784
        $result = Database::query($sql);
2785
        $courseList = [];
2786
2787
        if (Database::num_rows($result) > 0) {
2788
            while ($resultRow = Database::fetch_array($result)) {
2789
                $courseList[] = $resultRow['code'];
2790
            }
2791
        }
2792
2793
        return $courseList;
2794
    }
2795
2796
    /**
2797
     * Get list of courses for a given user.
2798
     *
2799
     * @param int   $user_id
2800
     * @param bool  $include_sessions                   Whether to include courses from session or not
2801
     * @param bool  $adminGetsAllCourses                If the user is platform admin,
2802
     *                                                  whether he gets all the courses or just his. Note: This does *not* include all sessions
2803
     * @param bool  $loadSpecialCourses
2804
     * @param array $skipCourseList                     List of course ids to skip
2805
     * @param bool  $useUserLanguageFilterIfAvailable
2806
     * @param bool  $showCoursesSessionWithDifferentKey
2807
     *
2808
     * @return array List of codes and db name
2809
     *
2810
     * @author isaac flores paz
2811
     */
2812
    public static function get_courses_list_by_user_id(
2813
        $user_id,
2814
        $include_sessions = false,
2815
        $adminGetsAllCourses = false,
2816
        $loadSpecialCourses = true,
2817
        $skipCourseList = [],
2818
        $useUserLanguageFilterIfAvailable = true,
2819
        $showCoursesSessionWithDifferentKey = false
2820
    ) {
2821
        $user_id = intval($user_id);
2822
        $urlId = api_get_current_access_url_id();
2823
        $course_list = [];
2824
        $codes = [];
2825
2826
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2827
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2828
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
2829
        $tableCourseUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2830
2831
        $languageCondition = '';
2832
        $onlyInUserLanguage = api_get_configuration_value('my_courses_show_courses_in_user_language_only');
2833
        if ($useUserLanguageFilterIfAvailable && $onlyInUserLanguage) {
2834
            $userInfo = api_get_user_info(api_get_user_id());
2835
            if (!empty($userInfo['language'])) {
2836
                $languageCondition = " AND course.course_language = '".$userInfo['language']."' ";
2837
            }
2838
        }
2839
2840
        if ($adminGetsAllCourses && UserManager::is_admin($user_id)) {
2841
            // get the whole courses list
2842
            $sql = "SELECT DISTINCT(course.code), course.id as real_id, course.title
2843
                    FROM $tbl_course course 
2844
                    INNER JOIN $tableCourseUrl url 
2845
                    ON (course.id = url.c_id)
2846
                    WHERE 
2847
                        url.access_url_id = $urlId     
2848
                        $languageCondition 
2849
                ";
2850
        } else {
2851
            $withSpecialCourses = $withoutSpecialCourses = '';
2852
2853
            if ($loadSpecialCourses) {
2854
                $specialCourseList = self::get_special_course_list();
2855
2856
                if (!empty($specialCourseList)) {
2857
                    $specialCourseToString = '"'.implode('","', $specialCourseList).'"';
2858
                    $withSpecialCourses = ' AND course.id IN ('.$specialCourseToString.')';
2859
                    $withoutSpecialCourses = ' AND course.id NOT IN ('.$specialCourseToString.')';
2860
                }
2861
2862
                if (!empty($withSpecialCourses)) {
2863
                    $sql = "SELECT DISTINCT (course.code), 
2864
                            course.id as real_id,
2865
                            course.category_code AS category,
2866
                            course.title
2867
                            FROM $tbl_course_user course_rel_user
2868
                            LEFT JOIN $tbl_course course
2869
                            ON course.id = course_rel_user.c_id
2870
                            LEFT JOIN $tbl_user_course_category user_course_category
2871
                            ON course_rel_user.user_course_cat = user_course_category.id
2872
                            INNER JOIN $tableCourseUrl url 
2873
                            ON (course.id = url.c_id)  
2874
                            WHERE url.access_url_id = $urlId 
2875
                            $withSpecialCourses
2876
                            $languageCondition                        
2877
                            GROUP BY course.code
2878
                            ORDER BY user_course_category.sort, course.title, course_rel_user.sort ASC
2879
                    ";
2880
                    $result = Database::query($sql);
2881
                    if (Database::num_rows($result) > 0) {
2882
                        while ($result_row = Database::fetch_array($result, 'ASSOC')) {
2883
                            $result_row['special_course'] = 1;
2884
                            $course_list[] = $result_row;
2885
                            $codes[] = $result_row['real_id'];
2886
                        }
2887
                    }
2888
                }
2889
            }
2890
2891
            // get course list not auto-register. Use Distinct to avoid multiple
2892
            // entries when a course is assigned to a HRD (DRH) as watcher
2893
            $sql = "SELECT 
2894
                        DISTINCT(course.code), 
2895
                        course.id as real_id, 
2896
                        course.category_code AS category,
2897
                        course.title
2898
                    FROM $tbl_course course
2899
                    INNER JOIN $tbl_course_user cru 
2900
                    ON (course.id = cru.c_id)
2901
                    INNER JOIN $tableCourseUrl url 
2902
                    ON (course.id = url.c_id) 
2903
                    WHERE 
2904
                        url.access_url_id = $urlId AND 
2905
                        cru.user_id = $user_id 
2906
                        $withoutSpecialCourses
2907
                        $languageCondition
2908
                    ORDER BY course.title
2909
                    ";
2910
        }
2911
        $result = Database::query($sql);
2912
2913
        if (Database::num_rows($result)) {
2914
            while ($row = Database::fetch_array($result, 'ASSOC')) {
2915
                if (!empty($skipCourseList)) {
2916
                    if (in_array($row['real_id'], $skipCourseList)) {
2917
                        continue;
2918
                    }
2919
                }
2920
                $course_list[] = $row;
2921
                $codes[] = $row['real_id'];
2922
            }
2923
        }
2924
2925
        if ($include_sessions === true) {
2926
            $sql = "SELECT DISTINCT (c.code), 
2927
                        c.id as real_id, 
2928
                        c.category_code AS category,
2929
                        s.id as session_id,
2930
                        s.name as session_name
2931
                    FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)." scu                     
2932
                    INNER JOIN $tbl_course c
2933
                    ON (scu.c_id = c.id)
2934
                    INNER JOIN ".Database::get_main_table(TABLE_MAIN_SESSION)." s
2935
                    ON (s.id = scu.session_id)
2936
                    WHERE user_id = $user_id ";
2937
            $r = Database::query($sql);
2938
            while ($row = Database::fetch_array($r, 'ASSOC')) {
2939
                if (!empty($skipCourseList)) {
2940
                    if (in_array($row['real_id'], $skipCourseList)) {
2941
                        continue;
2942
                    }
2943
                }
2944
2945
                if ($showCoursesSessionWithDifferentKey) {
2946
                    $course_list[] = $row;
2947
                } else {
2948
                    if (!in_array($row['real_id'], $codes)) {
2949
                        $course_list[] = $row;
2950
                    }
2951
                }
2952
            }
2953
        }
2954
2955
        return $course_list;
2956
    }
2957
2958
    /**
2959
     * Get course ID from a given course directory name.
2960
     *
2961
     * @param string $path Course directory (without any slash)
2962
     *
2963
     * @return string Course code, or false if not found
2964
     */
2965
    public static function getCourseCodeFromDirectory($path)
2966
    {
2967
        $path = Database::escape_string(str_replace('.', '', str_replace('/', '', $path)));
2968
        $res = Database::query("SELECT code FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
2969
                WHERE directory LIKE BINARY '$path'");
2970
        if ($res === false) {
2971
            return false;
2972
        }
2973
        if (Database::num_rows($res) != 1) {
2974
            return false;
2975
        }
2976
        $row = Database::fetch_array($res);
2977
2978
        return $row['code'];
2979
    }
2980
2981
    /**
2982
     * Get course code(s) from visual code.
2983
     *
2984
     * @deprecated
2985
     *
2986
     * @param   string  Visual code
2987
     *
2988
     * @return array List of codes for the given visual code
2989
     */
2990
    public static function get_courses_info_from_visual_code($code)
2991
    {
2992
        $result = [];
2993
        $sql_result = Database::query("SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
2994
                WHERE visual_code = '".Database::escape_string($code)."'");
2995
        while ($virtual_course = Database::fetch_array($sql_result)) {
2996
            $result[] = $virtual_course;
2997
        }
2998
2999
        return $result;
3000
    }
3001
3002
    /**
3003
     * Creates a new extra field for a given course.
3004
     *
3005
     * @param string $variable    Field's internal variable name
3006
     * @param int    $fieldType   Field's type
3007
     * @param string $displayText Field's language var name
3008
     * @param string $default     Optional. The default value
3009
     *
3010
     * @return int New extra field ID
3011
     */
3012
    public static function create_course_extra_field($variable, $fieldType, $displayText, $default = '')
3013
    {
3014
        $extraField = new ExtraField('course');
3015
        $params = [
3016
            'variable' => $variable,
3017
            'field_type' => $fieldType,
3018
            'display_text' => $displayText,
3019
            'default_value' => $default,
3020
        ];
3021
3022
        return $extraField->save($params);
3023
    }
3024
3025
    /**
3026
     * Update course attributes. Will only update attributes with a non-empty value.
3027
     * Note that you NEED to check that your attributes are valid before using this function.
3028
     *
3029
     * @param int Course id
3030
     * @param array Associative array with field names as keys and field values as values
3031
     *
3032
     * @return Doctrine\DBAL\Driver\Statement|null True if update was successful, false otherwise
3033
     */
3034
    public static function update_attributes($id, $attributes)
3035
    {
3036
        $id = (int) $id;
3037
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
3038
        $sql = "UPDATE $table SET ";
3039
        $i = 0;
3040
        foreach ($attributes as $name => $value) {
3041
            if ($value != '') {
3042
                if ($i > 0) {
3043
                    $sql .= ", ";
3044
                }
3045
                $sql .= " $name = '".Database::escape_string($value)."'";
3046
                $i++;
3047
            }
3048
        }
3049
        $sql .= " WHERE id = $id";
3050
3051
        return Database::query($sql);
3052
    }
3053
3054
    /**
3055
     * Update an extra field value for a given course.
3056
     *
3057
     * @param string $course_code Course code
3058
     * @param string $variable    Field variable name
3059
     * @param string $value       Optional. Default field value
3060
     *
3061
     * @return bool|int An integer when register a new extra field. And boolean when update the extrafield
3062
     */
3063
    public static function update_course_extra_field_value($course_code, $variable, $value = '')
3064
    {
3065
        $courseInfo = api_get_course_info($course_code);
3066
        $courseId = $courseInfo['real_id'];
3067
3068
        $extraFieldValues = new ExtraFieldValue('course');
3069
        $params = [
3070
            'item_id' => $courseId,
3071
            'variable' => $variable,
3072
            'value' => $value,
3073
        ];
3074
3075
        return $extraFieldValues->save($params);
3076
    }
3077
3078
    /**
3079
     * @param int $sessionId
3080
     *
3081
     * @return mixed
3082
     */
3083
    public static function get_session_category_id_by_session_id($sessionId)
3084
    {
3085
        if (empty($sessionId)) {
3086
            return [];
3087
        }
3088
        $sessionId = intval($sessionId);
3089
        $sql = 'SELECT sc.id session_category
3090
                FROM '.Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY).' sc
3091
                INNER JOIN '.Database::get_main_table(TABLE_MAIN_SESSION).' s
3092
                ON sc.id = s.session_category_id 
3093
                WHERE s.id = '.$sessionId;
3094
3095
        return Database::result(
3096
            Database::query($sql),
3097
            0,
3098
            'session_category'
3099
        );
3100
    }
3101
3102
    /**
3103
     * Gets the value of a course extra field. Returns null if it was not found.
3104
     *
3105
     * @param string $variable Name of the extra field
3106
     * @param string $code     Course code
3107
     *
3108
     * @return string Value
3109
     */
3110
    public static function get_course_extra_field_value($variable, $code)
3111
    {
3112
        $courseInfo = api_get_course_info($code);
3113
        $courseId = $courseInfo['real_id'];
3114
3115
        $extraFieldValues = new ExtraFieldValue('course');
3116
        $result = $extraFieldValues->get_values_by_handler_and_field_variable($courseId, $variable);
3117
        if (!empty($result['value'])) {
3118
            return $result['value'];
3119
        }
3120
3121
        return null;
3122
    }
3123
3124
    /**
3125
     * Lists details of the course description.
3126
     *
3127
     * @param array        The course description
3128
     * @param string    The encoding
3129
     * @param bool        If true is displayed if false is hidden
3130
     *
3131
     * @return string The course description in html
3132
     */
3133
    public static function get_details_course_description_html(
3134
        $descriptions,
3135
        $charset,
3136
        $action_show = true
3137
    ) {
3138
        $data = null;
3139
        if (isset($descriptions) && count($descriptions) > 0) {
3140
            foreach ($descriptions as $description) {
3141
                $data .= '<div class="sectiontitle">';
3142
                if (api_is_allowed_to_edit() && $action_show) {
3143
                    //delete
3144
                    $data .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=delete&description_id='.$description->id.'" onclick="javascript:if(!confirm(\''.addslashes(api_htmlentities(
3145
                        get_lang('Please confirm your choice'),
3146
                                ENT_QUOTES,
3147
                        $charset
3148
                    )).'\')) return false;">';
3149
                    $data .= Display::return_icon(
3150
                        'delete.gif',
3151
                        get_lang('Delete'),
3152
                        ['style' => 'vertical-align:middle;float:right;']
3153
                    );
3154
                    $data .= '</a> ';
3155
                    //edit
3156
                    $data .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&description_id='.$description->id.'">';
3157
                    $data .= Display::return_icon(
3158
                        'edit.png',
3159
                        get_lang('Edit'),
3160
                        ['style' => 'vertical-align:middle;float:right; padding-right:4px;'],
3161
                        ICON_SIZE_SMALL
3162
                    );
3163
                    $data .= '</a> ';
3164
                }
3165
                $data .= $description->title;
3166
                $data .= '</div>';
3167
                $data .= '<div class="sectioncomment">';
3168
                $data .= Security::remove_XSS($description->content);
3169
                $data .= '</div>';
3170
            }
3171
        } else {
3172
            $data .= '<em>'.get_lang('There is no course description so far.').'</em>';
3173
        }
3174
3175
        return $data;
3176
    }
3177
3178
    /**
3179
     * Returns the details of a course category.
3180
     *
3181
     * @param string $code Category code
3182
     *
3183
     * @return array Course category
3184
     */
3185
    public static function get_course_category($code)
3186
    {
3187
        $table = Database::get_main_table(TABLE_MAIN_CATEGORY);
3188
        $code = Database::escape_string($code);
3189
        $sql = "SELECT * FROM $table WHERE code = '$code'";
3190
3191
        return Database::fetch_array(Database::query($sql));
3192
    }
3193
3194
    /**
3195
     * Subscribes courses to human resource manager (Dashboard feature).
3196
     *
3197
     * @param int   $hr_manager_id Human Resource Manager id
3198
     * @param array $courses_list  Courses code
3199
     *
3200
     * @return int
3201
     */
3202
    public static function subscribeCoursesToDrhManager($hr_manager_id, $courses_list)
3203
    {
3204
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3205
        $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3206
3207
        $hr_manager_id = intval($hr_manager_id);
3208
        $affected_rows = 0;
3209
3210
        //Deleting assigned courses to hrm_id
3211
        if (api_is_multiple_url_enabled()) {
3212
            $sql = "SELECT s.c_id FROM $tbl_course_rel_user s
3213
                    INNER JOIN $tbl_course_rel_access_url a
3214
                    ON (a.c_id = s.c_id)
3215
                    WHERE
3216
                        user_id = $hr_manager_id AND
3217
                        relation_type = ".COURSE_RELATION_TYPE_RRHH." AND
3218
                        access_url_id = ".api_get_current_access_url_id();
3219
        } else {
3220
            $sql = "SELECT c_id FROM $tbl_course_rel_user
3221
                    WHERE user_id = $hr_manager_id AND relation_type = ".COURSE_RELATION_TYPE_RRHH;
3222
        }
3223
        $result = Database::query($sql);
3224
        if (Database::num_rows($result) > 0) {
3225
            while ($row = Database::fetch_array($result)) {
3226
                $sql = "DELETE FROM $tbl_course_rel_user
3227
                        WHERE
3228
                            c_id = {$row['c_id']} AND
3229
                            user_id = $hr_manager_id AND
3230
                            relation_type = ".COURSE_RELATION_TYPE_RRHH;
3231
                Database::query($sql);
3232
            }
3233
        }
3234
3235
        // inserting new courses list
3236
        if (is_array($courses_list)) {
3237
            foreach ($courses_list as $course_code) {
3238
                $courseInfo = api_get_course_info($course_code);
3239
                $courseId = $courseInfo['real_id'];
3240
                $sql = "INSERT IGNORE INTO $tbl_course_rel_user(c_id, user_id, status, relation_type)
3241
                        VALUES($courseId, $hr_manager_id, ".DRH.", ".COURSE_RELATION_TYPE_RRHH.")";
3242
                $result = Database::query($sql);
3243
                if (Database::affected_rows($result)) {
3244
                    $affected_rows++;
3245
                }
3246
            }
3247
        }
3248
3249
        return $affected_rows;
3250
    }
3251
3252
    /**
3253
     * get courses followed by human resources manager.
3254
     *
3255
     * @param int    $user_id
3256
     * @param int    $status
3257
     * @param int    $from
3258
     * @param int    $limit
3259
     * @param string $column
3260
     * @param string $direction
3261
     * @param bool   $getCount
3262
     *
3263
     * @return array courses
3264
     */
3265
    public static function get_courses_followed_by_drh(
3266
        $user_id,
3267
        $status = DRH,
3268
        $from = null,
3269
        $limit = null,
3270
        $column = null,
3271
        $direction = null,
3272
        $getCount = false
3273
    ) {
3274
        return self::getCoursesFollowedByUser(
3275
            $user_id,
3276
            $status,
3277
            $from,
3278
            $limit,
3279
            $column,
3280
            $direction,
3281
            $getCount
3282
        );
3283
    }
3284
3285
    /**
3286
     * get courses followed by user.
3287
     *
3288
     * @param int    $user_id
3289
     * @param int    $status
3290
     * @param int    $from
3291
     * @param int    $limit
3292
     * @param string $column
3293
     * @param string $direction
3294
     * @param bool   $getCount
3295
     * @param string $keyword
3296
     * @param int    $sessionId
3297
     * @param bool   $showAllAssignedCourses
3298
     *
3299
     * @return array courses
3300
     */
3301
    public static function getCoursesFollowedByUser(
3302
        $user_id,
3303
        $status = null,
3304
        $from = null,
3305
        $limit = null,
3306
        $column = null,
3307
        $direction = null,
3308
        $getCount = false,
3309
        $keyword = null,
3310
        $sessionId = 0,
3311
        $showAllAssignedCourses = false
3312
    ) {
3313
        // Database Table Definitions
3314
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3315
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3316
        $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3317
        $sessionId = (int) $sessionId;
3318
        $user_id = (int) $user_id;
3319
        $select = "SELECT DISTINCT c.*, c.id as real_id ";
3320
3321
        if ($getCount) {
3322
            $select = "SELECT COUNT(DISTINCT c.id) as count";
3323
        }
3324
3325
        $whereConditions = '';
3326
        switch ($status) {
3327
            case COURSEMANAGER:
3328
                $whereConditions .= " AND cru.user_id = $user_id";
3329
                if (!$showAllAssignedCourses) {
3330
                    $whereConditions .= " AND cru.status = ".COURSEMANAGER;
3331
                } else {
3332
                    $whereConditions .= " AND relation_type = ".COURSE_RELATION_TYPE_COURSE_MANAGER;
3333
                }
3334
                break;
3335
            case DRH:
3336
                $whereConditions .= " AND
3337
                    cru.user_id = $user_id AND
3338
                    cru.status = ".DRH." AND
3339
                    relation_type = '".COURSE_RELATION_TYPE_RRHH."'
3340
                ";
3341
                break;
3342
        }
3343
3344
        $keywordCondition = null;
3345
        if (!empty($keyword)) {
3346
            $keyword = Database::escape_string($keyword);
3347
            $keywordCondition = " AND (c.code LIKE '%$keyword%' OR c.title LIKE '%$keyword%' ) ";
3348
        }
3349
3350
        $orderBy = null;
3351
        $extraInnerJoin = null;
3352
3353
        if (!empty($sessionId)) {
3354
            if ($status == COURSEMANAGER) {
3355
                // Teacher of course or teacher inside session
3356
                $whereConditions = " AND (cru.status = ".COURSEMANAGER." OR srcru.status = 2) ";
3357
            }
3358
            $courseList = SessionManager::get_course_list_by_session_id($sessionId);
3359
            if (!empty($courseList)) {
3360
                $courseListToString = implode("','", array_keys($courseList));
3361
                $whereConditions .= " AND c.id IN ('".$courseListToString."')";
3362
            }
3363
            $tableSessionRelCourse = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3364
            $tableSessionRelCourseRelUser = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3365
            $orderBy = ' ORDER BY position';
3366
            $extraInnerJoin = " INNER JOIN $tableSessionRelCourse src
3367
                                ON (c.id = src.c_id AND src.session_id = $sessionId)
3368
                                INNER JOIN $tableSessionRelCourseRelUser srcru 
3369
                                ON (src.session_id = srcru.session_id AND srcru.c_id = src.c_id)
3370
                            ";
3371
        }
3372
3373
        $whereConditions .= $keywordCondition;
3374
        $sql = "$select
3375
                FROM $tbl_course c
3376
                INNER JOIN $tbl_course_rel_user cru 
3377
                ON (cru.c_id = c.id)
3378
                INNER JOIN $tbl_course_rel_access_url a 
3379
                ON (a.c_id = c.id)
3380
                $extraInnerJoin
3381
                WHERE
3382
                    access_url_id = ".api_get_current_access_url_id()."
3383
                    $whereConditions
3384
                $orderBy
3385
                ";
3386
        if (isset($from) && isset($limit)) {
3387
            $from = intval($from);
3388
            $limit = intval($limit);
3389
            $sql .= " LIMIT $from, $limit";
3390
        }
3391
3392
        $result = Database::query($sql);
3393
3394
        if ($getCount) {
3395
            $row = Database::fetch_array($result);
3396
3397
            return $row['count'];
3398
        }
3399
3400
        $courses = [];
3401
        if (Database::num_rows($result) > 0) {
3402
            while ($row = Database::fetch_array($result)) {
3403
                $courses[$row['code']] = $row;
3404
            }
3405
        }
3406
3407
        return $courses;
3408
    }
3409
3410
    /**
3411
     * check if a course is special (autoregister).
3412
     *
3413
     * @param int $courseId
3414
     *
3415
     * @return bool
3416
     */
3417
    public static function isSpecialCourse($courseId)
3418
    {
3419
        $extraFieldValue = new ExtraFieldValue('course');
3420
        $result = $extraFieldValue->get_values_by_handler_and_field_variable(
3421
            $courseId,
3422
            'special_course'
3423
        );
3424
3425
        if (!empty($result)) {
3426
            if ($result['value'] == 1) {
3427
                return true;
3428
            }
3429
        }
3430
3431
        return false;
3432
    }
3433
3434
    /**
3435
     * Display special courses (and only these) as several HTML divs of class userportal-course-item.
3436
     *
3437
     * Special courses are courses that stick on top of the list and are "auto-registerable"
3438
     * in the sense that any user clicking them is registered as a student
3439
     *
3440
     * @param int  $user_id                          User id
3441
     * @param bool $load_dirs                        Whether to show the document quick-loader or not
3442
     * @param bool $useUserLanguageFilterIfAvailable
3443
     *
3444
     * @return array
3445
     */
3446
    public static function returnSpecialCourses(
3447
        $user_id,
3448
        $load_dirs = false,
3449
        $useUserLanguageFilterIfAvailable = true
3450
    ) {
3451
        $user_id = (int) $user_id;
3452
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
3453
        $specialCourseList = self::get_special_course_list();
3454
3455
        if (empty($specialCourseList)) {
3456
            return [];
3457
        }
3458
3459
        // Filter by language
3460
        $languageCondition = '';
3461
        $onlyInUserLanguage = api_get_configuration_value('my_courses_show_courses_in_user_language_only');
3462
        if ($useUserLanguageFilterIfAvailable && $onlyInUserLanguage) {
3463
            $userInfo = api_get_user_info(api_get_user_id());
3464
            if (!empty($userInfo['language'])) {
3465
                $languageCondition = " AND course_language = '".$userInfo['language']."' ";
3466
            }
3467
        }
3468
3469
        $sql = "SELECT
3470
                    id,
3471
                    code,
3472
                    subscribe subscr,
3473
                    unsubscribe unsubscr
3474
                FROM $table                      
3475
                WHERE 
3476
                    id IN ('".implode("','", $specialCourseList)."')
3477
                    $languageCondition
3478
                GROUP BY code";
3479
3480
        $rs_special_course = Database::query($sql);
3481
        $number_of_courses = Database::num_rows($rs_special_course);
3482
        $showCustomIcon = api_get_setting('course_images_in_courses_list');
3483
3484
        $courseList = [];
3485
        if ($number_of_courses > 0) {
3486
            while ($course = Database::fetch_array($rs_special_course)) {
3487
                $course_info = api_get_course_info($course['code']);
3488
                $courseId = $course_info['real_id'];
3489
                if ($course_info['visibility'] == COURSE_VISIBILITY_HIDDEN) {
3490
                    continue;
3491
                }
3492
3493
                $params = [];
3494
                //Param (course_code) needed to get the student info in page "My courses"
3495
                $params['course_code'] = $course['code'];
3496
                $params['code'] = $course['code'];
3497
                // Get notifications.
3498
                $course_info['id_session'] = null;
3499
                $courseUserInfo = self::getUserCourseInfo($user_id, $courseId);
3500
3501
                if (empty($courseUserInfo)) {
3502
                    $course_info['status'] = STUDENT;
3503
                } else {
3504
                    $course_info['status'] = $courseUserInfo['status'];
3505
                }
3506
                $show_notification = !api_get_configuration_value('hide_course_notification')
3507
                    ? Display::show_notification($course_info)
3508
                    : '';
3509
                $params['edit_actions'] = '';
3510
                $params['document'] = '';
3511
                if (api_is_platform_admin()) {
3512
                    $params['edit_actions'] .= api_get_path(WEB_CODE_PATH).'course_info/infocours.php?cidReq='.$course['code'];
3513
                    if ($load_dirs) {
3514
                        $params['document'] = '<a id="document_preview_'.$courseId.'_0" class="document_preview btn btn-outline-secondary btn-sm" href="javascript:void(0);">'
3515
                           .Display::returnFontAwesomeIcon('folder-open').'</a>';
3516
                        $params['document'] .= Display::div('', ['id' => 'document_result_'.$courseId.'_0', 'class' => 'document_preview_container']);
3517
                    }
3518
                } else {
3519
                    if ($course_info['visibility'] != COURSE_VISIBILITY_CLOSED && $load_dirs) {
3520
                        $params['document'] = '<a id="document_preview_'.$courseId.'_0" class="document_preview btn btn-outline-secondary btn-sm" href="javascript:void(0);">'
3521
                           .Display::returnFontAwesomeIcon('folder-open').'</a>';
3522
                        $params['document'] .= Display::div('', ['id' => 'document_result_'.$courseId.'_0', 'class' => 'document_preview_container']);
3523
                    }
3524
                }
3525
3526
                $params['visibility'] = $course_info['visibility'];
3527
                $params['status'] = $course_info['status'];
3528
                $params['category'] = $course_info['categoryName'];
3529
                $params['category_code'] = $course_info['categoryCode'];
3530
                $params['icon'] = Display::return_icon(
3531
                    'drawing-pin.png',
3532
                    null,
3533
                    null,
3534
                    ICON_SIZE_LARGE,
3535
                    null
3536
                );
3537
3538
                if (api_get_setting('display_coursecode_in_courselist') == 'true') {
3539
                    $params['code_course'] = '('.$course_info['visual_code'].')';
3540
                }
3541
3542
                $params['title'] = $course_info['title'];
3543
                $params['title_cut'] = $course_info['title'];
3544
                $params['link'] = $course_info['course_public_url'].'?id_session=0&autoreg=1';
3545
                if (api_get_setting('display_teacher_in_courselist') === 'true') {
3546
                    $params['teachers'] = self::getTeachersFromCourse(
3547
                        $courseId,
3548
                        true
3549
                    );
3550
                }
3551
3552
                if ($showCustomIcon === 'true') {
3553
                    $params['thumbnails'] = $course_info['course_image'];
3554
                    $params['image'] = $course_info['course_image_large'];
3555
                }
3556
3557
                if ($course_info['visibility'] != COURSE_VISIBILITY_CLOSED) {
3558
                    $params['notifications'] = $show_notification;
3559
                }
3560
3561
                $params['is_special_course'] = true;
3562
                $courseList[] = $params;
3563
            }
3564
        }
3565
3566
        return $courseList;
3567
    }
3568
3569
    /**
3570
     * Display courses (without special courses) as several HTML divs
3571
     * of course categories, as class userportal-catalog-item.
3572
     *
3573
     * @uses \displayCoursesInCategory() to display the courses themselves
3574
     *
3575
     * @param int  $user_id
3576
     * @param bool $load_dirs                        Whether to show the document quick-loader or not
3577
     * @param bool $useUserLanguageFilterIfAvailable
3578
     *
3579
     * @return array
3580
     */
3581
    public static function returnCourses(
3582
        $user_id,
3583
        $load_dirs = false,
3584
        $useUserLanguageFilterIfAvailable = true
3585
    ) {
3586
        $user_id = (int) $user_id;
3587
        if (empty($user_id)) {
3588
            $user_id = api_get_user_id();
3589
        }
3590
        // Step 1: We get all the categories of the user
3591
        $table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3592
        $sql = "SELECT * FROM $table
3593
                WHERE user_id = $user_id
3594
                ORDER BY sort ASC";
3595
3596
        $result = Database::query($sql);
3597
        $listItems = [
3598
            'in_category' => [],
3599
            'not_category' => [],
3600
        ];
3601
        $collapsable = api_get_configuration_value('allow_user_course_category_collapsable');
3602
        $stok = Security::get_token();
3603
        while ($row = Database::fetch_array($result)) {
3604
            // We simply display the title of the category.
3605
            $courseInCategory = self::returnCoursesCategories(
3606
                $row['id'],
3607
                $load_dirs,
3608
                $user_id,
3609
                $useUserLanguageFilterIfAvailable
3610
            );
3611
3612
            $collapsed = 0;
3613
            $collapsableLink = '';
3614
            if ($collapsable) {
3615
                $url = api_get_path(WEB_CODE_PATH).
3616
                    'auth/sort_my_courses.php?categoryid='.$row['id'].'&sec_token='.$stok.'&redirect=home';
3617
                $collapsed = isset($row['collapsed']) && $row['collapsed'] ? 1 : 0;
3618
                if ($collapsed === 0) {
3619
                    $collapsableLink = Display::url(
3620
                        '<i class="fa fa-folder-open"></i>',
3621
                        $url.'&action=set_collapsable&option=1'
3622
                    );
3623
                } else {
3624
                    $collapsableLink = Display::url(
3625
                        '<i class="fa fa-folder"></i>',
3626
                        $url.'&action=set_collapsable&option=0'
3627
                    );
3628
                }
3629
            }
3630
3631
            $params = [
3632
                'id_category' => $row['id'],
3633
                'title_category' => $row['title'],
3634
                'collapsed' => $collapsed,
3635
                'collapsable_link' => $collapsableLink,
3636
                'courses' => $courseInCategory,
3637
            ];
3638
            $listItems['in_category'][] = $params;
3639
        }
3640
3641
        // Step 2: We display the course without a user category.
3642
        $coursesNotCategory = self::returnCoursesCategories(
3643
            0,
3644
            $load_dirs,
3645
            $user_id,
3646
            $useUserLanguageFilterIfAvailable
3647
        );
3648
3649
        if ($coursesNotCategory) {
3650
            $listItems['not_category'] = $coursesNotCategory;
3651
        }
3652
3653
        return $listItems;
3654
    }
3655
3656
    /**
3657
     *  Display courses inside a category (without special courses) as HTML dics of
3658
     *  class userportal-course-item.
3659
     *
3660
     * @param int  $user_category_id                 User category id
3661
     * @param bool $load_dirs                        Whether to show the document quick-loader or not
3662
     * @param int  $user_id
3663
     * @param bool $useUserLanguageFilterIfAvailable
3664
     *
3665
     * @return array
3666
     */
3667
    public static function returnCoursesCategories(
3668
        $user_category_id,
3669
        $load_dirs = false,
3670
        $user_id = 0,
3671
        $useUserLanguageFilterIfAvailable = true
3672
    ) {
3673
        $user_id = $user_id ? (int) $user_id : api_get_user_id();
3674
        $user_category_id = (int) $user_category_id;
3675
3676
        // Table definitions
3677
        $TABLECOURS = Database::get_main_table(TABLE_MAIN_COURSE);
3678
        $TABLECOURSUSER = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3679
        $TABLE_ACCESS_URL_REL_COURSE = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3680
        $current_url_id = api_get_current_access_url_id();
3681
3682
        // Get course list auto-register
3683
        $special_course_list = self::get_special_course_list();
3684
        $without_special_courses = '';
3685
        if (!empty($special_course_list)) {
3686
            $without_special_courses = ' AND course.id NOT IN ("'.implode('","', $special_course_list).'")';
3687
        }
3688
3689
        $userCategoryCondition = " (course_rel_user.user_course_cat = $user_category_id) ";
3690
        if (empty($user_category_id)) {
3691
            $userCategoryCondition = ' (course_rel_user.user_course_cat = 0 OR course_rel_user.user_course_cat IS NULL) ';
3692
        }
3693
3694
        $languageCondition = '';
3695
        $onlyInUserLanguage = api_get_configuration_value('my_courses_show_courses_in_user_language_only');
3696
        if ($useUserLanguageFilterIfAvailable && $onlyInUserLanguage) {
3697
            $userInfo = api_get_user_info(api_get_user_id());
3698
            if (!empty($userInfo['language'])) {
3699
                $languageCondition = " AND course.course_language = '".$userInfo['language']."' ";
3700
            }
3701
        }
3702
3703
        $sql = "SELECT DISTINCT
3704
                    course.id,
3705
                    course_rel_user.status status,
3706
                    course.code as course_code,
3707
                    user_course_cat,
3708
                    course_rel_user.sort
3709
                FROM $TABLECOURS course 
3710
                INNER JOIN $TABLECOURSUSER course_rel_user
3711
                ON (course.id = course_rel_user.c_id)
3712
                INNER JOIN $TABLE_ACCESS_URL_REL_COURSE url
3713
                ON (url.c_id = course.id)
3714
                WHERE
3715
                    course_rel_user.user_id = $user_id AND
3716
                    $userCategoryCondition
3717
                    $without_special_courses
3718
                    $languageCondition
3719
                ";
3720
        // If multiple URL access mode is enabled, only fetch courses
3721
        // corresponding to the current URL.
3722
        if (api_get_multiple_access_url() && $current_url_id != -1) {
3723
            $sql .= " AND access_url_id = $current_url_id";
3724
        }
3725
        // Use user's classification for courses (if any).
3726
        $sql .= ' ORDER BY course_rel_user.user_course_cat, course_rel_user.sort ASC';
3727
        $result = Database::query($sql);
3728
3729
        $showCustomIcon = api_get_setting('course_images_in_courses_list');
3730
        // Browse through all courses.
3731
        $courseAdded = [];
3732
        $courseList = [];
3733
        while ($row = Database::fetch_array($result)) {
3734
            $course_info = api_get_course_info_by_id($row['id']);
3735
            if (empty($course_info)) {
3736
                continue;
3737
            }
3738
3739
            if (isset($course_info['visibility']) &&
3740
                $course_info['visibility'] == COURSE_VISIBILITY_HIDDEN
3741
            ) {
3742
                continue;
3743
            }
3744
3745
            // Skip if already in list
3746
            if (in_array($course_info['real_id'], $courseAdded)) {
3747
                continue;
3748
            }
3749
            $course_info['id_session'] = null;
3750
            $course_info['status'] = $row['status'];
3751
            // For each course, get if there is any notification icon to show
3752
            // (something that would have changed since the user's last visit).
3753
            $showNotification = !api_get_configuration_value('hide_course_notification')
3754
                ? Display::show_notification($course_info)
3755
                : '';
3756
            $iconName = basename($course_info['course_image']);
3757
3758
            $params = [];
3759
            //Param (course_code) needed to get the student process
3760
            $params['course_code'] = $row['course_code'];
3761
            $params['code'] = $row['course_code'];
3762
3763
            if ($showCustomIcon === 'true' && $iconName != 'course.png') {
3764
                $params['thumbnails'] = $course_info['course_image'];
3765
                $params['image'] = $course_info['course_image_large'];
3766
            }
3767
3768
            $thumbnails = null;
3769
            $image = null;
3770
            if ($showCustomIcon === 'true' && $iconName != 'course.png') {
3771
                $thumbnails = $course_info['course_image'];
3772
                $image = $course_info['course_image_large'];
3773
            } else {
3774
                $image = Display::return_icon(
3775
                    'session_default.png',
3776
                    null,
3777
                    null,
3778
                    null,
3779
                    null,
3780
                    true
3781
                );
3782
            }
3783
3784
            $params['course_id'] = $course_info['real_id'];
3785
            $params['edit_actions'] = '';
3786
            $params['document'] = '';
3787
            if (api_is_platform_admin()) {
3788
                $params['edit_actions'] .= api_get_path(WEB_CODE_PATH).'course_info/infocours.php?cidReq='.$course_info['code'];
3789
                if ($load_dirs) {
3790
                    $params['document'] = '<a id="document_preview_'.$course_info['real_id'].'_0" class="document_preview btn btn-default btn-sm" href="javascript:void(0);">'
3791
                               .Display::returnFontAwesomeIcon('folder-open').'</a>';
3792
                    $params['document'] .= Display::div(
3793
                        '',
3794
                        [
3795
                            'id' => 'document_result_'.$course_info['real_id'].'_0',
3796
                            'class' => 'document_preview_container',
3797
                        ]
3798
                    );
3799
                }
3800
            }
3801
            if ($load_dirs) {
3802
                $params['document'] = '<a id="document_preview_'.$course_info['real_id'].'_0" class="document_preview btn btn-default btn-sm" href="javascript:void(0);">'
3803
                    .Display::returnFontAwesomeIcon('folder-open').'</a>';
3804
                $params['document'] .= Display::div(
3805
                    '',
3806
                    [
3807
                        'id' => 'document_result_'.$course_info['real_id'].'_0',
3808
                        'class' => 'document_preview_container',
3809
                    ]
3810
                );
3811
            }
3812
3813
            $courseUrl = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/index.php?id_session=0';
3814
            $teachers = [];
3815
            if (api_get_setting('display_teacher_in_courselist') === 'true') {
3816
                $teachers = self::getTeachersFromCourse(
3817
                    $course_info['real_id'],
3818
                    true
3819
                );
3820
            }
3821
3822
            $params['status'] = $row['status'];
3823
            if (api_get_setting('display_coursecode_in_courselist') === 'true') {
3824
                $params['code_course'] = '('.$course_info['visual_code'].') ';
3825
            }
3826
3827
            $params['current_user_is_teacher'] = false;
3828
            /** @var array $teacher */
3829
            foreach ($teachers as $teacher) {
3830
                if ($teacher['id'] != $user_id) {
3831
                    continue;
3832
                }
3833
                $params['current_user_is_teacher'] = true;
3834
            }
3835
3836
            $params['visibility'] = $course_info['visibility'];
3837
            $params['link'] = $courseUrl;
3838
            $params['thumbnails'] = $thumbnails;
3839
            $params['image'] = $image;
3840
            $params['title'] = $course_info['title'];
3841
            $params['title_cut'] = $params['title'];
3842
            $params['category'] = $course_info['categoryName'];
3843
            $params['category_code'] = $course_info['categoryCode'];
3844
            $params['teachers'] = $teachers;
3845
            $params['real_id'] = $course_info['real_id'];
3846
3847
            if ($course_info['visibility'] != COURSE_VISIBILITY_CLOSED) {
3848
                $params['notifications'] = $showNotification;
3849
            }
3850
            $courseAdded[] = $course_info['real_id'];
3851
            $courseList[] = $params;
3852
        }
3853
3854
        return $courseList;
3855
    }
3856
3857
    /**
3858
     * Retrieves the user defined course categories.
3859
     *
3860
     * @param int $userId
3861
     *
3862
     * @return array
3863
     */
3864
    public static function get_user_course_categories($userId = 0)
3865
    {
3866
        $userId = empty($userId) ? api_get_user_id() : (int) $userId;
3867
        $table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3868
        $sql = "SELECT * FROM $table 
3869
                WHERE user_id = $userId
3870
                ORDER BY sort ASC
3871
                ";
3872
        $result = Database::query($sql);
3873
        $output = [];
3874
        while ($row = Database::fetch_array($result, 'ASSOC')) {
3875
            $output[$row['id']] = $row;
3876
        }
3877
3878
        return $output;
3879
    }
3880
3881
    /**
3882
     * Return an array the user_category id and title for the course $courseId for user $userId.
3883
     *
3884
     * @param $userId
3885
     * @param $courseId
3886
     *
3887
     * @return array
3888
     */
3889
    public static function getUserCourseCategoryForCourse($userId, $courseId)
3890
    {
3891
        $tblCourseRelUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3892
        $tblUserCategory = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3893
        $courseId = intval($courseId);
3894
        $userId = intval($userId);
3895
3896
        $sql = "SELECT user_course_cat, title
3897
                FROM $tblCourseRelUser cru
3898
                LEFT JOIN $tblUserCategory ucc
3899
                ON cru.user_course_cat = ucc.id
3900
                WHERE
3901
                    cru.user_id = $userId AND c_id = $courseId ";
3902
3903
        $res = Database::query($sql);
3904
3905
        $data = [];
3906
        if (Database::num_rows($res) > 0) {
3907
            $data = Database::fetch_assoc($res);
3908
        }
3909
3910
        return $data;
3911
    }
3912
3913
    /**
3914
     * Get the course id based on the original id and field name in the extra fields.
3915
     * Returns 0 if course was not found.
3916
     *
3917
     * @param string $value    Original course code
3918
     * @param string $variable Original field name
3919
     *
3920
     * @return array
3921
     */
3922
    public static function getCourseInfoFromOriginalId($value, $variable)
3923
    {
3924
        $extraFieldValue = new ExtraFieldValue('course');
3925
        $result = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
3926
            $variable,
3927
            $value
3928
        );
3929
3930
        if (!empty($result)) {
3931
            $courseInfo = api_get_course_info_by_id($result['item_id']);
3932
3933
            return $courseInfo;
3934
        }
3935
3936
        return [];
3937
    }
3938
3939
    /**
3940
     * Display code for one specific course a logged in user is subscribed to.
3941
     * Shows a link to the course, what's new icons...
3942
     *
3943
     * $my_course['d'] - course directory
3944
     * $my_course['i'] - course title
3945
     * $my_course['c'] - visual course code
3946
     * $my_course['k']  - system course code
3947
     *
3948
     * @param   array       Course details
3949
     * @param   int     Session ID
3950
     * @param   string      CSS class to apply to course entry
3951
     * @param   bool     Whether the session is supposedly accessible now
3952
     * (not in the case it has passed and is in invisible/unaccessible mode)
3953
     * @param bool      Whether to show the document quick-loader or not
3954
     *
3955
     * @return string The HTML to be printed for the course entry
3956
     *
3957
     * @version 1.0.3
3958
     *
3959
     * @todo refactor into different functions for database calls | logic | display
3960
     * @todo replace single-character $my_course['d'] indices
3961
     * @todo move code for what's new icons to a separate function to clear things up
3962
     * @todo add a parameter user_id so that it is possible to show the
3963
     * courselist of other users (=generalisation).
3964
     * This will prevent having to write a new function for this.
3965
     */
3966
    public static function get_logged_user_course_html(
3967
        $course,
3968
        $session_id = 0,
3969
        $class = 'courses',
3970
        $session_accessible = true,
3971
        $load_dirs = false
3972
    ) {
3973
        $now = date('Y-m-d h:i:s');
3974
        $user_id = api_get_user_id();
3975
        $course_info = api_get_course_info_by_id($course['real_id']);
3976
        $course_visibility = (int) $course_info['visibility'];
3977
3978
        if ($course_visibility === COURSE_VISIBILITY_HIDDEN) {
3979
            return '';
3980
        }
3981
3982
        $userInCourseStatus = self::getUserInCourseStatus(
3983
            $user_id,
3984
            $course_info['real_id']
3985
        );
3986
3987
        $course_info['status'] = empty($session_id) ? $userInCourseStatus : STUDENT;
3988
        $course_info['id_session'] = $session_id;
3989
3990
        $is_coach = api_is_coach($session_id, $course_info['real_id']);
3991
3992
        // Display course entry.
3993
        // Show a hyperlink to the course, unless the course is closed and user is not course admin.
3994
        $session_url = '';
3995
        $params = [];
3996
        $params['icon'] = Display::return_icon(
3997
            'session.png',
3998
            null,
3999
            [],
4000
            ICON_SIZE_LARGE,
4001
            null,
4002
            true
4003
        );
4004
        $params['real_id'] = $course_info['real_id'];
4005
4006
        // Display the "what's new" icons
4007
        $notifications = '';
4008
        if (
4009
            ($course_visibility != COURSE_VISIBILITY_CLOSED && $course_visibility != COURSE_VISIBILITY_HIDDEN) ||
4010
            !api_get_configuration_value('hide_course_notification')
4011
        ) {
4012
            $notifications .= Display::show_notification($course_info);
4013
        }
4014
4015
        if ($session_accessible) {
4016
            if ($course_visibility != COURSE_VISIBILITY_CLOSED ||
4017
                $userInCourseStatus == COURSEMANAGER
4018
            ) {
4019
                if (empty($course_info['id_session'])) {
4020
                    $course_info['id_session'] = 0;
4021
                }
4022
4023
                $sessionCourseAvailable = false;
4024
                $sessionCourseStatus = api_get_session_visibility($session_id, $course_info['real_id']);
4025
4026
                if (in_array(
4027
                    $sessionCourseStatus,
4028
                    [SESSION_VISIBLE_READ_ONLY, SESSION_VISIBLE, SESSION_AVAILABLE]
4029
                )) {
4030
                    $sessionCourseAvailable = true;
4031
                }
4032
4033
                if ($userInCourseStatus === COURSEMANAGER || $sessionCourseAvailable) {
4034
                    $session_url = $course_info['course_public_url'].'?id_session='.$course_info['id_session'];
4035
                    $session_title = '<a title="'.$course_info['name'].'" href="'.$session_url.'">'.
4036
                        $course_info['name'].'</a>'.$notifications;
4037
                } else {
4038
                    $session_title = $course_info['name'];
4039
                }
4040
            } else {
4041
                $session_title =
4042
                    $course_info['name'].' '.
4043
                    Display::tag('span', get_lang('(the course is currently closed)'), ['class' => 'item_closed']);
4044
            }
4045
        } else {
4046
            $session_title = $course_info['name'];
4047
        }
4048
4049
        $thumbnails = null;
4050
        $image = null;
4051
        $showCustomIcon = api_get_setting('course_images_in_courses_list');
4052
        $iconName = basename($course_info['course_image']);
4053
4054
        if ($showCustomIcon === 'true' && $iconName != 'course.png') {
4055
            $thumbnails = $course_info['course_image'];
4056
            $image = $course_info['course_image_large'];
4057
        } else {
4058
            $image = Display::return_icon(
4059
                'session_default.png',
4060
                null,
4061
                null,
4062
                null,
4063
                null,
4064
                true
4065
            );
4066
        }
4067
        $params['thumbnails'] = $thumbnails;
4068
        $params['image'] = $image;
4069
        $params['html_image'] = '';
4070
        if (!empty($thumbnails)) {
4071
            $params['html_image'] = Display::img($thumbnails, $course_info['name'], ['class' => 'img-responsive']);
4072
        } else {
4073
            $params['html_image'] = Display::return_icon('session.png', $course_info['name'], ['class' => 'img-responsive'], ICON_SIZE_LARGE, $course_info['name']);
4074
        }
4075
        $params['link'] = $session_url;
4076
        $params['title'] = $session_title;
4077
        $params['name'] = $course_info['name'];
4078
        $params['edit_actions'] = '';
4079
        $params['document'] = '';
4080
        $params['category'] = $course_info['categoryName'];
4081
4082
        if ($course_visibility != COURSE_VISIBILITY_CLOSED &&
4083
            $course_visibility != COURSE_VISIBILITY_HIDDEN
4084
        ) {
4085
            if (api_is_platform_admin()) {
4086
                $params['edit_actions'] .= api_get_path(WEB_CODE_PATH).'course_info/infocours.php?cidReq='.$course_info['code'];
4087
                if ($load_dirs) {
4088
                    $params['document'] .= '<a 
4089
                        id="document_preview_'.$course_info['real_id'].'_'.$course_info['id_session'].'" 
4090
                        class="document_preview btn btn-default btn-sm" 
4091
                        href="javascript:void(0);">'.
4092
                        Display::returnFontAwesomeIcon('folder-open').'</a>';
4093
                    $params['document'] .= Display::div('', [
4094
                        'id' => 'document_result_'.$course_info['real_id'].'_'.$course_info['id_session'],
4095
                        'class' => 'document_preview_container',
4096
                    ]);
4097
                }
4098
            }
4099
        }
4100
        if (api_get_setting('display_teacher_in_courselist') === 'true') {
4101
            $teacher_list = self::getTeachersFromCourse(
4102
                $course_info['real_id'],
4103
                true
4104
            );
4105
            $course_coachs = self::get_coachs_from_course(
4106
                $course_info['id_session'],
4107
                $course_info['real_id']
4108
            );
4109
            $params['teachers'] = $teacher_list;
4110
4111
            if (($course_info['status'] == STUDENT && !empty($course_info['id_session'])) ||
4112
                ($is_coach && $course_info['status'] != COURSEMANAGER)
4113
            ) {
4114
                $params['coaches'] = $course_coachs;
4115
            }
4116
        }
4117
        $special = isset($course['special_course']) ? true : false;
4118
        $params['title'] = $session_title;
4119
        $params['special'] = $special;
4120
        if (api_get_setting('display_coursecode_in_courselist') === 'true') {
4121
            $params['visual_code'] = '('.$course_info['visual_code'].')';
4122
        }
4123
        $params['extra'] = '';
4124
        $html = $params;
4125
4126
        $session_category_id = null;
4127
        if (1) {
4128
            $session = '';
4129
            $active = false;
4130
            if (!empty($course_info['id_session'])) {
4131
                $session = api_get_session_info($course_info['id_session']);
4132
                $sessionCoachName = '';
4133
                if (!empty($session['id_coach'])) {
4134
                    $coachInfo = api_get_user_info($session['id_coach']);
4135
                    $sessionCoachName = $coachInfo['complete_name'];
4136
                }
4137
4138
                $session_category_id = self::get_session_category_id_by_session_id($course_info['id_session']);
4139
4140
                if (
4141
                    $session['access_start_date'] === '0000-00-00 00:00:00' || empty($session['access_start_date']) ||
4142
                    $session['access_start_date'] === '0000-00-00'
4143
                ) {
4144
                    $session['dates'] = '';
4145
                    if (api_get_setting('show_session_coach') === 'true') {
4146
                        $session['coach'] = get_lang('General coach').': '.$sessionCoachName;
4147
                    }
4148
                    $active = true;
4149
                } else {
4150
                    $session['dates'] = ' - '.
4151
                        get_lang('From').' '.$session['access_start_date'].' '.
4152
                        get_lang('To').' '.$session['access_end_date'];
4153
                    if (api_get_setting('show_session_coach') === 'true') {
4154
                        $session['coach'] = get_lang('General coach').': '.$sessionCoachName;
4155
                    }
4156
                    $date_start = $session['access_start_date'];
4157
                    $date_end = $session['access_end_date'];
4158
                    $active = !$date_end ? ($date_start <= $now) : ($date_start <= $now && $date_end >= $now);
4159
                }
4160
            }
4161
            $user_course_category = '';
4162
            if (isset($course_info['user_course_cat'])) {
4163
                $user_course_category = $course_info['user_course_cat'];
4164
            }
4165
            $output = [
4166
                $user_course_category,
4167
                $html,
4168
                $course_info['id_session'],
4169
                $session,
4170
                'active' => $active,
4171
                'session_category_id' => $session_category_id,
4172
            ];
4173
4174
            if (Skill::isAllowed($user_id, false)) {
4175
                $em = Database::getManager();
4176
                $objUser = api_get_user_entity($user_id);
4177
                /** @var Course $objCourse */
4178
                $objCourse = $em->find('ChamiloCoreBundle:Course', $course['real_id']);
4179
                $objSession = $em->find('ChamiloCoreBundle:Session', $session_id);
4180
4181
                $skill = $em->getRepository('ChamiloCoreBundle:Skill')->getLastByUser($objUser, $objCourse, $objSession);
4182
4183
                $output['skill'] = null;
4184
                if ($skill) {
4185
                    $output['skill']['name'] = $skill->getName();
4186
                    $output['skill']['icon'] = $skill->getIcon();
4187
                }
4188
            }
4189
        } else {
4190
            $output = [$course_info['user_course_cat'], $html];
4191
        }
4192
4193
        return $output;
4194
    }
4195
4196
    /**
4197
     * @param string $source_course_code
4198
     * @param int    $source_session_id
4199
     * @param string $destination_course_code
4200
     * @param int    $destination_session_id
4201
     * @param array  $params
4202
     *
4203
     * @return bool
4204
     */
4205
    public static function copy_course(
4206
        $source_course_code,
4207
        $source_session_id,
4208
        $destination_course_code,
4209
        $destination_session_id,
4210
        $params = []
4211
    ) {
4212
        $course_info = api_get_course_info($source_course_code);
4213
4214
        if (!empty($course_info)) {
4215
            $cb = new CourseBuilder('', $course_info);
4216
            $course = $cb->build($source_session_id, $source_course_code, true);
4217
            $course_restorer = new CourseRestorer($course);
4218
            $course_restorer->skip_content = $params;
4219
            $course_restorer->restore(
4220
                $destination_course_code,
4221
                $destination_session_id,
4222
                true,
4223
                true
4224
            );
4225
4226
            return true;
4227
        }
4228
4229
        return false;
4230
    }
4231
4232
    /**
4233
     * A simpler version of the copy_course, the function creates an empty course with an autogenerated course code.
4234
     *
4235
     * @param string $new_title new course title
4236
     * @param string source course code
4237
     * @param int source session id
4238
     * @param int destination session id
4239
     * @param array $params
4240
     *
4241
     * @return array
4242
     */
4243
    public static function copy_course_simple(
4244
        $new_title,
4245
        $source_course_code,
4246
        $source_session_id = 0,
4247
        $destination_session_id = 0,
4248
        $params = []
4249
    ) {
4250
        $source_course_info = api_get_course_info($source_course_code);
4251
        if (!empty($source_course_info)) {
4252
            $new_course_code = self::generate_nice_next_course_code($source_course_code);
4253
            if ($new_course_code) {
4254
                $new_course_info = self::create_course(
4255
                    $new_title,
4256
                    $new_course_code,
4257
                    false
4258
                );
4259
                if (!empty($new_course_info['code'])) {
4260
                    $result = self::copy_course(
4261
                        $source_course_code,
4262
                        $source_session_id,
4263
                        $new_course_info['code'],
4264
                        $destination_session_id,
4265
                        $params
4266
                    );
4267
                    if ($result) {
4268
                        return $new_course_info;
4269
                    }
4270
                }
4271
            }
4272
        }
4273
4274
        return false;
4275
    }
4276
4277
    /**
4278
     * Creates a new course code based in a given code.
4279
     *
4280
     * @param string    wanted code
4281
     * <code>    $wanted_code = 'curse' if there are in the DB codes like curse1 curse2 the function will return: course3</code>
4282
     * if the course code doest not exist in the DB the same course code will be returned
4283
     *
4284
     * @return string wanted unused code
4285
     */
4286
    public static function generate_nice_next_course_code($wanted_code)
4287
    {
4288
        $course_code_ok = !self::course_code_exists($wanted_code);
4289
        if (!$course_code_ok) {
4290
            $wanted_code = self::generate_course_code($wanted_code);
4291
            $table = Database::get_main_table(TABLE_MAIN_COURSE);
4292
            $wanted_code = Database::escape_string($wanted_code);
4293
            $sql = "SELECT count(id) as count
4294
                    FROM $table
4295
                    WHERE code LIKE '$wanted_code%'";
4296
            $result = Database::query($sql);
4297
            if (Database::num_rows($result) > 0) {
4298
                $row = Database::fetch_array($result);
4299
                $count = $row['count'] + 1;
4300
                $wanted_code = $wanted_code.'_'.$count;
4301
                $result = api_get_course_info($wanted_code);
4302
                if (empty($result)) {
4303
                    return $wanted_code;
4304
                }
4305
            }
4306
4307
            return false;
4308
        }
4309
4310
        return $wanted_code;
4311
    }
4312
4313
    /**
4314
     * Gets the status of the users agreement in a course course-session.
4315
     *
4316
     * @param int    $user_id
4317
     * @param string $course_code
4318
     * @param int    $session_id
4319
     *
4320
     * @return bool
4321
     */
4322
    public static function is_user_accepted_legal($user_id, $course_code, $session_id = 0)
4323
    {
4324
        $user_id = intval($user_id);
4325
        $course_code = Database::escape_string($course_code);
4326
        $session_id = intval($session_id);
4327
4328
        $courseInfo = api_get_course_info($course_code);
4329
        $courseId = $courseInfo['real_id'];
4330
4331
        // Course legal
4332
        $enabled = api_get_plugin_setting('courselegal', 'tool_enable');
4333
4334
        if ($enabled == 'true') {
4335
            require_once api_get_path(SYS_PLUGIN_PATH).'courselegal/config.php';
4336
            $plugin = CourseLegalPlugin::create();
4337
4338
            return $plugin->isUserAcceptedLegal($user_id, $course_code, $session_id);
4339
        }
4340
4341
        if (empty($session_id)) {
4342
            $table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4343
            $sql = "SELECT legal_agreement FROM $table
4344
                    WHERE user_id = $user_id AND c_id = $courseId ";
4345
            $result = Database::query($sql);
4346
            if (Database::num_rows($result) > 0) {
4347
                $result = Database::fetch_array($result);
4348
                if ($result['legal_agreement'] == 1) {
4349
                    return true;
4350
                }
4351
            }
4352
4353
            return false;
4354
        } else {
4355
            $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4356
            $sql = "SELECT legal_agreement FROM $table
4357
                    WHERE user_id = $user_id AND c_id = $courseId AND session_id = $session_id";
4358
            $result = Database::query($sql);
4359
            if (Database::num_rows($result) > 0) {
4360
                $result = Database::fetch_array($result);
4361
                if ($result['legal_agreement'] == 1) {
4362
                    return true;
4363
                }
4364
            }
4365
4366
            return false;
4367
        }
4368
    }
4369
4370
    /**
4371
     * Saves the user-course legal agreement.
4372
     *
4373
     * @param   int user id
4374
     * @param   string course code
4375
     * @param   int session id
4376
     *
4377
     * @return mixed
4378
     */
4379
    public static function save_user_legal($user_id, $course_code, $session_id = null)
4380
    {
4381
        // Course plugin legal
4382
        $enabled = api_get_plugin_setting('courselegal', 'tool_enable');
4383
        if ($enabled == 'true') {
4384
            require_once api_get_path(SYS_PLUGIN_PATH).'courselegal/config.php';
4385
            $plugin = CourseLegalPlugin::create();
4386
4387
            return $plugin->saveUserLegal($user_id, $course_code, $session_id);
4388
        }
4389
4390
        $user_id = intval($user_id);
4391
        $course_code = Database::escape_string($course_code);
4392
        $session_id = intval($session_id);
4393
        $courseInfo = api_get_course_info($course_code);
4394
        $courseId = $courseInfo['real_id'];
4395
4396
        if (empty($session_id)) {
4397
            $table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4398
            $sql = "UPDATE $table SET legal_agreement = '1'
4399
                    WHERE user_id = $user_id AND c_id  = $courseId ";
4400
            Database::query($sql);
4401
        } else {
4402
            $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4403
            $sql = "UPDATE  $table SET legal_agreement = '1'
4404
                    WHERE user_id = $user_id AND c_id = $courseId AND session_id = $session_id";
4405
            Database::query($sql);
4406
        }
4407
    }
4408
4409
    /**
4410
     * @param int $user_id
4411
     * @param int $course_id
4412
     * @param int $session_id
4413
     * @param int $url_id
4414
     *
4415
     * @return bool
4416
     */
4417
    public static function get_user_course_vote($user_id, $course_id, $session_id = 0, $url_id = 0)
4418
    {
4419
        $table_user_course_vote = Database::get_main_table(TABLE_MAIN_USER_REL_COURSE_VOTE);
4420
        $session_id = !isset($session_id) ? api_get_session_id() : intval($session_id);
4421
        $url_id = empty($url_id) ? api_get_current_access_url_id() : intval($url_id);
4422
        $user_id = intval($user_id);
4423
4424
        if (empty($user_id)) {
4425
            return false;
4426
        }
4427
4428
        $params = [
4429
            'user_id' => $user_id,
4430
            'c_id' => $course_id,
4431
            'session_id' => $session_id,
4432
            'url_id' => $url_id,
4433
        ];
4434
4435
        $result = Database::select(
4436
            'vote',
4437
            $table_user_course_vote,
4438
            [
4439
                'where' => [
4440
                    'user_id = ? AND c_id = ? AND session_id = ? AND url_id = ?' => $params,
4441
                ],
4442
            ],
4443
            'first'
4444
        );
4445
        if (!empty($result)) {
4446
            return $result['vote'];
4447
        }
4448
4449
        return false;
4450
    }
4451
4452
    /**
4453
     * @param int $course_id
4454
     * @param int $session_id
4455
     * @param int $url_id
4456
     *
4457
     * @return array
4458
     */
4459
    public static function get_course_ranking(
4460
        $course_id,
4461
        $session_id = 0,
4462
        $url_id = 0
4463
    ) {
4464
        $table_course_ranking = Database::get_main_table(TABLE_STATISTIC_TRACK_COURSE_RANKING);
4465
4466
        $session_id = empty($session_id) ? api_get_session_id() : intval($session_id);
4467
        $url_id = empty($url_id) ? api_get_current_access_url_id() : intval($url_id);
4468
        $now = api_get_utc_datetime();
4469
4470
        $params = [
4471
            'c_id' => $course_id,
4472
            'session_id' => $session_id,
4473
            'url_id' => $url_id,
4474
            'creation_date' => $now,
4475
        ];
4476
4477
        $result = Database::select(
4478
            'c_id, accesses, total_score, users',
4479
            $table_course_ranking,
4480
            ['where' => ['c_id = ? AND session_id = ? AND url_id = ?' => $params]],
4481
            'first'
4482
        );
4483
4484
        $point_average_in_percentage = 0;
4485
        $point_average_in_star = 0;
4486
        $users_who_voted = 0;
4487
4488
        if (!empty($result['users'])) {
4489
            $users_who_voted = $result['users'];
4490
            $point_average_in_percentage = round($result['total_score'] / $result['users'] * 100 / 5, 2);
4491
            $point_average_in_star = round($result['total_score'] / $result['users'], 1);
4492
        }
4493
4494
        $result['user_vote'] = false;
4495
        if (!api_is_anonymous()) {
4496
            $result['user_vote'] = self::get_user_course_vote(api_get_user_id(), $course_id, $session_id, $url_id);
4497
        }
4498
4499
        $result['point_average'] = $point_average_in_percentage;
4500
        $result['point_average_star'] = $point_average_in_star;
4501
        $result['users_who_voted'] = $users_who_voted;
4502
4503
        return $result;
4504
    }
4505
4506
    /**
4507
     * Updates the course ranking.
4508
     *
4509
     * @param int   course id
4510
     * @param int $session_id
4511
     * @param int    url id
4512
     * @param $points_to_add
4513
     * @param bool $add_access
4514
     * @param bool $add_user
4515
     *
4516
     * @return array
4517
     */
4518
    public static function update_course_ranking(
4519
        $course_id = 0,
4520
        $session_id = 0,
4521
        $url_id = 0,
4522
        $points_to_add = null,
4523
        $add_access = true,
4524
        $add_user = true
4525
    ) {
4526
        // Course catalog stats modifications see #4191
4527
        $table_course_ranking = Database::get_main_table(TABLE_STATISTIC_TRACK_COURSE_RANKING);
4528
        $now = api_get_utc_datetime();
4529
        $course_id = empty($course_id) ? api_get_course_int_id() : intval($course_id);
4530
        $session_id = empty($session_id) ? api_get_session_id() : intval($session_id);
4531
        $url_id = empty($url_id) ? api_get_current_access_url_id() : intval($url_id);
4532
4533
        $params = [
4534
            'c_id' => $course_id,
4535
            'session_id' => $session_id,
4536
            'url_id' => $url_id,
4537
            'creation_date' => $now,
4538
            'total_score' => 0,
4539
            'users' => 0,
4540
        ];
4541
4542
        $result = Database::select(
4543
            'id, accesses, total_score, users',
4544
            $table_course_ranking,
4545
            ['where' => ['c_id = ? AND session_id = ? AND url_id = ?' => $params]],
4546
            'first'
4547
        );
4548
4549
        // Problem here every time we load the courses/XXXX/index.php course home page we update the access
4550
        if (empty($result)) {
4551
            if ($add_access) {
4552
                $params['accesses'] = 1;
4553
            }
4554
            //The votes and users are empty
4555
            if (isset($points_to_add) && !empty($points_to_add)) {
4556
                $params['total_score'] = intval($points_to_add);
4557
            }
4558
            if ($add_user) {
4559
                $params['users'] = 1;
4560
            }
4561
            $result = Database::insert($table_course_ranking, $params);
4562
        } else {
4563
            $my_params = [];
4564
4565
            if ($add_access) {
4566
                $my_params['accesses'] = intval($result['accesses']) + 1;
4567
            }
4568
            if (isset($points_to_add) && !empty($points_to_add)) {
4569
                $my_params['total_score'] = $result['total_score'] + $points_to_add;
4570
            }
4571
            if ($add_user) {
4572
                $my_params['users'] = $result['users'] + 1;
4573
            }
4574
4575
            if (!empty($my_params)) {
4576
                $result = Database::update(
4577
                    $table_course_ranking,
4578
                    $my_params,
4579
                    ['c_id = ? AND session_id = ? AND url_id = ?' => $params]
4580
                );
4581
            }
4582
        }
4583
4584
        return $result;
4585
    }
4586
4587
    /**
4588
     * Add user vote to a course.
4589
     *
4590
     * @param   int user id
4591
     * @param   int vote [1..5]
4592
     * @param   int course id
4593
     * @param   int session id
4594
     * @param   int url id (access_url_id)
4595
     *
4596
     * @return false|string 'added', 'updated' or 'nothing'
4597
     */
4598
    public static function add_course_vote(
4599
        $user_id,
4600
        $vote,
4601
        $course_id,
4602
        $session_id = 0,
4603
        $url_id = 0
4604
    ) {
4605
        $table_user_course_vote = Database::get_main_table(TABLE_MAIN_USER_REL_COURSE_VOTE);
4606
        $course_id = empty($course_id) ? api_get_course_int_id() : intval($course_id);
4607
4608
        if (empty($course_id) || empty($user_id)) {
4609
            return false;
4610
        }
4611
4612
        if (!in_array($vote, [1, 2, 3, 4, 5])) {
4613
            return false;
4614
        }
4615
4616
        $session_id = empty($session_id) ? api_get_session_id() : intval($session_id);
4617
        $url_id = empty($url_id) ? api_get_current_access_url_id() : intval($url_id);
4618
        $vote = intval($vote);
4619
4620
        $params = [
4621
            'user_id' => intval($user_id),
4622
            'c_id' => $course_id,
4623
            'session_id' => $session_id,
4624
            'url_id' => $url_id,
4625
            'vote' => $vote,
4626
        ];
4627
4628
        $action_done = 'nothing';
4629
        $result = Database::select(
4630
            'id, vote',
4631
            $table_user_course_vote,
4632
            ['where' => ['user_id = ? AND c_id = ? AND session_id = ? AND url_id = ?' => $params]],
4633
            'first'
4634
        );
4635
4636
        if (empty($result)) {
4637
            Database::insert($table_user_course_vote, $params);
4638
            $points_to_add = $vote;
4639
            $add_user = true;
4640
            $action_done = 'added';
4641
        } else {
4642
            $my_params = ['vote' => $vote];
4643
            $points_to_add = $vote - $result['vote'];
4644
            $add_user = false;
4645
4646
            Database::update(
4647
                $table_user_course_vote,
4648
                $my_params,
4649
                ['user_id = ? AND c_id = ? AND session_id = ? AND url_id = ?' => $params]
4650
            );
4651
            $action_done = 'updated';
4652
        }
4653
4654
        // Current points
4655
        if (!empty($points_to_add)) {
4656
            self::update_course_ranking(
4657
                $course_id,
4658
                $session_id,
4659
                $url_id,
4660
                $points_to_add,
4661
                false,
4662
                $add_user
4663
            );
4664
        }
4665
4666
        return $action_done;
4667
    }
4668
4669
    /**
4670
     * Remove course ranking + user votes.
4671
     *
4672
     * @param int $course_id
4673
     * @param int $session_id
4674
     * @param int $url_id
4675
     */
4676
    public static function remove_course_ranking($course_id, $session_id, $url_id = null)
4677
    {
4678
        $table_course_ranking = Database::get_main_table(TABLE_STATISTIC_TRACK_COURSE_RANKING);
4679
        $table_user_course_vote = Database::get_main_table(TABLE_MAIN_USER_REL_COURSE_VOTE);
4680
4681
        if (!empty($course_id) && isset($session_id)) {
4682
            $url_id = empty($url_id) ? api_get_current_access_url_id() : intval($url_id);
4683
            $params = [
4684
                'c_id' => $course_id,
4685
                'session_id' => $session_id,
4686
                'url_id' => $url_id,
4687
            ];
4688
            Database::delete($table_course_ranking, ['c_id = ? AND session_id = ? AND url_id = ?' => $params]);
4689
            Database::delete($table_user_course_vote, ['c_id = ? AND session_id = ? AND url_id = ?' => $params]);
4690
        }
4691
    }
4692
4693
    /**
4694
     * Returns an array with the hottest courses.
4695
     *
4696
     * @param int $days  number of days
4697
     * @param int $limit number of hottest courses
4698
     *
4699
     * @return array
4700
     */
4701
    public static function return_hot_courses($days = 30, $limit = 6)
4702
    {
4703
        if (api_is_invitee()) {
4704
            return [];
4705
        }
4706
4707
        $limit = (int) $limit;
4708
        $userId = api_get_user_id();
4709
4710
        // Getting my courses
4711
        $my_course_list = self::get_courses_list_by_user_id($userId);
4712
4713
        $codeList = [];
4714
        foreach ($my_course_list as $course) {
4715
            $codeList[$course['real_id']] = $course['real_id'];
4716
        }
4717
4718
        if (api_is_drh()) {
4719
            $courses = self::get_courses_followed_by_drh($userId);
4720
            foreach ($courses as $course) {
4721
                $codeList[$course['real_id']] = $course['real_id'];
4722
            }
4723
        }
4724
4725
        $table_course_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
4726
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
4727
        $table_course_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4728
        $urlId = api_get_current_access_url_id();
4729
        //$table_course_access table uses the now() and interval ...
4730
        $now = api_get_utc_datetime();
4731
        $sql = "SELECT COUNT(course_access_id) course_count, a.c_id, visibility
4732
                FROM $table_course c
4733
                INNER JOIN $table_course_access a
4734
                ON (c.id = a.c_id)
4735
                INNER JOIN $table_course_url u
4736
                ON u.c_id = c.id
4737
                WHERE
4738
                    u.access_url_id = $urlId AND
4739
                    login_course_date <= '$now' AND
4740
                    login_course_date > DATE_SUB('$now', INTERVAL $days DAY) AND
4741
                    visibility <> ".COURSE_VISIBILITY_CLOSED." AND
4742
                    visibility <> ".COURSE_VISIBILITY_HIDDEN." 
4743
                GROUP BY a.c_id
4744
                ORDER BY course_count DESC
4745
                LIMIT $limit
4746
            ";
4747
4748
        $result = Database::query($sql);
4749
        $courses = [];
4750
        if (Database::num_rows($result)) {
4751
            $courses = Database::store_result($result, 'ASSOC');
4752
            $courses = self::processHotCourseItem($courses, $codeList);
4753
        }
4754
4755
        return $courses;
4756
    }
4757
4758
    /**
4759
     * @param array $courses
4760
     * @param array $codeList
4761
     *
4762
     * @return mixed
4763
     */
4764
    public static function processHotCourseItem($courses, $codeList = [])
4765
    {
4766
        $hotCourses = [];
4767
        $ajax_url = api_get_path(WEB_AJAX_PATH).'course.ajax.php?a=add_course_vote';
4768
        $stok = Security::get_existing_token();
4769
        $user_id = api_get_user_id();
4770
4771
        foreach ($courses as $courseId) {
4772
            $course_info = api_get_course_info_by_id($courseId['c_id']);
4773
            $courseCode = $course_info['code'];
4774
            $categoryCode = !empty($course_info['categoryCode']) ? $course_info['categoryCode'] : "";
4775
            $my_course = $course_info;
4776
            $my_course['go_to_course_button'] = '';
4777
            $my_course['register_button'] = '';
4778
4779
            $access_link = self::get_access_link_by_user(
4780
                api_get_user_id(),
4781
                $course_info,
4782
                $codeList
4783
            );
4784
4785
            $userRegisteredInCourse = self::is_user_subscribed_in_course($user_id, $course_info['code']);
4786
            $userRegisteredInCourseAsTeacher = self::is_course_teacher($user_id, $course_info['code']);
4787
            $userRegistered = $userRegisteredInCourse && $userRegisteredInCourseAsTeacher;
4788
            $my_course['is_course_student'] = $userRegisteredInCourse;
4789
            $my_course['is_course_teacher'] = $userRegisteredInCourseAsTeacher;
4790
            $my_course['is_registered'] = $userRegistered;
4791
            $my_course['title_cut'] = cut($course_info['title'], 45);
4792
4793
            // Course visibility
4794
            if ($access_link && in_array('register', $access_link)) {
4795
                $my_course['register_button'] = Display::url(
4796
                    get_lang('Subscribe').' '.
4797
                    Display::returnFontAwesomeIcon('sign-in'),
4798
                    api_get_path(WEB_COURSE_PATH).$course_info['path'].
4799
                     '/index.php?action=subscribe&sec_token='.$stok,
4800
                    [
4801
                        'class' => 'btn btn-success btn-sm',
4802
                        'title' => get_lang('Subscribe'),
4803
                        'aria-label' => get_lang('Subscribe'),
4804
                    ]
4805
                );
4806
            }
4807
4808
            if ($access_link && in_array('enter', $access_link) ||
4809
                $course_info['visibility'] == COURSE_VISIBILITY_OPEN_WORLD
4810
            ) {
4811
                $my_course['go_to_course_button'] = Display::url(
4812
                    get_lang('Go to the course').' '.
4813
                    Display::returnFontAwesomeIcon('share'),
4814
                    api_get_path(WEB_COURSE_PATH).$course_info['path'].'/index.php',
4815
                    [
4816
                        'class' => 'btn btn-default btn-sm',
4817
                        'title' => get_lang('Go to the course'),
4818
                        'aria-label' => get_lang('Go to the course'),
4819
                    ]
4820
                );
4821
            }
4822
4823
            if ($access_link && in_array('unsubscribe', $access_link)) {
4824
                $my_course['unsubscribe_button'] = Display::url(
4825
                    get_lang('Unsubscribe').' '.
4826
                    Display::returnFontAwesomeIcon('sign-out'),
4827
                    api_get_path(WEB_CODE_PATH).'auth/courses.php?action=unsubscribe&unsubscribe='.$courseCode
4828
                    .'&sec_token='.$stok.'&category_code='.$categoryCode,
4829
                    [
4830
                        'class' => 'btn btn-danger btn-sm',
4831
                        'title' => get_lang('Unsubscribe'),
4832
                        'aria-label' => get_lang('Unsubscribe'),
4833
                    ]
4834
                );
4835
            }
4836
4837
            // start buycourse validation
4838
            // display the course price and buy button if the buycourses plugin is enabled and this course is configured
4839
            $plugin = BuyCoursesPlugin::create();
4840
            $isThisCourseInSale = $plugin->buyCoursesForGridCatalogValidator(
4841
                $course_info['real_id'],
4842
                BuyCoursesPlugin::PRODUCT_TYPE_COURSE
4843
            );
4844
            if ($isThisCourseInSale) {
4845
                // set the price label
4846
                $my_course['price'] = $isThisCourseInSale['html'];
4847
                // set the Buy button instead register.
4848
                if ($isThisCourseInSale['verificator'] && !empty($my_course['register_button'])) {
4849
                    $my_course['register_button'] = $plugin->returnBuyCourseButton(
4850
                        $course_info['real_id'],
4851
                        BuyCoursesPlugin::PRODUCT_TYPE_COURSE
4852
                    );
4853
                }
4854
            }
4855
            // end buycourse validation
4856
4857
            // Description
4858
            $my_course['description_button'] = self::returnDescriptionButton($course_info);
4859
            $my_course['teachers'] = self::getTeachersFromCourse($course_info['real_id'], true);
4860
            $point_info = self::get_course_ranking($course_info['real_id'], 0);
4861
            $my_course['rating_html'] = '';
4862
            if (api_get_configuration_value('hide_course_rating') === false) {
4863
                $my_course['rating_html'] = Display::return_rating_system(
4864
                    'star_'.$course_info['real_id'],
4865
                    $ajax_url.'&course_id='.$course_info['real_id'],
4866
                    $point_info
4867
                );
4868
            }
4869
            $hotCourses[] = $my_course;
4870
        }
4871
4872
        return $hotCourses;
4873
    }
4874
4875
    public function totalSubscribedUsersInCourses($urlId)
4876
    {
4877
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
4878
        $table_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4879
        $courseUsers = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4880
4881
        $urlId = (int) $urlId;
4882
4883
        $sql = "SELECT count(cu.user_id) count 
4884
                FROM $courseUsers cu 
4885
                INNER JOIN $table_course_rel_access_url u 
4886
                ON cu.c_id = u.c_id                
4887
                WHERE
4888
                    relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
4889
                    u.access_url_id = $urlId AND 
4890
                    visibility <> ".COURSE_VISIBILITY_CLOSED." AND
4891
                    visibility <> ".COURSE_VISIBILITY_HIDDEN." 
4892
                     ";
4893
4894
        $res = Database::query($sql);
4895
        $row = Database::fetch_array($res);
4896
4897
        return $row['count'];
4898
    }
4899
4900
    /**
4901
     * Get courses count.
4902
     *
4903
     * @param int $access_url_id Access URL ID (optional)
4904
     * @param int $visibility
4905
     *
4906
     * @return int Number of courses
4907
     */
4908
    public static function count_courses($access_url_id = null, $visibility = null)
4909
    {
4910
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
4911
        $table_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4912
        $sql = "SELECT count(c.id) FROM $table_course c";
4913
        if (!empty($access_url_id) && $access_url_id == intval($access_url_id)) {
4914
            $sql .= ", $table_course_rel_access_url u
4915
                    WHERE c.id = u.c_id AND u.access_url_id = $access_url_id";
4916
            if (!empty($visibility)) {
4917
                $visibility = intval($visibility);
4918
                $sql .= " AND visibility = $visibility ";
4919
            }
4920
        } else {
4921
            if (!empty($visibility)) {
4922
                $visibility = intval($visibility);
4923
                $sql .= " WHERE visibility = $visibility ";
4924
            }
4925
        }
4926
4927
        $res = Database::query($sql);
4928
        $row = Database::fetch_row($res);
4929
4930
        return $row[0];
4931
    }
4932
4933
    /**
4934
     * Get active courses count.
4935
     * Active = all courses except the ones with hidden visibility.
4936
     *
4937
     * @param int $urlId Access URL ID (optional)
4938
     *
4939
     * @return int Number of courses
4940
     */
4941
    public static function countActiveCourses($urlId = null)
4942
    {
4943
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
4944
        $table_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4945
        $sql = "SELECT count(c.id) FROM $table_course c";
4946
        if (!empty($urlId)) {
4947
            $urlId = (int) $urlId;
4948
            $sql .= ", $table_course_rel_access_url u
4949
                    WHERE
4950
                        c.id = u.c_id AND
4951
                        u.access_url_id = $urlId AND
4952
                        visibility <> ".COURSE_VISIBILITY_HIDDEN;
4953
        } else {
4954
            $sql .= " WHERE visibility <> ".COURSE_VISIBILITY_HIDDEN;
4955
        }
4956
        $res = Database::query($sql);
4957
        $row = Database::fetch_row($res);
4958
4959
        return $row[0];
4960
    }
4961
4962
    /**
4963
     * Returns the SQL conditions to filter course only visible by the user in the catalogue.
4964
     *
4965
     * @param string $courseTableAlias Alias of the course table
4966
     * @param bool   $hideClosed       Whether to hide closed and hidden courses
4967
     *
4968
     * @return string SQL conditions
4969
     */
4970
    public static function getCourseVisibilitySQLCondition(
4971
        $courseTableAlias,
4972
        $hideClosed = false
4973
    ) {
4974
        $visibilityCondition = '';
4975
        $hidePrivate = api_get_setting('course_catalog_hide_private');
4976
        if ($hidePrivate === 'true') {
4977
            $visibilityCondition .= " AND $courseTableAlias.visibility <> ".COURSE_VISIBILITY_REGISTERED;
4978
        }
4979
        if ($hideClosed) {
4980
            $visibilityCondition .= " AND $courseTableAlias.visibility NOT IN (".COURSE_VISIBILITY_CLOSED.','.COURSE_VISIBILITY_HIDDEN.')';
4981
        }
4982
4983
        // Check if course have users allowed to see it in the catalogue, then show only if current user is allowed to see it
4984
        $currentUserId = api_get_user_id();
4985
        $restrictedCourses = self::getCatalogueCourseList(true);
4986
        $allowedCoursesToCurrentUser = self::getCatalogueCourseList(true, $currentUserId);
4987
        if (!empty($restrictedCourses)) {
4988
            $visibilityCondition .= ' AND ('.$courseTableAlias.'.code NOT IN ("'.implode('","', $restrictedCourses).'")';
4989
            $visibilityCondition .= ' OR '.$courseTableAlias.'.code IN ("'.implode('","', $allowedCoursesToCurrentUser).'"))';
4990
        }
4991
4992
        // Check if course have users denied to see it in the catalogue, then show only if current user is not denied to see it
4993
        $restrictedCourses = self::getCatalogueCourseList(false);
4994
        $notAllowedCoursesToCurrentUser = self::getCatalogueCourseList(false, $currentUserId);
4995
        if (!empty($restrictedCourses)) {
4996
            $visibilityCondition .= ' AND ('.$courseTableAlias.'.code NOT IN ("'.implode('","', $restrictedCourses).'")';
4997
            $visibilityCondition .= ' OR '.$courseTableAlias.'.code NOT IN ("'.implode('","', $notAllowedCoursesToCurrentUser).'"))';
4998
        }
4999
5000
        return $visibilityCondition;
5001
    }
5002
5003
    /**
5004
     * Return a link to go to the course, validating the visibility of the
5005
     * course and the user status.
5006
     *
5007
     * @param int $uid User ID
5008
     * @param array Course details array
5009
     * @param array  List of courses to which the user is subscribed (if not provided, will be generated)
5010
     *
5011
     * @return mixed 'enter' for a link to go to the course or 'register' for a link to subscribe, or false if no access
5012
     */
5013
    public static function get_access_link_by_user($uid, $course, $user_courses = [])
5014
    {
5015
        if (empty($uid) || empty($course)) {
5016
            return false;
5017
        }
5018
5019
        if (empty($user_courses)) {
5020
            // get the array of courses to which the user is subscribed
5021
            $user_courses = self::get_courses_list_by_user_id($uid);
5022
            foreach ($user_courses as $k => $v) {
5023
                $user_courses[$k] = $v['real_id'];
5024
            }
5025
        }
5026
5027
        if (!isset($course['real_id']) && empty($course['real_id'])) {
5028
            $course = api_get_course_info($course['code']);
5029
        }
5030
5031
        if ($course['visibility'] == COURSE_VISIBILITY_HIDDEN) {
5032
            return [];
5033
        }
5034
5035
        $is_admin = api_is_platform_admin_by_id($uid);
5036
        $options = [];
5037
        // Register button
5038
        if (!api_is_anonymous($uid) &&
5039
            (
5040
            ($course['visibility'] == COURSE_VISIBILITY_OPEN_WORLD || $course['visibility'] == COURSE_VISIBILITY_OPEN_PLATFORM)
5041
                //$course['visibility'] == COURSE_VISIBILITY_REGISTERED && $course['subscribe'] == SUBSCRIBE_ALLOWED
5042
            ) &&
5043
            $course['subscribe'] == SUBSCRIBE_ALLOWED &&
5044
            (!in_array($course['real_id'], $user_courses) || empty($user_courses))
5045
        ) {
5046
            $options[] = 'register';
5047
        }
5048
5049
        // Go To Course button (only if admin, if course public or if student already subscribed)
5050
        if ($is_admin ||
5051
            $course['visibility'] == COURSE_VISIBILITY_OPEN_WORLD && empty($course['registration_code']) ||
5052
            (api_user_is_login($uid) && $course['visibility'] == COURSE_VISIBILITY_OPEN_PLATFORM && empty($course['registration_code'])) ||
5053
            (in_array($course['real_id'], $user_courses) && $course['visibility'] != COURSE_VISIBILITY_CLOSED)
5054
        ) {
5055
            $options[] = 'enter';
5056
        }
5057
5058
        if ($is_admin ||
5059
            $course['visibility'] == COURSE_VISIBILITY_OPEN_WORLD && empty($course['registration_code']) ||
5060
            (api_user_is_login($uid) && $course['visibility'] == COURSE_VISIBILITY_OPEN_PLATFORM && empty($course['registration_code'])) ||
5061
            (in_array($course['real_id'], $user_courses) && $course['visibility'] != COURSE_VISIBILITY_CLOSED)
5062
        ) {
5063
            $options[] = 'enter';
5064
        }
5065
5066
        if ($course['visibility'] != COURSE_VISIBILITY_HIDDEN &&
5067
            empty($course['registration_code']) &&
5068
            $course['unsubscribe'] == UNSUBSCRIBE_ALLOWED &&
5069
            api_user_is_login($uid) &&
5070
            in_array($course['real_id'], $user_courses)
5071
        ) {
5072
            $options[] = 'unsubscribe';
5073
        }
5074
5075
        return $options;
5076
    }
5077
5078
    /**
5079
     * @param array          $courseInfo
5080
     * @param array          $teachers
5081
     * @param bool           $deleteTeachersNotInList
5082
     * @param bool           $editTeacherInSessions
5083
     * @param bool           $deleteSessionTeacherNotInList
5084
     * @param array          $teacherBackup
5085
     * @param Monolog\Logger $logger
5086
     *
5087
     * @return false|null
5088
     */
5089
    public static function updateTeachers(
5090
        $courseInfo,
5091
        $teachers,
5092
        $deleteTeachersNotInList = true,
5093
        $editTeacherInSessions = false,
5094
        $deleteSessionTeacherNotInList = false,
5095
        $teacherBackup = [],
5096
        $logger = null
5097
    ) {
5098
        if (!is_array($teachers)) {
5099
            $teachers = [$teachers];
5100
        }
5101
5102
        if (empty($courseInfo) || !isset($courseInfo['real_id'])) {
5103
            return false;
5104
        }
5105
5106
        $teachers = array_filter($teachers);
5107
        $courseId = $courseInfo['real_id'];
5108
        $course_code = $courseInfo['code'];
5109
5110
        $course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5111
        $alreadyAddedTeachers = self::get_teacher_list_from_course_code($course_code);
5112
5113
        if ($deleteTeachersNotInList) {
5114
            // Delete only teacher relations that doesn't match the selected teachers
5115
            $cond = null;
5116
            if (count($teachers) > 0) {
5117
                foreach ($teachers as $key) {
5118
                    $key = Database::escape_string($key);
5119
                    $cond .= " AND user_id <> '".$key."'";
5120
                }
5121
            }
5122
5123
            // Recover user categories
5124
            $sql = "SELECT * FROM $course_user_table
5125
                    WHERE c_id = $courseId AND status = 1 AND relation_type = 0 ".$cond;
5126
            $result = Database::query($sql);
5127
            if (Database::num_rows($result)) {
5128
                $teachersToDelete = Database::store_result($result, 'ASSOC');
5129
                foreach ($teachersToDelete as $data) {
5130
                    $userId = $data['user_id'];
5131
                    $teacherBackup[$userId][$course_code] = $data;
5132
                }
5133
            }
5134
5135
            $sql = "DELETE FROM $course_user_table
5136
                    WHERE c_id = $courseId AND status = 1 AND relation_type = 0 ".$cond;
5137
5138
            Database::query($sql);
5139
        }
5140
5141
        if (count($teachers) > 0) {
5142
            foreach ($teachers as $userId) {
5143
                $userId = intval($userId);
5144
                // We check if the teacher is already subscribed in this course
5145
                $sql = "SELECT 1 FROM $course_user_table
5146
                        WHERE user_id = $userId AND c_id = $courseId";
5147
                $result = Database::query($sql);
5148
                if (Database::num_rows($result)) {
5149
                    $sql = "UPDATE $course_user_table 
5150
                            SET status = 1
5151
                            WHERE c_id = $courseId AND user_id = $userId ";
5152
                } else {
5153
                    $userCourseCategory = '0';
5154
                    if (isset($teacherBackup[$userId]) &&
5155
                        isset($teacherBackup[$userId][$course_code])
5156
                    ) {
5157
                        $courseUserData = $teacherBackup[$userId][$course_code];
5158
                        $userCourseCategory = $courseUserData['user_course_cat'];
5159
                        if ($logger) {
5160
                            $logger->addInfo("Recovering user_course_cat: $userCourseCategory");
5161
                        }
5162
                    }
5163
5164
                    $sql = "INSERT INTO $course_user_table SET
5165
                            c_id = $courseId,
5166
                            user_id = $userId,
5167
                            status = 1,
5168
                            is_tutor = 0,
5169
                            sort = 0,
5170
                            relation_type = 0,
5171
                            user_course_cat = $userCourseCategory
5172
                    ";
5173
                }
5174
                Database::query($sql);
5175
            }
5176
        }
5177
5178
        if ($editTeacherInSessions) {
5179
            $sessions = SessionManager::get_session_by_course($courseId);
5180
            if (!empty($sessions)) {
5181
                if ($logger) {
5182
                    $logger->addInfo("Edit teachers in sessions");
5183
                }
5184
                foreach ($sessions as $session) {
5185
                    $sessionId = $session['id'];
5186
                    // Remove old and add new
5187
                    if ($deleteSessionTeacherNotInList) {
5188
                        foreach ($teachers as $userId) {
5189
                            if ($logger) {
5190
                                $logger->addInfo("Set coach #$userId in session #$sessionId of course #$courseId ");
5191
                            }
5192
                            SessionManager::set_coach_to_course_session(
5193
                                $userId,
5194
                                $sessionId,
5195
                                $courseId
5196
                            );
5197
                        }
5198
5199
                        $teachersToDelete = [];
5200
                        if (!empty($alreadyAddedTeachers)) {
5201
                            $teachersToDelete = array_diff(array_keys($alreadyAddedTeachers), $teachers);
5202
                        }
5203
5204
                        if (!empty($teachersToDelete)) {
5205
                            foreach ($teachersToDelete as $userId) {
5206
                                if ($logger) {
5207
                                    $logger->addInfo("Delete coach #$userId in session #$sessionId of course #$courseId ");
5208
                                }
5209
                                SessionManager::set_coach_to_course_session(
5210
                                    $userId,
5211
                                    $sessionId,
5212
                                    $courseId,
5213
                                    true
5214
                                );
5215
                            }
5216
                        }
5217
                    } else {
5218
                        // Add new teachers only
5219
                        foreach ($teachers as $userId) {
5220
                            if ($logger) {
5221
                                $logger->addInfo("Add coach #$userId in session #$sessionId of course #$courseId ");
5222
                            }
5223
                            SessionManager::set_coach_to_course_session(
5224
                                $userId,
5225
                                $sessionId,
5226
                                $courseId
5227
                            );
5228
                        }
5229
                    }
5230
                }
5231
            }
5232
        }
5233
    }
5234
5235
    /**
5236
     * Course available settings variables see c_course_setting table.
5237
     *
5238
     * @param AppPlugin $appPlugin
5239
     *
5240
     * @return array
5241
     */
5242
    public static function getCourseSettingVariables(AppPlugin $appPlugin)
5243
    {
5244
        $pluginCourseSettings = $appPlugin->getAllPluginCourseSettings();
5245
        $courseSettings = [
5246
            // Get allow_learning_path_theme from table
5247
            'allow_learning_path_theme',
5248
            // Get allow_open_chat_window from table
5249
            'allow_open_chat_window',
5250
            'allow_public_certificates',
5251
            // Get allow_user_edit_agenda from table
5252
            'allow_user_edit_agenda',
5253
            // Get allow_user_edit_announcement from table
5254
            'allow_user_edit_announcement',
5255
            // Get allow_user_image_forum from table
5256
            'allow_user_image_forum',
5257
            //Get allow show user list
5258
            'allow_user_view_user_list',
5259
            // Get course_theme from table
5260
            'course_theme',
5261
            //Get allow show user list
5262
            'display_info_advance_inside_homecourse',
5263
            'documents_default_visibility',
5264
            // Get send_mail_setting (work)from table
5265
            'email_alert_manager_on_new_doc',
5266
            // Get send_mail_setting (work)from table
5267
            'email_alert_manager_on_new_quiz',
5268
            // Get send_mail_setting (dropbox) from table
5269
            'email_alert_on_new_doc_dropbox',
5270
            'email_alert_students_on_new_homework',
5271
            // Get send_mail_setting (auth)from table
5272
            'email_alert_to_teacher_on_new_user_in_course',
5273
            'enable_lp_auto_launch',
5274
            'enable_exercise_auto_launch',
5275
            'enable_document_auto_launch',
5276
            'pdf_export_watermark_text',
5277
            'show_system_folders',
5278
            'exercise_invisible_in_session',
5279
            'enable_forum_auto_launch',
5280
            'show_course_in_user_language',
5281
            'email_to_teachers_on_new_work_feedback',
5282
            'student_delete_own_publication',
5283
            'hide_forum_notifications',
5284
            'quiz_question_limit_per_day',
5285
            'subscribe_users_to_forum_notifications',
5286
        ];
5287
5288
        $courseModels = ExerciseLib::getScoreModels();
5289
        if (!empty($courseModels)) {
5290
            $courseSettings[] = 'score_model_id';
5291
        }
5292
5293
        $allowLPReturnLink = api_get_setting('allow_lp_return_link');
5294
        if ($allowLPReturnLink === 'true') {
5295
            $courseSettings[] = 'lp_return_link';
5296
        }
5297
5298
        if (!empty($pluginCourseSettings)) {
5299
            $courseSettings = array_merge(
5300
                $courseSettings,
5301
                $pluginCourseSettings
5302
            );
5303
        }
5304
5305
        return $courseSettings;
5306
    }
5307
5308
    /**
5309
     * @param AppPlugin    $appPlugin
5310
     * @param string       $variable
5311
     * @param string|array $value
5312
     * @param int          $courseId
5313
     *
5314
     * @return bool
5315
     */
5316
    public static function saveCourseConfigurationSetting(AppPlugin $appPlugin, $variable, $value, $courseId)
5317
    {
5318
        $settingList = self::getCourseSettingVariables($appPlugin);
5319
5320
        if (!in_array($variable, $settingList)) {
5321
            return false;
5322
        }
5323
5324
        $courseSettingTable = Database::get_course_table(TABLE_COURSE_SETTING);
5325
5326
        if (is_array($value)) {
5327
            $value = implode(',', $value);
5328
        }
5329
5330
        if (self::hasCourseSetting($variable, $courseId)) {
5331
            // Update
5332
            Database::update(
5333
                $courseSettingTable,
5334
                ['value' => $value],
5335
                ['variable = ? AND c_id = ?' => [$variable, $courseId]]
5336
            );
5337
        } else {
5338
            // Create
5339
            Database::insert(
5340
                $courseSettingTable,
5341
                [
5342
                    'title' => $variable,
5343
                    'value' => $value,
5344
                    'c_id' => $courseId,
5345
                    'variable' => $variable,
5346
                ]
5347
            );
5348
        }
5349
5350
        return true;
5351
    }
5352
5353
    /**
5354
     * Check if course setting exists.
5355
     *
5356
     * @param string $variable
5357
     * @param int    $courseId
5358
     *
5359
     * @return bool
5360
     */
5361
    public static function hasCourseSetting($variable, $courseId)
5362
    {
5363
        $courseSetting = Database::get_course_table(TABLE_COURSE_SETTING);
5364
        $courseId = (int) $courseId;
5365
        $variable = Database::escape_string($variable);
5366
        $sql = "SELECT variable FROM $courseSetting
5367
                WHERE c_id = $courseId AND variable = '$variable'";
5368
        $result = Database::query($sql);
5369
5370
        return Database::num_rows($result) > 0;
5371
    }
5372
5373
    /**
5374
     * Get information from the track_e_course_access table.
5375
     *
5376
     * @param int    $courseId
5377
     * @param int    $sessionId
5378
     * @param string $startDate
5379
     * @param string $endDate
5380
     *
5381
     * @return array
5382
     */
5383
    public static function getCourseAccessPerCourseAndSession(
5384
        $courseId,
5385
        $sessionId,
5386
        $startDate,
5387
        $endDate
5388
    ) {
5389
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5390
        $courseId = (int) $courseId;
5391
        $sessionId = (int) $sessionId;
5392
        $startDate = Database::escape_string($startDate);
5393
        $endDate = Database::escape_string($endDate);
5394
5395
        $sql = "SELECT * FROM $table
5396
                WHERE
5397
                    c_id = $courseId AND
5398
                    session_id = $sessionId AND
5399
                    login_course_date BETWEEN '$startDate' AND '$endDate'
5400
                ";
5401
5402
        $result = Database::query($sql);
5403
5404
        return Database::store_result($result);
5405
    }
5406
5407
    /**
5408
     * Get login information from the track_e_course_access table, for any
5409
     * course in the given session.
5410
     *
5411
     * @param int $sessionId
5412
     * @param int $userId
5413
     *
5414
     * @return array
5415
     */
5416
    public static function getFirstCourseAccessPerSessionAndUser($sessionId, $userId)
5417
    {
5418
        $sessionId = (int) $sessionId;
5419
        $userId = (int) $userId;
5420
5421
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5422
        $sql = "SELECT * FROM $table
5423
                WHERE session_id = $sessionId AND user_id = $userId
5424
                ORDER BY login_course_date ASC
5425
                LIMIT 1";
5426
5427
        $result = Database::query($sql);
5428
        $courseAccess = [];
5429
        if (Database::num_rows($result)) {
5430
            $courseAccess = Database::fetch_array($result, 'ASSOC');
5431
        }
5432
5433
        return $courseAccess;
5434
    }
5435
5436
    /**
5437
     * @param int  $courseId
5438
     * @param int  $sessionId
5439
     * @param bool $getAllSessions
5440
     *
5441
     * @return mixed
5442
     */
5443
    public static function getCountForum(
5444
        $courseId,
5445
        $sessionId = 0,
5446
        $getAllSessions = false
5447
    ) {
5448
        $forum = Database::get_course_table(TABLE_FORUM);
5449
        if ($getAllSessions) {
5450
            $sql = "SELECT count(*) as count
5451
                    FROM $forum f
5452
                    WHERE f.c_id = %s";
5453
        } else {
5454
            $sql = "SELECT count(*) as count
5455
                    FROM $forum f
5456
                    WHERE f.c_id = %s and f.session_id = %s";
5457
        }
5458
5459
        $sql = sprintf($sql, intval($courseId), intval($sessionId));
5460
        $result = Database::query($sql);
5461
        $row = Database::fetch_array($result);
5462
5463
        return $row['count'];
5464
    }
5465
5466
    /**
5467
     * @param int $userId
5468
     * @param int $courseId
5469
     * @param int $sessionId
5470
     *
5471
     * @return mixed
5472
     */
5473
    public static function getCountPostInForumPerUser(
5474
        $userId,
5475
        $courseId,
5476
        $sessionId = 0
5477
    ) {
5478
        $forum = Database::get_course_table(TABLE_FORUM);
5479
        $forum_post = Database::get_course_table(TABLE_FORUM_POST);
5480
5481
        $sql = "SELECT count(distinct post_id) as count
5482
                FROM $forum_post p
5483
                INNER JOIN $forum f
5484
                ON f.forum_id = p.forum_id AND f.c_id = p.c_id
5485
                WHERE p.poster_id = %s and f.session_id = %s and p.c_id = %s";
5486
5487
        $sql = sprintf(
5488
            $sql,
5489
            intval($userId),
5490
            intval($sessionId),
5491
            intval($courseId)
5492
        );
5493
5494
        $result = Database::query($sql);
5495
        $row = Database::fetch_array($result);
5496
5497
        return $row['count'];
5498
    }
5499
5500
    /**
5501
     * @param int $userId
5502
     * @param int $courseId
5503
     * @param int $sessionId
5504
     *
5505
     * @return mixed
5506
     */
5507
    public static function getCountForumPerUser(
5508
        $userId,
5509
        $courseId,
5510
        $sessionId = 0
5511
    ) {
5512
        $forum = Database::get_course_table(TABLE_FORUM);
5513
        $forum_post = Database::get_course_table(TABLE_FORUM_POST);
5514
5515
        $sql = "SELECT count(distinct f.forum_id) as count
5516
                FROM $forum_post p
5517
                INNER JOIN $forum f
5518
                ON f.forum_id = p.forum_id AND f.c_id = p.c_id
5519
                WHERE p.poster_id = %s and f.session_id = %s and p.c_id = %s";
5520
5521
        $sql = sprintf(
5522
            $sql,
5523
            intval($userId),
5524
            intval($sessionId),
5525
            intval($courseId)
5526
        );
5527
5528
        $result = Database::query($sql);
5529
        $row = Database::fetch_array($result);
5530
5531
        return $row['count'];
5532
    }
5533
5534
    /**
5535
     * Returns the course name from a given code.
5536
     *
5537
     * @param string $code
5538
     *
5539
     * @return string
5540
     */
5541
    public static function getCourseNameFromCode($code)
5542
    {
5543
        $tbl_main_categories = Database::get_main_table(TABLE_MAIN_COURSE);
5544
        $code = Database::escape_string($code);
5545
        $sql = "SELECT title
5546
                FROM $tbl_main_categories
5547
                WHERE code = '$code'";
5548
        $result = Database::query($sql);
5549
        if ($col = Database::fetch_array($result)) {
5550
            return $col['title'];
5551
        }
5552
    }
5553
5554
    /**
5555
     * Generates a course code from a course title.
5556
     *
5557
     * @todo Such a function might be useful in other places too. It might be moved in the CourseManager class.
5558
     * @todo the function might be upgraded for avoiding code duplications (currently,
5559
     * it might suggest a code that is already in use)
5560
     *
5561
     * @param string $title A course title
5562
     *
5563
     * @return string A proposed course code
5564
     *                +
5565
     * @assert (null,null) === false
5566
     * @assert ('ABC_DEF', null) === 'ABCDEF'
5567
     * @assert ('ABC09*^[%A', null) === 'ABC09A'
5568
     */
5569
    public static function generate_course_code($title)
5570
    {
5571
        return substr(
5572
            preg_replace('/[^A-Z0-9]/', '', strtoupper(api_replace_dangerous_char($title))),
5573
            0,
5574
            self::MAX_COURSE_LENGTH_CODE
5575
        );
5576
    }
5577
5578
    /**
5579
     * this function gets all the users of the course,
5580
     * including users from linked courses.
5581
     *
5582
     * @param $filterByActive
5583
     *
5584
     * @return array
5585
     */
5586
    public static function getCourseUsers($filterByActive = null)
5587
    {
5588
        // This would return only the users from real courses:
5589
        $userList = self::get_user_list_from_course_code(
5590
            api_get_course_id(),
5591
            api_get_session_id(),
5592
            null,
5593
            null,
5594
            null,
5595
            null,
5596
            false,
5597
            false,
5598
            [],
5599
            [],
5600
            [],
5601
            $filterByActive
5602
        );
5603
5604
        return $userList;
5605
    }
5606
5607
    /**
5608
     * this function gets all the groups of the course,
5609
     * not including linked courses.
5610
     */
5611
    public static function getCourseGroups()
5612
    {
5613
        $sessionId = api_get_session_id();
5614
        if ($sessionId != 0) {
5615
            $groupList = self::get_group_list_of_course(
5616
                api_get_course_id(),
5617
                $sessionId,
5618
                1
5619
            );
5620
        } else {
5621
            $groupList = self::get_group_list_of_course(
5622
                api_get_course_id(),
5623
                0,
5624
                1
5625
            );
5626
        }
5627
5628
        return $groupList;
5629
    }
5630
5631
    /**
5632
     * @param FormValidator $form
5633
     * @param array         $alreadySelected
5634
     *
5635
     * @return HTML_QuickForm_element
5636
     */
5637
    public static function addUserGroupMultiSelect(&$form, $alreadySelected)
5638
    {
5639
        $userList = self::getCourseUsers(true);
5640
        $groupList = self::getCourseGroups();
5641
5642
        $array = self::buildSelectOptions(
5643
            $groupList,
5644
            $userList,
5645
            $alreadySelected
5646
        );
5647
5648
        $result = [];
5649
        foreach ($array as $content) {
5650
            $result[$content['value']] = $content['content'];
5651
        }
5652
5653
        return $form->addElement(
5654
            'advmultiselect',
5655
            'users',
5656
            get_lang('Users'),
5657
            $result,
5658
            ['select_all_checkbox' => true]
5659
        );
5660
    }
5661
5662
    /**
5663
     * This function separates the users from the groups
5664
     * users have a value USER:XXX (with XXX the groups id have a value
5665
     *  GROUP:YYY (with YYY the group id).
5666
     *
5667
     * @param array $to Array of strings that define the type and id of each destination
5668
     *
5669
     * @return array Array of groups and users (each an array of IDs)
5670
     */
5671
    public static function separateUsersGroups($to)
5672
    {
5673
        $groupList = [];
5674
        $userList = [];
5675
5676
        foreach ($to as $to_item) {
5677
            if (!empty($to_item)) {
5678
                $parts = explode(':', $to_item);
5679
                $type = isset($parts[0]) ? $parts[0] : '';
5680
                $id = isset($parts[1]) ? $parts[1] : '';
5681
5682
                switch ($type) {
5683
                    case 'GROUP':
5684
                        $groupList[] = (int) $id;
5685
                        break;
5686
                    case 'USER':
5687
                        $userList[] = (int) $id;
5688
                        break;
5689
                }
5690
            }
5691
        }
5692
5693
        $send_to['groups'] = $groupList;
5694
        $send_to['users'] = $userList;
5695
5696
        return $send_to;
5697
    }
5698
5699
    /**
5700
     * Shows the form for sending a message to a specific group or user.
5701
     *
5702
     * @param FormValidator $form
5703
     * @param array         $groupInfo
5704
     * @param array         $to
5705
     *
5706
     * @return HTML_QuickForm_element
5707
     */
5708
    public static function addGroupMultiSelect($form, $groupInfo, $to = [])
5709
    {
5710
        $groupUsers = GroupManager::get_subscribed_users($groupInfo);
5711
        $array = self::buildSelectOptions([$groupInfo], $groupUsers, $to);
5712
5713
        $result = [];
5714
        foreach ($array as $content) {
5715
            $result[$content['value']] = $content['content'];
5716
        }
5717
5718
        return $form->addElement('advmultiselect', 'users', get_lang('Users'), $result);
5719
    }
5720
5721
    /**
5722
     * this function shows the form for sending a message to a specific group or user.
5723
     *
5724
     * @param array $groupList
5725
     * @param array $userList
5726
     * @param array $alreadySelected
5727
     *
5728
     * @return array
5729
     */
5730
    public static function buildSelectOptions(
5731
        $groupList = [],
5732
        $userList = [],
5733
        $alreadySelected = []
5734
    ) {
5735
        if (empty($alreadySelected)) {
5736
            $alreadySelected = [];
5737
        }
5738
5739
        $result = [];
5740
        // adding the groups to the select form
5741
        if ($groupList) {
5742
            foreach ($groupList as $thisGroup) {
5743
                $groupId = $thisGroup['iid'];
5744
                if (is_array($alreadySelected)) {
5745
                    if (!in_array(
5746
                        "GROUP:".$groupId,
5747
                        $alreadySelected
5748
                    )
5749
                    ) {
5750
                        $userCount = isset($thisGroup['userNb']) ? $thisGroup['userNb'] : 0;
5751
                        if (empty($userCount)) {
5752
                            $userCount = isset($thisGroup['count_users']) ? $thisGroup['count_users'] : 0;
5753
                        }
5754
                        // $alreadySelected is the array containing the groups (and users) that are already selected
5755
                        $user_label = ($userCount > 0) ? get_lang('Users') : get_lang('user');
5756
                        $user_disabled = ($userCount > 0) ? "" : "disabled=disabled";
5757
                        $result[] = [
5758
                            'disabled' => $user_disabled,
5759
                            'value' => "GROUP:".$groupId,
5760
                            // The space before "G" is needed in order to advmultiselect.php js puts groups first
5761
                            'content' => " G: ".$thisGroup['name']." - ".$userCount." ".$user_label,
5762
                        ];
5763
                    }
5764
                }
5765
            }
5766
        }
5767
5768
        // adding the individual users to the select form
5769
        if ($userList) {
5770
            foreach ($userList as $user) {
5771
                if (is_array($alreadySelected)) {
5772
                    if (!in_array(
5773
                        "USER:".$user['user_id'],
5774
                        $alreadySelected
5775
                    )
5776
                    ) {
5777
                        // $alreadySelected is the array containing the users (and groups) that are already selected
5778
                        $result[] = [
5779
                            'value' => "USER:".$user['user_id'],
5780
                            'content' => api_get_person_name($user['firstname'], $user['lastname']),
5781
                        ];
5782
                    }
5783
                }
5784
            }
5785
        }
5786
5787
        return $result;
5788
    }
5789
5790
    /**
5791
     * @return array a list (array) of all courses
5792
     */
5793
    public static function get_course_list()
5794
    {
5795
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
5796
5797
        return Database::store_result(Database::query("SELECT *, id as real_id FROM $table"));
5798
    }
5799
5800
    /**
5801
     * Returns course code from a given gradebook category's id.
5802
     *
5803
     * @param int  Category ID
5804
     *
5805
     * @return string Course code
5806
     */
5807
    public static function get_course_by_category($category_id)
5808
    {
5809
        $category_id = (int) $category_id;
5810
        $sql = 'SELECT c_id FROM '.Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY).'
5811
                WHERE id='.$category_id;
5812
        $info = Database::fetch_array(Database::query($sql), 'ASSOC');
5813
5814
        return $info ? $info['c_id'] : false;
5815
    }
5816
5817
    /**
5818
     * This function gets all the courses that are not in a session.
5819
     *
5820
     * @param date Start date
5821
     * @param date End date
5822
     * @param bool $includeClosed Whether to include closed and hidden courses
5823
     *
5824
     * @return array Not-in-session courses
5825
     */
5826
    public static function getCoursesWithoutSession(
5827
        $startDate = null,
5828
        $endDate = null,
5829
        $includeClosed = false
5830
    ) {
5831
        $dateConditional = ($startDate && $endDate) ?
5832
            " WHERE session_id IN (SELECT id FROM ".Database::get_main_table(TABLE_MAIN_SESSION).
5833
            " WHERE access_start_date = '$startDate' AND access_end_date = '$endDate')" : null;
5834
        $visibility = ($includeClosed ? '' : 'visibility NOT IN (0, 4) AND ');
5835
5836
        $sql = "SELECT id, code, title
5837
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
5838
                WHERE $visibility code NOT IN (
5839
                    SELECT DISTINCT course_code 
5840
                    FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE).$dateConditional."
5841
                )
5842
                ORDER BY id";
5843
5844
        $result = Database::query($sql);
5845
        $courses = [];
5846
        while ($row = Database::fetch_array($result)) {
5847
            $courses[] = $row;
5848
        }
5849
5850
        return $courses;
5851
    }
5852
5853
    /**
5854
     * Get list of courses based on users of a group for a group admin.
5855
     *
5856
     * @param int $userId The user id
5857
     *
5858
     * @return array
5859
     */
5860
    public static function getCoursesFollowedByGroupAdmin($userId)
5861
    {
5862
        $coursesList = [];
5863
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
5864
        $courseUserTable = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5865
        $userGroup = new UserGroup();
5866
        $userIdList = $userGroup->getGroupUsersByUser($userId);
5867
5868
        if (empty($userIdList)) {
5869
            return [];
5870
        }
5871
5872
        $sql = "SELECT DISTINCT(c.id), c.title
5873
                FROM $courseTable c
5874
                INNER JOIN $courseUserTable cru ON c.id = cru.c_id
5875
                WHERE (
5876
                    cru.user_id IN (".implode(', ', $userIdList).")
5877
                    AND cru.relation_type = 0
5878
                )";
5879
5880
        if (api_is_multiple_url_enabled()) {
5881
            $courseAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
5882
            $accessUrlId = api_get_current_access_url_id();
5883
5884
            if ($accessUrlId != -1) {
5885
                $sql = "SELECT DISTINCT(c.id), c.title
5886
                        FROM $courseTable c
5887
                        INNER JOIN $courseUserTable cru ON c.id = cru.c_id
5888
                        INNER JOIN $courseAccessUrlTable crau ON c.id = crau.c_id
5889
                        WHERE crau.access_url_id = $accessUrlId
5890
                            AND (
5891
                            cru.id_user IN (".implode(', ', $userIdList).") AND
5892
                            cru.relation_type = 0
5893
                        )";
5894
            }
5895
        }
5896
5897
        $result = Database::query($sql);
5898
        while ($row = Database::fetch_assoc($result)) {
5899
            $coursesList[] = $row;
5900
        }
5901
5902
        return $coursesList;
5903
    }
5904
5905
    /**
5906
     * Direct course link see #5299.
5907
     *
5908
     * You can send to your students an URL like this
5909
     * http://chamilodev.beeznest.com/main/auth/inscription.php?c=ABC&e=3
5910
     * Where "c" is the course code and "e" is the exercise Id, after a successful
5911
     * registration the user will be sent to the course or exercise
5912
     *
5913
     * @param array $form_data
5914
     *
5915
     * @return array
5916
     */
5917
    public static function redirectToCourse($form_data)
5918
    {
5919
        $course_code_redirect = Session::read('course_redirect');
5920
        $_user = api_get_user_info();
5921
        $userId = api_get_user_id();
5922
5923
        if (!empty($course_code_redirect)) {
5924
            $course_info = api_get_course_info($course_code_redirect);
5925
            if (!empty($course_info)) {
5926
                if (in_array(
5927
                    $course_info['visibility'],
5928
                    [COURSE_VISIBILITY_OPEN_PLATFORM, COURSE_VISIBILITY_OPEN_WORLD]
5929
                )
5930
                ) {
5931
                    if (self::is_user_subscribed_in_course($userId, $course_info['code'])) {
5932
                        $form_data['action'] = $course_info['course_public_url'];
5933
                        $form_data['message'] = sprintf(get_lang('You have been registered to course %s'), $course_info['title']);
5934
                        $form_data['button'] = Display::button(
5935
                            'next',
5936
                            get_lang('Go to the course', null, $_user['language']),
5937
                            ['class' => 'btn btn-primary btn-large']
5938
                        );
5939
5940
                        $exercise_redirect = intval(Session::read('exercise_redirect'));
5941
                        // Specify the course id as the current context does not
5942
                        // hold a global $_course array
5943
                        $objExercise = new Exercise($course_info['real_id']);
5944
                        $result = $objExercise->read($exercise_redirect);
5945
5946
                        if (!empty($exercise_redirect) && !empty($result)) {
5947
                            $form_data['action'] = api_get_path(WEB_CODE_PATH).
5948
                                'exercise/overview.php?exerciseId='.$exercise_redirect.'&cidReq='.$course_info['code'];
5949
                            $form_data['message'] .= '<br />'.get_lang('Go to the test');
5950
                            $form_data['button'] = Display::button(
5951
                                'next',
5952
                                get_lang('Go', null, $_user['language']),
5953
                                ['class' => 'btn btn-primary btn-large']
5954
                            );
5955
                        }
5956
5957
                        if (!empty($form_data['action'])) {
5958
                            header('Location: '.$form_data['action']);
5959
                            exit;
5960
                        }
5961
                    }
5962
                }
5963
            }
5964
        }
5965
5966
        return $form_data;
5967
    }
5968
5969
    /**
5970
     * Return tab of params to display a course title in the My Courses tab
5971
     * Check visibility, right, and notification icons, and load_dirs option
5972
     * get html course params.
5973
     *
5974
     * @param $courseId
5975
     * @param bool $loadDirs
5976
     *
5977
     * @return array with keys ['right_actions'] ['teachers'] ['notifications']
5978
     */
5979
    public static function getCourseParamsForDisplay($courseId, $loadDirs = false)
5980
    {
5981
        $userId = api_get_user_id();
5982
        $courseId = intval($courseId);
5983
        // Table definitions
5984
        $TABLECOURS = Database::get_main_table(TABLE_MAIN_COURSE);
5985
        $TABLECOURSUSER = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5986
        $TABLE_ACCESS_URL_REL_COURSE = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
5987
        $current_url_id = api_get_current_access_url_id();
5988
5989
        // Get course list auto-register
5990
        $special_course_list = self::get_special_course_list();
5991
5992
        $without_special_courses = '';
5993
        if (!empty($special_course_list)) {
5994
            $without_special_courses = ' AND course.id NOT IN ("'.implode('","', $special_course_list).'")';
5995
        }
5996
5997
        //AND course_rel_user.relation_type<>".COURSE_RELATION_TYPE_RRHH."
5998
        $sql = "SELECT 
5999
                    course.id, 
6000
                    course.title, 
6001
                    course.code, 
6002
                    course.subscribe subscr, 
6003
                    course.unsubscribe unsubscr, 
6004
                    course_rel_user.status status,
6005
                    course_rel_user.sort sort, 
6006
                    course_rel_user.user_course_cat user_course_cat
6007
                FROM
6008
                $TABLECOURS course 
6009
                INNER JOIN $TABLECOURSUSER course_rel_user                
6010
                ON (course.id = course_rel_user.c_id)
6011
                INNER JOIN $TABLE_ACCESS_URL_REL_COURSE url
6012
                ON (url.c_id = course.id)
6013
                WHERE
6014
                    course.id = $courseId AND
6015
                    course_rel_user.user_id = $userId
6016
                    $without_special_courses
6017
                ";
6018
6019
        // If multiple URL access mode is enabled, only fetch courses
6020
        // corresponding to the current URL.
6021
        if (api_get_multiple_access_url() && $current_url_id != -1) {
6022
            $sql .= " AND url.c_id = course.id AND access_url_id = $current_url_id";
6023
        }
6024
        // Use user's classification for courses (if any).
6025
        $sql .= " ORDER BY course_rel_user.user_course_cat, course_rel_user.sort ASC";
6026
6027
        $result = Database::query($sql);
6028
6029
        // Browse through all courses. We can only have one course because
6030
        // of the  course.id=".intval($courseId) in sql query
6031
        $course = Database::fetch_array($result);
6032
        $course_info = api_get_course_info_by_id($courseId);
6033
        if (empty($course_info)) {
6034
            return '';
6035
        }
6036
6037
        //$course['id_session'] = null;
6038
        $course_info['id_session'] = null;
6039
        $course_info['status'] = $course['status'];
6040
6041
        // For each course, get if there is any notification icon to show
6042
        // (something that would have changed since the user's last visit).
6043
        $show_notification = !api_get_configuration_value('hide_course_notification')
6044
            ? Display::show_notification($course_info)
6045
            : '';
6046
6047
        // New code displaying the user's status in respect to this course.
6048
        $status_icon = Display::return_icon(
6049
            'blackboard.png',
6050
            $course_info['title'],
6051
            [],
6052
            ICON_SIZE_LARGE
6053
        );
6054
6055
        $params = [];
6056
        $params['right_actions'] = '';
6057
6058
        if (api_is_platform_admin()) {
6059
            if ($loadDirs) {
6060
                $params['right_actions'] .= '<a id="document_preview_'.$course_info['real_id'].'_0" class="document_preview" href="javascript:void(0);">'.Display::return_icon('folder.png', get_lang('Documents'), ['align' => 'absmiddle'], ICON_SIZE_SMALL).'</a>';
6061
                $params['right_actions'] .= '<a href="'.api_get_path(WEB_CODE_PATH).'course_info/infocours.php?cidReq='.$course['code'].'">'.
6062
                    Display::return_icon('edit.png', get_lang('Edit'), ['align' => 'absmiddle'], ICON_SIZE_SMALL).
6063
                    '</a>';
6064
                $params['right_actions'] .= Display::div(
6065
                    '',
6066
                    [
6067
                        'id' => 'document_result_'.$course_info['real_id'].'_0',
6068
                        'class' => 'document_preview_container',
6069
                    ]
6070
                );
6071
            } else {
6072
                $params['right_actions'] .= '<a class="btn btn-default btn-sm" title="'.get_lang('Edit').'" href="'.api_get_path(WEB_CODE_PATH).'course_info/infocours.php?cidReq='.$course['code'].'">'.
6073
                    Display::returnFontAwesomeIcon('pencil').'</a>';
6074
            }
6075
        } else {
6076
            if ($course_info['visibility'] != COURSE_VISIBILITY_CLOSED) {
6077
                if ($loadDirs) {
6078
                    $params['right_actions'] .= '<a id="document_preview_'.$course_info['real_id'].'_0" class="document_preview" href="javascript:void(0);">'.
6079
                        Display::return_icon('folder.png', get_lang('Documents'), ['align' => 'absmiddle'], ICON_SIZE_SMALL).'</a>';
6080
                    $params['right_actions'] .= Display::div(
6081
                        '',
6082
                        [
6083
                            'id' => 'document_result_'.$course_info['real_id'].'_0',
6084
                            'class' => 'document_preview_container',
6085
                        ]
6086
                    );
6087
                } else {
6088
                    if ($course_info['status'] == COURSEMANAGER) {
6089
                        $params['right_actions'] .= '<a class="btn btn-default btn-sm" title="'.get_lang('Edit').'" href="'.api_get_path(WEB_CODE_PATH).'course_info/infocours.php?cidReq='.$course['code'].'">'.
6090
                            Display::returnFontAwesomeIcon('pencil').'</a>';
6091
                    }
6092
                }
6093
            }
6094
        }
6095
6096
        $course_title_url = '';
6097
        if ($course_info['visibility'] != COURSE_VISIBILITY_CLOSED || $course['status'] == COURSEMANAGER) {
6098
            $course_title_url = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/?id_session=0';
6099
            $course_title = Display::url($course_info['title'], $course_title_url);
6100
        } else {
6101
            $course_title = $course_info['title'].' '.Display::tag(
6102
                'span',
6103
                get_lang('(the course is currently closed)'),
6104
                ['class' => 'item_closed']
6105
            );
6106
        }
6107
6108
        // Start displaying the course block itself
6109
        if (api_get_setting('display_coursecode_in_courselist') === 'true') {
6110
            $course_title .= ' ('.$course_info['visual_code'].') ';
6111
        }
6112
        $teachers = '';
6113
        if (api_get_setting('display_teacher_in_courselist') === 'true') {
6114
            $teachers = self::getTeacherListFromCourseCodeToString(
6115
                $course['code'],
6116
                self::USER_SEPARATOR,
6117
                true
6118
            );
6119
        }
6120
        $params['link'] = $course_title_url;
6121
        $params['icon'] = $status_icon;
6122
        $params['title'] = $course_title;
6123
        $params['teachers'] = $teachers;
6124
        if ($course_info['visibility'] != COURSE_VISIBILITY_CLOSED) {
6125
            $params['notifications'] = $show_notification;
6126
        }
6127
6128
        return $params;
6129
    }
6130
6131
    /**
6132
     * Get the course id based on the original id and field name in the extra fields.
6133
     * Returns 0 if course was not found.
6134
     *
6135
     * @param string $original_course_id_value Original course id
6136
     * @param string $original_course_id_name  Original field name
6137
     *
6138
     * @return int Course id
6139
     */
6140
    public static function get_course_id_from_original_id($original_course_id_value, $original_course_id_name)
6141
    {
6142
        $extraFieldValue = new ExtraFieldValue('course');
6143
        $value = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
6144
            $original_course_id_name,
6145
            $original_course_id_value
6146
        );
6147
6148
        if ($value) {
6149
            return $value['item_id'];
6150
        }
6151
6152
        return 0;
6153
    }
6154
6155
    /**
6156
     * Helper function to create a default gradebook (if necessary) upon course creation.
6157
     *
6158
     * @param int    $modelId    The gradebook model ID
6159
     * @param string $courseCode Course code
6160
     */
6161
    public static function createDefaultGradebook($modelId, $courseCode)
6162
    {
6163
        if (api_get_setting('gradebook_enable_grade_model') === 'true') {
6164
            //Create gradebook_category for the new course and add
6165
            // a gradebook model for the course
6166
            if (isset($modelId) &&
6167
                !empty($modelId) &&
6168
                $modelId != '-1'
6169
            ) {
6170
                GradebookUtils::create_default_course_gradebook(
6171
                    $courseCode,
6172
                    $modelId
6173
                );
6174
            }
6175
        }
6176
    }
6177
6178
    /**
6179
     * Helper function to check if there is a course template and, if so, to
6180
     * copy the template as basis for the new course.
6181
     *
6182
     * @param string $courseCode     Course code
6183
     * @param int    $courseTemplate 0 if no course template is defined
6184
     */
6185
    public static function useTemplateAsBasisIfRequired($courseCode, $courseTemplate)
6186
    {
6187
        $template = api_get_setting('course_creation_use_template');
6188
        $teacherCanSelectCourseTemplate = api_get_setting('teacher_can_select_course_template') === 'true';
6189
        $courseTemplate = isset($courseTemplate) ? intval($courseTemplate) : 0;
6190
6191
        $useTemplate = false;
6192
6193
        if ($teacherCanSelectCourseTemplate && $courseTemplate) {
6194
            $useTemplate = true;
6195
            $originCourse = api_get_course_info_by_id($courseTemplate);
6196
        } elseif (!empty($template)) {
6197
            $useTemplate = true;
6198
            $originCourse = api_get_course_info_by_id($template);
6199
        }
6200
6201
        if ($useTemplate) {
6202
            // Include the necessary libraries to generate a course copy
6203
            // Call the course copy object
6204
            $originCourse['official_code'] = $originCourse['code'];
6205
            $cb = new CourseBuilder(null, $originCourse);
6206
            $course = $cb->build(null, $originCourse['code']);
6207
            $cr = new CourseRestorer($course);
6208
            $cr->set_file_option();
6209
            $cr->restore($courseCode);
6210
        }
6211
    }
6212
6213
    /**
6214
     * Helper method to get the number of users defined with a specific course extra field.
6215
     *
6216
     * @param string $name                 Field title
6217
     * @param string $tableExtraFields     The extra fields table name
6218
     * @param string $tableUserFieldValues The user extra field value table name
6219
     *
6220
     * @return int The number of users with this extra field with a specific value
6221
     */
6222
    public static function getCountRegisteredUsersWithCourseExtraField(
6223
        $name,
6224
        $tableExtraFields = '',
6225
        $tableUserFieldValues = ''
6226
    ) {
6227
        if (empty($tableExtraFields)) {
6228
            $tableExtraFields = Database::get_main_table(TABLE_EXTRA_FIELD);
6229
        }
6230
        if (empty($tableUserFieldValues)) {
6231
            $tableUserFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
6232
        }
6233
6234
        $registered_users_with_extra_field = 0;
6235
        if (!empty($name) && $name != '-') {
6236
            $extraFieldType = EntityExtraField::COURSE_FIELD_TYPE;
6237
            $name = Database::escape_string($name);
6238
            $sql = "SELECT count(v.item_id) as count
6239
                    FROM $tableUserFieldValues v 
6240
                    INNER JOIN $tableExtraFields f
6241
                    ON (f.id = v.field_id)
6242
                    WHERE value = '$name' AND extra_field_type = $extraFieldType";
6243
            $result_count = Database::query($sql);
6244
            if (Database::num_rows($result_count)) {
6245
                $row_count = Database::fetch_array($result_count);
6246
                $registered_users_with_extra_field = $row_count['count'];
6247
            }
6248
        }
6249
6250
        return $registered_users_with_extra_field;
6251
    }
6252
6253
    /**
6254
     * Get the course categories form a course list.
6255
     *
6256
     * @param array $courseList
6257
     *
6258
     * @return array
6259
     */
6260
    public static function getCourseCategoriesFromCourseList(array $courseList)
6261
    {
6262
        $allCategories = array_column($courseList, 'category');
6263
        $categories = array_unique($allCategories);
6264
6265
        sort($categories);
6266
6267
        return $categories;
6268
    }
6269
6270
    /**
6271
     * Display the description button of a course in the course catalog.
6272
     *
6273
     * @param array $course
6274
     *
6275
     * @return string HTML string
6276
     */
6277
    public static function returnDescriptionButton($course)
6278
    {
6279
        if (empty($course)) {
6280
            return '';
6281
        }
6282
6283
        if (api_get_setting('show_courses_descriptions_in_catalog') == 'true') {
6284
            $title = $course['title'];
6285
            $url = api_get_path(WEB_CODE_PATH).'inc/ajax/course_home.ajax.php?a=show_course_information&code='.$course['code'];
6286
            $html = Display::url(
6287
                Display::returnFontAwesomeIcon('info-circle', 'lg'),
6288
                $url,
6289
                [
6290
                    'class' => 'ajax btn btn-light btn-sm',
6291
                    'data-title' => $title,
6292
                    'title' => get_lang('Description'),
6293
                    'aria-label' => get_lang('Description'),
6294
                    'data-size' => 'lg',
6295
                ]
6296
            );
6297
6298
            return $html;
6299
        }
6300
6301
        return '';
6302
    }
6303
6304
    /**
6305
     * @param Course $course
6306
     *
6307
     * @return bool
6308
     */
6309
    public static function hasPicture(Course $course)
6310
    {
6311
        return file_exists(api_get_path(SYS_COURSE_PATH).$course->getDirectory().'/course-pic85x85.png');
6312
    }
6313
6314
    /**
6315
     * Get the course picture path.
6316
     *
6317
     * @param Course $course
6318
     * @param bool   $fullSize
6319
     *
6320
     * @return string|null
6321
     */
6322
    public static function getPicturePath(Course $course, $fullSize = false)
6323
    {
6324
        if (!self::hasPicture($course)) {
6325
            return null;
6326
        }
6327
6328
        if ($fullSize) {
6329
            return api_get_path(WEB_COURSE_PATH).$course->getDirectory().'/course-pic.png';
6330
        }
6331
6332
        return api_get_path(WEB_COURSE_PATH).$course->getDirectory().'/course-pic85x85.png';
6333
    }
6334
6335
    /**
6336
     * @return int
6337
     */
6338
    public static function getCountOpenCourses()
6339
    {
6340
        $visibility = [
6341
            COURSE_VISIBILITY_REGISTERED,
6342
            COURSE_VISIBILITY_OPEN_PLATFORM,
6343
            COURSE_VISIBILITY_OPEN_WORLD,
6344
        ];
6345
6346
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
6347
        $sql = "SELECT count(id) count 
6348
                FROM $table 
6349
                WHERE visibility IN (".implode(',', $visibility).")";
6350
        $result = Database::query($sql);
6351
        $row = Database::fetch_array($result);
6352
6353
        return (int) $row['count'];
6354
    }
6355
6356
    /**
6357
     * @return int
6358
     */
6359
    public static function getCountExercisesFromOpenCourse()
6360
    {
6361
        $visibility = [
6362
            COURSE_VISIBILITY_REGISTERED,
6363
            COURSE_VISIBILITY_OPEN_PLATFORM,
6364
            COURSE_VISIBILITY_OPEN_WORLD,
6365
        ];
6366
6367
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
6368
        $tableExercise = Database::get_course_table(TABLE_QUIZ_TEST);
6369
        $sql = "SELECT count(e.iid) count 
6370
                FROM $table c INNER JOIN $tableExercise e
6371
                ON (c.id = e.c_id)
6372
                WHERE e.active <> -1 AND visibility IN (".implode(',', $visibility).")";
6373
        $result = Database::query($sql);
6374
        $row = Database::fetch_array($result);
6375
6376
        return (int) $row['count'];
6377
    }
6378
6379
    /**
6380
     * @param ToolChain $toolList
6381
     */
6382
    public static function setToolList($toolList)
6383
    {
6384
        self::$toolList = $toolList;
6385
    }
6386
6387
    /**
6388
     * @return ToolChain
6389
     */
6390
    public static function getToolList()
6391
    {
6392
        return self::$toolList;
6393
    }
6394
6395
    /**
6396
     * Check if a specific access-url-related setting is a problem or not.
6397
     *
6398
     * @param array  $_configuration The $_configuration array
6399
     * @param int    $accessUrlId    The access URL ID
6400
     * @param string $param
6401
     * @param string $msgLabel
6402
     *
6403
     * @return bool|string
6404
     */
6405
    private static function checkCreateCourseAccessUrlParam($_configuration, $accessUrlId, $param, $msgLabel)
6406
    {
6407
        if (isset($_configuration[$accessUrlId][$param]) && $_configuration[$accessUrlId][$param] > 0) {
6408
            $num = self::count_courses($accessUrlId);
6409
            if ($num >= $_configuration[$accessUrlId][$param]) {
6410
                api_warn_hosting_contact($param);
6411
6412
                Display::addFlash(
6413
                    Display::return_message($msgLabel)
6414
                );
6415
            }
6416
        }
6417
6418
        return false;
6419
    }
6420
6421
    /**
6422
     * Fill course with all necessary items.
6423
     *
6424
     * @param array $courseInfo Course info array
6425
     * @param array $params     Parameters from the course creation form
6426
     * @param int   $authorId
6427
     */
6428
    private static function fillCourse($courseInfo, $params, $authorId = 0)
6429
    {
6430
        $authorId = empty($authorId) ? api_get_user_id() : (int) $authorId;
6431
6432
        AddCourse::prepare_course_repository($courseInfo['directory']);
6433
        AddCourse::fillCourse(
6434
            $courseInfo,
6435
            $params['exemplary_content'],
6436
            $authorId
6437
        );
6438
6439
        if (isset($params['gradebook_model_id'])) {
6440
            self::createDefaultGradebook(
6441
                $params['gradebook_model_id'],
6442
                $courseInfo['code']
6443
            );
6444
        }
6445
6446
        // If parameter defined, copy the contents from a specific
6447
        // template course into this new course
6448
        if (isset($params['course_template'])) {
6449
            self::useTemplateAsBasisIfRequired(
6450
                $courseInfo['id'],
6451
                $params['course_template']
6452
            );
6453
        }
6454
        $params['course_code'] = $courseInfo['code'];
6455
        $params['item_id'] = $courseInfo['real_id'];
6456
6457
        $courseFieldValue = new ExtraFieldValue('course');
6458
        $courseFieldValue->saveFieldValues($params);
6459
    }
6460
}
6461