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

CourseManager::getUserListFromCourseId()   F

Complexity

Conditions 63

Size

Total Lines 397
Code Lines 230

Duplication

Lines 0
Ratio 0 %

Importance

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