Passed
Push — dependabot/npm_and_yarn/nanoid... ( aaf2c9...c4aa90 )
by
unknown
14:37 queued 06:22
created

CourseManager::getCoursesFollowedByUser()   C

Complexity

Conditions 14

Size

Total Lines 107
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

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