Passed
Pull Request — master (#6073)
by
unknown
08:30
created

CourseManager::getCoursesFollowedByUser()   C

Complexity

Conditions 14

Size

Total Lines 107
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Importance

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