Passed
Push — master ( b23148...5d3f0a )
by
unknown
14:03 queued 06:25
created

CourseManager::get_courses_list()   F

Complexity

Conditions 21
Paths 10368

Size

Total Lines 116
Code Lines 72

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 72
nc 10368
nop 10
dl 0
loc 116
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

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