CourseManager::getUserListFromCourseId()   F
last analyzed

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