Passed
Push — master ( b95980...a81919 )
by Julito
08:46
created

CourseManager::get_course_code_from_course_id()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 1
dl 0
loc 11
rs 9.9666
c 0
b 0
f 0
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\ExtraField as EntityExtraField;
8
use Chamilo\CoreBundle\Entity\SequenceResource;
9
use Chamilo\CoreBundle\Entity\User;
10
use Chamilo\CoreBundle\Framework\Container;
11
use Chamilo\CoreBundle\Repository\Node\CourseRepository;
12
use Chamilo\CoreBundle\Repository\SequenceResourceRepository;
13
use Chamilo\CoreBundle\ToolChain;
14
use Chamilo\CourseBundle\Component\CourseCopy\CourseBuilder;
15
use Chamilo\CourseBundle\Component\CourseCopy\CourseRestorer;
16
use Chamilo\CourseBundle\Entity\CGroup;
17
use Chamilo\CourseBundle\Settings\SettingsCourseManager;
18
use ChamiloSession as Session;
19
use Doctrine\Common\Collections\Criteria;
20
use Doctrine\ORM\EntityManager;
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 mixed false if the course was not created, array with the course info
46
     */
47
    public static function create_course($params, $authorId = 0, $accessUrlId = 0)
48
    {
49
        global $_configuration;
50
51
        // Check portal limits
52
        $accessUrlId = !empty($accessUrlId) ? (int) $accessUrlId : api_get_current_access_url_id();
53
        $authorId = empty($authorId) ? api_get_user_id() : (int) $authorId;
54
55
        if (isset($_configuration[$accessUrlId]) && is_array($_configuration[$accessUrlId])) {
56
            $return = self::checkCreateCourseAccessUrlParam(
57
                $_configuration,
58
                $accessUrlId,
59
                'hosting_limit_courses',
60
                'PortalCoursesLimitReached'
61
            );
62
            if (false != $return) {
63
                return $return;
64
            }
65
            $return = self::checkCreateCourseAccessUrlParam(
66
                $_configuration,
67
                $accessUrlId,
68
                'hosting_limit_active_courses',
69
                'PortalActiveCoursesLimitReached'
70
            );
71
            if (false != $return) {
72
                return $return;
73
            }
74
        }
75
76
        if (empty($params['title'])) {
77
            return false;
78
        }
79
80
        if (empty($params['wanted_code'])) {
81
            $params['wanted_code'] = $params['title'];
82
            // Check whether the requested course code has already been occupied.
83
            $substring = api_substr($params['title'], 0, self::MAX_COURSE_LENGTH_CODE);
84
            if (false === $substring || empty($substring)) {
85
                return false;
86
            } else {
87
                $params['wanted_code'] = self::generate_course_code($substring);
88
            }
89
        }
90
91
        // Create the course keys
92
        $keys = AddCourse::define_course_keys($params['wanted_code']);
93
        $params['exemplary_content'] = isset($params['exemplary_content']) ? $params['exemplary_content'] : false;
94
95
        if (count($keys)) {
96
            $params['code'] = $keys['currentCourseCode'];
97
            $params['visual_code'] = $keys['currentCourseId'];
98
            $params['directory'] = $keys['currentCourseRepository'];
99
            $courseInfo = api_get_course_info($params['code']);
100
            if (empty($courseInfo)) {
101
                $courseId = AddCourse::register_course($params, $accessUrlId);
102
                $courseInfo = api_get_course_info_by_id($courseId);
103
                if (!empty($courseInfo)) {
104
                    self::fillCourse($courseInfo, $params, $authorId);
105
106
                    return $courseInfo;
107
                }
108
            }
109
        }
110
111
        return false;
112
    }
113
114
    /**
115
     * Returns all the information of a given course code.
116
     *
117
     * @param string $course_code , the course code
118
     *
119
     * @return array with all the fields of the course table
120
     *
121
     * @deprecated Use api_get_course_info() instead
122
     *
123
     * @author Patrick Cool <[email protected]>, Ghent University
124
     * @assert ('') === false
125
     */
126
    public static function get_course_information($course_code)
127
    {
128
        return Database::fetch_array(
129
            Database::query(
130
                "SELECT *, id as real_id FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
131
                WHERE code = '".Database::escape_string($course_code)."'"
132
            ),
133
            'ASSOC'
134
        );
135
    }
136
137
    /**
138
     * Returns a list of courses. Should work with quickform syntax.
139
     *
140
     * @param int    $from               Offset (from the 7th = '6'). Optional.
141
     * @param int    $howmany            Number of results we want. Optional.
142
     * @param int    $orderby            The column we want to order it by. Optional, defaults to first column.
143
     * @param string $orderdirection     The direction of the order (ASC or DESC). Optional, defaults to ASC.
144
     * @param int    $visibility         the visibility of the course, or all by default
145
     * @param string $startwith          If defined, only return results for which the course *title* begins with this
146
     *                                   string
147
     * @param string $urlId              The Access URL ID, if using multiple URLs
148
     * @param bool   $alsoSearchCode     An extension option to indicate that we also want to search for course codes
149
     *                                   (not *only* titles)
150
     * @param array  $conditionsLike
151
     * @param array  $onlyThisCourseList
152
     *
153
     * @return array
154
     */
155
    public static function get_courses_list(
156
        $from = 0,
157
        $howmany = 0,
158
        $orderby = 1,
159
        $orderdirection = 'ASC',
160
        $visibility = -1,
161
        $startwith = '',
162
        $urlId = null,
163
        $alsoSearchCode = false,
164
        $conditionsLike = [],
165
        $onlyThisCourseList = []
166
    ) {
167
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
168
        //$tblCourseCategory = Database::get_main_table(TABLE_MAIN_CATEGORY);
169
        // @todo fix , course_category.code AS category_code
170
        $sql = "SELECT course.*, course.id as real_id
171
                FROM $courseTable course  ";
172
173
        if (!empty($urlId)) {
174
            $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
175
            $sql .= " INNER JOIN $table url ON (url.c_id = course.id) ";
176
        }
177
178
        //$sql .= " LEFT JOIN $tblCourseCategory ON course.category_id = course_category.id ";
179
180
        $visibility = (int) $visibility;
181
182
        if (!empty($startwith)) {
183
            $sql .= "WHERE (title LIKE '".Database::escape_string($startwith)."%' ";
184
            if ($alsoSearchCode) {
185
                $sql .= "OR code LIKE '".Database::escape_string($startwith)."%' ";
186
            }
187
            $sql .= ') ';
188
            if (-1 !== $visibility) {
189
                $sql .= " AND visibility = $visibility ";
190
            }
191
        } else {
192
            $sql .= 'WHERE 1 ';
193
            if (-1 !== $visibility) {
194
                $sql .= " AND visibility = $visibility ";
195
            }
196
        }
197
198
        if (!empty($urlId)) {
199
            $urlId = (int) $urlId;
200
            $sql .= " AND access_url_id = $urlId";
201
        }
202
203
        if (!empty($onlyThisCourseList)) {
204
            $onlyThisCourseList = array_map('intval', $onlyThisCourseList);
205
            $onlyThisCourseList = implode("','", $onlyThisCourseList);
206
            $sql .= " AND course.id IN ('$onlyThisCourseList') ";
207
        }
208
209
        $allowedFields = [
210
            'title',
211
            'code',
212
        ];
213
214
        if (count($conditionsLike) > 0) {
215
            $sql .= ' AND ';
216
            $temp_conditions = [];
217
            foreach ($conditionsLike as $field => $value) {
218
                if (!in_array($field, $allowedFields)) {
219
                    continue;
220
                }
221
                $field = Database::escape_string($field);
222
                $value = Database::escape_string($value);
223
                $simple_like = false;
224
                if ($simple_like) {
225
                    $temp_conditions[] = $field." LIKE '$value%'";
226
                } else {
227
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
228
                }
229
            }
230
            $condition = ' AND ';
231
            if (!empty($temp_conditions)) {
232
                $sql .= implode(' '.$condition.' ', $temp_conditions);
233
            }
234
        }
235
236
        if (!empty($orderby)) {
237
            $sql .= " ORDER BY ".Database::escape_string($orderby)." ";
238
        } else {
239
            $sql .= ' ORDER BY 1 ';
240
        }
241
242
        if (!in_array($orderdirection, ['ASC', 'DESC'])) {
243
            $sql .= 'ASC';
244
        } else {
245
            $sql .= ('ASC' === $orderdirection ? 'ASC' : 'DESC');
246
        }
247
248
        if (!empty($howmany) && is_int($howmany) and $howmany > 0) {
249
            $sql .= ' LIMIT '.Database::escape_string($howmany);
250
        } else {
251
            $sql .= ' LIMIT 1000000'; //virtually no limit
252
        }
253
        if (!empty($from)) {
254
            $from = (int) $from;
255
            $sql .= ' OFFSET '.$from;
256
        } else {
257
            $sql .= ' OFFSET 0';
258
        }
259
260
        $data = [];
261
        $res = Database::query($sql);
262
        if (Database::num_rows($res) > 0) {
263
            while ($row = Database::fetch_array($res, 'ASSOC')) {
264
                $data[] = $row;
265
            }
266
        }
267
268
        return $data;
269
    }
270
271
    /**
272
     * Returns the status of a user in a course, which is COURSEMANAGER or STUDENT.
273
     *
274
     * @param int $userId
275
     * @param int $courseId
276
     *
277
     * @return int|bool the status of the user in that course (or false if the user is not in that course)
278
     */
279
    public static function getUserInCourseStatus($userId, $courseId)
280
    {
281
        $courseId = (int) $courseId;
282
        $userId = (int) $userId;
283
        if (empty($courseId)) {
284
            return false;
285
        }
286
287
        $result = Database::fetch_array(
288
            Database::query(
289
                "SELECT status FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
290
                WHERE
291
                    c_id  = $courseId AND
292
                    user_id = $userId"
293
            )
294
        );
295
296
        if (empty($result['status'])) {
297
            return false;
298
        }
299
300
        return $result['status'];
301
    }
302
303
    /**
304
     * @param int $userId
305
     * @param int $courseId
306
     *
307
     * @return mixed
308
     */
309
    public static function getUserCourseInfo($userId, $courseId)
310
    {
311
        $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
312
                WHERE
313
                    c_id  = ".intval($courseId)." AND
314
                    user_id = ".intval($userId);
315
316
        return Database::fetch_array(Database::query($sql));
317
    }
318
319
    /**
320
     * @param int  $userId
321
     * @param int  $courseId
322
     * @param bool $isTutor
323
     *
324
     * @return bool
325
     */
326
    public static function updateUserCourseTutor($userId, $courseId, $isTutor)
327
    {
328
        $table = Database::escape_string(TABLE_MAIN_COURSE_USER);
329
330
        $courseId = (int) $courseId;
331
        $isTutor = (int) $isTutor;
332
333
        $sql = "UPDATE $table SET is_tutor = '".$isTutor."'
334
			    WHERE
335
				    user_id = ".$userId." AND
336
				    c_id = ".$courseId;
337
338
        $result = Database::query($sql);
339
340
        if (Database::affected_rows($result) > 0) {
341
            return true;
342
        }
343
344
        return false;
345
    }
346
347
    /**
348
     * @param int $userId
349
     * @param int $courseId
350
     *
351
     * @return mixed
352
     */
353
    public static function get_tutor_in_course_status($userId, $courseId)
354
    {
355
        $userId = intval($userId);
356
        $courseId = intval($courseId);
357
        $result = Database::fetch_array(
358
            Database::query(
359
                "SELECT is_tutor
360
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
361
                WHERE
362
                    c_id = $courseId AND
363
                    user_id = $userId"
364
            )
365
        );
366
367
        return $result['is_tutor'];
368
    }
369
370
    /**
371
     * Unsubscribe one or more users from a course.
372
     *
373
     * @param   mixed   user_id or an array with user ids
374
     * @param   string  course code
375
     * @param   int     session id
376
     *
377
     * @return bool
378
     *
379
     * @assert ('', '') === false
380
     */
381
    public static function unsubscribe_user($user_id, $course_code, $session_id = 0)
382
    {
383
        if (empty($user_id)) {
384
            return false;
385
        }
386
        if (!is_array($user_id)) {
387
            $user_id = [$user_id];
388
        }
389
390
        if (0 == count($user_id)) {
391
            return false;
392
        }
393
394
        if (!empty($session_id)) {
395
            $session_id = (int) $session_id;
396
        } else {
397
            $session_id = api_get_session_id();
398
        }
399
400
        if (empty($course_code)) {
401
            return false;
402
        }
403
404
        $userList = [];
405
        // Cleaning the $user_id variable
406
        if (is_array($user_id)) {
407
            $new_user_id_list = [];
408
            foreach ($user_id as $my_user_id) {
409
                $new_user_id_list[] = (int) $my_user_id;
410
            }
411
            $new_user_id_list = array_filter($new_user_id_list);
412
            $userList = $new_user_id_list;
413
            $user_ids = implode(',', $new_user_id_list);
414
        } else {
415
            $user_ids = (int) $user_id;
416
            $userList[] = $user_id;
417
        }
418
419
        $course_info = api_get_course_info($course_code);
420
        $course_id = $course_info['real_id'];
421
422
        // Unsubscribe user from all groups in the course.
423
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_GROUP_USER)."
424
                WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
425
        Database::query($sql);
426
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_GROUP_TUTOR)."
427
                WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
428
        Database::query($sql);
429
430
        // Erase user student publications (works) in the course - by André Boivin
431
        if (!empty($userList)) {
432
            foreach ($userList as $userId) {
433
                // Getting all work from user
434
                $workList = getWorkPerUser($userId);
435
                if (!empty($workList)) {
436
                    foreach ($workList as $work) {
437
                        $work = $work['work'];
438
                        // Getting user results
439
                        if (!empty($work->user_results)) {
440
                            foreach ($work->user_results as $workSent) {
441
                                deleteWorkItem($workSent['id'], $course_info);
442
                            }
443
                        }
444
                    }
445
                }
446
            }
447
        }
448
449
        // Unsubscribe user from all blogs in the course.
450
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_BLOGS_REL_USER)."
451
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
452
        Database::query($sql);
453
454
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_BLOGS_TASKS_REL_USER)."
455
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
456
        Database::query($sql);
457
458
        // Deleting users in forum_notification and mailqueue course tables
459
        $sql = "DELETE FROM  ".Database::get_course_table(TABLE_FORUM_NOTIFICATION)."
460
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
461
        Database::query($sql);
462
463
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_FORUM_MAIL_QUEUE)."
464
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
465
        Database::query($sql);
466
467
        // Unsubscribe user from the course.
468
        if (!empty($session_id)) {
469
            foreach ($userList as $uid) {
470
                SessionManager::unSubscribeUserFromCourseSession($uid, $course_id, $session_id);
471
472
                // check if a user is register in the session with other course
473
                $sql = "SELECT user_id FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
474
                        WHERE session_id = $session_id AND user_id = $uid";
475
                $rs = Database::query($sql);
476
477
                if (0 == Database::num_rows($rs)) {
478
                    SessionManager::unsubscribe_user_from_session($uid, $session_id);
479
                }
480
            }
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
                $session_id
490
            );
491
        } else {
492
            $sql = "DELETE FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
493
                    WHERE
494
                        user_id IN ($user_ids) AND
495
                        relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
496
                        c_id = $course_id";
497
            Database::query($sql);
498
499
            // add event to system log
500
            $user_id = api_get_user_id();
501
502
            Event::addEvent(
503
                LOG_UNSUBSCRIBE_USER_FROM_COURSE,
504
                LOG_COURSE_CODE,
505
                $course_code,
506
                api_get_utc_datetime(),
507
                $user_id,
508
                $course_id
509
            );
510
511
            foreach ($userList as $userId) {
512
                $userInfo = api_get_user_info($userId);
513
                Event::addEvent(
514
                    LOG_UNSUBSCRIBE_USER_FROM_COURSE,
515
                    LOG_USER_OBJECT,
516
                    $userInfo,
517
                    api_get_utc_datetime(),
518
                    $user_id,
519
                    $course_id
520
                );
521
            }
522
        }
523
        if (api_get_configuration_value('catalog_course_subscription_in_user_s_session')) {
524
            // Also unlink the course from the users' currently accessible sessions
525
            /** @var Course $course */
526
            $course = Container::getCourseRepository()->findOneBy([
527
                'code' => $course_code,
528
            ]);
529
            if (is_null($course)) {
530
                return false;
531
            }
532
            /** @var Chamilo\CoreBundle\Entity\User $user */
533
            foreach (UserManager::getRepository()->matching(
534
                Criteria::create()->where(Criteria::expr()->in('id', $userList))
535
            ) as $user) {
536
                foreach ($user->getCurrentlyAccessibleSessions() as $session) {
537
                    $session->removeCourse($course);
538
                    // unsubscribe user from course within this session
539
                    SessionManager::unSubscribeUserFromCourseSession($user->getId(), $course->getId(), $session->getId());
540
                }
541
            }
542
            try {
543
                Database::getManager()->flush();
544
            } catch (\Doctrine\ORM\OptimisticLockException $exception) {
545
                Display::addFlash(
546
                    Display::return_message(
547
                        get_lang('InternalDatabaseError').': '.$exception->getMessage(),
548
                        'warning'
549
                    )
550
                );
551
552
                return false;
553
            }
554
        }
555
556
        return true;
557
    }
558
559
    /**
560
     * @param string $courseCode
561
     * @param int    $status
562
     *
563
     * @return bool
564
     */
565
    public static function autoSubscribeToCourse($courseCode, $status = STUDENT)
566
    {
567
        if (api_is_anonymous()) {
568
            return false;
569
        }
570
571
        $course = Container::getCourseRepository()->findOneBy(['code' => $courseCode]);
572
573
        if (null === $course) {
574
            return false;
575
        }
576
577
        $visibility = (int) $course->getVisibility();
578
579
        if (in_array($visibility, [
580
                COURSE_VISIBILITY_CLOSED,
581
                //COURSE_VISIBILITY_REGISTERED,
582
                COURSE_VISIBILITY_HIDDEN,
583
        ])) {
584
            Display::addFlash(
585
                Display::return_message(
586
                    get_lang('SubscribingNotAllowed'),
587
                    'warning'
588
        )
589
            );
590
591
            return false;
592
        }
593
594
        // Private course can allow auto subscription
595
        if (COURSE_VISIBILITY_REGISTERED === $visibility && false === $course->getSubscribe()) {
596
            Display::addFlash(
597
                Display::return_message(
598
                    get_lang('Subscribing not allowed'),
599
                    'warning'
600
                )
601
            );
602
603
            return false;
604
        }
605
606
        $userId = api_get_user_id();
607
608
        if (api_get_configuration_value('catalog_course_subscription_in_user_s_session')) {
609
            $user = api_get_user_entity($userId);
610
            $sessions = $user->getCurrentlyAccessibleSessions();
611
            if (empty($sessions)) {
612
                // user has no accessible session
613
                if ($user->getStudentSessions()) {
614
                    // user has ancient or future student session(s) but not available now
615
                    Display::addFlash(
616
                        Display::return_message(
617
                            get_lang('CanNotSubscribeToCourseUserSessionExpired'),
618
                            'warning'
619
                        )
620
                    );
621
622
                    return false;
623
                }
624
                // user has no session at all, create one starting now
625
                $numberOfDays = api_get_configuration_value('user_s_session_duration') ?: 3 * 365;
626
                try {
627
                    $duration = new DateInterval(sprintf('P%dD', $numberOfDays));
628
                } catch (Exception $exception) {
629
                    Display::addFlash(
630
                        Display::return_message(
631
                            get_lang('WrongNumberOfDays').': '.$numberOfDays.': '.$exception->getMessage(),
632
                            'warning'
633
                        )
634
                    );
635
636
                    return false;
637
                }
638
                $endDate = new DateTime();
639
                $endDate->add($duration);
640
                $session = new \Chamilo\CoreBundle\Entity\Session();
641
                $session->setName(
642
                    sprintf(get_lang('FirstnameLastnameCourses'), $user->getFirstname(), $user->getLastname())
643
                );
644
                $session->setAccessEndDate($endDate);
645
                $session->setCoachAccessEndDate($endDate);
646
                $session->setDisplayEndDate($endDate);
647
                $session->setSendSubscriptionNotification(false);
648
                $adminId = api_get_configuration_value('session_automatic_creation_user_id') ?: 1;
649
                $session->setSessionAdmin(api_get_user_entity($adminId));
650
                $session->addUserInSession(0, $user);
651
                Database::getManager()->persist($session);
652
                try {
653
                    Database::getManager()->flush();
654
                } catch (\Doctrine\ORM\OptimisticLockException $exception) {
655
                    Display::addFlash(
656
                        Display::return_message(
657
                            get_lang('InternalDatabaseError').': '.$exception->getMessage(),
658
                            'warning'
659
                        )
660
                    );
661
662
                    return false;
663
                }
664
                $accessUrlRelSession = new AccessUrlRelSession();
665
                $accessUrlRelSession->setUrl(api_get_url_entity());
666
                $accessUrlRelSession->setSession($session);
667
                Database::getManager()->persist($accessUrlRelSession);
668
                try {
669
                    Database::getManager()->flush();
670
                } catch (\Doctrine\ORM\OptimisticLockException $exception) {
671
                    Display::addFlash(
672
                        Display::return_message(
673
                            get_lang('InternalDatabaseError').': '.$exception->getMessage(),
674
                            'warning'
675
                        )
676
                    );
677
678
                    return false;
679
                }
680
            } else {
681
                // user has at least one accessible session, let's use it
682
                $session = $sessions[0];
683
            }
684
            // add chosen course to the user session
685
            $session->addCourse($course);
686
            Database::getManager()->persist($session);
687
            try {
688
                Database::getManager()->flush();
689
            } catch (\Doctrine\ORM\OptimisticLockException $exception) {
690
                Display::addFlash(
691
                    Display::return_message(
692
                        get_lang('InternalDatabaseError').': '.$exception->getMessage(),
693
                        'warning'
694
                    )
695
                );
696
697
                return false;
698
            }
699
            // subscribe user to course within this session
700
            SessionManager::subscribe_users_to_session_course([$userId], $session->getId(), $course->getCode());
701
702
            return true;
703
        }
704
705
        return self::subscribeUser($userId, $course->getCode(), $status, 0);
706
    }
707
708
    /**
709
     * Subscribe a user to a course. No checks are performed here to see if
710
     * course subscription is allowed.
711
     *
712
     * @param int    $userId
713
     * @param string $courseCode
714
     * @param int    $status                 (STUDENT, COURSEMANAGER, COURSE_ADMIN, NORMAL_COURSE_MEMBER)
715
     * @param int    $sessionId
716
     * @param int    $userCourseCategoryId
717
     * @param bool   $checkTeacherPermission
718
     *
719
     * @return bool True on success, false on failure
720
     *
721
     * @assert ('', '') === false
722
     */
723
    public static function subscribeUser(
724
        $userId,
725
        $courseCode,
726
        $status = STUDENT,
727
        $sessionId = 0,
728
        $userCourseCategoryId = 0,
729
        $checkTeacherPermission = true
730
    ) {
731
        $userId = (int) $userId;
732
        $status = (int) $status;
733
734
        if (empty($userId) || empty($courseCode)) {
735
            return false;
736
        }
737
738
        $courseInfo = api_get_course_info($courseCode);
739
740
        if (empty($courseInfo)) {
741
            Display::addFlash(Display::return_message(get_lang('This course doesn\'t exist'), 'warning'));
742
743
            return false;
744
        }
745
746
        $userInfo = api_get_user_info($userId);
747
748
        if (empty($userInfo)) {
749
            Display::addFlash(Display::return_message(get_lang('This user doesn\'t exist'), 'warning'));
750
751
            return false;
752
        }
753
754
        $courseId = $courseInfo['real_id'];
755
        $courseCode = $courseInfo['code'];
756
        $userCourseCategoryId = (int) $userCourseCategoryId;
757
        $sessionId = empty($sessionId) ? api_get_session_id() : (int) $sessionId;
758
        $status = STUDENT === $status || COURSEMANAGER === $status ? $status : STUDENT;
759
        $courseUserTable = Database::get_main_table(TABLE_MAIN_COURSE_USER);
760
761
        if (!empty($sessionId)) {
762
            SessionManager::subscribe_users_to_session_course(
763
                [$userId],
764
                $sessionId,
765
                $courseCode
766
            );
767
768
            // Add event to the system log
769
            Event::addEvent(
770
                LOG_SUBSCRIBE_USER_TO_COURSE,
771
                LOG_COURSE_CODE,
772
                $courseCode,
773
                api_get_utc_datetime(),
774
                api_get_user_id(),
775
                $courseId,
776
                $sessionId
777
            );
778
            Event::addEvent(
779
                LOG_SUBSCRIBE_USER_TO_COURSE,
780
                LOG_USER_OBJECT,
781
                $userInfo,
782
                api_get_utc_datetime(),
783
                api_get_user_id(),
784
                $courseId,
785
                $sessionId
786
            );
787
788
            return true;
789
        } else {
790
            // Check whether the user has not been already subscribed to the course.
791
            $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
792
                    WHERE
793
                        user_id = $userId AND
794
                        relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
795
                        c_id = $courseId
796
                    ";
797
            if (Database::num_rows(Database::query($sql)) > 0) {
798
                Display::addFlash(Display::return_message(get_lang('Already registered in course'), 'warning'));
799
800
                return false;
801
            }
802
803
            if ($checkTeacherPermission && !api_is_course_admin()) {
804
                // Check in advance whether subscription is allowed or not for this course.
805
                if (SUBSCRIBE_NOT_ALLOWED === (int) $courseInfo['subscribe']) {
806
                    Display::addFlash(Display::return_message(get_lang('SubscriptionNotAllowed'), 'warning'));
807
808
                    return false;
809
                }
810
            }
811
812
            if (STUDENT === $status) {
813
                // Check if max students per course extra field is set
814
                $extraFieldValue = new ExtraFieldValue('course');
815
                $value = $extraFieldValue->get_values_by_handler_and_field_variable($courseId, 'max_subscribed_students');
816
                if (!empty($value) && isset($value['value'])) {
817
                    $maxStudents = $value['value'];
818
                    if ('' !== $maxStudents) {
819
                        $maxStudents = (int) $maxStudents;
820
                        $count = self::get_user_list_from_course_code(
821
                            $courseCode,
822
                            0,
823
                            null,
824
                            null,
825
                            STUDENT,
826
                            true,
827
                            false
828
                        );
829
830
                        if ($count >= $maxStudents) {
831
                            Display::addFlash(Display::return_message(get_lang('The maximum number of student has already been reached, it is not possible to subscribe more student.'), 'warning'));
832
833
                            return false;
834
                        }
835
                    }
836
                }
837
            }
838
839
            $maxSort = api_max_sort_value('0', $userId);
840
            $params = [
841
                'c_id' => $courseId,
842
                'user_id' => $userId,
843
                'status' => $status,
844
                'sort' => $maxSort + 1,
845
                'relation_type' => 0,
846
                'user_course_cat' => $userCourseCategoryId,
847
            ];
848
            $insertId = Database::insert($courseUserTable, $params);
849
850
            if ($insertId) {
851
                Display::addFlash(
852
                    Display::return_message(
853
                        sprintf(
854
                            get_lang('User %s has been registered to course %s'),
855
                            $userInfo['complete_name_with_username'],
856
                            $courseInfo['title']
857
                        )
858
                    )
859
                );
860
861
                $send = api_get_course_setting('email_alert_to_teacher_on_new_user_in_course', $courseInfo);
862
863
                if (1 == $send) {
864
                    self::email_to_tutor(
865
                        $userId,
866
                        $courseInfo['real_id'],
867
                        false
868
                    );
869
                } elseif (2 == $send) {
870
                    self::email_to_tutor(
871
                        $userId,
872
                        $courseInfo['real_id'],
873
                        true
874
                    );
875
                }
876
877
                $subscribe = (int) api_get_course_setting('subscribe_users_to_forum_notifications', $courseInfo);
878
                if (1 === $subscribe) {
879
                    $forums = get_forums(0, $courseCode, true, $sessionId);
880
                    foreach ($forums as $forum) {
881
                        set_notification('forum', $forum->getIid(), false, $userInfo, $courseInfo);
882
                    }
883
                }
884
885
                // Add event to the system log
886
                Event::addEvent(
887
                    LOG_SUBSCRIBE_USER_TO_COURSE,
888
                    LOG_COURSE_CODE,
889
                    $courseCode,
890
                    api_get_utc_datetime(),
891
                    api_get_user_id(),
892
                    $courseId
893
                );
894
895
                Event::addEvent(
896
                    LOG_SUBSCRIBE_USER_TO_COURSE,
897
                    LOG_USER_OBJECT,
898
                    $userInfo,
899
                    api_get_utc_datetime(),
900
                    api_get_user_id(),
901
                    $courseId
902
                );
903
904
                return true;
905
            }
906
907
            return false;
908
        }
909
    }
910
911
    /**
912
     * Get the course id based on the original id and field name in the
913
     * extra fields. Returns 0 if course was not found.
914
     *
915
     * @param string $original_course_id_value
916
     * @param string $original_course_id_name
917
     *
918
     * @return int Course id
919
     *
920
     * @assert ('', '') === false
921
     */
922
    public static function get_course_code_from_original_id(
923
        $original_course_id_value,
924
        $original_course_id_name
925
    ) {
926
        $t_cfv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
927
        $table_field = Database::get_main_table(TABLE_EXTRA_FIELD);
928
        $extraFieldType = EntityExtraField::COURSE_FIELD_TYPE;
929
        $original_course_id_value = Database::escape_string($original_course_id_value);
930
        $original_course_id_name = Database::escape_string($original_course_id_name);
931
932
        $sql = "SELECT item_id
933
                FROM $table_field cf
934
                INNER JOIN $t_cfv cfv
935
                ON cfv.field_id=cf.id
936
                WHERE
937
                    variable = '$original_course_id_name' AND
938
                    value = '$original_course_id_value' AND
939
                    cf.extra_field_type = $extraFieldType
940
                ";
941
        $res = Database::query($sql);
942
        $row = Database::fetch_object($res);
943
        if ($row) {
944
            return $row->item_id;
945
        } else {
946
            return 0;
947
        }
948
    }
949
950
    /**
951
     * Gets the course code from the course id. Returns null if course id was not found.
952
     *
953
     * @param int $id Course id
954
     *
955
     * @return string Course code
956
     * @assert ('') === false
957
     */
958
    public static function get_course_code_from_course_id($id)
959
    {
960
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
961
        $id = intval($id);
962
        $sql = "SELECT code FROM $table WHERE id = $id ";
963
        $res = Database::query($sql);
964
        $row = Database::fetch_object($res);
965
        if ($row) {
966
            return $row->code;
967
        } else {
968
            return null;
969
        }
970
    }
971
972
    /**
973
     * Add the user $userId visibility to the course $courseCode in the catalogue.
974
     *
975
     * @author David Nos (https://github.com/dnos)
976
     *
977
     * @param int    $userId     the id of the user
978
     * @param string $courseCode the course code
979
     * @param int    $visible    (optional) The course visibility in the catalogue to the user (1=visible, 0=invisible)
980
     *
981
     * @return bool true if added succesfully, false otherwise
982
     */
983
    public static function addUserVisibilityToCourseInCatalogue(
984
        $userId,
985
        $courseCode,
986
        $visible = 1
987
    ) {
988
        $debug = false;
989
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
990
        $courseUserTable = Database::get_main_table(TABLE_MAIN_COURSE_CATALOGUE_USER);
991
        $visible = (int) $visible;
992
        if (empty($userId) || empty($courseCode) || ($userId != strval(intval($userId)))) {
993
            return false;
994
        }
995
996
        $courseCode = Database::escape_string($courseCode);
997
        $courseInfo = api_get_course_info($courseCode);
998
        $courseId = $courseInfo['real_id'];
999
1000
        // Check in advance whether the user has already been registered on the platform.
1001
        $sql = "SELECT status FROM ".$userTable." WHERE user_id = $userId ";
1002
        if (0 == Database::num_rows(Database::query($sql))) {
1003
            if ($debug) {
1004
                error_log('The user has not been registered to the platform');
1005
            }
1006
1007
            return false; // The user has not been registered to the platform.
1008
        }
1009
1010
        // Check whether the user has already been registered to the course visibility in the catalogue.
1011
        $sql = "SELECT * FROM $courseUserTable
1012
                WHERE
1013
                    user_id = $userId AND
1014
                    visible = $visible AND
1015
                    c_id = $courseId";
1016
        if (Database::num_rows(Database::query($sql)) > 0) {
1017
            if ($debug) {
1018
                error_log('The user has been already registered to the course visibility in the catalogue');
1019
            }
1020
1021
            return true; // The visibility of the user to the course in the catalogue does already exist.
1022
        }
1023
1024
        // Register the user visibility to course in catalogue.
1025
        $params = [
1026
            'user_id' => $userId,
1027
            'c_id' => $courseId,
1028
            'visible' => $visible,
1029
        ];
1030
1031
        return Database::insert($courseUserTable, $params);
1032
    }
1033
1034
    /**
1035
     * Remove the user $userId visibility to the course $courseCode in the catalogue.
1036
     *
1037
     * @author David Nos (https://github.com/dnos)
1038
     *
1039
     * @param int    $userId     the id of the user
1040
     * @param string $courseCode the course code
1041
     * @param int    $visible    (optional) The course visibility in the catalogue to the user (1=visible, 0=invisible)
1042
     *
1043
     * @return bool true if removed succesfully or register not found, false otherwise
1044
     */
1045
    public static function removeUserVisibilityToCourseInCatalogue(
1046
        $userId,
1047
        $courseCode,
1048
        $visible = 1
1049
    ) {
1050
        $courseUserTable = Database::get_main_table(TABLE_MAIN_COURSE_CATALOGUE_USER);
1051
1052
        if (empty($userId) || empty($courseCode) || ($userId != strval(intval($userId)))) {
1053
            return false;
1054
        }
1055
1056
        $courseCode = Database::escape_string($courseCode);
1057
        $courseInfo = api_get_course_info($courseCode);
1058
        $courseId = $courseInfo['real_id'];
1059
1060
        // Check whether the user has already been registered to the course visibility in the catalogue.
1061
        $sql = "SELECT * FROM $courseUserTable
1062
                WHERE
1063
                    user_id = $userId AND
1064
                    visible = $visible AND
1065
                    c_id = $courseId";
1066
        if (Database::num_rows(Database::query($sql)) > 0) {
1067
            $cond = [
1068
                'user_id = ? AND c_id = ? AND visible = ? ' => [
1069
                    $userId,
1070
                    $courseId,
1071
                    $visible,
1072
                ],
1073
            ];
1074
1075
            return Database::delete($courseUserTable, $cond);
1076
        } else {
1077
            return true; // Register does not exist
1078
        }
1079
    }
1080
1081
    /**
1082
     * @param string $code
1083
     *
1084
     * @return bool if there already are one or more courses
1085
     *              with the same code OR visual_code (visualcode), false otherwise
1086
     */
1087
    public static function course_code_exists($code)
1088
    {
1089
        $code = Database::escape_string($code);
1090
        $sql = "SELECT COUNT(*) as number
1091
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
1092
                WHERE code = '$code' OR visual_code = '$code'";
1093
        $result = Database::fetch_array(Database::query($sql));
1094
1095
        return $result['number'] > 0;
1096
    }
1097
1098
    /**
1099
     * @param int    $userId
1100
     * @param string $keyword
1101
     *
1102
     * @return array an array with the course info of all the courses (real and virtual)
1103
     *               of which the current user is course admin
1104
     */
1105
    public static function get_course_list_of_user_as_course_admin($userId, $keyword = '')
1106
    {
1107
        $user = api_get_user_entity($userId);
1108
1109
        if (null === $user) {
1110
            return [];
1111
        }
1112
1113
        $url = api_get_url_entity();
1114
        $user = api_get_user_entity($userId);
1115
        $repo = Container::getUserRepository();
1116
1117
        return $repo->getCourses($user, $url, COURSEMANAGER, $keyword);
1118
    }
1119
1120
    /**
1121
     * @param int   $userId
1122
     * @param array $courseInfo
1123
     *
1124
     * @return bool|null
1125
     */
1126
    public static function isUserSubscribedInCourseAsDrh($userId, $courseInfo)
1127
    {
1128
        $userId = intval($userId);
1129
1130
        if (!api_is_drh()) {
1131
            return false;
1132
        }
1133
1134
        if (empty($courseInfo) || empty($userId)) {
1135
            return false;
1136
        }
1137
1138
        $courseId = intval($courseInfo['real_id']);
1139
        $table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1140
1141
        $sql = "SELECT * FROM $table
1142
                WHERE
1143
                    user_id = $userId AND
1144
                    relation_type = ".COURSE_RELATION_TYPE_RRHH." AND
1145
                    c_id = $courseId";
1146
1147
        $result = Database::fetch_array(Database::query($sql));
1148
1149
        if (!empty($result)) {
1150
            // The user has been registered in this course.
1151
            return true;
1152
        }
1153
    }
1154
1155
    /**
1156
     * Check if user is subscribed inside a course.
1157
     *
1158
     * @param int    $userId
1159
     * @param string $course_code  , if this parameter is null, it'll check for all courses
1160
     * @param bool   $in_a_session True for checking inside sessions too, by default is not checked
1161
     * @param int    $session_id
1162
     *
1163
     * @return bool $session_id true if the user is registered in the course, false otherwise
1164
     */
1165
    public static function is_user_subscribed_in_course(
1166
        $userId,
1167
        $course_code = null,
1168
        $in_a_session = false,
1169
        $session_id = 0
1170
    ) {
1171
        $userId = (int) $userId;
1172
        $session_id = (int) $session_id;
1173
1174
        if (api_get_configuration_value('catalog_course_subscription_in_user_s_session')) {
1175
            // with this option activated, only check whether the course is in one of the users' sessions
1176
            $course = Container::getCourseRepository()->findOneBy([
1177
                'code' => $course_code,
1178
            ]);
1179
            if (is_null($course)) {
1180
                return false;
1181
            }
1182
1183
            $user = api_get_user_entity($userId);
1184
            if (is_null($user)) {
1185
                return false;
1186
            }
1187
            foreach ($user->getStudentSessions() as $session) {
1188
                if ($session->isRelatedToCourse($course)) {
1189
                    return true;
1190
                }
1191
            }
1192
1193
            return false;
1194
        }
1195
1196
        if (empty($session_id)) {
1197
            $session_id = api_get_session_id();
1198
        }
1199
1200
        $condition_course = '';
1201
        if (isset($course_code)) {
1202
            $courseInfo = api_get_course_info($course_code);
1203
            if (empty($courseInfo)) {
1204
                return false;
1205
            }
1206
            $courseId = $courseInfo['real_id'];
1207
            $condition_course = " AND c_id = $courseId";
1208
        }
1209
1210
        $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
1211
                WHERE
1212
                    user_id = $userId AND
1213
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
1214
                    $condition_course ";
1215
1216
        $result = Database::fetch_array(Database::query($sql));
1217
1218
        if (!empty($result)) {
1219
            // The user has been registered in this course.
1220
            return true;
1221
        }
1222
1223
        if (!$in_a_session) {
1224
            // The user has not been registered in this course.
1225
            return false;
1226
        }
1227
1228
        $tableSessionCourseUser = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1229
        $sql = "SELECT 1 FROM $tableSessionCourseUser
1230
                WHERE user_id = $userId AND session_id = $session_id $condition_course";
1231
1232
        if (Database::num_rows(Database::query($sql)) > 0) {
1233
            return true;
1234
        }
1235
1236
        $sql = "SELECT 1 FROM $tableSessionCourseUser
1237
                WHERE user_id = $userId AND session_id = $session_id AND status = 2 $condition_course";
1238
1239
        if (Database::num_rows(Database::query($sql)) > 0) {
1240
            return true;
1241
        }
1242
1243
        $sql = 'SELECT 1 FROM '.Database::get_main_table(TABLE_MAIN_SESSION).
1244
              " WHERE id = $session_id AND id_coach = $userId";
1245
1246
        if (Database::num_rows(Database::query($sql)) > 0) {
1247
            return true;
1248
        }
1249
1250
        return false;
1251
    }
1252
1253
    /**
1254
     * Is the user a teacher in the given course?
1255
     *
1256
     * @param int $userId
1257
     * @param int $courseId
1258
     *
1259
     * @return bool if the user is a teacher in the course, false otherwise
1260
     */
1261
    public static function isCourseTeacher($userId, $courseId)
1262
    {
1263
        $userId = (int) $userId;
1264
        $courseId = (int) $courseId;
1265
1266
        if (empty($courseId) || empty($userId)) {
1267
            return false;
1268
        }
1269
1270
        $sql = "SELECT status FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
1271
                WHERE c_id = $courseId AND user_id = $userId ";
1272
        $result = Database::query($sql);
1273
1274
        if (Database::num_rows($result) > 0) {
1275
            return 1 == Database::result($result, 0, 'status');
1276
        }
1277
1278
        return false;
1279
    }
1280
1281
    /**
1282
     * Return user info array of all users registered in a course
1283
     * This only returns the users that are registered in this actual course, not linked courses.
1284
     *
1285
     * @param string    $course_code
1286
     * @param int       $sessionId
1287
     * @param string    $limit
1288
     * @param string    $order_by         the field to order the users by.
1289
     *                                    Valid values are 'lastname', 'firstname', 'username', 'email',
1290
     *                                    'official_code' OR a part of a SQL statement that starts with ORDER BY ...
1291
     * @param int|null  $filter_by_status if using the session_id: 0 or 2 (student, coach),
1292
     *                                    if using session_id = 0 STUDENT or COURSEMANAGER
1293
     * @param bool|null $return_count
1294
     * @param bool      $add_reports
1295
     * @param bool      $resumed_report
1296
     * @param array     $extra_field
1297
     * @param array     $courseCodeList
1298
     * @param array     $userIdList
1299
     * @param string    $filterByActive
1300
     * @param array     $sessionIdList
1301
     * @param string    $searchByKeyword
1302
     *
1303
     * @return array|int
1304
     */
1305
    public static function get_user_list_from_course_code(
1306
        $course_code = null,
1307
        $sessionId = 0,
1308
        $limit = null,
1309
        $order_by = null,
1310
        $filter_by_status = null,
1311
        $return_count = null,
1312
        $add_reports = false,
1313
        $resumed_report = false,
1314
        $extra_field = [],
1315
        $courseCodeList = [],
1316
        $userIdList = [],
1317
        $filterByActive = null,
1318
        $sessionIdList = [],
1319
        $searchByKeyword = '',
1320
        $options = []
1321
    ) {
1322
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
1323
        $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
1324
1325
        $sessionId = (int) $sessionId;
1326
        $course_code = Database::escape_string($course_code);
1327
        $courseInfo = api_get_course_info($course_code);
1328
        $courseId = 0;
1329
        if (!empty($courseInfo)) {
1330
            $courseId = $courseInfo['real_id'];
1331
        }
1332
1333
        $where = [];
1334
        if (empty($order_by)) {
1335
            $order_by = 'user.lastname, user.firstname';
1336
            if (api_is_western_name_order()) {
1337
                $order_by = 'user.firstname, user.lastname';
1338
            }
1339
        }
1340
1341
        // if the $order_by does not contain 'ORDER BY'
1342
        // we have to check if it is a valid field that can be sorted on
1343
        if (!strstr($order_by, 'ORDER BY')) {
1344
            if (!empty($order_by)) {
1345
                $order_by = "ORDER BY $order_by";
1346
            } else {
1347
                $order_by = '';
1348
            }
1349
        }
1350
1351
        $filter_by_status_condition = null;
1352
        $sqlInjectWhere = '';
1353
        $whereExtraField = '';
1354
        $injectExtraFields = ' , ';
1355
        $sqlInjectJoins = '';
1356
        if (!empty($options)) {
1357
            $extraFieldModel = new ExtraField('user');
1358
            $conditions = $extraFieldModel->parseConditions($options, 'user');
1359
            if (!empty($conditions)) {
1360
                $injectExtraFields = $conditions['inject_extra_fields'];
1361
1362
                if (!empty($injectExtraFields)) {
1363
                    $injectExtraFields = ', '.$injectExtraFields;
1364
                } else {
1365
                    $injectExtraFields = ' , ';
1366
                }
1367
                $sqlInjectJoins = $conditions['inject_joins'];
1368
                $whereExtraField = $conditions['where'];
1369
            }
1370
        }
1371
1372
        if (!empty($sessionId) || !empty($sessionIdList)) {
1373
            $sql = 'SELECT DISTINCT
1374
                        user.id as user_id,
1375
                        user.email,
1376
                        session_course_user.status as status_session,
1377
                        session_id,
1378
                        user.*,
1379
                        course.*,
1380
                        course.id AS c_id
1381
                         '.$injectExtraFields.'
1382
                        session.name as session_name
1383
                    ';
1384
            if ($return_count) {
1385
                $sql = ' SELECT COUNT(user.id) as count';
1386
            }
1387
1388
            $sessionCondition = " session_course_user.session_id = $sessionId";
1389
            if (!empty($sessionIdList)) {
1390
                $sessionIdListToString = implode("','", array_map('intval', $sessionIdList));
1391
                $sessionCondition = " session_course_user.session_id IN ('$sessionIdListToString') ";
1392
            }
1393
1394
            $courseCondition = " course.id = $courseId";
1395
            if (!empty($courseCodeList)) {
1396
                $courseCodeListForSession = array_map(['Database', 'escape_string'], $courseCodeList);
1397
                $courseCodeListForSession = implode("','", $courseCodeListForSession);
1398
                $courseCondition = " course.code IN ('$courseCodeListForSession')  ";
1399
            }
1400
1401
            $sql .= ' FROM '.Database::get_main_table(TABLE_MAIN_USER).' as user ';
1402
            $sql .= " LEFT JOIN ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)." as session_course_user
1403
                      ON
1404
                        user.id = session_course_user.user_id AND
1405
                        $sessionCondition
1406
                        INNER JOIN $course_table course
1407
                        ON session_course_user.c_id = course.id AND
1408
                        $courseCondition
1409
                        INNER JOIN $sessionTable session
1410
                        ON session_course_user.session_id = session.id
1411
                    $sqlInjectJoins
1412
                   ";
1413
            $where[] = ' session_course_user.c_id IS NOT NULL ';
1414
1415
            // 2 = coach
1416
            // 0 = student
1417
            if (isset($filter_by_status)) {
1418
                $filter_by_status = (int) $filter_by_status;
1419
                $filter_by_status_condition = " session_course_user.status = $filter_by_status AND ";
1420
            }
1421
        } else {
1422
            if ($return_count) {
1423
                $sql = " SELECT COUNT(*) as count";
1424
            } else {
1425
                if (empty($course_code)) {
1426
                    $sql = 'SELECT DISTINCT
1427
                                course.title,
1428
                                course.code,
1429
                                course.id AS c_id,
1430
                                course_rel_user.status as status_rel,
1431
                                user.id as user_id,
1432
                                user.email,
1433
                                course_rel_user.is_tutor
1434
                                '.$injectExtraFields.'
1435
                                user.*  ';
1436
                } else {
1437
                    $sql = 'SELECT DISTINCT
1438
                                course_rel_user.status as status_rel,
1439
                                user.id as user_id,
1440
                                user.email,
1441
                                course_rel_user.is_tutor
1442
                                '.$injectExtraFields.'
1443
                                user.*  ';
1444
                }
1445
            }
1446
1447
            $sql .= " FROM ".Database::get_main_table(TABLE_MAIN_USER)." as user
1448
                      LEFT JOIN ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." as course_rel_user
1449
                      ON
1450
                        user.id = course_rel_user.user_id AND
1451
                        course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
1452
                       INNER JOIN $course_table course
1453
                       ON (course_rel_user.c_id = course.id)
1454
                       $sqlInjectJoins
1455
                       ";
1456
1457
            if (!empty($course_code)) {
1458
                $sql .= " AND course_rel_user.c_id = $courseId";
1459
            }
1460
            $where[] = ' course_rel_user.c_id IS NOT NULL ';
1461
1462
            if (isset($filter_by_status) && is_numeric($filter_by_status)) {
1463
                $filter_by_status = (int) $filter_by_status;
1464
                $filter_by_status_condition = " course_rel_user.status = $filter_by_status AND ";
1465
            }
1466
        }
1467
1468
        $multiple_access_url = api_get_multiple_access_url();
1469
        if ($multiple_access_url) {
1470
            $sql .= ' LEFT JOIN '.Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER).' au
1471
                      ON (au.user_id = user.id) ';
1472
        }
1473
1474
        $extraFieldWasAdded = false;
1475
        if ($return_count && $resumed_report) {
1476
            foreach ($extra_field as $extraField) {
1477
                $extraFieldInfo = UserManager::get_extra_field_information_by_name($extraField);
1478
                if (!empty($extraFieldInfo)) {
1479
                    $fieldValuesTable = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1480
                    $sql .= " LEFT JOIN $fieldValuesTable as ufv
1481
                            ON (
1482
                                user.id = ufv.item_id AND
1483
                                (field_id = ".$extraFieldInfo['id']." OR field_id IS NULL)
1484
                            )";
1485
                    $extraFieldWasAdded = true;
1486
                }
1487
            }
1488
        }
1489
1490
        $sql .= " WHERE
1491
            $filter_by_status_condition
1492
            ".implode(' OR ', $where);
1493
1494
        if ($multiple_access_url) {
1495
            $current_access_url_id = api_get_current_access_url_id();
1496
            $sql .= " AND (access_url_id =  $current_access_url_id ) ";
1497
        }
1498
1499
        if ($return_count && $resumed_report && $extraFieldWasAdded) {
1500
            $sql .= ' AND field_id IS NOT NULL GROUP BY value ';
1501
        }
1502
1503
        if (!empty($courseCodeList)) {
1504
            $courseCodeList = array_map(['Database', 'escape_string'], $courseCodeList);
1505
            $courseCodeList = implode('","', $courseCodeList);
1506
            if (empty($sessionIdList)) {
1507
                $sql .= ' AND course.code IN ("'.$courseCodeList.'")';
1508
            }
1509
        }
1510
1511
        if (!empty($userIdList)) {
1512
            $userIdList = array_map('intval', $userIdList);
1513
            $userIdList = implode('","', $userIdList);
1514
            $sql .= ' AND user.id IN ("'.$userIdList.'")';
1515
        }
1516
1517
        if (isset($filterByActive)) {
1518
            $filterByActive = (int) $filterByActive;
1519
            $sql .= " AND user.active = $filterByActive";
1520
        }
1521
1522
        if (!empty($searchByKeyword)) {
1523
            $searchByKeyword = Database::escape_string($searchByKeyword);
1524
            $sql .= " AND (
1525
                        user.firstname LIKE '$searchByKeyword' OR
1526
                        user.username LIKE '$searchByKeyword' OR
1527
                        user.lastname LIKE '$searchByKeyword'
1528
                    ) ";
1529
        }
1530
1531
        $sql .= $whereExtraField;
1532
        $sql .= " $order_by $limit";
1533
1534
        $rs = Database::query($sql);
1535
        $users = [];
1536
1537
        $extra_fields = UserManager::get_extra_fields(
1538
            0,
1539
            100,
1540
            null,
1541
            null,
1542
            true,
1543
            true
1544
        );
1545
1546
        $counter = 1;
1547
        $count_rows = Database::num_rows($rs);
1548
1549
        if ($return_count && $resumed_report) {
1550
            return $count_rows;
1551
        }
1552
        $table_user_field_value = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1553
        $tableExtraField = Database::get_main_table(TABLE_EXTRA_FIELD);
1554
        if ($count_rows) {
1555
            while ($user = Database::fetch_array($rs)) {
1556
                if ($return_count) {
1557
                    return $user['count'];
1558
                }
1559
1560
                $report_info = [];
1561
                $user_info = $user;
1562
                $user_info['status'] = $user['status'];
1563
                if (isset($user['is_tutor'])) {
1564
                    $user_info['is_tutor'] = $user['is_tutor'];
1565
                }
1566
                if (!empty($sessionId)) {
1567
                    $user_info['status_session'] = $user['status_session'];
1568
                }
1569
1570
                $sessionId = isset($user['session_id']) ? $user['session_id'] : 0;
1571
                $course_code = isset($user['code']) ? $user['code'] : null;
1572
                $sessionName = isset($user['session_name']) ? ' ('.$user['session_name'].') ' : '';
1573
1574
                if ($add_reports) {
1575
                    if ($resumed_report) {
1576
                        $extra = [];
1577
                        if (!empty($extra_fields)) {
1578
                            foreach ($extra_fields as $extra) {
1579
                                if (in_array($extra['1'], $extra_field)) {
1580
                                    $user_data = UserManager::get_extra_user_data_by_field(
1581
                                        $user['user_id'],
1582
                                        $extra['1']
1583
                                    );
1584
                                    break;
1585
                                }
1586
                            }
1587
                        }
1588
1589
                        $row_key = '-1';
1590
                        $name = '-';
1591
                        if (!empty($extra)) {
1592
                            if (!empty($user_data[$extra['1']])) {
1593
                                $row_key = $user_data[$extra['1']];
1594
                                $name = $user_data[$extra['1']];
1595
                                $users[$row_key]['extra_'.$extra['1']] = $name;
1596
                            }
1597
                        }
1598
1599
                        if (empty($users[$row_key])) {
1600
                            $users[$row_key] = [];
1601
                        }
1602
1603
                        if (!array_key_exists('training_hours', $users[$row_key])) {
1604
                            $users[$row_key]['training_hours'] = 0;
1605
                        }
1606
1607
                        $users[$row_key]['training_hours'] += Tracking::get_time_spent_on_the_course(
1608
                            $user['user_id'],
1609
                            $courseId,
1610
                            $sessionId
1611
                        );
1612
1613
                        if (!array_key_exists('count_users', $users[$row_key])) {
1614
                            $users[$row_key]['count_users'] = 0;
1615
                        }
1616
1617
                        $users[$row_key]['count_users'] += $counter;
1618
1619
                        $registered_users_with_extra_field = self::getCountRegisteredUsersWithCourseExtraField(
1620
                            $name,
1621
                            $tableExtraField,
1622
                            $table_user_field_value
1623
                        );
1624
1625
                        $users[$row_key]['count_users_registered'] = $registered_users_with_extra_field;
1626
                        $users[$row_key]['average_hours_per_user'] = $users[$row_key]['training_hours'] / $users[$row_key]['count_users'];
1627
1628
                        $category = Category:: load(
1629
                            null,
1630
                            null,
1631
                            $course_code,
1632
                            null,
1633
                            null,
1634
                            $sessionId
1635
                        );
1636
1637
                        if (!isset($users[$row_key]['count_certificates'])) {
1638
                            $users[$row_key]['count_certificates'] = 0;
1639
                        }
1640
1641
                        if (isset($category[0]) && $category[0]->is_certificate_available($user['user_id'])) {
1642
                            $users[$row_key]['count_certificates']++;
1643
                        }
1644
1645
                        foreach ($extra_fields as $extra) {
1646
                            if ('ruc' == $extra['1']) {
1647
                                continue;
1648
                            }
1649
1650
                            if (!isset($users[$row_key][$extra['1']])) {
1651
                                $user_data = UserManager::get_extra_user_data_by_field($user['user_id'], $extra['1']);
1652
                                if (!empty($user_data[$extra['1']])) {
1653
                                    $users[$row_key][$extra['1']] = $user_data[$extra['1']];
1654
                                }
1655
                            }
1656
                        }
1657
                    } else {
1658
                        $report_info['course'] = $user['title'].$sessionName;
1659
                        $report_info['user'] = api_get_person_name($user['firstname'], $user['lastname']);
1660
                        $report_info['email'] = $user['email'];
1661
                        $report_info['time'] = api_time_to_hms(
1662
                            Tracking::get_time_spent_on_the_course(
1663
                                $user['user_id'],
1664
                                empty($user['c_id']) ? $courseId : $user['c_id'],
1665
                                $sessionId
1666
                            )
1667
                        );
1668
1669
                        $category = Category:: load(
1670
                            null,
1671
                            null,
1672
                            $course_code,
1673
                            null,
1674
                            null,
1675
                            $sessionId
1676
                        );
1677
1678
                        $report_info['certificate'] = Display::label(get_lang('No'));
1679
                        if (isset($category[0]) && $category[0]->is_certificate_available($user['user_id'])) {
1680
                            $report_info['certificate'] = Display::label(get_lang('Yes'), 'success');
1681
                        }
1682
1683
                        $progress = intval(
1684
                            Tracking::get_avg_student_progress(
1685
                                $user['user_id'],
1686
                                $course_code,
1687
                                [],
1688
                                $sessionId
1689
                            )
1690
                        );
1691
1692
                        $report_info['progress_100'] = 100 == $progress ? Display::label(get_lang('Yes'), 'success') : Display::label(get_lang('No'));
1693
                        $report_info['progress'] = $progress."%";
1694
1695
                        foreach ($extra_fields as $extra) {
1696
                            $user_data = UserManager::get_extra_user_data_by_field($user['user_id'], $extra['1']);
1697
                            $report_info[$extra['1']] = $user_data[$extra['1']];
1698
                        }
1699
                        $report_info['user_id'] = $user['user_id'];
1700
                        $users[] = $report_info;
1701
                    }
1702
                } else {
1703
                    $users[$user['user_id']] = $user_info;
1704
                }
1705
            }
1706
        }
1707
1708
        return $users;
1709
    }
1710
1711
    /**
1712
     * @param bool  $resumed_report
1713
     * @param array $extra_field
1714
     * @param array $courseCodeList
1715
     * @param array $userIdList
1716
     * @param array $sessionIdList
1717
     * @param array $options
1718
     *
1719
     * @return array|int
1720
     */
1721
    public static function get_count_user_list_from_course_code(
1722
        $resumed_report = false,
1723
        $extra_field = [],
1724
        $courseCodeList = [],
1725
        $userIdList = [],
1726
        $sessionIdList = [],
1727
        $options = []
1728
    ) {
1729
        return self::get_user_list_from_course_code(
1730
            null,
1731
            0,
1732
            null,
1733
            null,
1734
            null,
1735
            true,
1736
            false,
1737
            $resumed_report,
1738
            $extra_field,
1739
            $courseCodeList,
1740
            $userIdList,
1741
            null,
1742
            $sessionIdList,
1743
            null,
1744
            $options
1745
        );
1746
    }
1747
1748
    /**
1749
     * Gets subscribed users in a course or in a course/session.
1750
     *
1751
     * @param string $course_code
1752
     * @param int    $session_id
1753
     *
1754
     * @return int
1755
     */
1756
    public static function get_users_count_in_course(
1757
        $course_code,
1758
        $session_id = 0,
1759
        $status = null
1760
    ) {
1761
        // variable initialisation
1762
        $session_id = (int) $session_id;
1763
        $course_code = Database::escape_string($course_code);
1764
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
1765
        $tblSessionCourseUser = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1766
        $tblCourseUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1767
        $tblUrlUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1768
1769
        $courseInfo = api_get_course_info($course_code);
1770
        $courseId = $courseInfo['real_id'];
1771
1772
        $sql = "
1773
            SELECT DISTINCT count(user.id) as count
1774
            FROM $tblUser as user
1775
        ";
1776
        $where = [];
1777
        if (!empty($session_id)) {
1778
            $sql .= "
1779
                LEFT JOIN $tblSessionCourseUser as session_course_user
1780
                ON user.id = session_course_user.user_id
1781
                    AND session_course_user.c_id = $courseId
1782
                    AND session_course_user.session_id = $session_id
1783
            ";
1784
1785
            $where[] = ' session_course_user.c_id IS NOT NULL ';
1786
        } else {
1787
            $sql .= "
1788
                LEFT JOIN $tblCourseUser as course_rel_user
1789
                    ON user.id = course_rel_user.user_id
1790
                    AND course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
1791
                    AND course_rel_user.c_id = $courseId
1792
            ";
1793
            $where[] = ' course_rel_user.c_id IS NOT NULL ';
1794
        }
1795
1796
        $multiple_access_url = api_get_multiple_access_url();
1797
        if ($multiple_access_url) {
1798
            $sql .= " LEFT JOIN $tblUrlUser au ON (au.user_id = user.id) ";
1799
        }
1800
1801
        $sql .= ' WHERE '.implode(' OR ', $where);
1802
1803
        if ($multiple_access_url) {
1804
            $current_access_url_id = api_get_current_access_url_id();
1805
            $sql .= " AND (access_url_id =  $current_access_url_id ) ";
1806
        }
1807
        $rs = Database::query($sql);
1808
        $count = 0;
1809
        if (Database::num_rows($rs)) {
1810
            $user = Database::fetch_array($rs);
1811
            $count = $user['count'];
1812
        }
1813
1814
        return $count;
1815
    }
1816
1817
    /**
1818
     * Get a list of coaches of a course and a session.
1819
     *
1820
     * @param string $course_code
1821
     * @param int    $session_id
1822
     * @param bool   $addGeneralCoach
1823
     *
1824
     * @return array List of users
1825
     */
1826
    public static function get_coach_list_from_course_code(
1827
        $course_code,
1828
        $session_id,
1829
        $addGeneralCoach = true
1830
    ) {
1831
        if (empty($course_code) || empty($session_id)) {
1832
            return [];
1833
        }
1834
1835
        $course_code = Database::escape_string($course_code);
1836
        $courseInfo = api_get_course_info($course_code);
1837
        $courseId = $courseInfo['real_id'];
1838
        $session_id = (int) $session_id;
1839
        $users = [];
1840
1841
        // We get the coach for the given course in a given session.
1842
        $sql = 'SELECT user_id FROM '.Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER).
1843
               " WHERE session_id = $session_id AND c_id = $courseId AND status = 2";
1844
        $rs = Database::query($sql);
1845
        while ($user = Database::fetch_array($rs)) {
1846
            $userInfo = api_get_user_info($user['user_id']);
1847
            if ($userInfo) {
1848
                $users[$user['user_id']] = $userInfo;
1849
            }
1850
        }
1851
1852
        if ($addGeneralCoach) {
1853
            $table = Database::get_main_table(TABLE_MAIN_SESSION);
1854
            // We get the session coach.
1855
            $sql = "SELECT id_coach FROM $table WHERE id = $session_id";
1856
            $rs = Database::query($sql);
1857
            $session_id_coach = Database::result($rs, 0, 'id_coach');
1858
            if (is_int($session_id_coach)) {
1859
                $userInfo = api_get_user_info($session_id_coach);
1860
                if ($userInfo) {
1861
                    $users[$session_id_coach] = $userInfo;
1862
                }
1863
            }
1864
        }
1865
1866
        return $users;
1867
    }
1868
1869
    /**
1870
     *  Return user info array of all users registered in a course
1871
     *  This only returns the users that are registered in this actual course, not linked courses.
1872
     *
1873
     * @param string $course_code
1874
     * @param bool   $with_session
1875
     * @param int    $sessionId
1876
     * @param string $date_from
1877
     * @param string $date_to
1878
     * @param bool   $includeInvitedUsers Whether include the invited users
1879
     * @param int    $groupId
1880
     * @param bool   $getCount
1881
     * @param int    $start
1882
     * @param int    $limit
1883
     *
1884
     * @return array with user id
1885
     */
1886
    public static function get_student_list_from_course_code(
1887
        $course_code,
1888
        $with_session = false,
1889
        $sessionId = 0,
1890
        $date_from = null,
1891
        $date_to = null,
1892
        $includeInvitedUsers = true,
1893
        $groupId = 0,
1894
        $getCount = false,
1895
        $start = 0,
1896
        $limit = 0
1897
    ) {
1898
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1899
        $sessionId = (int) $sessionId;
1900
        $courseInfo = api_get_course_info($course_code);
1901
        if (empty($courseInfo)) {
1902
            return [];
1903
        }
1904
        $courseId = $courseInfo['real_id'];
1905
        $students = [];
1906
1907
        $limitCondition = '';
1908
        if (isset($start) && isset($limit) && !empty($limit)) {
1909
            $start = (int) $start;
1910
            $limit = (int) $limit;
1911
            $limitCondition = " LIMIT $start, $limit";
1912
        }
1913
1914
        $select = '*';
1915
        if ($getCount) {
1916
            $select = 'count(u.id) as count';
1917
        }
1918
1919
        if (empty($sessionId)) {
1920
            if (empty($groupId)) {
1921
                // students directly subscribed to the course
1922
                $sql = "SELECT $select
1923
                        FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." cu
1924
                        INNER JOIN $userTable u
1925
                        ON cu.user_id = u.id
1926
                        WHERE c_id = $courseId AND cu.status = ".STUDENT;
1927
1928
                if (!$includeInvitedUsers) {
1929
                    $sql .= " AND u.status != ".INVITEE;
1930
                }
1931
                $sql .= $limitCondition;
1932
                $rs = Database::query($sql);
1933
1934
                if ($getCount) {
1935
                    $row = Database::fetch_array($rs);
1936
1937
                    return (int) $row['count'];
1938
                }
1939
1940
                while ($student = Database::fetch_array($rs)) {
1941
                    $students[$student['user_id']] = $student;
1942
                }
1943
            } else {
1944
                $students = GroupManager::get_users(
1945
                    $groupId,
1946
                    false,
1947
                    $start,
1948
                    $limit,
1949
                    $getCount,
1950
                    $courseInfo['real_id']
1951
                );
1952
                $students = array_flip($students);
1953
            }
1954
        }
1955
1956
        // students subscribed to the course through a session
1957
        if ($with_session) {
1958
            $joinSession = '';
1959
            //Session creation date
1960
            if (!empty($date_from) && !empty($date_to)) {
1961
                $joinSession = "INNER JOIN ".Database::get_main_table(TABLE_MAIN_SESSION)." s";
1962
            }
1963
1964
            $sql = "SELECT $select
1965
                      FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)." scu
1966
                      $joinSession
1967
                      INNER JOIN $userTable u
1968
                      ON scu.user_id = u.id
1969
                      WHERE scu.c_id = $courseId AND scu.status <> 2";
1970
1971
            if (!empty($date_from) && !empty($date_to)) {
1972
                $date_from = Database::escape_string($date_from);
1973
                $date_to = Database::escape_string($date_to);
1974
                $sql .= " AND s.access_start_date >= '$date_from' AND s.access_end_date <= '$date_to'";
1975
            }
1976
1977
            if (0 != $sessionId) {
1978
                $sql .= " AND scu.session_id = $sessionId";
1979
            }
1980
1981
            if (!$includeInvitedUsers) {
1982
                $sql .= " AND u.status != ".INVITEE;
1983
            }
1984
            $sql .= $limitCondition;
1985
1986
            $rs = Database::query($sql);
1987
1988
            if ($getCount) {
1989
                $row = Database::fetch_array($rs);
1990
1991
                return (int) $row['count'];
1992
            }
1993
1994
            while ($student = Database::fetch_array($rs)) {
1995
                $students[$student['user_id']] = $student;
1996
            }
1997
        }
1998
1999
        return $students;
2000
    }
2001
2002
    /**
2003
     * Return user info array of all teacher-users registered in a course
2004
     * This only returns the users that are registered in this actual course, not linked courses.
2005
     *
2006
     * @param string $course_code
2007
     *
2008
     * @return array with user id
2009
     */
2010
    public static function get_teacher_list_from_course_code($course_code)
2011
    {
2012
        $courseInfo = api_get_course_info($course_code);
2013
        $courseId = $courseInfo['real_id'];
2014
        if (empty($courseId)) {
2015
            return false;
2016
        }
2017
2018
        $sql = "SELECT DISTINCT
2019
                    u.id as user_id,
2020
                    u.lastname,
2021
                    u.firstname,
2022
                    u.email,
2023
                    u.username,
2024
                    u.status
2025
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." cu
2026
                INNER JOIN ".Database::get_main_table(TABLE_MAIN_USER)." u
2027
                ON (cu.user_id = u.id)
2028
                WHERE
2029
                    cu.c_id = $courseId AND
2030
                    cu.status = 1 ";
2031
        $rs = Database::query($sql);
2032
        $teachers = [];
2033
        while ($teacher = Database::fetch_array($rs)) {
2034
            $teachers[$teacher['user_id']] = $teacher;
2035
        }
2036
2037
        return $teachers;
2038
    }
2039
2040
    /**
2041
     * Return user info array of all teacher-users registered in a course
2042
     * This only returns the users that are registered in this actual course, not linked courses.
2043
     *
2044
     * @param int  $courseId
2045
     * @param bool $loadAvatars
2046
     *
2047
     * @return array with user id
2048
     */
2049
    public static function getTeachersFromCourse($courseId, $loadAvatars = true)
2050
    {
2051
        $courseId = (int) $courseId;
2052
2053
        if (empty($courseId)) {
2054
            return false;
2055
        }
2056
2057
        $sql = "SELECT DISTINCT
2058
                    u.id as user_id,
2059
                    u.lastname,
2060
                    u.firstname,
2061
                    u.email,
2062
                    u.username,
2063
                    u.status
2064
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." cu
2065
                INNER JOIN ".Database::get_main_table(TABLE_MAIN_USER)." u
2066
                ON (cu.user_id = u.id)
2067
                WHERE
2068
                    cu.c_id = $courseId AND
2069
                    cu.status = 1 ";
2070
        $rs = Database::query($sql);
2071
        $listTeachers = [];
2072
        $teachers = [];
2073
        $url = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&course_id='.$courseId;
2074
        while ($teacher = Database::fetch_array($rs)) {
2075
            $teachers['id'] = $teacher['user_id'];
2076
            $teachers['lastname'] = $teacher['lastname'];
2077
            $teachers['firstname'] = $teacher['firstname'];
2078
            $teachers['email'] = $teacher['email'];
2079
            $teachers['username'] = $teacher['username'];
2080
            $teachers['status'] = $teacher['status'];
2081
            $teachers['fullname'] = api_get_person_name($teacher['firstname'], $teacher['lastname']);
2082
            $teachers['avatar'] = '';
2083
            /*if ($loadAvatars) {
2084
                $userPicture = UserManager::getUserPicture($teacher['user_id'], USER_IMAGE_SIZE_SMALL);
2085
                $teachers['avatar'] = $userPicture;
2086
            }*/
2087
            $teachers['url'] = $url.'&user_id='.$teacher['user_id'];
2088
            $listTeachers[] = $teachers;
2089
        }
2090
2091
        return $listTeachers;
2092
    }
2093
2094
    /**
2095
     * Returns a string list of teachers assigned to the given course.
2096
     *
2097
     * @param string $course_code
2098
     * @param string $separator           between teachers names
2099
     * @param bool   $add_link_to_profile Whether to add a link to the teacher's profile
2100
     * @param bool   $orderList
2101
     *
2102
     * @return string List of teachers teaching the course
2103
     */
2104
    public static function getTeacherListFromCourseCodeToString(
2105
        $course_code,
2106
        $separator = self::USER_SEPARATOR,
2107
        $add_link_to_profile = false,
2108
        $orderList = false
2109
    ) {
2110
        $teacher_list = self::get_teacher_list_from_course_code($course_code);
2111
        $html = '';
2112
        $list = [];
2113
        if (!empty($teacher_list)) {
2114
            foreach ($teacher_list as $teacher) {
2115
                $teacher_name = api_get_person_name(
2116
                    $teacher['firstname'],
2117
                    $teacher['lastname']
2118
                );
2119
                if ($add_link_to_profile) {
2120
                    $url = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$teacher['user_id'];
2121
                    $teacher_name = Display::url(
2122
                        $teacher_name,
2123
                        $url,
2124
                        [
2125
                            'class' => 'ajax',
2126
                            'data-title' => $teacher_name,
2127
                        ]
2128
                    );
2129
                }
2130
                $list[] = $teacher_name;
2131
            }
2132
2133
            if (!empty($list)) {
2134
                if (true === $orderList) {
2135
                    $html .= '<ul class="user-teacher">';
2136
                    foreach ($list as $teacher) {
2137
                        $html .= '<li>';
2138
                        $html .= Display::return_icon('teacher.png', '', null, ICON_SIZE_TINY);
2139
                        $html .= ' '.$teacher;
2140
                        $html .= '</li>';
2141
                    }
2142
                    $html .= '</ul>';
2143
                } else {
2144
                    $html .= array_to_string($list, $separator);
2145
                }
2146
            }
2147
        }
2148
2149
        return $html;
2150
    }
2151
2152
    /**
2153
     * This function returns information about coachs from a course in session.
2154
     *
2155
     * @param int $session_id
2156
     * @param int $courseId
2157
     *
2158
     * @return array containing user_id, lastname, firstname, username
2159
     */
2160
    public static function get_coachs_from_course($session_id = 0, $courseId = 0)
2161
    {
2162
        if (!empty($session_id)) {
2163
            $session_id = intval($session_id);
2164
        } else {
2165
            $session_id = api_get_session_id();
2166
        }
2167
2168
        if (!empty($courseId)) {
2169
            $courseId = intval($courseId);
2170
        } else {
2171
            $courseId = api_get_course_int_id();
2172
        }
2173
2174
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2175
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2176
2177
        $sql = "SELECT DISTINCT
2178
                    u.id as user_id,
2179
                    u.lastname,
2180
                    u.firstname,
2181
                    u.username
2182
                FROM $tbl_user u
2183
                INNER JOIN $tbl_session_course_user scu
2184
                ON (u.id = scu.user_id)
2185
                WHERE
2186
                    scu.session_id = $session_id AND
2187
                    scu.c_id = $courseId AND
2188
                    scu.status = 2";
2189
        $rs = Database::query($sql);
2190
2191
        $coaches = [];
2192
        if (Database::num_rows($rs) > 0) {
2193
            while ($row = Database::fetch_array($rs)) {
2194
                $completeName = api_get_person_name($row['firstname'], $row['lastname']);
2195
                $coaches[] = $row + ['full_name' => $completeName];
2196
            }
2197
        }
2198
2199
        return $coaches;
2200
    }
2201
2202
    /**
2203
     * @param int    $session_id
2204
     * @param int    $courseId
2205
     * @param string $separator
2206
     * @param bool   $add_link_to_profile
2207
     * @param bool   $orderList
2208
     *
2209
     * @return string
2210
     */
2211
    public static function get_coachs_from_course_to_string(
2212
        $session_id = 0,
2213
        $courseId = 0,
2214
        $separator = self::USER_SEPARATOR,
2215
        $add_link_to_profile = false,
2216
        $orderList = false
2217
    ) {
2218
        $coachList = self::get_coachs_from_course($session_id, $courseId);
2219
        $course_coachs = [];
2220
        if (!empty($coachList)) {
2221
            foreach ($coachList as $coach_course) {
2222
                $coach_name = $coach_course['full_name'];
2223
                if ($add_link_to_profile) {
2224
                    $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;
2225
                    $coach_name = Display::url(
2226
                        $coach_name,
2227
                        $url,
2228
                        [
2229
                            'class' => 'ajax',
2230
                            'data-title' => $coach_name,
2231
                        ]
2232
                    );
2233
                }
2234
                $course_coachs[] = $coach_name;
2235
            }
2236
        }
2237
2238
        $html = '';
2239
        if (!empty($course_coachs)) {
2240
            if (true === $orderList) {
2241
                $html .= '<ul class="user-coachs">';
2242
                foreach ($course_coachs as $coachs) {
2243
                    $html .= Display::tag(
2244
                        'li',
2245
                        Display::return_icon(
2246
                            'teacher.png',
2247
                            get_lang('Coach'),
2248
                            null,
2249
                            ICON_SIZE_TINY
2250
                        ).' '.$coachs
2251
                    );
2252
                }
2253
                $html .= '</ul>';
2254
            } else {
2255
                $html = array_to_string($course_coachs, $separator);
2256
            }
2257
        }
2258
2259
        return $html;
2260
    }
2261
2262
    /**
2263
     * Get the list of groups from the course.
2264
     *
2265
     * @param string $course_code
2266
     * @param int    $session_id     Session ID (optional)
2267
     * @param int    $getEmptyGroups get empty groups (optional)
2268
     *
2269
     * @return CGroup[]
2270
     */
2271
    public static function get_group_list_of_course(
2272
        $course_code,
2273
        $session_id = 0,
2274
        $getEmptyGroups = 0
2275
    ) {
2276
        $course_info = api_get_course_info($course_code);
2277
2278
        if (empty($course_info)) {
2279
            return [];
2280
        }
2281
        $course_id = $course_info['real_id'];
2282
2283
        if (empty($course_id)) {
2284
            return [];
2285
        }
2286
2287
        $repo = Container::getGroupRepository();
2288
2289
        $course = api_get_course_entity($course_info['real_id']);
2290
        $session = api_get_session_entity($session_id);
2291
        $qb = $repo->getResourcesByCourse($course, $session);
2292
        $groups = $qb->getQuery()->getResult();
2293
        $groupList = [];
2294
        /** @var CGroup $group */
2295
        foreach ($groups as $group) {
2296
            if (0 === $getEmptyGroups) {
2297
                if (!$group->hasMembers()) {
2298
                    continue;
2299
                }
2300
            }
2301
            $groupList[$group->getIid()] = $group;
2302
        }
2303
2304
        /* 0 != $session_id ? $session_condition = ' WHERE g.session_id IN(1,'.intval($session_id).')' : $session_condition = ' WHERE g.session_id = 0';
2305
         if (0 == $in_get_empty_group) {
2306
             // get only groups that are not empty
2307
             $sql = "SELECT DISTINCT g.iid, g.name
2308
                     FROM ".Database::get_course_table(TABLE_GROUP)." AS g
2309
                     INNER JOIN ".Database::get_course_table(TABLE_GROUP_USER)." gu
2310
                     ON (g.iid = gu.group_id)
2311
                     $session_condition
2312
                     ORDER BY g.name";
2313
         } else {
2314
             // get all groups even if they are empty
2315
             $sql = "SELECT g.iid, g.name
2316
                     FROM ".Database::get_course_table(TABLE_GROUP)." AS g
2317
                     $session_condition
2318
                     AND c_id = $course_id";
2319
         }
2320
2321
         $result = Database::query($sql);
2322
         $groupList = [];
2323
         while ($groupData = Database::fetch_array($result)) {
2324
             $groupData['userNb'] = GroupManager::number_of_students($groupData['iid'], $course_id);
2325
             $groupList[$groupData['iid']] = $groupData;
2326
         }*/
2327
2328
        return $groupList;
2329
    }
2330
2331
    /**
2332
     * Delete a course
2333
     * This function deletes a whole course-area from the platform. When the
2334
     * given course is a virtual course, the database and directory will not be
2335
     * deleted.
2336
     * When the given course is a real course, also all virtual courses refering
2337
     * to the given course will be deleted.
2338
     * Considering the fact that we remove all traces of the course in the main
2339
     * database, it makes sense to remove all tracking as well (if stats databases exist)
2340
     * so that a new course created with this code would not use the remains of an older
2341
     * course.
2342
     *
2343
     * @param string $code The code of the course to delete
2344
     */
2345
    public static function delete_course($code)
2346
    {
2347
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2348
        $table_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
2349
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2350
        $table_course_survey = Database::get_main_table(TABLE_MAIN_SHARED_SURVEY);
2351
        $table_course_survey_question = Database::get_main_table(TABLE_MAIN_SHARED_SURVEY_QUESTION);
2352
        $table_course_survey_question_option = Database::get_main_table(TABLE_MAIN_SHARED_SURVEY_QUESTION_OPTION);
2353
2354
        $table_stats_hotpots = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTPOTATOES);
2355
        $table_stats_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2356
        $table_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2357
        $table_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
2358
        $table_stats_lastaccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
2359
        $table_stats_course_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2360
        $table_stats_online = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
2361
        $table_stats_downloads = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
2362
        $table_stats_links = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
2363
        $table_stats_uploads = Database::get_main_table(TABLE_STATISTIC_TRACK_E_UPLOADS);
2364
2365
        if (empty($code)) {
2366
            return false;
2367
        }
2368
2369
        $course = api_get_course_info($code);
2370
2371
        if (empty($course)) {
2372
            return false;
2373
        }
2374
2375
        $codeFiltered = $course['code'];
2376
        $courseId = $course['real_id'];
2377
        $courseEntity = api_get_course_entity($courseId);
2378
2379
        /** @var SequenceResourceRepository $repo */
2380
        $repo = Database::getManager()->getRepository(SequenceResource::class);
2381
        $sequenceResource = $repo->findRequirementForResource(
2382
            $courseId,
2383
            SequenceResource::COURSE_TYPE
2384
        );
2385
2386
        if ($sequenceResource) {
2387
            Display::addFlash(
2388
                Display::return_message(
2389
                    get_lang('ThereIsASequenceResourceLinkedToThisCourseYouNeedToDeleteItFirst'),
2390
                    'error'
2391
                )
2392
            );
2393
2394
            return false;
2395
        }
2396
2397
        $count = 0;
2398
        if (api_is_multiple_url_enabled()) {
2399
            $url_id = 1;
2400
            if (-1 != api_get_current_access_url_id()) {
2401
                $url_id = api_get_current_access_url_id();
2402
            }
2403
            UrlManager::delete_url_rel_course($courseId, $url_id);
2404
            $count = UrlManager::getCountUrlRelCourse($courseId);
2405
        }
2406
2407
        if (0 === $count) {
2408
            self::create_database_dump($code);
2409
2410
            // Cleaning group categories
2411
            $groupCategories = GroupManager::get_categories($courseEntity);
2412
            if (!empty($groupCategories)) {
2413
                foreach ($groupCategories as $category) {
2414
                    GroupManager::delete_category($category['iid'], $course['code']);
2415
                }
2416
            }
2417
2418
            // Cleaning groups
2419
            // @todo should be cleaned by the resource.
2420
            /*$groups = GroupManager::get_groups($courseId);
2421
            if (!empty($groups)) {
2422
                foreach ($groups as $group) {
2423
                    GroupManager::deleteGroup($group, $course['code']);
2424
                }
2425
            }*/
2426
2427
            $course_tables = AddCourse::get_course_tables();
2428
            // Cleaning c_x tables
2429
            if (!empty($courseId)) {
2430
                foreach ($course_tables as $table) {
2431
                    if ('document' === $table) {
2432
                        // Table document will be deleted by Doctrine.
2433
                        continue;
2434
                    }
2435
                    $table = Database::get_course_table($table);
2436
                    //$sql = "DELETE FROM $table WHERE c_id = $courseId ";
2437
                    //Database::query($sql);
2438
                }
2439
            }
2440
2441
            // Unsubscribe all users from the course
2442
            $sql = "DELETE FROM $table_course_user WHERE c_id = $courseId";
2443
            Database::query($sql);
2444
            // Delete the course from the sessions tables
2445
            $sql = "DELETE FROM $table_session_course WHERE c_id = $courseId";
2446
            Database::query($sql);
2447
            $sql = "DELETE FROM $table_session_course_user WHERE c_id = $courseId";
2448
            Database::query($sql);
2449
2450
            // Delete from Course - URL
2451
            // Already deleted because of entities.
2452
            //$sql = "DELETE FROM $table_course_rel_url WHERE c_id = $courseId";
2453
            //Database::query($sql);
2454
2455
            $sql = "SELECT survey_id FROM $table_course_survey WHERE course_code = '$codeFiltered'";
2456
            $result_surveys = Database::query($sql);
2457
            while ($surveys = Database::fetch_array($result_surveys)) {
2458
                $survey_id = $surveys[0]; //int
2459
                $sql = "DELETE FROM $table_course_survey_question WHERE survey_id = $survey_id";
2460
                Database::query($sql);
2461
                $sql = "DELETE FROM $table_course_survey_question_option WHERE survey_id = $survey_id";
2462
                Database::query($sql);
2463
                $sql = "DELETE FROM $table_course_survey WHERE survey_id = $survey_id";
2464
                Database::query($sql);
2465
            }
2466
2467
            // Delete the course from the stats tables
2468
            $sql = "DELETE FROM $table_stats_hotpots WHERE c_id = $courseId";
2469
            Database::query($sql);
2470
            $sql = "DELETE FROM $table_stats_attempt WHERE c_id = $courseId";
2471
            Database::query($sql);
2472
            $sql = "DELETE FROM $table_stats_exercises WHERE c_id = $courseId";
2473
            Database::query($sql);
2474
            $sql = "DELETE FROM $table_stats_access WHERE c_id = $courseId";
2475
            Database::query($sql);
2476
            $sql = "DELETE FROM $table_stats_lastaccess WHERE c_id = $courseId";
2477
            Database::query($sql);
2478
            $sql = "DELETE FROM $table_stats_course_access WHERE c_id = $courseId";
2479
            Database::query($sql);
2480
            $sql = "DELETE FROM $table_stats_online WHERE c_id = $courseId";
2481
            Database::query($sql);
2482
            // Do not delete rows from track_e_default as these include course
2483
            // creation and other important things that do not take much space
2484
            // but give information on the course history
2485
            //$sql = "DELETE FROM $table_stats_default WHERE c_id = $courseId";
2486
            //Database::query($sql);
2487
            $sql = "DELETE FROM $table_stats_downloads WHERE c_id = $courseId";
2488
            Database::query($sql);
2489
            $sql = "DELETE FROM $table_stats_links WHERE c_id = $courseId";
2490
            Database::query($sql);
2491
            $sql = "DELETE FROM $table_stats_uploads WHERE c_id = $courseId";
2492
            Database::query($sql);
2493
2494
            // Update ticket
2495
            $table = Database::get_main_table(TABLE_TICKET_TICKET);
2496
            $sql = "UPDATE $table SET course_id = NULL WHERE course_id = $courseId";
2497
            Database::query($sql);
2498
2499
            $repo->deleteSequenceResource(
2500
                $courseId,
2501
                SequenceResource::COURSE_TYPE
2502
            );
2503
2504
            // Class
2505
            $table = Database::get_main_table(TABLE_USERGROUP_REL_COURSE);
2506
            $sql = "DELETE FROM $table
2507
                    WHERE course_id = $courseId";
2508
            Database::query($sql);
2509
2510
            // Skills
2511
            $table = Database::get_main_table(TABLE_MAIN_SKILL_REL_USER);
2512
            $argumentation = Database::escape_string(sprintf(get_lang('This skill was obtained through course %s which has been removed since then.'), $course['code']));
2513
            $sql = "UPDATE $table SET course_id = NULL, session_id = NULL, argumentation = '$argumentation'
2514
                    WHERE course_id = $courseId";
2515
            Database::query($sql);
2516
2517
            $sql = "DELETE FROM skill_rel_course WHERE c_id = $courseId";
2518
            Database::query($sql);
2519
2520
            // Deletes all groups, group-users, group-tutors information
2521
            // To prevent fK mix up on some tables
2522
            //GroupManager::deleteAllGroupsFromCourse($courseId);
2523
2524
            $appPlugin = new AppPlugin();
2525
            $appPlugin->performActionsWhenDeletingItem('course', $courseId);
2526
2527
            $repo = Container::getQuizRepository();
2528
            $repo->deleteAllByCourse($courseEntity);
2529
2530
            // Delete the course from the database
2531
            $repo = Container::getCourseRepository();
2532
            $repo->deleteCourse($courseEntity);
2533
2534
            // delete extra course fields
2535
            $extraFieldValues = new ExtraFieldValue('course');
2536
            $extraFieldValues->deleteValuesByItem($courseId);
2537
2538
            // Add event to system log
2539
            Event::addEvent(
2540
                LOG_COURSE_DELETE,
2541
                LOG_COURSE_CODE,
2542
                $code,
2543
                api_get_utc_datetime(),
2544
                api_get_user_id(),
2545
                $courseId
2546
            );
2547
2548
            return true;
2549
        }
2550
    }
2551
2552
    /**
2553
     * Creates a file called mysql_dump.sql in the course folder.
2554
     *
2555
     * @param string $course_code The code of the course
2556
     *
2557
     * @todo Implementation for single database
2558
     */
2559
    public static function create_database_dump($course_code)
2560
    {
2561
        return false;
2562
        $sql_dump = '';
0 ignored issues
show
Unused Code introduced by
$sql_dump = '' is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
2563
        $course_code = Database::escape_string($course_code);
2564
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
2565
        $sql = "SELECT * FROM $table_course WHERE code = '$course_code'";
2566
        $res = Database::query($sql);
2567
        $course = Database::fetch_array($res);
2568
2569
        $course_tables = AddCourse::get_course_tables();
2570
2571
        if (!empty($course['id'])) {
2572
            //Cleaning c_x tables
2573
            foreach ($course_tables as $table) {
2574
                $table = Database::get_course_table($table);
2575
                $sql = "SELECT * FROM $table WHERE c_id = {$course['id']} ";
2576
                $res_table = Database::query($sql);
2577
2578
                while ($row = Database::fetch_array($res_table, 'ASSOC')) {
2579
                    $row_to_save = [];
2580
                    foreach ($row as $key => $value) {
2581
                        $row_to_save[$key] = $key."='".Database::escape_string($row[$key])."'";
2582
                    }
2583
                    $sql_dump .= "\nINSERT INTO $table SET ".implode(', ', $row_to_save).';';
2584
                }
2585
            }
2586
        }
2587
2588
        if (is_dir(api_get_path(SYS_COURSE_PATH).$course['directory'])) {
2589
            $file_name = api_get_path(SYS_COURSE_PATH).$course['directory'].'/mysql_dump.sql';
2590
            $handle = fopen($file_name, 'a+');
2591
            if (false !== $handle) {
2592
                fwrite($handle, $sql_dump);
2593
                fclose($handle);
2594
            } else {
2595
                //TODO trigger exception in a try-catch
2596
            }
2597
        }
2598
    }
2599
2600
    /**
2601
     * Sort courses for a specific user ??
2602
     *
2603
     * @param int    $user_id     User ID
2604
     * @param string $course_code Course code
2605
     *
2606
     * @return int Minimum course order
2607
     *
2608
     * @todo Review documentation
2609
     */
2610
    public static function userCourseSort($user_id, $course_code)
2611
    {
2612
        $user_id = (int) $user_id;
2613
2614
        if (empty($user_id) || empty($course_code)) {
2615
            return 0;
2616
        }
2617
2618
        $course_code = Database::escape_string($course_code);
2619
        $TABLECOURSE = Database::get_main_table(TABLE_MAIN_COURSE);
2620
        $TABLECOURSUSER = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2621
2622
        $course_title = Database::result(
2623
            Database::query(
2624
                "SELECT title FROM $TABLECOURSE WHERE code = '$course_code'"
2625
            ),
2626
            0,
2627
            0
2628
        );
2629
        if (false === $course_title) {
2630
            $course_title = '';
2631
        }
2632
2633
        $sql = "SELECT course.code as code, course.title as title, cu.sort as sort
2634
                FROM $TABLECOURSUSER as cu, $TABLECOURSE as course
2635
                WHERE   course.id = cu.c_id AND user_id = $user_id AND
2636
                        cu.relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
2637
                        user_course_cat = 0
2638
                ORDER BY cu.sort";
2639
        $result = Database::query($sql);
2640
2641
        $course_title_precedent = '';
2642
        $counter = 0;
2643
        $course_found = false;
2644
        $course_sort = 1;
2645
2646
        if (Database::num_rows($result) > 0) {
2647
            while ($courses = Database::fetch_array($result)) {
2648
                if ('' == $course_title_precedent) {
2649
                    $course_title_precedent = $courses['title'];
2650
                }
2651
                if (api_strcasecmp($course_title_precedent, $course_title) < 0) {
2652
                    $course_found = true;
2653
                    if (!empty($courses['sort'])) {
2654
                        $course_sort = $courses['sort'];
2655
                    }
2656
                    if (0 == $counter) {
2657
                        $sql = "UPDATE $TABLECOURSUSER
2658
                                SET sort = sort+1
2659
                                WHERE
2660
                                    user_id= $user_id AND
2661
                                    relation_type <> ".COURSE_RELATION_TYPE_RRHH."
2662
                                    AND user_course_cat = 0
2663
                                    AND sort > $course_sort";
2664
                        $course_sort++;
2665
                    } else {
2666
                        $sql = "UPDATE $TABLECOURSUSER SET sort = sort+1
2667
                                WHERE
2668
                                    user_id= $user_id AND
2669
                                    relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
2670
                                    user_course_cat = 0 AND
2671
                                    sort >= $course_sort";
2672
                    }
2673
                    Database::query($sql);
2674
                    break;
2675
                } else {
2676
                    $course_title_precedent = $courses['title'];
2677
                }
2678
                $counter++;
2679
            }
2680
2681
            // We must register the course in the beginning of the list
2682
            if (!$course_found) {
2683
                $course_sort = Database::result(
2684
                    Database::query(
2685
                        'SELECT min(sort) as min_sort FROM '.$TABLECOURSUSER.' WHERE user_id = "'.$user_id.'" AND user_course_cat="0"'
2686
                    ),
2687
                    0,
2688
                    0
2689
                );
2690
                Database::query("UPDATE $TABLECOURSUSER SET sort = sort+1 WHERE user_id = $user_id AND user_course_cat = 0");
2691
            }
2692
        }
2693
2694
        return (int) $course_sort;
2695
    }
2696
2697
    /**
2698
     * check if course exists.
2699
     *
2700
     * @param string $courseCode
2701
     *
2702
     * @return int if exists, false else
2703
     */
2704
    public static function course_exists($courseCode)
2705
    {
2706
        $courseCode = Database::escape_string($courseCode);
2707
        $sql = "SELECT 1 FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
2708
                WHERE code = '$courseCode'";
2709
2710
        return Database::num_rows(Database::query($sql));
2711
    }
2712
2713
    /**
2714
     * Send an email to tutor after the auth-suscription of a student in your course.
2715
     *
2716
     * @author Carlos Vargas <[email protected]>, Dokeos Latino
2717
     *
2718
     * @param int    $user_id            the id of the user
2719
     * @param string $courseId           the course code
2720
     * @param bool   $send_to_tutor_also
2721
     *
2722
     * @return false|null we return the message that is displayed when the action is successful
2723
     */
2724
    public static function email_to_tutor($user_id, $courseId, $send_to_tutor_also = false)
2725
    {
2726
        $user_id = (int) $user_id;
2727
        $courseId = (int) $courseId;
2728
        $information = api_get_course_info_by_id($courseId);
2729
        $course_code = $information['code'];
2730
        $student = api_get_user_info($user_id);
2731
2732
        $name_course = $information['title'];
2733
        $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
2734
                WHERE c_id = $courseId";
2735
2736
        // TODO: Ivan: This is a mistake, please, have a look at it. Intention here is diffcult to be guessed.
2737
        //if ($send_to_tutor_also = true)
2738
        // Proposed change:
2739
        if ($send_to_tutor_also) {
2740
            $sql .= ' AND is_tutor = 1';
2741
        } else {
2742
            $sql .= ' AND status = 1';
2743
        }
2744
2745
        $result = Database::query($sql);
2746
        while ($row = Database::fetch_array($result)) {
2747
            $tutor = api_get_user_info($row['user_id']);
2748
            $emailto = $tutor['email'];
2749
            $emailsubject = get_lang('New user in the course').': '.$name_course;
2750
            $emailbody = get_lang('Dear').': '.api_get_person_name($tutor['firstname'], $tutor['lastname'])."\n";
2751
            $emailbody .= get_lang('MessageNew user in the course').': '.$name_course."\n";
2752
            $emailbody .= get_lang('Username').': '.$student['username']."\n";
2753
            if (api_is_western_name_order()) {
2754
                $emailbody .= get_lang('First name').': '.$student['firstname']."\n";
2755
                $emailbody .= get_lang('Last name').': '.$student['lastname']."\n";
2756
            } else {
2757
                $emailbody .= get_lang('Last name').': '.$student['lastname']."\n";
2758
                $emailbody .= get_lang('First name').': '.$student['firstname']."\n";
2759
            }
2760
            $emailbody .= get_lang('e-mail').': <a href="mailto:'.$student['email'].'">'.$student['email']."</a>\n\n";
2761
            $recipient_name = api_get_person_name(
2762
                $tutor['firstname'],
2763
                $tutor['lastname'],
2764
                null,
2765
                PERSON_NAME_EMAIL_ADDRESS
2766
            );
2767
            $sender_name = api_get_person_name(
2768
                api_get_setting('administratorName'),
2769
                api_get_setting('administratorSurname'),
2770
                null,
2771
                PERSON_NAME_EMAIL_ADDRESS
2772
            );
2773
            $email_admin = api_get_setting('emailAdministrator');
2774
2775
            $additionalParameters = [
2776
                'smsType' => SmsPlugin::NEW_USER_SUBSCRIBED_COURSE,
2777
                'userId' => $tutor['user_id'],
2778
                'userUsername' => $student['username'],
2779
                'courseCode' => $course_code,
2780
            ];
2781
            api_mail_html(
2782
                $recipient_name,
2783
                $emailto,
2784
                $emailsubject,
2785
                $emailbody,
2786
                $sender_name,
2787
                $email_admin,
2788
                null,
2789
                null,
2790
                null,
2791
                $additionalParameters
2792
            );
2793
        }
2794
    }
2795
2796
    /**
2797
     * @return array
2798
     */
2799
    public static function get_special_course_list()
2800
    {
2801
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
2802
        $tbl_course_field = Database::get_main_table(TABLE_EXTRA_FIELD);
2803
        $tbl_course_field_value = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2804
        $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2805
2806
        $extraFieldType = EntityExtraField::COURSE_FIELD_TYPE;
2807
2808
        $courseList = [];
2809
        // get course list auto-register
2810
2811
        $sql = "SELECT id FROM $tbl_course_field
2812
                WHERE extra_field_type = $extraFieldType AND
2813
                variable = 'special_course'";
2814
        $result = Database::query($sql);
2815
        $courseList = [];
2816
2817
        if (Database::num_rows($result) > 0) {
2818
            $row = Database::fetch_assoc($result);
2819
            // Get list of special courses (appear to all)
2820
            // Note: The value is better indexed as string, so
2821
            // using '1' instead of integer is more efficient
2822
            $sql = "SELECT DISTINCT(item_id) as cid
2823
                FROM $tbl_course_field_value
2824
                WHERE field_id = ".$row['id']." AND value = '1'";
2825
            $result = Database::query($sql);
2826
            while ($row = Database::fetch_assoc($result)) {
2827
                $courseList[] = $row['cid'];
2828
            }
2829
            if (count($courseList) < 1) {
2830
                return $courseList;
2831
            }
2832
            if (api_get_multiple_access_url()) {
2833
                //we filter the courses by the active URL
2834
                $coursesSelect = '';
2835
                if (1 == count($courseList)) {
2836
                    $coursesSelect = $courseList[0];
2837
                } else {
2838
                    $coursesSelect = implode(',', $courseList);
2839
                }
2840
                $access_url_id = api_get_current_access_url_id();
2841
                if (-1 != $access_url_id) {
2842
                    $sql = "SELECT c_id FROM $tbl_url_course
2843
                            WHERE access_url_id = $access_url_id
2844
                            AND c_id IN ($coursesSelect)";
2845
                    $result = Database::query($sql);
2846
                    while ($row = Database::fetch_assoc($result)) {
2847
                        $courseList[] = $row['c_id'];
2848
                    }
2849
                }
2850
            }
2851
        }
2852
2853
        return $courseList;
2854
    }
2855
2856
    /**
2857
     * Get the course codes that have been restricted in the catalogue, and if byUserId is set
2858
     * then the courses that the user is allowed or not to see in catalogue.
2859
     *
2860
     * @param bool $allowed  Either if the courses have some users that are or are not allowed to see in catalogue
2861
     * @param int  $byUserId if the courses are or are not allowed to see to the user
2862
     *
2863
     * @return array Course codes allowed or not to see in catalogue by some user or the user
2864
     */
2865
    public static function getCatalogCourseList($allowed = true, $byUserId = -1)
2866
    {
2867
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
2868
        $tblCourseRelUserCatalogue = Database::get_main_table(TABLE_MAIN_COURSE_CATALOGUE_USER);
2869
        $visibility = $allowed ? 1 : 0;
2870
2871
        // Restriction by user id
2872
        $currentUserRestriction = '';
2873
2874
        $byUserId = (int) $byUserId;
2875
        if ($byUserId > 0) {
2876
            $currentUserRestriction = " AND tcruc.user_id = $byUserId ";
2877
        }
2878
2879
        //we filter the courses from the URL
2880
        $joinAccessUrl = '';
2881
        $whereAccessUrl = '';
2882
        if (api_get_multiple_access_url()) {
2883
            $accessUrlId = api_get_current_access_url_id();
2884
            if (-1 != $accessUrlId) {
2885
                $tblUrlCourse = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2886
                $joinAccessUrl = "LEFT JOIN $tblUrlCourse url_rel_course
2887
                                  ON url_rel_course.c_id = c.id ";
2888
                $whereAccessUrl = " AND access_url_id = $accessUrlId ";
2889
            }
2890
        }
2891
2892
        // get course list auto-register
2893
        $sql = "SELECT DISTINCT(c.code)
2894
                FROM $tblCourseRelUserCatalogue tcruc
2895
                INNER JOIN $courseTable c
2896
                ON (c.id = tcruc.c_id) $joinAccessUrl
2897
                WHERE tcruc.visible = $visibility $currentUserRestriction $whereAccessUrl";
2898
2899
        $result = Database::query($sql);
2900
        $courseList = [];
2901
2902
        if (Database::num_rows($result) > 0) {
2903
            while ($resultRow = Database::fetch_array($result)) {
2904
                $courseList[] = $resultRow['code'];
2905
            }
2906
        }
2907
2908
        return $courseList;
2909
    }
2910
2911
    /**
2912
     * Get list of courses for a given user.
2913
     *
2914
     * @param int   $user_id
2915
     * @param bool  $include_sessions                   Whether to include courses from session or not
2916
     * @param bool  $adminGetsAllCourses                If the user is platform admin,
2917
     *                                                  whether he gets all the courses or just his. Note: This does
2918
     *                                                  *not* include all sessions
2919
     * @param bool  $loadSpecialCourses
2920
     * @param array $skipCourseList                     List of course ids to skip
2921
     * @param bool  $useUserLanguageFilterIfAvailable
2922
     * @param bool  $showCoursesSessionWithDifferentKey
2923
     *
2924
     * @return array List of codes and db name
2925
     *
2926
     * @author isaac flores paz
2927
     */
2928
    public static function get_courses_list_by_user_id(
2929
        $user_id,
2930
        $include_sessions = false,
2931
        $adminGetsAllCourses = false,
2932
        $loadSpecialCourses = true,
2933
        $skipCourseList = [],
2934
        $useUserLanguageFilterIfAvailable = true,
2935
        $showCoursesSessionWithDifferentKey = false
2936
    ) {
2937
        $user_id = intval($user_id);
2938
        $urlId = api_get_current_access_url_id();
2939
        $course_list = [];
2940
        $codes = [];
2941
2942
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2943
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2944
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
2945
        $tableCourseUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2946
        $tblCourseCategory = Database::get_main_table(TABLE_MAIN_CATEGORY);
2947
2948
        $languageCondition = '';
2949
        $onlyInUserLanguage = api_get_configuration_value('my_courses_show_courses_in_user_language_only');
2950
        if ($useUserLanguageFilterIfAvailable && $onlyInUserLanguage) {
2951
            $userInfo = api_get_user_info(api_get_user_id());
2952
            if (!empty($userInfo['language'])) {
2953
                $languageCondition = " AND course.course_language = '".$userInfo['language']."' ";
2954
            }
2955
        }
2956
2957
        if ($adminGetsAllCourses && UserManager::is_admin($user_id)) {
2958
            // get the whole courses list
2959
            $sql = "SELECT DISTINCT(course.code), course.id as real_id, course.title
2960
                    FROM $tbl_course course
2961
                    INNER JOIN $tableCourseUrl url
2962
                    ON (course.id = url.c_id)
2963
                    WHERE
2964
                        url.access_url_id = $urlId
2965
                        $languageCondition
2966
                ";
2967
        } else {
2968
            $withSpecialCourses = $withoutSpecialCourses = '';
2969
2970
            if ($loadSpecialCourses) {
2971
                $specialCourseList = self::get_special_course_list();
2972
2973
                if (!empty($specialCourseList)) {
2974
                    $specialCourseToString = '"'.implode('","', $specialCourseList).'"';
2975
                    $withSpecialCourses = ' AND course.id IN ('.$specialCourseToString.')';
2976
                    $withoutSpecialCourses = ' AND course.id NOT IN ('.$specialCourseToString.')';
2977
                }
2978
2979
                if (!empty($withSpecialCourses)) {
2980
                    $sql = "SELECT DISTINCT (course.code),
2981
                            course.id as real_id,
2982
                            course_category.code AS category,
2983
                            course.title
2984
                            FROM $tbl_course_user course_rel_user
2985
                            LEFT JOIN $tbl_course course
2986
                            ON course.id = course_rel_user.c_id
2987
                            LEFT JOIN $tblCourseCategory ON course_category.id = course.category_id
2988
                            LEFT JOIN $tbl_user_course_category user_course_category
2989
                            ON course_rel_user.user_course_cat = user_course_category.id
2990
                            INNER JOIN $tableCourseUrl url
2991
                            ON (course.id = url.c_id)
2992
                            WHERE url.access_url_id = $urlId
2993
                            $withSpecialCourses
2994
                            $languageCondition
2995
                            GROUP BY course.code
2996
                            ORDER BY user_course_category.sort, course.title, course_rel_user.sort ASC
2997
                    ";
2998
                    $result = Database::query($sql);
2999
                    if (Database::num_rows($result) > 0) {
3000
                        while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3001
                            $result_row['special_course'] = 1;
3002
                            $course_list[] = $result_row;
3003
                            $codes[] = $result_row['real_id'];
3004
                        }
3005
                    }
3006
                }
3007
            }
3008
3009
            // get course list not auto-register. Use Distinct to avoid multiple
3010
            // entries when a course is assigned to a HRD (DRH) as watcher
3011
            $sql = "SELECT
3012
                        DISTINCT(course.code),
3013
                        course.id as real_id,
3014
                        course.category_id AS category,
3015
                        course.title
3016
                    FROM $tbl_course course
3017
                    INNER JOIN $tbl_course_user cru
3018
                    ON (course.id = cru.c_id)
3019
                    INNER JOIN $tableCourseUrl url
3020
                    ON (course.id = url.c_id)
3021
                    WHERE
3022
                        url.access_url_id = $urlId AND
3023
                        cru.user_id = $user_id
3024
                        $withoutSpecialCourses
3025
                        $languageCondition
3026
                    ORDER BY course.title
3027
                    ";
3028
        }
3029
        $result = Database::query($sql);
3030
3031
        if (Database::num_rows($result)) {
3032
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3033
                if (!empty($skipCourseList)) {
3034
                    if (in_array($row['real_id'], $skipCourseList)) {
3035
                        continue;
3036
                    }
3037
                }
3038
                $course_list[] = $row;
3039
                $codes[] = $row['real_id'];
3040
            }
3041
        }
3042
3043
        if (true === $include_sessions) {
3044
            $sql = "SELECT DISTINCT (c.code),
3045
                        c.id as real_id,
3046
                        c.category_code AS category,
3047
                        s.id as session_id,
3048
                        s.name as session_name
3049
                    FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)." scu
3050
                    INNER JOIN $tbl_course c
3051
                    ON (scu.c_id = c.id)
3052
                    INNER JOIN ".Database::get_main_table(TABLE_MAIN_SESSION)." s
3053
                    ON (s.id = scu.session_id)
3054
                    WHERE user_id = $user_id ";
3055
            $r = Database::query($sql);
3056
            while ($row = Database::fetch_array($r, 'ASSOC')) {
3057
                if (!empty($skipCourseList)) {
3058
                    if (in_array($row['real_id'], $skipCourseList)) {
3059
                        continue;
3060
                    }
3061
                }
3062
3063
                if ($showCoursesSessionWithDifferentKey) {
3064
                    $course_list[] = $row;
3065
                } else {
3066
                    if (!in_array($row['real_id'], $codes)) {
3067
                        $course_list[] = $row;
3068
                    }
3069
                }
3070
            }
3071
        }
3072
3073
        return $course_list;
3074
    }
3075
3076
    /**
3077
     * Get course ID from a given course directory name.
3078
     *
3079
     * @param string $path Course directory (without any slash)
3080
     *
3081
     * @return string Course code, or false if not found
3082
     */
3083
    public static function getCourseCodeFromDirectory($path)
3084
    {
3085
        $path = Database::escape_string(str_replace('.', '', str_replace('/', '', $path)));
3086
        $res = Database::query("SELECT code FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
3087
                WHERE directory LIKE BINARY '$path'");
3088
        if (false === $res) {
3089
            return false;
3090
        }
3091
        if (1 != Database::num_rows($res)) {
3092
            return false;
3093
        }
3094
        $row = Database::fetch_array($res);
3095
3096
        return $row['code'];
3097
    }
3098
3099
    /**
3100
     * Get course code(s) from visual code.
3101
     *
3102
     * @param   string  Visual code
3103
     *
3104
     * @return array List of codes for the given visual code
3105
     */
3106
    public static function get_courses_info_from_visual_code($code)
3107
    {
3108
        $result = [];
3109
        $sql_result = Database::query("SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
3110
                WHERE visual_code = '".Database::escape_string($code)."'");
3111
        while ($virtual_course = Database::fetch_array($sql_result)) {
3112
            $result[] = $virtual_course;
3113
        }
3114
3115
        return $result;
3116
    }
3117
3118
    /**
3119
     * Creates a new extra field for a given course.
3120
     *
3121
     * @param string $variable    Field's internal variable name
3122
     * @param int    $fieldType   Field's type
3123
     * @param string $displayText Field's language var name
3124
     * @param string $default     Optional. The default value
3125
     *
3126
     * @return int New extra field ID
3127
     */
3128
    public static function create_course_extra_field($variable, $fieldType, $displayText, $default = '')
3129
    {
3130
        $extraField = new ExtraField('course');
3131
        $params = [
3132
            'variable' => $variable,
3133
            'field_type' => $fieldType,
3134
            'display_text' => $displayText,
3135
            'default_value' => $default,
3136
        ];
3137
3138
        return $extraField->save($params);
3139
    }
3140
3141
    /**
3142
     * Update course attributes. Will only update attributes with a non-empty value.
3143
     * Note that you NEED to check that your attributes are valid before using this function.
3144
     *
3145
     * @param int Course id
3146
     * @param array Associative array with field names as keys and field values as values
3147
     *
3148
     * @return Doctrine\DBAL\Driver\Statement|null True if update was successful, false otherwise
3149
     */
3150
    public static function update_attributes($id, $attributes)
3151
    {
3152
        $id = (int) $id;
3153
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
3154
        $sql = "UPDATE $table SET ";
3155
        $i = 0;
3156
        foreach ($attributes as $name => $value) {
3157
            if ('' != $value) {
3158
                if ($i > 0) {
3159
                    $sql .= ", ";
3160
                }
3161
                $sql .= " $name = '".Database::escape_string($value)."'";
3162
                $i++;
3163
            }
3164
        }
3165
        $sql .= " WHERE id = $id";
3166
3167
        return Database::query($sql);
3168
    }
3169
3170
    /**
3171
     * Update an extra field value for a given course.
3172
     *
3173
     * @param string $course_code Course code
3174
     * @param string $variable    Field variable name
3175
     * @param string $value       Optional. Default field value
3176
     *
3177
     * @return bool|int An integer when register a new extra field. And boolean when update the extrafield
3178
     */
3179
    public static function update_course_extra_field_value($course_code, $variable, $value = '')
3180
    {
3181
        $courseInfo = api_get_course_info($course_code);
3182
        $courseId = $courseInfo['real_id'];
3183
3184
        $extraFieldValues = new ExtraFieldValue('course');
3185
        $params = [
3186
            'item_id' => $courseId,
3187
            'variable' => $variable,
3188
            'value' => $value,
3189
        ];
3190
3191
        return $extraFieldValues->save($params);
3192
    }
3193
3194
    /**
3195
     * @param int $sessionId
3196
     *
3197
     * @return mixed
3198
     */
3199
    public static function get_session_category_id_by_session_id($sessionId)
3200
    {
3201
        if (empty($sessionId)) {
3202
            return [];
3203
        }
3204
        $sessionId = intval($sessionId);
3205
        $sql = 'SELECT sc.id session_category
3206
                FROM '.Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY).' sc
3207
                INNER JOIN '.Database::get_main_table(TABLE_MAIN_SESSION).' s
3208
                ON sc.id = s.session_category_id
3209
                WHERE s.id = '.$sessionId;
3210
3211
        return Database::result(
3212
            Database::query($sql),
3213
            0,
3214
            'session_category'
3215
        );
3216
    }
3217
3218
    /**
3219
     * Gets the value of a course extra field. Returns null if it was not found.
3220
     *
3221
     * @param string $variable Name of the extra field
3222
     * @param string $code     Course code
3223
     *
3224
     * @return string Value
3225
     */
3226
    public static function get_course_extra_field_value($variable, $code)
3227
    {
3228
        $courseInfo = api_get_course_info($code);
3229
        $courseId = $courseInfo['real_id'];
3230
3231
        $extraFieldValues = new ExtraFieldValue('course');
3232
        $result = $extraFieldValues->get_values_by_handler_and_field_variable($courseId, $variable);
3233
        if (!empty($result['value'])) {
3234
            return $result['value'];
3235
        }
3236
3237
        return null;
3238
    }
3239
3240
    /**
3241
     * Gets extra field value data and formatted values of a course
3242
     * for extra fields listed in configuration.php in my_course_course_extrafields_to_be_presented
3243
     * (array of variables as value of key 'fields').
3244
     *
3245
     * @param $courseId  int The numeric identifier of the course
3246
     *
3247
     * @return array of data and formatted values as returned by ExtraField::getDataAndFormattedValues
3248
     */
3249
    public static function getExtraFieldsToBePresented($courseId)
3250
    {
3251
        $extraFields = [];
3252
        $fields = api_get_configuration_sub_value('my_course_course_extrafields_to_be_presented/fields');
3253
        if (!empty($fields) && is_array($fields)) {
3254
            $extraFieldManager = new ExtraField('course');
3255
            $dataAndFormattedValues = $extraFieldManager->getDataAndFormattedValues($courseId);
3256
            foreach ($fields as $variable) {
3257
                foreach ($dataAndFormattedValues as $value) {
3258
                    if ($value['variable'] === $variable && !empty($value['value'])) {
3259
                        $extraFields[] = $value;
3260
                    }
3261
                }
3262
            }
3263
        }
3264
3265
        return $extraFields;
3266
    }
3267
3268
    /**
3269
     * Lists details of the course description.
3270
     *
3271
     * @param array        The course description
3272
     * @param string    The encoding
3273
     * @param bool        If true is displayed if false is hidden
3274
     *
3275
     * @return string The course description in html
3276
     */
3277
    public static function get_details_course_description_html(
3278
        $descriptions,
3279
        $charset,
3280
        $action_show = true
3281
    ) {
3282
        $data = null;
3283
        if (isset($descriptions) && count($descriptions) > 0) {
3284
            foreach ($descriptions as $description) {
3285
                $data .= '<div class="sectiontitle">';
3286
                if (api_is_allowed_to_edit() && $action_show) {
3287
                    //delete
3288
                    $data .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=delete&description_id='.$description->id.'" onclick="javascript:if(!confirm(\''.addslashes(api_htmlentities(
3289
                        get_lang('Please confirm your choice'),
3290
                                ENT_QUOTES,
3291
                        $charset
3292
                    )).'\')) return false;">';
3293
                    $data .= Display::return_icon(
3294
                        'delete.gif',
3295
                        get_lang('Delete'),
3296
                        ['style' => 'vertical-align:middle;float:right;']
3297
                    );
3298
                    $data .= '</a> ';
3299
                    //edit
3300
                    $data .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&description_id='.$description->id.'">';
3301
                    $data .= Display::return_icon(
3302
                        'edit.png',
3303
                        get_lang('Edit'),
3304
                        ['style' => 'vertical-align:middle;float:right; padding-right:4px;'],
3305
                        ICON_SIZE_SMALL
3306
                    );
3307
                    $data .= '</a> ';
3308
                }
3309
                $data .= $description->title;
3310
                $data .= '</div>';
3311
                $data .= '<div class="sectioncomment">';
3312
                $data .= Security::remove_XSS($description->content);
3313
                $data .= '</div>';
3314
            }
3315
        } else {
3316
            $data .= '<em>'.get_lang('There is no course description so far.').'</em>';
3317
        }
3318
3319
        return $data;
3320
    }
3321
3322
    /**
3323
     * Returns the details of a course category.
3324
     *
3325
     * @param string $code Category code
3326
     *
3327
     * @return array Course category
3328
     */
3329
    public static function get_course_category($code)
3330
    {
3331
        $table = Database::get_main_table(TABLE_MAIN_CATEGORY);
3332
        $code = Database::escape_string($code);
3333
        $sql = "SELECT * FROM $table WHERE code = '$code'";
3334
3335
        return Database::fetch_array(Database::query($sql));
3336
    }
3337
3338
    /**
3339
     * Subscribes courses to human resource manager (Dashboard feature).
3340
     *
3341
     * @param int   $hr_manager_id Human Resource Manager id
3342
     * @param array $courses_list  Courses code
3343
     *
3344
     * @return int
3345
     */
3346
    public static function subscribeCoursesToDrhManager($hr_manager_id, $courses_list)
3347
    {
3348
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3349
        $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3350
3351
        $hr_manager_id = intval($hr_manager_id);
3352
        $affected_rows = 0;
3353
3354
        //Deleting assigned courses to hrm_id
3355
        if (api_is_multiple_url_enabled()) {
3356
            $sql = "SELECT s.c_id FROM $tbl_course_rel_user s
3357
                    INNER JOIN $tbl_course_rel_access_url a
3358
                    ON (a.c_id = s.c_id)
3359
                    WHERE
3360
                        user_id = $hr_manager_id AND
3361
                        relation_type = ".COURSE_RELATION_TYPE_RRHH." AND
3362
                        access_url_id = ".api_get_current_access_url_id();
3363
        } else {
3364
            $sql = "SELECT c_id FROM $tbl_course_rel_user
3365
                    WHERE user_id = $hr_manager_id AND relation_type = ".COURSE_RELATION_TYPE_RRHH;
3366
        }
3367
        $result = Database::query($sql);
3368
        if (Database::num_rows($result) > 0) {
3369
            while ($row = Database::fetch_array($result)) {
3370
                $sql = "DELETE FROM $tbl_course_rel_user
3371
                        WHERE
3372
                            c_id = {$row['c_id']} AND
3373
                            user_id = $hr_manager_id AND
3374
                            relation_type = ".COURSE_RELATION_TYPE_RRHH;
3375
                Database::query($sql);
3376
            }
3377
        }
3378
3379
        // inserting new courses list
3380
        if (is_array($courses_list)) {
3381
            foreach ($courses_list as $course_code) {
3382
                $courseInfo = api_get_course_info($course_code);
3383
                $courseId = $courseInfo['real_id'];
3384
                $sql = "INSERT IGNORE INTO $tbl_course_rel_user(c_id, user_id, status, relation_type)
3385
                        VALUES($courseId, $hr_manager_id, ".DRH.", ".COURSE_RELATION_TYPE_RRHH.")";
3386
                $result = Database::query($sql);
3387
                if (Database::affected_rows($result)) {
3388
                    $affected_rows++;
3389
                }
3390
            }
3391
        }
3392
3393
        return $affected_rows;
3394
    }
3395
3396
    /**
3397
     * get courses followed by human resources manager.
3398
     *
3399
     * @param int    $user_id
3400
     * @param int    $status
3401
     * @param int    $from
3402
     * @param int    $limit
3403
     * @param string $column
3404
     * @param string $direction
3405
     * @param bool   $getCount
3406
     *
3407
     * @return array courses
3408
     */
3409
    public static function get_courses_followed_by_drh(
3410
        $user_id,
3411
        $status = DRH,
3412
        $from = null,
3413
        $limit = null,
3414
        $column = null,
3415
        $direction = null,
3416
        $getCount = false
3417
    ) {
3418
        return self::getCoursesFollowedByUser(
3419
            $user_id,
3420
            $status,
3421
            $from,
3422
            $limit,
3423
            $column,
3424
            $direction,
3425
            $getCount
3426
        );
3427
    }
3428
3429
    /**
3430
     * get courses followed by user.
3431
     *
3432
     * @param int    $user_id
3433
     * @param int    $status
3434
     * @param int    $from
3435
     * @param int    $limit
3436
     * @param string $column
3437
     * @param string $direction
3438
     * @param bool   $getCount
3439
     * @param string $keyword
3440
     * @param int    $sessionId
3441
     * @param bool   $showAllAssignedCourses
3442
     *
3443
     * @return array courses
3444
     */
3445
    public static function getCoursesFollowedByUser(
3446
        $user_id,
3447
        $status = null,
3448
        $from = null,
3449
        $limit = null,
3450
        $column = null,
3451
        $direction = null,
3452
        $getCount = false,
3453
        $keyword = null,
3454
        $sessionId = 0,
3455
        $showAllAssignedCourses = false
3456
    ) {
3457
        // Database Table Definitions
3458
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3459
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3460
        $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3461
        $sessionId = (int) $sessionId;
3462
        $user_id = (int) $user_id;
3463
        $select = "SELECT DISTINCT c.*, c.id as real_id ";
3464
3465
        if ($getCount) {
3466
            $select = "SELECT COUNT(DISTINCT c.id) as count";
3467
        }
3468
3469
        $whereConditions = '';
3470
        switch ($status) {
3471
            case COURSEMANAGER:
3472
                $whereConditions .= " AND cru.user_id = $user_id";
3473
                if (!$showAllAssignedCourses) {
3474
                    $whereConditions .= " AND cru.status = ".COURSEMANAGER;
3475
                } else {
3476
                    $whereConditions .= " AND relation_type = ".COURSE_RELATION_TYPE_COURSE_MANAGER;
3477
                }
3478
                break;
3479
            case DRH:
3480
                $whereConditions .= " AND
3481
                    cru.user_id = $user_id AND
3482
                    cru.status = ".DRH." AND
3483
                    relation_type = '".COURSE_RELATION_TYPE_RRHH."'
3484
                ";
3485
                break;
3486
        }
3487
3488
        $keywordCondition = null;
3489
        if (!empty($keyword)) {
3490
            $keyword = Database::escape_string($keyword);
3491
            $keywordCondition = " AND (c.code LIKE '%$keyword%' OR c.title LIKE '%$keyword%' ) ";
3492
        }
3493
3494
        $orderBy = null;
3495
        $extraInnerJoin = null;
3496
3497
        if (!empty($sessionId)) {
3498
            if (COURSEMANAGER == $status) {
3499
                // Teacher of course or teacher inside session
3500
                $whereConditions = " AND (cru.status = ".COURSEMANAGER." OR srcru.status = 2) ";
3501
            }
3502
            $courseList = SessionManager::get_course_list_by_session_id($sessionId);
3503
            if (!empty($courseList)) {
3504
                $courseListToString = implode("','", array_keys($courseList));
3505
                $whereConditions .= " AND c.id IN ('".$courseListToString."')";
3506
            }
3507
            $tableSessionRelCourse = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3508
            $tableSessionRelCourseRelUser = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3509
            $orderBy = ' ORDER BY position';
3510
            $extraInnerJoin = " INNER JOIN $tableSessionRelCourse src
3511
                                ON (c.id = src.c_id AND src.session_id = $sessionId)
3512
                                INNER JOIN $tableSessionRelCourseRelUser srcru
3513
                                ON (src.session_id = srcru.session_id AND srcru.c_id = src.c_id)
3514
                            ";
3515
        }
3516
3517
        $whereConditions .= $keywordCondition;
3518
        $sql = "$select
3519
                FROM $tbl_course c
3520
                INNER JOIN $tbl_course_rel_user cru
3521
                ON (cru.c_id = c.id)
3522
                INNER JOIN $tbl_course_rel_access_url a
3523
                ON (a.c_id = c.id)
3524
                $extraInnerJoin
3525
                WHERE
3526
                    access_url_id = ".api_get_current_access_url_id()."
3527
                    $whereConditions
3528
                $orderBy
3529
                ";
3530
        if (isset($from) && isset($limit)) {
3531
            $from = intval($from);
3532
            $limit = intval($limit);
3533
            $sql .= " LIMIT $from, $limit";
3534
        }
3535
3536
        $result = Database::query($sql);
3537
3538
        if ($getCount) {
3539
            $row = Database::fetch_array($result);
3540
3541
            return $row['count'];
3542
        }
3543
3544
        $courses = [];
3545
        if (Database::num_rows($result) > 0) {
3546
            while ($row = Database::fetch_array($result)) {
3547
                $courses[$row['code']] = $row;
3548
            }
3549
        }
3550
3551
        return $courses;
3552
    }
3553
3554
    /**
3555
     * check if a course is special (autoregister).
3556
     *
3557
     * @param int $courseId
3558
     *
3559
     * @return bool
3560
     */
3561
    public static function isSpecialCourse($courseId)
3562
    {
3563
        $extraFieldValue = new ExtraFieldValue('course');
3564
        $result = $extraFieldValue->get_values_by_handler_and_field_variable(
3565
            $courseId,
3566
            'special_course'
3567
        );
3568
3569
        if (!empty($result)) {
3570
            if (1 == $result['value']) {
3571
                return true;
3572
            }
3573
        }
3574
3575
        return false;
3576
    }
3577
3578
    /**
3579
     * Display special courses (and only these) as several HTML divs of class userportal-course-item.
3580
     *
3581
     * Special courses are courses that stick on top of the list and are "auto-registerable"
3582
     * in the sense that any user clicking them is registered as a student
3583
     *
3584
     * @param int  $user_id                          User id
3585
     * @param bool $load_dirs                        Whether to show the document quick-loader or not
3586
     * @param bool $useUserLanguageFilterIfAvailable
3587
     *
3588
     * @return array
3589
     */
3590
    public static function returnSpecialCourses(
3591
        $user_id,
3592
        $load_dirs = false,
3593
        $useUserLanguageFilterIfAvailable = true
3594
    ) {
3595
        $user_id = (int) $user_id;
3596
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
3597
        $specialCourseList = self::get_special_course_list();
3598
3599
        if (empty($specialCourseList)) {
3600
            return [];
3601
        }
3602
3603
        // Filter by language
3604
        $languageCondition = '';
3605
        $onlyInUserLanguage = api_get_configuration_value('my_courses_show_courses_in_user_language_only');
3606
        if ($useUserLanguageFilterIfAvailable && $onlyInUserLanguage) {
3607
            $userInfo = api_get_user_info(api_get_user_id());
3608
            if (!empty($userInfo['language'])) {
3609
                $languageCondition = " AND course_language = '".$userInfo['language']."' ";
3610
            }
3611
        }
3612
3613
        $sql = "SELECT
3614
                    id,
3615
                    code,
3616
                    subscribe subscr,
3617
                    unsubscribe unsubscr
3618
                FROM $table
3619
                WHERE
3620
                    id IN ('".implode("','", $specialCourseList)."')
3621
                    $languageCondition
3622
                GROUP BY code";
3623
3624
        $rs_special_course = Database::query($sql);
3625
        $number_of_courses = Database::num_rows($rs_special_course);
3626
        $showCustomIcon = api_get_setting('course_images_in_courses_list');
3627
3628
        $courseList = [];
3629
        if ($number_of_courses > 0) {
3630
            while ($course = Database::fetch_array($rs_special_course)) {
3631
                $course_info = api_get_course_info($course['code']);
3632
                $courseId = $course_info['real_id'];
3633
                if (COURSE_VISIBILITY_HIDDEN == $course_info['visibility']) {
3634
                    continue;
3635
                }
3636
3637
                $params = [];
3638
                //Param (course_code) needed to get the student info in page "My courses"
3639
                $params['course_code'] = $course['code'];
3640
                $params['code'] = $course['code'];
3641
                // Get notifications.
3642
                $course_info['id_session'] = null;
3643
                $courseUserInfo = self::getUserCourseInfo($user_id, $courseId);
3644
3645
                if (empty($courseUserInfo)) {
3646
                    $course_info['status'] = STUDENT;
3647
                } else {
3648
                    $course_info['status'] = $courseUserInfo['status'];
3649
                }
3650
                $show_notification = !api_get_configuration_value('hide_course_notification')
3651
                    ? Display::show_notification($course_info)
3652
                    : '';
3653
                $params['edit_actions'] = '';
3654
                $params['document'] = '';
3655
                if (api_is_platform_admin()) {
3656
                    $params['edit_actions'] .= api_get_path(WEB_CODE_PATH).'course_info/infocours.php?cid='.$course['real_id'];
3657
                    if ($load_dirs) {
3658
                        $params['document'] = '<a id="document_preview_'.$courseId.'_0" class="document_preview btn btn-outline-secondary btn-sm" href="javascript:void(0);">'
3659
                           .Display::returnFontAwesomeIcon('folder-open').'</a>';
3660
                        $params['document'] .= Display::div('', ['id' => 'document_result_'.$courseId.'_0', 'class' => 'document_preview_container']);
3661
                    }
3662
                } else {
3663
                    if (COURSE_VISIBILITY_CLOSED != $course_info['visibility'] && $load_dirs) {
3664
                        $params['document'] = '<a id="document_preview_'.$courseId.'_0" class="document_preview btn btn-outline-secondary btn-sm" href="javascript:void(0);">'
3665
                           .Display::returnFontAwesomeIcon('folder-open').'</a>';
3666
                        $params['document'] .= Display::div('', ['id' => 'document_result_'.$courseId.'_0', 'class' => 'document_preview_container']);
3667
                    }
3668
                }
3669
3670
                $params['visibility'] = $course_info['visibility'];
3671
                $params['status'] = $course_info['status'];
3672
                $params['category'] = $course_info['categoryName'];
3673
                $params['category_code'] = $course_info['categoryCode'];
3674
                $params['icon'] = Display::return_icon(
3675
                    'drawing-pin.png',
3676
                    null,
3677
                    null,
3678
                    ICON_SIZE_LARGE,
3679
                    null
3680
                );
3681
3682
                if ('true' == api_get_setting('display_coursecode_in_courselist')) {
3683
                    $params['code_course'] = '('.$course_info['visual_code'].')';
3684
                }
3685
3686
                $params['title'] = $course_info['title'];
3687
                $params['title_cut'] = $course_info['title'];
3688
                $params['link'] = $course_info['course_public_url'].'?sid=0&autoreg=1';
3689
                if ('true' === api_get_setting('display_teacher_in_courselist')) {
3690
                    $params['teachers'] = self::getTeachersFromCourse(
3691
                        $courseId,
3692
                        true
3693
                    );
3694
                }
3695
3696
                $params['extrafields'] = CourseManager::getExtraFieldsToBePresented($course_info['real_id']);
3697
3698
                if ('true' === $showCustomIcon) {
3699
                    $params['thumbnails'] = $course_info['course_image'];
3700
                    $params['image'] = $course_info['course_image_large'];
3701
                }
3702
3703
                if (COURSE_VISIBILITY_CLOSED != $course_info['visibility']) {
3704
                    $params['notifications'] = $show_notification;
3705
                }
3706
3707
                $params['is_special_course'] = true;
3708
                $courseList[] = $params;
3709
            }
3710
        }
3711
3712
        return $courseList;
3713
    }
3714
3715
    /**
3716
     * Display courses (without special courses) as several HTML divs
3717
     * of course categories, as class userportal-catalog-item.
3718
     *
3719
     * @uses \displayCoursesInCategory() to display the courses themselves
3720
     *
3721
     * @param int  $user_id
3722
     * @param bool $load_dirs                        Whether to show the document quick-loader or not
3723
     * @param bool $useUserLanguageFilterIfAvailable
3724
     *
3725
     * @return array
3726
     */
3727
    public static function returnCourses(
3728
        $user_id,
3729
        $load_dirs = false,
3730
        $useUserLanguageFilterIfAvailable = true
3731
    ) {
3732
        $user_id = (int) $user_id;
3733
        if (empty($user_id)) {
3734
            $user_id = api_get_user_id();
3735
        }
3736
        // Step 1: We get all the categories of the user
3737
        $table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3738
        $sql = "SELECT * FROM $table
3739
                WHERE user_id = $user_id
3740
                ORDER BY sort ASC";
3741
3742
        $result = Database::query($sql);
3743
        $listItems = [
3744
            'in_category' => [],
3745
            'not_category' => [],
3746
        ];
3747
        $collapsable = api_get_configuration_value('allow_user_course_category_collapsable');
3748
        $stok = Security::get_existing_token();
3749
        while ($row = Database::fetch_array($result)) {
3750
            // We simply display the title of the category.
3751
            $courseInCategory = self::returnCoursesCategories(
3752
                $row['id'],
3753
                $load_dirs,
3754
                $user_id,
3755
                $useUserLanguageFilterIfAvailable
3756
            );
3757
3758
            $collapsed = 0;
3759
            $collapsableLink = '';
3760
            if ($collapsable) {
3761
                $url = api_get_path(WEB_CODE_PATH).
3762
                    'auth/sort_my_courses.php?categoryid='.$row['id'].'&sec_token='.$stok.'&redirect=home';
3763
                $collapsed = isset($row['collapsed']) && $row['collapsed'] ? 1 : 0;
3764
                if (0 === $collapsed) {
3765
                    $collapsableLink = Display::url(
3766
                        '<i class="fa fa-folder-open"></i>',
3767
                        $url.'&action=set_collapsable&option=1'
3768
                    );
3769
                } else {
3770
                    $collapsableLink = Display::url(
3771
                        '<i class="fa fa-folder"></i>',
3772
                        $url.'&action=set_collapsable&option=0'
3773
                    );
3774
                }
3775
            }
3776
3777
            $params = [
3778
                'id_category' => $row['id'],
3779
                'title_category' => $row['title'],
3780
                'collapsed' => $collapsed,
3781
                'collapsable_link' => $collapsableLink,
3782
                'courses' => $courseInCategory,
3783
            ];
3784
            $listItems['in_category'][] = $params;
3785
        }
3786
3787
        // Step 2: We display the course without a user category.
3788
        $coursesNotCategory = self::returnCoursesCategories(
3789
            0,
3790
            $load_dirs,
3791
            $user_id,
3792
            $useUserLanguageFilterIfAvailable
3793
        );
3794
3795
        if ($coursesNotCategory) {
3796
            $listItems['not_category'] = $coursesNotCategory;
3797
        }
3798
3799
        return $listItems;
3800
    }
3801
3802
    /**
3803
     *  Display courses inside a category (without special courses) as HTML dics of
3804
     *  class userportal-course-item.
3805
     *
3806
     * @param int  $user_category_id                 User category id
3807
     * @param bool $load_dirs                        Whether to show the document quick-loader or not
3808
     * @param int  $user_id
3809
     * @param bool $useUserLanguageFilterIfAvailable
3810
     *
3811
     * @return array
3812
     */
3813
    public static function returnCoursesCategories(
3814
        $user_category_id,
3815
        $load_dirs = false,
3816
        $user_id = 0,
3817
        $useUserLanguageFilterIfAvailable = true
3818
    ) {
3819
        $user_id = $user_id ? (int) $user_id : api_get_user_id();
3820
        $user_category_id = (int) $user_category_id;
3821
3822
        // Table definitions
3823
        $TABLECOURS = Database::get_main_table(TABLE_MAIN_COURSE);
3824
        $TABLECOURSUSER = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3825
        $TABLE_ACCESS_URL_REL_COURSE = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3826
        $current_url_id = api_get_current_access_url_id();
3827
3828
        // Get course list auto-register
3829
        $special_course_list = self::get_special_course_list();
3830
        $without_special_courses = '';
3831
        if (!empty($special_course_list)) {
3832
            $without_special_courses = ' AND course.id NOT IN ("'.implode('","', $special_course_list).'")';
3833
        }
3834
3835
        $userCategoryCondition = " (course_rel_user.user_course_cat = $user_category_id) ";
3836
        if (empty($user_category_id)) {
3837
            $userCategoryCondition = ' (course_rel_user.user_course_cat = 0 OR course_rel_user.user_course_cat IS NULL) ';
3838
        }
3839
3840
        $languageCondition = '';
3841
        $onlyInUserLanguage = api_get_configuration_value('my_courses_show_courses_in_user_language_only');
3842
        if ($useUserLanguageFilterIfAvailable && $onlyInUserLanguage) {
3843
            $userInfo = api_get_user_info(api_get_user_id());
3844
            if (!empty($userInfo['language'])) {
3845
                $languageCondition = " AND course.course_language = '".$userInfo['language']."' ";
3846
            }
3847
        }
3848
3849
        $sql = "SELECT DISTINCT
3850
                    course.id,
3851
                    course_rel_user.status status,
3852
                    course.code as course_code,
3853
                    user_course_cat,
3854
                    course_rel_user.sort
3855
                FROM $TABLECOURS course
3856
                INNER JOIN $TABLECOURSUSER course_rel_user
3857
                ON (course.id = course_rel_user.c_id)
3858
                INNER JOIN $TABLE_ACCESS_URL_REL_COURSE url
3859
                ON (url.c_id = course.id)
3860
                WHERE
3861
                    course_rel_user.user_id = $user_id AND
3862
                    $userCategoryCondition
3863
                    $without_special_courses
3864
                    $languageCondition
3865
                ";
3866
        // If multiple URL access mode is enabled, only fetch courses
3867
        // corresponding to the current URL.
3868
        if (api_get_multiple_access_url() && -1 != $current_url_id) {
3869
            $sql .= " AND access_url_id = $current_url_id";
3870
        }
3871
        // Use user's classification for courses (if any).
3872
        $sql .= ' ORDER BY course_rel_user.user_course_cat, course_rel_user.sort ASC';
3873
        $result = Database::query($sql);
3874
3875
        $showCustomIcon = api_get_setting('course_images_in_courses_list');
3876
        // Browse through all courses.
3877
        $courseAdded = [];
3878
        $courseList = [];
3879
3880
        while ($row = Database::fetch_array($result)) {
3881
            $course_info = api_get_course_info_by_id($row['id']);
3882
            if (empty($course_info)) {
3883
                continue;
3884
            }
3885
3886
            if (isset($course_info['visibility']) &&
3887
                COURSE_VISIBILITY_HIDDEN == $course_info['visibility']
3888
            ) {
3889
                continue;
3890
            }
3891
3892
            // Skip if already in list
3893
            if (in_array($course_info['real_id'], $courseAdded)) {
3894
                continue;
3895
            }
3896
            $course_info['id_session'] = null;
3897
            $course_info['status'] = $row['status'];
3898
            // For each course, get if there is any notification icon to show
3899
            // (something that would have changed since the user's last visit).
3900
            $showNotification = !api_get_configuration_value('hide_course_notification')
3901
                ? Display::show_notification($course_info)
3902
                : '';
3903
            $iconName = basename($course_info['course_image']);
3904
3905
            $params = [];
3906
            //Param (course_code) needed to get the student process
3907
            $params['course_code'] = $row['course_code'];
3908
            $params['code'] = $row['course_code'];
3909
3910
            if ('true' === $showCustomIcon && 'course.png' != $iconName) {
3911
                $params['thumbnails'] = $course_info['course_image'];
3912
                $params['image'] = $course_info['course_image_large'];
3913
            }
3914
3915
            $thumbnails = null;
3916
            $image = null;
3917
            if ('true' === $showCustomIcon && 'course.png' != $iconName) {
3918
                $thumbnails = $course_info['course_image'];
3919
                $image = $course_info['course_image_large'];
3920
            } else {
3921
                $image = Display::return_icon(
3922
                    'session_default.png',
3923
                    null,
3924
                    null,
3925
                    null,
3926
                    null,
3927
                    true
3928
                );
3929
            }
3930
3931
            $params['course_id'] = $course_info['real_id'];
3932
            $params['edit_actions'] = '';
3933
            $params['document'] = '';
3934
            if (api_is_platform_admin()) {
3935
                $params['edit_actions'] .= api_get_path(WEB_CODE_PATH).'course_info/infocours.php?cid='.$course_info['real_id'];
3936
                if ($load_dirs) {
3937
                    $params['document'] = '<a id="document_preview_'.$course_info['real_id'].'_0" class="document_preview btn btn-default btn-sm" href="javascript:void(0);">'
3938
                               .Display::returnFontAwesomeIcon('folder-open').'</a>';
3939
                    $params['document'] .= Display::div(
3940
                        '',
3941
                        [
3942
                            'id' => 'document_result_'.$course_info['real_id'].'_0',
3943
                            'class' => 'document_preview_container',
3944
                        ]
3945
                    );
3946
                }
3947
            }
3948
            if ($load_dirs) {
3949
                $params['document'] = '<a id="document_preview_'.$course_info['real_id'].'_0" class="document_preview btn btn-default btn-sm" href="javascript:void(0);">'
3950
                    .Display::returnFontAwesomeIcon('folder-open').'</a>';
3951
                $params['document'] .= Display::div(
3952
                    '',
3953
                    [
3954
                        'id' => 'document_result_'.$course_info['real_id'].'_0',
3955
                        'class' => 'document_preview_container',
3956
                    ]
3957
                );
3958
            }
3959
3960
            $courseUrl = $course_info['course_public_url'].'?sid=0';
3961
            $teachers = [];
3962
            if ('true' === api_get_setting('display_teacher_in_courselist')) {
3963
                $teachers = self::getTeachersFromCourse(
3964
                    $course_info['real_id'],
3965
                    true
3966
                );
3967
            }
3968
3969
            $params['status'] = $row['status'];
3970
            if ('true' === api_get_setting('display_coursecode_in_courselist')) {
3971
                $params['code_course'] = '('.$course_info['visual_code'].') ';
3972
            }
3973
3974
            $params['current_user_is_teacher'] = false;
3975
            /** @var array $teacher */
3976
            foreach ($teachers as $teacher) {
3977
                if ($teacher['id'] != $user_id) {
3978
                    continue;
3979
                }
3980
                $params['current_user_is_teacher'] = true;
3981
            }
3982
3983
            $params['visibility'] = $course_info['visibility'];
3984
            $params['link'] = $courseUrl;
3985
            $params['thumbnails'] = $thumbnails;
3986
            $params['image'] = $image;
3987
            $params['title'] = $course_info['title'];
3988
            $params['title_cut'] = $params['title'];
3989
            $params['category'] = $course_info['categoryName'];
3990
            $params['category_code'] = $course_info['categoryCode'];
3991
            $params['teachers'] = $teachers;
3992
            $params['extrafields'] = CourseManager::getExtraFieldsToBePresented($course_info['real_id']);
3993
            $params['real_id'] = $course_info['real_id'];
3994
3995
            if (api_get_configuration_value('enable_unsubscribe_button_on_my_course_page')
3996
                && '1' === $course_info['unsubscribe']
3997
            ) {
3998
                $params['unregister_button'] = CoursesAndSessionsCatalog::return_unregister_button(
3999
                    $course_info,
4000
                    Security::get_existing_token(),
4001
                    '',
4002
                    ''
4003
                );
4004
            }
4005
4006
            if (COURSE_VISIBILITY_CLOSED != $course_info['visibility']) {
4007
                $params['notifications'] = $showNotification;
4008
            }
4009
            $courseAdded[] = $course_info['real_id'];
4010
            $courseList[] = $params;
4011
        }
4012
4013
        return $courseList;
4014
    }
4015
4016
    /**
4017
     * Retrieves the user defined course categories.
4018
     *
4019
     * @param int $userId
4020
     *
4021
     * @return array
4022
     */
4023
    public static function get_user_course_categories($userId = 0)
4024
    {
4025
        $userId = empty($userId) ? api_get_user_id() : (int) $userId;
4026
        $table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
4027
        $sql = "SELECT * FROM $table
4028
                WHERE user_id = $userId
4029
                ORDER BY sort ASC
4030
                ";
4031
        $result = Database::query($sql);
4032
        $output = [];
4033
        while ($row = Database::fetch_array($result, 'ASSOC')) {
4034
            $output[$row['id']] = $row;
4035
        }
4036
4037
        return $output;
4038
    }
4039
4040
    /**
4041
     * Return an array the user_category id and title for the course $courseId for user $userId.
4042
     *
4043
     * @param $userId
4044
     * @param $courseId
4045
     *
4046
     * @return array
4047
     */
4048
    public static function getUserCourseCategoryForCourse($userId, $courseId)
4049
    {
4050
        $tblCourseRelUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4051
        $tblUserCategory = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
4052
        $courseId = intval($courseId);
4053
        $userId = intval($userId);
4054
4055
        $sql = "SELECT user_course_cat, title
4056
                FROM $tblCourseRelUser cru
4057
                LEFT JOIN $tblUserCategory ucc
4058
                ON cru.user_course_cat = ucc.id
4059
                WHERE
4060
                    cru.user_id = $userId AND c_id = $courseId ";
4061
4062
        $res = Database::query($sql);
4063
4064
        $data = [];
4065
        if (Database::num_rows($res) > 0) {
4066
            $data = Database::fetch_assoc($res);
4067
        }
4068
4069
        return $data;
4070
    }
4071
4072
    /**
4073
     * Get the course id based on the original id and field name in the extra fields.
4074
     * Returns 0 if course was not found.
4075
     *
4076
     * @param string $value    Original course code
4077
     * @param string $variable Original field name
4078
     *
4079
     * @return array
4080
     */
4081
    public static function getCourseInfoFromOriginalId($value, $variable)
4082
    {
4083
        $extraFieldValue = new ExtraFieldValue('course');
4084
        $result = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
4085
            $variable,
4086
            $value
4087
        );
4088
4089
        if (!empty($result)) {
4090
            $courseInfo = api_get_course_info_by_id($result['item_id']);
4091
4092
            return $courseInfo;
4093
        }
4094
4095
        return [];
4096
    }
4097
4098
    /**
4099
     * Display code for one specific course a logged in user is subscribed to.
4100
     * Shows a link to the course, what's new icons...
4101
     *
4102
     * $my_course['d'] - course directory
4103
     * $my_course['i'] - course title
4104
     * $my_course['c'] - visual course code
4105
     * $my_course['k']  - system course code
4106
     *
4107
     * @param   array       Course details
4108
     * @param   int     Session ID
4109
     * @param   string      CSS class to apply to course entry
4110
     * @param   bool     Whether the session is supposedly accessible now
4111
     * (not in the case it has passed and is in invisible/unaccessible mode)
4112
     * @param bool      Whether to show the document quick-loader or not
4113
     *
4114
     * @return string The HTML to be printed for the course entry
4115
     *
4116
     * @version 1.0.3
4117
     *
4118
     * @todo refactor into different functions for database calls | logic | display
4119
     * @todo replace single-character $my_course['d'] indices
4120
     * @todo move code for what's new icons to a separate function to clear things up
4121
     * @todo add a parameter user_id so that it is possible to show the
4122
     * courselist of other users (=generalisation).
4123
     * This will prevent having to write a new function for this.
4124
     */
4125
    public static function get_logged_user_course_html(
4126
        $course,
4127
        $session_id = 0,
4128
        $class = 'courses',
4129
        $session_accessible = true,
4130
        $load_dirs = false
4131
    ) {
4132
        $now = date('Y-m-d h:i:s');
4133
        $user_id = api_get_user_id();
4134
        $course_info = api_get_course_info_by_id($course['real_id']);
4135
        $course_visibility = (int) $course_info['visibility'];
4136
        $allowUnsubscribe = api_get_configuration_value('enable_unsubscribe_button_on_my_course_page');
4137
4138
        if (COURSE_VISIBILITY_HIDDEN === $course_visibility) {
4139
            return '';
4140
        }
4141
4142
        $sessionInfo = [];
4143
        if (!empty($session_id)) {
4144
            $sessionInfo = api_get_session_info($session_id);
4145
        }
4146
        $userInCourseStatus = self::getUserInCourseStatus($user_id, $course_info['real_id']);
4147
        $course_info['status'] = empty($session_id) ? $userInCourseStatus : STUDENT;
4148
        $course_info['id_session'] = $session_id;
4149
4150
        $is_coach = api_is_coach($session_id, $course_info['real_id']);
4151
        $isAdmin = api_is_platform_admin();
4152
4153
        // Display course entry.
4154
        // Show a hyperlink to the course, unless the course is closed and user is not course admin.
4155
        $session_url = '';
4156
        $params = [];
4157
        $params['icon'] = Display::return_icon(
4158
            'session.png',
4159
            null,
4160
            [],
4161
            ICON_SIZE_LARGE,
4162
            null,
4163
            true
4164
        );
4165
        $params['real_id'] = $course_info['real_id'];
4166
        $params['visibility'] = $course_info['visibility'];
4167
4168
        // Display the "what's new" icons
4169
        $notifications = '';
4170
        if (
4171
            (COURSE_VISIBILITY_CLOSED != $course_visibility && COURSE_VISIBILITY_HIDDEN != $course_visibility) ||
4172
            !api_get_configuration_value('hide_course_notification')
4173
        ) {
4174
            $notifications .= Display::show_notification($course_info);
4175
        }
4176
4177
        $sessionCourseAvailable = false;
4178
        if ($session_accessible) {
4179
            if (COURSE_VISIBILITY_CLOSED != $course_visibility ||
4180
                COURSEMANAGER == $userInCourseStatus
4181
            ) {
4182
                if (empty($course_info['id_session'])) {
4183
                    $course_info['id_session'] = 0;
4184
                }
4185
4186
                $sessionCourseStatus = api_get_session_visibility($session_id, $course_info['real_id']);
4187
4188
                if (in_array(
4189
                    $sessionCourseStatus,
4190
                    [SESSION_VISIBLE_READ_ONLY, SESSION_VISIBLE, SESSION_AVAILABLE]
4191
                )) {
4192
                    $sessionCourseAvailable = true;
4193
                }
4194
4195
                if (COURSEMANAGER === $userInCourseStatus || $sessionCourseAvailable) {
4196
                    $session_url = $course_info['course_public_url'].'?sid='.$course_info['id_session'];
4197
                    $session_title = '<a title="'.$course_info['name'].'" href="'.$session_url.'">'.
4198
                        $course_info['name'].'</a>'.$notifications;
4199
                } else {
4200
                    $session_title = $course_info['name'];
4201
                }
4202
            } else {
4203
                $session_title =
4204
                    $course_info['name'].' '.
4205
                    Display::tag('span', get_lang('(the course is currently closed)'), ['class' => 'item_closed']);
4206
            }
4207
        } else {
4208
            $session_title = $course_info['name'];
4209
        }
4210
4211
        $thumbnails = null;
4212
        $image = null;
4213
        $showCustomIcon = api_get_setting('course_images_in_courses_list');
4214
        $iconName = basename($course_info['course_image']);
4215
4216
        if ('true' === $showCustomIcon && 'course.png' != $iconName) {
4217
            $thumbnails = $course_info['course_image'];
4218
            $image = $course_info['course_image_large'];
4219
        } else {
4220
            $image = Display::return_icon(
4221
                'session_default.png',
4222
                null,
4223
                null,
4224
                null,
4225
                null,
4226
                true
4227
            );
4228
        }
4229
        $params['thumbnails'] = $thumbnails;
4230
        $params['image'] = $image;
4231
        $params['html_image'] = '';
4232
        if (!empty($thumbnails)) {
4233
            $params['html_image'] = Display::img($thumbnails, $course_info['name'], ['class' => 'img-responsive']);
4234
        } else {
4235
            $params['html_image'] = Display::return_icon(
4236
                'session.png',
4237
                $course_info['name'],
4238
                ['class' => 'img-responsive'],
4239
                ICON_SIZE_LARGE,
4240
                $course_info['name']
4241
            );
4242
        }
4243
        $params['link'] = $session_url;
4244
        $entityManager = Database::getManager();
4245
        /** @var SequenceResourceRepository $repo */
4246
        $repo = $entityManager->getRepository(SequenceResource::class);
4247
4248
        $sequences = $repo->getRequirements($course_info['real_id'], SequenceResource::COURSE_TYPE);
4249
        $sequenceList = $repo->checkRequirementsForUser(
4250
            $sequences,
4251
            SequenceResource::COURSE_TYPE,
4252
            $user_id,
4253
            $session_id
4254
        );
4255
        $completed = $repo->checkSequenceAreCompleted($sequenceList);
4256
        //var_dump($course_info['real_id'], $completed);
4257
        $params['completed'] = $completed;
4258
        $params['requirements'] = '';
4259
4260
        if ($isAdmin ||
4261
            COURSEMANAGER === $userInCourseStatus ||
4262
            $is_coach ||
4263
            $user_id == $sessionInfo['session_admin_id']
4264
        ) {
4265
            $params['completed'] = true;
4266
            $params['requirements'] = '';
4267
        } else {
4268
            if ($sequences && false === $completed) {
4269
                $hasRequirements = false;
4270
                foreach ($sequences as $sequence) {
4271
                    if (!empty($sequence['requirements'])) {
4272
                        $hasRequirements = true;
4273
                        break;
4274
                    }
4275
                }
4276
                if ($hasRequirements) {
4277
                    $params['requirements'] = CoursesAndSessionsCatalog::getRequirements(
4278
                    $course_info['real_id'],
4279
                    SequenceResource::COURSE_TYPE,
4280
                    false,
4281
                        false,
4282
                        $session_id
4283
                );
4284
                }
4285
            }
4286
        }
4287
4288
        $params['title'] = $session_title;
4289
        $params['name'] = $course_info['name'];
4290
        $params['edit_actions'] = '';
4291
        $params['document'] = '';
4292
        $params['category'] = $course_info['categoryName'];
4293
        if (COURSE_VISIBILITY_CLOSED != $course_visibility &&
4294
            false === $is_coach && $allowUnsubscribe && '1' === $course_info['unsubscribe']) {
4295
            $params['unregister_button'] =
4296
                CoursesAndSessionsCatalog::return_unregister_button(
4297
                    ['code' => $course_info['code']],
4298
                    Security::get_existing_token(),
4299
                    '',
4300
                    '',
4301
                    $session_id
4302
                );
4303
        }
4304
4305
        if (COURSE_VISIBILITY_CLOSED != $course_visibility &&
4306
            COURSE_VISIBILITY_HIDDEN != $course_visibility
4307
        ) {
4308
            if ($isAdmin) {
4309
                $params['edit_actions'] .= api_get_path(WEB_CODE_PATH).'course_info/infocours.php?cidReq='.$course_info['code'];
4310
                if ($load_dirs) {
4311
                    $params['document'] .= '<a
4312
                        id="document_preview_'.$course_info['real_id'].'_'.$session_id.'"
4313
                        class="document_preview btn btn-default btn-sm"
4314
                        href="javascript:void(0);">'.
4315
                        Display::returnFontAwesomeIcon('folder-open').'</a>';
4316
                    $params['document'] .= Display::div('', [
4317
                        'id' => 'document_result_'.$course_info['real_id'].'_'.$session_id,
4318
                        'class' => 'document_preview_container',
4319
                    ]);
4320
                }
4321
            }
4322
        }
4323
        if ('true' === api_get_setting('display_teacher_in_courselist')) {
4324
            $teacher_list = self::getTeachersFromCourse($course_info['real_id'], true);
4325
            $course_coachs = self::get_coachs_from_course(
4326
                $session_id,
4327
                $course_info['real_id'],
4328
                true
4329
            );
4330
            $params['teachers'] = $teacher_list;
4331
4332
            if ((STUDENT == $course_info['status'] && !empty($session_id)) ||
4333
                ($is_coach && COURSEMANAGER != $course_info['status'])
4334
            ) {
4335
                $params['coaches'] = $course_coachs;
4336
            }
4337
        }
4338
        $special = isset($course['special_course']) ? true : false;
4339
        $params['title'] = $session_title;
4340
        $params['special'] = $special;
4341
        if ('true' === api_get_setting('display_coursecode_in_courselist')) {
4342
            $params['visual_code'] = '('.$course_info['visual_code'].')';
4343
        }
4344
        $params['extra'] = '';
4345
        $html = $params;
4346
4347
        $session_category_id = null;
4348
        $active = false;
4349
        if (!empty($session_id)) {
4350
            $sessionCoachName = '';
4351
            if (!empty($sessionInfo['id_coach'])) {
4352
                $coachInfo = api_get_user_info($sessionInfo['id_coach']);
4353
                $sessionCoachName = $coachInfo['complete_name'];
4354
            }
4355
4356
            $session_category_id = self::get_session_category_id_by_session_id($course_info['id_session']);
4357
4358
            if (
4359
                '0000-00-00 00:00:00' === $sessionInfo['access_start_date'] ||
4360
                empty($sessionInfo['access_start_date']) ||
4361
                '0000-00-00' === $sessionInfo['access_start_date']
4362
                ) {
4363
                $sessionInfo['dates'] = '';
4364
                if ('true' === api_get_setting('show_session_coach')) {
4365
                    $sessionInfo['coach'] = get_lang('GeneralCoach').': '.$sessionCoachName;
4366
                }
4367
                $active = true;
4368
            } else {
4369
                $sessionInfo['dates'] = ' - '.
4370
                    get_lang('From').' '.$sessionInfo['access_start_date'].' '.
4371
                    get_lang('To').' '.$sessionInfo['access_end_date'];
4372
                if ('true' === api_get_setting('show_session_coach')) {
4373
                    $sessionInfo['coach'] = get_lang('GeneralCoach').': '.$sessionCoachName;
4374
                }
4375
                $date_start = $sessionInfo['access_start_date'];
4376
                $date_end = $sessionInfo['access_end_date'];
4377
                $active = !$date_end ? ($date_start <= $now) : ($date_start <= $now && $date_end >= $now);
4378
            }
4379
        }
4380
        $user_course_category = '';
4381
        if (isset($course_info['user_course_cat'])) {
4382
            $user_course_category = $course_info['user_course_cat'];
4383
        }
4384
        $output = [
4385
                $user_course_category,
4386
                $html,
4387
                $course_info['id_session'],
4388
                $sessionInfo,
4389
                'active' => $active,
4390
                'session_category_id' => $session_category_id,
4391
            ];
4392
4393
        if (Skill::isAllowed($user_id, false)) {
4394
            $em = Database::getManager();
4395
            $objUser = api_get_user_entity($user_id);
4396
            $objCourse = api_get_course_entity($course['real_id']);
4397
            $objSession = api_get_session_entity($session_id);
4398
            $skill = $em->getRepository(\Chamilo\CoreBundle\Entity\Skill::class)->getLastByUser($objUser, $objCourse, $objSession);
4399
4400
            $output['skill'] = null;
4401
            if ($skill) {
4402
                $output['skill']['name'] = $skill->getName();
4403
                $output['skill']['icon'] = $skill->getIcon();
4404
            }
4405
        }
4406
4407
        return $output;
4408
    }
4409
4410
    /**
4411
     * @param string $source_course_code
4412
     * @param int    $source_session_id
4413
     * @param string $destination_course_code
4414
     * @param int    $destination_session_id
4415
     * @param array  $params
4416
     *
4417
     * @return bool
4418
     */
4419
    public static function copy_course(
4420
        $source_course_code,
4421
        $source_session_id,
4422
        $destination_course_code,
4423
        $destination_session_id,
4424
        $params = [],
4425
        $withBaseContent = true
4426
    ) {
4427
        $course_info = api_get_course_info($source_course_code);
4428
4429
        if (!empty($course_info)) {
4430
            $cb = new CourseBuilder('', $course_info);
4431
            $course = $cb->build($source_session_id, $source_course_code, $withBaseContent);
4432
            $restorer = new CourseRestorer($course);
4433
            $restorer->skip_content = $params;
4434
            $restorer->restore(
4435
                $destination_course_code,
4436
                $destination_session_id,
4437
                true,
4438
                $withBaseContent
4439
            );
4440
4441
            return true;
4442
        }
4443
4444
        return false;
4445
    }
4446
4447
    /**
4448
     * A simpler version of the copy_course, the function creates an empty course with an autogenerated course code.
4449
     *
4450
     * @param string $new_title new course title
4451
     * @param string source course code
4452
     * @param int source session id
4453
     * @param int destination session id
4454
     * @param array $params
4455
     * @param bool  $copySessionContent
4456
     *
4457
     * @return array
4458
     */
4459
    public static function copy_course_simple(
4460
        $new_title,
4461
        $source_course_code,
4462
        $source_session_id = 0,
4463
        $destination_session_id = 0,
4464
        $params = [],
4465
        $copySessionContent = false
4466
    ) {
4467
        $source_course_info = api_get_course_info($source_course_code);
4468
        if (!empty($source_course_info)) {
4469
            $new_course_code = self::generate_nice_next_course_code($source_course_code);
4470
            if ($new_course_code) {
4471
                $new_course_info = self::create_course(
4472
                    $new_title,
4473
                    $new_course_code,
4474
                    false
4475
                );
4476
                if (!empty($new_course_info['code'])) {
4477
                    $result = self::copy_course(
4478
                        $source_course_code,
4479
                        $source_session_id,
4480
                        $new_course_info['code'],
4481
                        $destination_session_id,
4482
                        $params,
4483
                        true
4484
                    );
4485
                    if ($result) {
4486
                        return $new_course_info;
4487
                    }
4488
                }
4489
            }
4490
        }
4491
4492
        return false;
4493
    }
4494
4495
    /**
4496
     * Creates a new course code based in a given code.
4497
     *
4498
     * @param string    wanted code
4499
     * <code>    $wanted_code = 'curse' if there are in the DB codes like curse1 curse2 the function will return:
4500
     * course3</code> if the course code doest not exist in the DB the same course code will be returned
4501
     *
4502
     * @return string wanted unused code
4503
     */
4504
    public static function generate_nice_next_course_code($wanted_code)
4505
    {
4506
        $course_code_ok = !self::course_code_exists($wanted_code);
4507
        if (!$course_code_ok) {
4508
            $wanted_code = self::generate_course_code($wanted_code);
4509
            $table = Database::get_main_table(TABLE_MAIN_COURSE);
4510
            $wanted_code = Database::escape_string($wanted_code);
4511
            $sql = "SELECT count(id) as count
4512
                    FROM $table
4513
                    WHERE code LIKE '$wanted_code%'";
4514
            $result = Database::query($sql);
4515
            if (Database::num_rows($result) > 0) {
4516
                $row = Database::fetch_array($result);
4517
                $count = $row['count'] + 1;
4518
                $wanted_code = $wanted_code.'_'.$count;
4519
                $result = api_get_course_info($wanted_code);
4520
                if (empty($result)) {
4521
                    return $wanted_code;
4522
                }
4523
            }
4524
4525
            return false;
4526
        }
4527
4528
        return $wanted_code;
4529
    }
4530
4531
    /**
4532
     * Gets the status of the users agreement in a course course-session.
4533
     *
4534
     * @param int    $user_id
4535
     * @param string $course_code
4536
     * @param int    $session_id
4537
     *
4538
     * @return bool
4539
     */
4540
    public static function is_user_accepted_legal($user_id, $course_code, $session_id = 0)
4541
    {
4542
        $user_id = (int) $user_id;
4543
        $session_id = (int) $session_id;
4544
        $course_code = Database::escape_string($course_code);
4545
4546
        $courseInfo = api_get_course_info($course_code);
4547
        $courseId = $courseInfo['real_id'];
4548
4549
        // Course legal
4550
        $enabled = api_get_plugin_setting('courselegal', 'tool_enable');
4551
4552
        if ('true' == $enabled) {
4553
            require_once api_get_path(SYS_PLUGIN_PATH).'courselegal/config.php';
4554
            $plugin = CourseLegalPlugin::create();
4555
4556
            return $plugin->isUserAcceptedLegal($user_id, $course_code, $session_id);
4557
        }
4558
4559
        if (empty($session_id)) {
4560
            $table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4561
            $sql = "SELECT legal_agreement FROM $table
4562
                    WHERE user_id = $user_id AND c_id = $courseId ";
4563
            $result = Database::query($sql);
4564
            if (Database::num_rows($result) > 0) {
4565
                $result = Database::fetch_array($result);
4566
                if (1 == $result['legal_agreement']) {
4567
                    return true;
4568
                }
4569
            }
4570
4571
            return false;
4572
        } else {
4573
            $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4574
            $sql = "SELECT legal_agreement FROM $table
4575
                    WHERE user_id = $user_id AND c_id = $courseId AND session_id = $session_id";
4576
            $result = Database::query($sql);
4577
            if (Database::num_rows($result) > 0) {
4578
                $result = Database::fetch_array($result);
4579
                if (1 == $result['legal_agreement']) {
4580
                    return true;
4581
                }
4582
            }
4583
4584
            return false;
4585
        }
4586
    }
4587
4588
    /**
4589
     * Saves the user-course legal agreement.
4590
     *
4591
     * @param   int user id
4592
     * @param   string course code
4593
     * @param   int session id
4594
     *
4595
     * @return bool
4596
     */
4597
    public static function save_user_legal($user_id, $courseInfo, $session_id = 0)
4598
    {
4599
        if (empty($courseInfo)) {
4600
            return false;
4601
        }
4602
        $course_code = $courseInfo['code'];
4603
4604
        // Course plugin legal
4605
        $enabled = api_get_plugin_setting('courselegal', 'tool_enable');
4606
        if ('true' == $enabled) {
4607
            require_once api_get_path(SYS_PLUGIN_PATH).'courselegal/config.php';
4608
            $plugin = CourseLegalPlugin::create();
4609
4610
            return $plugin->saveUserLegal($user_id, $course_code, $session_id);
4611
        }
4612
4613
        $user_id = (int) $user_id;
4614
        $session_id = (int) $session_id;
4615
        $courseId = $courseInfo['real_id'];
4616
4617
        if (empty($session_id)) {
4618
            $table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4619
            $sql = "UPDATE $table SET legal_agreement = '1'
4620
                    WHERE user_id = $user_id AND c_id  = $courseId ";
4621
            Database::query($sql);
4622
        } else {
4623
            $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4624
            $sql = "UPDATE  $table SET legal_agreement = '1'
4625
                    WHERE user_id = $user_id AND c_id = $courseId AND session_id = $session_id";
4626
            Database::query($sql);
4627
        }
4628
4629
        return true;
4630
    }
4631
4632
    /**
4633
     * @param int $user_id
4634
     * @param int $course_id
4635
     * @param int $session_id
4636
     * @param int $url_id
4637
     *
4638
     * @return bool
4639
     */
4640
    public static function get_user_course_vote($user_id, $course_id, $session_id = 0, $url_id = 0)
4641
    {
4642
        $table_user_course_vote = Database::get_main_table(TABLE_MAIN_USER_REL_COURSE_VOTE);
4643
        $session_id = !isset($session_id) ? api_get_session_id() : intval($session_id);
4644
        $url_id = empty($url_id) ? api_get_current_access_url_id() : intval($url_id);
4645
        $user_id = intval($user_id);
4646
4647
        if (empty($user_id)) {
4648
            return false;
4649
        }
4650
4651
        $params = [
4652
            'user_id' => $user_id,
4653
            'c_id' => $course_id,
4654
            'session_id' => $session_id,
4655
            'url_id' => $url_id,
4656
        ];
4657
4658
        $result = Database::select(
4659
            'vote',
4660
            $table_user_course_vote,
4661
            [
4662
                'where' => [
4663
                    'user_id = ? AND c_id = ? AND session_id = ? AND url_id = ?' => $params,
4664
                ],
4665
            ],
4666
            'first'
4667
        );
4668
        if (!empty($result)) {
4669
            return $result['vote'];
4670
        }
4671
4672
        return false;
4673
    }
4674
4675
    /**
4676
     * @param int $course_id
4677
     * @param int $session_id
4678
     * @param int $url_id
4679
     *
4680
     * @return array
4681
     */
4682
    public static function get_course_ranking(
4683
        $course_id,
4684
        $session_id = 0,
4685
        $url_id = 0
4686
    ) {
4687
        $table_course_ranking = Database::get_main_table(TABLE_STATISTIC_TRACK_COURSE_RANKING);
4688
4689
        $session_id = empty($session_id) ? api_get_session_id() : intval($session_id);
4690
        $url_id = empty($url_id) ? api_get_current_access_url_id() : intval($url_id);
4691
        $now = api_get_utc_datetime();
4692
4693
        $params = [
4694
            'c_id' => $course_id,
4695
            'session_id' => $session_id,
4696
            'url_id' => $url_id,
4697
            'creation_date' => $now,
4698
        ];
4699
4700
        $result = Database::select(
4701
            'c_id, accesses, total_score, users',
4702
            $table_course_ranking,
4703
            ['where' => ['c_id = ? AND session_id = ? AND url_id = ?' => $params]],
4704
            'first'
4705
        );
4706
4707
        $point_average_in_percentage = 0;
4708
        $point_average_in_star = 0;
4709
        $users_who_voted = 0;
4710
4711
        if (!empty($result['users'])) {
4712
            $users_who_voted = $result['users'];
4713
            $point_average_in_percentage = round($result['total_score'] / $result['users'] * 100 / 5, 2);
4714
            $point_average_in_star = round($result['total_score'] / $result['users'], 1);
4715
        }
4716
4717
        $result['user_vote'] = false;
4718
        if (!api_is_anonymous()) {
4719
            $result['user_vote'] = self::get_user_course_vote(api_get_user_id(), $course_id, $session_id, $url_id);
4720
        }
4721
4722
        $result['point_average'] = $point_average_in_percentage;
4723
        $result['point_average_star'] = $point_average_in_star;
4724
        $result['users_who_voted'] = $users_who_voted;
4725
4726
        return $result;
4727
    }
4728
4729
    /**
4730
     * Updates the course ranking.
4731
     *
4732
     * @param int   course id
4733
     * @param int $session_id
4734
     * @param int    url id
4735
     * @param $points_to_add
4736
     * @param bool $add_access
4737
     * @param bool $add_user
4738
     *
4739
     * @return array
4740
     */
4741
    public static function update_course_ranking(
4742
        $course_id = 0,
4743
        $session_id = 0,
4744
        $url_id = 0,
4745
        $points_to_add = null,
4746
        $add_access = true,
4747
        $add_user = true
4748
    ) {
4749
        // Course catalog stats modifications see #4191
4750
        $table_course_ranking = Database::get_main_table(TABLE_STATISTIC_TRACK_COURSE_RANKING);
4751
        $now = api_get_utc_datetime();
4752
        $course_id = empty($course_id) ? api_get_course_int_id() : intval($course_id);
4753
        $session_id = empty($session_id) ? api_get_session_id() : intval($session_id);
4754
        $url_id = empty($url_id) ? api_get_current_access_url_id() : intval($url_id);
4755
4756
        $params = [
4757
            'c_id' => $course_id,
4758
            'session_id' => $session_id,
4759
            'url_id' => $url_id,
4760
            'creation_date' => $now,
4761
            'total_score' => 0,
4762
            'users' => 0,
4763
        ];
4764
4765
        $result = Database::select(
4766
            'id, accesses, total_score, users',
4767
            $table_course_ranking,
4768
            ['where' => ['c_id = ? AND session_id = ? AND url_id = ?' => $params]],
4769
            'first'
4770
        );
4771
4772
        // Problem here every time we load the courses/XXXX/index.php course home page we update the access
4773
        if (empty($result)) {
4774
            if ($add_access) {
4775
                $params['accesses'] = 1;
4776
            }
4777
            //The votes and users are empty
4778
            if (isset($points_to_add) && !empty($points_to_add)) {
4779
                $params['total_score'] = intval($points_to_add);
4780
            }
4781
            if ($add_user) {
4782
                $params['users'] = 1;
4783
            }
4784
            $result = Database::insert($table_course_ranking, $params);
4785
        } else {
4786
            $my_params = [];
4787
4788
            if ($add_access) {
4789
                $my_params['accesses'] = intval($result['accesses']) + 1;
4790
            }
4791
            if (isset($points_to_add) && !empty($points_to_add)) {
4792
                $my_params['total_score'] = $result['total_score'] + $points_to_add;
4793
            }
4794
            if ($add_user) {
4795
                $my_params['users'] = $result['users'] + 1;
4796
            }
4797
4798
            if (!empty($my_params)) {
4799
                $result = Database::update(
4800
                    $table_course_ranking,
4801
                    $my_params,
4802
                    ['c_id = ? AND session_id = ? AND url_id = ?' => $params]
4803
                );
4804
            }
4805
        }
4806
4807
        return $result;
4808
    }
4809
4810
    /**
4811
     * Add user vote to a course.
4812
     *
4813
     * @param   int user id
4814
     * @param   int vote [1..5]
4815
     * @param   int course id
4816
     * @param   int session id
4817
     * @param   int url id (access_url_id)
4818
     *
4819
     * @return false|string 'added', 'updated' or 'nothing'
4820
     */
4821
    public static function add_course_vote(
4822
        $user_id,
4823
        $vote,
4824
        $course_id,
4825
        $session_id = 0,
4826
        $url_id = 0
4827
    ) {
4828
        $table_user_course_vote = Database::get_main_table(TABLE_MAIN_USER_REL_COURSE_VOTE);
4829
        $course_id = empty($course_id) ? api_get_course_int_id() : intval($course_id);
4830
4831
        if (empty($course_id) || empty($user_id)) {
4832
            return false;
4833
        }
4834
4835
        if (!in_array($vote, [1, 2, 3, 4, 5])) {
4836
            return false;
4837
        }
4838
4839
        $session_id = empty($session_id) ? api_get_session_id() : intval($session_id);
4840
        $url_id = empty($url_id) ? api_get_current_access_url_id() : intval($url_id);
4841
        $vote = intval($vote);
4842
4843
        $params = [
4844
            'user_id' => intval($user_id),
4845
            'c_id' => $course_id,
4846
            'session_id' => $session_id,
4847
            'url_id' => $url_id,
4848
            'vote' => $vote,
4849
        ];
4850
4851
        $action_done = 'nothing';
4852
        $result = Database::select(
4853
            'id, vote',
4854
            $table_user_course_vote,
4855
            ['where' => ['user_id = ? AND c_id = ? AND session_id = ? AND url_id = ?' => $params]],
4856
            'first'
4857
        );
4858
4859
        if (empty($result)) {
4860
            Database::insert($table_user_course_vote, $params);
4861
            $points_to_add = $vote;
4862
            $add_user = true;
4863
            $action_done = 'added';
4864
        } else {
4865
            $my_params = ['vote' => $vote];
4866
            $points_to_add = $vote - $result['vote'];
4867
            $add_user = false;
4868
4869
            Database::update(
4870
                $table_user_course_vote,
4871
                $my_params,
4872
                ['user_id = ? AND c_id = ? AND session_id = ? AND url_id = ?' => $params]
4873
            );
4874
            $action_done = 'updated';
4875
        }
4876
4877
        // Current points
4878
        if (!empty($points_to_add)) {
4879
            self::update_course_ranking(
4880
                $course_id,
4881
                $session_id,
4882
                $url_id,
4883
                $points_to_add,
4884
                false,
4885
                $add_user
4886
            );
4887
        }
4888
4889
        return $action_done;
4890
    }
4891
4892
    /**
4893
     * Remove course ranking + user votes.
4894
     *
4895
     * @param int $course_id
4896
     * @param int $session_id
4897
     * @param int $url_id
4898
     */
4899
    public static function remove_course_ranking($course_id, $session_id, $url_id = null)
4900
    {
4901
        $table_course_ranking = Database::get_main_table(TABLE_STATISTIC_TRACK_COURSE_RANKING);
4902
        $table_user_course_vote = Database::get_main_table(TABLE_MAIN_USER_REL_COURSE_VOTE);
4903
4904
        if (!empty($course_id) && isset($session_id)) {
4905
            $url_id = empty($url_id) ? api_get_current_access_url_id() : intval($url_id);
4906
            $params = [
4907
                'c_id' => $course_id,
4908
                'session_id' => $session_id,
4909
                'url_id' => $url_id,
4910
            ];
4911
            Database::delete($table_course_ranking, ['c_id = ? AND session_id = ? AND url_id = ?' => $params]);
4912
            Database::delete($table_user_course_vote, ['c_id = ? AND session_id = ? AND url_id = ?' => $params]);
4913
        }
4914
    }
4915
4916
    /**
4917
     * Returns an array with the hottest courses.
4918
     *
4919
     * @param int $days  number of days
4920
     * @param int $limit number of hottest courses
4921
     *
4922
     * @return array
4923
     */
4924
    public static function return_hot_courses($days = 30, $limit = 6)
4925
    {
4926
        if (api_is_invitee()) {
4927
            return [];
4928
        }
4929
4930
        $limit = (int) $limit;
4931
        $userId = api_get_user_id();
4932
4933
        // Getting my courses
4934
        $my_course_list = self::get_courses_list_by_user_id($userId);
4935
4936
        $codeList = [];
4937
        foreach ($my_course_list as $course) {
4938
            $codeList[$course['real_id']] = $course['real_id'];
4939
        }
4940
4941
        if (api_is_drh()) {
4942
            $courses = self::get_courses_followed_by_drh($userId);
4943
            foreach ($courses as $course) {
4944
                $codeList[$course['real_id']] = $course['real_id'];
4945
            }
4946
        }
4947
4948
        $table_course_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
4949
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
4950
        $table_course_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4951
        $urlId = api_get_current_access_url_id();
4952
        //$table_course_access table uses the now() and interval ...
4953
        $now = api_get_utc_datetime();
4954
        $sql = "SELECT COUNT(course_access_id) course_count, a.c_id, visibility
4955
                FROM $table_course c
4956
                INNER JOIN $table_course_access a
4957
                ON (c.id = a.c_id)
4958
                INNER JOIN $table_course_url u
4959
                ON u.c_id = c.id
4960
                WHERE
4961
                    u.access_url_id = $urlId AND
4962
                    login_course_date <= '$now' AND
4963
                    login_course_date > DATE_SUB('$now', INTERVAL $days DAY) AND
4964
                    visibility <> ".COURSE_VISIBILITY_CLOSED." AND
4965
                    visibility <> ".COURSE_VISIBILITY_HIDDEN."
4966
                GROUP BY a.c_id
4967
                ORDER BY course_count DESC
4968
                LIMIT $limit
4969
            ";
4970
4971
        $result = Database::query($sql);
4972
        $courses = [];
4973
        if (Database::num_rows($result)) {
4974
            $courses = Database::store_result($result, 'ASSOC');
4975
            $courses = self::processHotCourseItem($courses, $codeList);
4976
        }
4977
4978
        return $courses;
4979
    }
4980
4981
    /**
4982
     * Returns an array with the "hand picked" popular courses.
4983
     * Courses only appear in this list if their extra field 'popular_courses'
4984
     * has been selected in the admin page of the course.
4985
     *
4986
     * @return array
4987
     */
4988
    public static function returnPopularCoursesHandPicked()
4989
    {
4990
        if (api_is_invitee()) {
4991
            return [];
4992
        }
4993
4994
        $userId = api_get_user_id();
4995
4996
        // Getting my courses
4997
        $my_course_list = self::get_courses_list_by_user_id($userId);
4998
4999
        $codeList = [];
5000
        foreach ($my_course_list as $course) {
5001
            $codeList[$course['real_id']] = $course['real_id'];
5002
        }
5003
5004
        if (api_is_drh()) {
5005
            $courses = self::get_courses_followed_by_drh($userId);
5006
            foreach ($courses as $course) {
5007
                $codeList[$course['real_id']] = $course['real_id'];
5008
            }
5009
        }
5010
5011
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
5012
        $tbl_course_field = Database::get_main_table(TABLE_EXTRA_FIELD);
5013
        $tbl_course_field_value = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
5014
5015
        //we filter the courses from the URL
5016
        $join_access_url = $where_access_url = '';
5017
        if (api_get_multiple_access_url()) {
5018
            $access_url_id = api_get_current_access_url_id();
5019
            if (-1 != $access_url_id) {
5020
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
5021
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course
5022
                ON url_rel_course.c_id = tcfv.item_id ";
5023
                $where_access_url = " AND access_url_id = $access_url_id ";
5024
            }
5025
        }
5026
5027
        $extraFieldType = EntityExtraField::COURSE_FIELD_TYPE;
5028
5029
        // get course list auto-register
5030
        $sql = "SELECT DISTINCT(c.id) AS c_id
5031
                FROM $tbl_course_field_value tcfv
5032
                INNER JOIN $tbl_course_field tcf
5033
                ON tcfv.field_id =  tcf.id $join_access_url
5034
                INNER JOIN $courseTable c
5035
                ON (c.id = tcfv.item_id)
5036
                WHERE
5037
                    tcf.extra_field_type = $extraFieldType AND
5038
                    tcf.variable = 'popular_courses' AND
5039
                    tcfv.value = 1 AND
5040
                    visibility <> ".COURSE_VISIBILITY_CLOSED." AND
5041
                    visibility <> ".COURSE_VISIBILITY_HIDDEN." $where_access_url";
5042
5043
        $result = Database::query($sql);
5044
        $courses = [];
5045
        if (Database::num_rows($result)) {
5046
            $courses = Database::store_result($result, 'ASSOC');
5047
            $courses = self::processHotCourseItem($courses, $codeList);
5048
        }
5049
5050
        return $courses;
5051
    }
5052
5053
    /**
5054
     * @param array $courses
5055
     * @param array $codeList
5056
     *
5057
     * @return mixed
5058
     */
5059
    public static function processHotCourseItem($courses, $codeList = [])
5060
    {
5061
        $hotCourses = [];
5062
        $ajax_url = api_get_path(WEB_AJAX_PATH).'course.ajax.php?a=add_course_vote';
5063
        $stok = Security::get_existing_token();
5064
        $user_id = api_get_user_id();
5065
5066
        foreach ($courses as $courseId) {
5067
            $course_info = api_get_course_info_by_id($courseId['c_id']);
5068
            $courseCode = $course_info['code'];
5069
            $categoryCode = !empty($course_info['categoryCode']) ? $course_info['categoryCode'] : "";
5070
            $my_course = $course_info;
5071
            $my_course['go_to_course_button'] = '';
5072
            $my_course['register_button'] = '';
5073
5074
            $access_link = self::get_access_link_by_user(
5075
                $user_id,
5076
                $course_info,
5077
                $codeList
5078
            );
5079
5080
            $userRegisteredInCourse = self::is_user_subscribed_in_course($user_id, $course_info['code']);
5081
            $userRegisteredInCourseAsTeacher = self::isCourseTeacher($user_id, $courseId['c_id']);
5082
            $userRegistered = $userRegisteredInCourse && $userRegisteredInCourseAsTeacher;
5083
            $my_course['is_course_student'] = $userRegisteredInCourse;
5084
            $my_course['is_course_teacher'] = $userRegisteredInCourseAsTeacher;
5085
            $my_course['is_registered'] = $userRegistered;
5086
            $my_course['title_cut'] = cut($course_info['title'], 45);
5087
5088
            // Course visibility
5089
            if ($access_link && in_array('register', $access_link)) {
5090
                $my_course['register_button'] = Display::url(
5091
                    get_lang('Subscribe').' '.
5092
                    Display::returnFontAwesomeIcon('sign-in'),
5093
                    api_get_path(WEB_COURSE_PATH).$course_info['path'].
5094
                     '/index.php?action=subscribe&sec_token='.$stok,
5095
                    [
5096
                        'class' => 'btn btn-success btn-sm',
5097
                        'title' => get_lang('Subscribe'),
5098
                        'aria-label' => get_lang('Subscribe'),
5099
                    ]
5100
                );
5101
            }
5102
5103
            if ($access_link && in_array('enter', $access_link) ||
5104
                COURSE_VISIBILITY_OPEN_WORLD == $course_info['visibility']
5105
            ) {
5106
                $my_course['go_to_course_button'] = Display::url(
5107
                    get_lang('Go to the course').' '.
5108
                    Display::returnFontAwesomeIcon('share'),
5109
                    api_get_path(WEB_COURSE_PATH).$course_info['path'].'/index.php',
5110
                    [
5111
                        'class' => 'btn btn-default btn-sm',
5112
                        'title' => get_lang('Go to the course'),
5113
                        'aria-label' => get_lang('Go to the course'),
5114
                    ]
5115
                );
5116
            }
5117
5118
            if ($access_link && in_array('unsubscribe', $access_link)) {
5119
                $my_course['unsubscribe_button'] = Display::url(
5120
                    get_lang('Unsubscribe').' '.
5121
                    Display::returnFontAwesomeIcon('sign-out'),
5122
                    api_get_path(WEB_CODE_PATH).'auth/courses.php?action=unsubscribe&unsubscribe='.$courseCode
5123
                    .'&sec_token='.$stok.'&category_code='.$categoryCode,
5124
                    [
5125
                        'class' => 'btn btn-danger btn-sm',
5126
                        'title' => get_lang('Unsubscribe'),
5127
                        'aria-label' => get_lang('Unsubscribe'),
5128
                    ]
5129
                );
5130
            }
5131
5132
            // start buycourse validation
5133
            // display the course price and buy button if the buycourses plugin is enabled and this course is configured
5134
            $plugin = BuyCoursesPlugin::create();
5135
            $isThisCourseInSale = $plugin->buyCoursesForGridCatalogValidator(
5136
                $course_info['real_id'],
5137
                BuyCoursesPlugin::PRODUCT_TYPE_COURSE
5138
            );
5139
            if ($isThisCourseInSale) {
5140
                // set the price label
5141
                $my_course['price'] = $isThisCourseInSale['html'];
5142
                // set the Buy button instead register.
5143
                if ($isThisCourseInSale['verificator'] && !empty($my_course['register_button'])) {
5144
                    $my_course['register_button'] = $plugin->returnBuyCourseButton(
5145
                        $course_info['real_id'],
5146
                        BuyCoursesPlugin::PRODUCT_TYPE_COURSE
5147
                    );
5148
                }
5149
            }
5150
            // end buycourse validation
5151
5152
            // Description
5153
            $my_course['description_button'] = self::returnDescriptionButton($course_info);
5154
            $my_course['teachers'] = self::getTeachersFromCourse($course_info['real_id'], true);
5155
            $point_info = self::get_course_ranking($course_info['real_id'], 0);
5156
            $my_course['rating_html'] = '';
5157
            if (false === api_get_configuration_value('hide_course_rating')) {
5158
                $my_course['rating_html'] = Display::return_rating_system(
5159
                    'star_'.$course_info['real_id'],
5160
                    $ajax_url.'&course_id='.$course_info['real_id'],
5161
                    $point_info
5162
                );
5163
            }
5164
            $hotCourses[] = $my_course;
5165
        }
5166
5167
        return $hotCourses;
5168
    }
5169
5170
    public function totalSubscribedUsersInCourses($urlId)
5171
    {
5172
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
5173
        $table_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
5174
        $courseUsers = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5175
5176
        $urlId = (int) $urlId;
5177
5178
        $sql = "SELECT count(cu.user_id) count
5179
                FROM $courseUsers cu
5180
                INNER JOIN $table_course_rel_access_url u
5181
                ON cu.c_id = u.c_id
5182
                WHERE
5183
                    relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
5184
                    u.access_url_id = $urlId AND
5185
                    visibility <> ".COURSE_VISIBILITY_CLOSED." AND
5186
                    visibility <> ".COURSE_VISIBILITY_HIDDEN."
5187
                     ";
5188
5189
        $res = Database::query($sql);
5190
        $row = Database::fetch_array($res);
5191
5192
        return $row['count'];
5193
    }
5194
5195
    /**
5196
     * Get courses count.
5197
     *
5198
     * @param int $access_url_id Access URL ID (optional)
5199
     * @param int $visibility
5200
     *
5201
     * @return int Number of courses
5202
     */
5203
    public static function count_courses($access_url_id = null, $visibility = null)
5204
    {
5205
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
5206
        $table_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
5207
        $sql = "SELECT count(c.id) FROM $table_course c";
5208
        if (!empty($access_url_id) && $access_url_id == intval($access_url_id)) {
5209
            $sql .= ", $table_course_rel_access_url u
5210
                    WHERE c.id = u.c_id AND u.access_url_id = $access_url_id";
5211
            if (!empty($visibility)) {
5212
                $visibility = intval($visibility);
5213
                $sql .= " AND visibility = $visibility ";
5214
            }
5215
        } else {
5216
            if (!empty($visibility)) {
5217
                $visibility = intval($visibility);
5218
                $sql .= " WHERE visibility = $visibility ";
5219
            }
5220
        }
5221
5222
        $res = Database::query($sql);
5223
        $row = Database::fetch_row($res);
5224
5225
        return $row[0];
5226
    }
5227
5228
    /**
5229
     * Get active courses count.
5230
     * Active = all courses except the ones with hidden visibility.
5231
     *
5232
     * @param int $urlId Access URL ID (optional)
5233
     *
5234
     * @return int Number of courses
5235
     */
5236
    public static function countActiveCourses($urlId = null)
5237
    {
5238
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
5239
        $table_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
5240
        $sql = "SELECT count(c.id) FROM $table_course c";
5241
        if (!empty($urlId)) {
5242
            $urlId = (int) $urlId;
5243
            $sql .= ", $table_course_rel_access_url u
5244
                    WHERE
5245
                        c.id = u.c_id AND
5246
                        u.access_url_id = $urlId AND
5247
                        visibility <> ".COURSE_VISIBILITY_HIDDEN;
5248
        } else {
5249
            $sql .= " WHERE visibility <> ".COURSE_VISIBILITY_HIDDEN;
5250
        }
5251
        $res = Database::query($sql);
5252
        $row = Database::fetch_row($res);
5253
5254
        return $row[0];
5255
    }
5256
5257
    /**
5258
     * Returns the SQL conditions to filter course only visible by the user in the catalogue.
5259
     *
5260
     * @param string $courseTableAlias Alias of the course table
5261
     * @param bool   $hideClosed       Whether to hide closed and hidden courses
5262
     * @param bool   $checkHidePrivate
5263
     *
5264
     * @return string SQL conditions
5265
     */
5266
    public static function getCourseVisibilitySQLCondition($courseTableAlias, $hideClosed = false, $checkHidePrivate = true)
5267
    {
5268
        $visibilityCondition = '';
5269
        if ($checkHidePrivate) {
5270
            $hidePrivateSetting = api_get_setting('course_catalog_hide_private');
5271
            if ('true' === $hidePrivateSetting) {
5272
                $visibilityCondition .= " AND $courseTableAlias.visibility <> ".COURSE_VISIBILITY_REGISTERED;
5273
            }
5274
        }
5275
        if ($hideClosed) {
5276
            $visibilityCondition .= " AND $courseTableAlias.visibility NOT IN (".COURSE_VISIBILITY_CLOSED.','.COURSE_VISIBILITY_HIDDEN.')';
5277
        }
5278
5279
        // Check if course have users allowed to see it in the catalogue, then show only if current user is allowed to see it
5280
        $currentUserId = api_get_user_id();
5281
        $restrictedCourses = self::getCatalogCourseList(true);
5282
        $allowedCoursesToCurrentUser = self::getCatalogCourseList(true, $currentUserId);
5283
        if (!empty($restrictedCourses)) {
5284
            $visibilityCondition .= ' AND ('.$courseTableAlias.'.code NOT IN ("'.implode('","', $restrictedCourses).'")';
5285
            $visibilityCondition .= ' OR '.$courseTableAlias.'.code IN ("'.implode('","', $allowedCoursesToCurrentUser).'"))';
5286
        }
5287
5288
        // Check if course have users denied to see it in the catalogue, then show only if current user is not denied to see it
5289
        $restrictedCourses = self::getCatalogCourseList(false);
5290
        $notAllowedCoursesToCurrentUser = self::getCatalogCourseList(false, $currentUserId);
5291
        if (!empty($restrictedCourses)) {
5292
            $visibilityCondition .= ' AND ('.$courseTableAlias.'.code NOT IN ("'.implode('","', $restrictedCourses).'")';
5293
            $visibilityCondition .= ' OR '.$courseTableAlias.'.code NOT IN ("'.implode('","', $notAllowedCoursesToCurrentUser).'"))';
5294
        }
5295
5296
        return $visibilityCondition;
5297
    }
5298
5299
    /**
5300
     * Return a link to go to the course, validating the visibility of the
5301
     * course and the user status.
5302
     *
5303
     * @param int $uid User ID
5304
     * @param array Course details array
5305
     * @param array  List of courses to which the user is subscribed (if not provided, will be generated)
5306
     *
5307
     * @return mixed 'enter' for a link to go to the course or 'register' for a link to subscribe, or false if no access
5308
     */
5309
    public static function get_access_link_by_user($uid, $course, $user_courses = [])
5310
    {
5311
        if (empty($uid) || empty($course)) {
5312
            return false;
5313
        }
5314
5315
        if (empty($user_courses)) {
5316
            // get the array of courses to which the user is subscribed
5317
            $user_courses = self::get_courses_list_by_user_id($uid);
5318
            foreach ($user_courses as $k => $v) {
5319
                $user_courses[$k] = $v['real_id'];
5320
            }
5321
        }
5322
5323
        if (!isset($course['real_id']) && empty($course['real_id'])) {
5324
            $course = api_get_course_info($course['code']);
5325
        }
5326
5327
        if (COURSE_VISIBILITY_HIDDEN == $course['visibility']) {
5328
            return [];
5329
        }
5330
5331
        $is_admin = api_is_platform_admin_by_id($uid);
5332
        $options = [];
5333
        // Register button
5334
        if (!api_is_anonymous($uid) &&
5335
            (
5336
            (COURSE_VISIBILITY_OPEN_WORLD == $course['visibility'] || COURSE_VISIBILITY_OPEN_PLATFORM == $course['visibility'])
5337
                //$course['visibility'] == COURSE_VISIBILITY_REGISTERED && $course['subscribe'] == SUBSCRIBE_ALLOWED
5338
            ) &&
5339
            SUBSCRIBE_ALLOWED == $course['subscribe'] &&
5340
            (!in_array($course['real_id'], $user_courses) || empty($user_courses))
5341
        ) {
5342
            $options[] = 'register';
5343
        }
5344
5345
        $isLogin = !api_is_anonymous();
5346
5347
        // Go To Course button (only if admin, if course public or if student already subscribed)
5348
        if ($is_admin ||
5349
            COURSE_VISIBILITY_OPEN_WORLD == $course['visibility'] && empty($course['registration_code']) ||
5350
            ($isLogin && COURSE_VISIBILITY_OPEN_PLATFORM == $course['visibility'] && empty($course['registration_code'])) ||
5351
            (in_array($course['real_id'], $user_courses) && COURSE_VISIBILITY_CLOSED != $course['visibility'])
5352
        ) {
5353
            $options[] = 'enter';
5354
        }
5355
5356
        if ($is_admin ||
5357
            COURSE_VISIBILITY_OPEN_WORLD == $course['visibility'] && empty($course['registration_code']) ||
5358
            ($isLogin && COURSE_VISIBILITY_OPEN_PLATFORM == $course['visibility'] && empty($course['registration_code'])) ||
5359
            (in_array($course['real_id'], $user_courses) && COURSE_VISIBILITY_CLOSED != $course['visibility'])
5360
        ) {
5361
            $options[] = 'enter';
5362
        }
5363
5364
        if (COURSE_VISIBILITY_HIDDEN != $course['visibility'] &&
5365
            empty($course['registration_code']) &&
5366
            UNSUBSCRIBE_ALLOWED == $course['unsubscribe'] &&
5367
            $isLogin &&
5368
            in_array($course['real_id'], $user_courses)
5369
        ) {
5370
            $options[] = 'unsubscribe';
5371
        }
5372
5373
        return $options;
5374
    }
5375
5376
    /**
5377
     * @param array          $courseInfo
5378
     * @param array          $teachers
5379
     * @param bool           $deleteTeachersNotInList
5380
     * @param bool           $editTeacherInSessions
5381
     * @param bool           $deleteSessionTeacherNotInList
5382
     * @param array          $teacherBackup
5383
     * @param Monolog\Logger $logger
5384
     *
5385
     * @return false|null
5386
     */
5387
    public static function updateTeachers(
5388
        $courseInfo,
5389
        $teachers,
5390
        $deleteTeachersNotInList = true,
5391
        $editTeacherInSessions = false,
5392
        $deleteSessionTeacherNotInList = false,
5393
        $teacherBackup = [],
5394
        $logger = null
5395
    ) {
5396
        if (!is_array($teachers)) {
5397
            $teachers = [$teachers];
5398
        }
5399
5400
        if (empty($courseInfo) || !isset($courseInfo['real_id'])) {
5401
            return false;
5402
        }
5403
5404
        $teachers = array_filter($teachers);
5405
        $courseId = $courseInfo['real_id'];
5406
        $course_code = $courseInfo['code'];
5407
5408
        $course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5409
        $alreadyAddedTeachers = self::get_teacher_list_from_course_code($course_code);
5410
5411
        if ($deleteTeachersNotInList) {
5412
            // Delete only teacher relations that doesn't match the selected teachers
5413
            $cond = null;
5414
            if (count($teachers) > 0) {
5415
                foreach ($teachers as $key) {
5416
                    $key = Database::escape_string($key);
5417
                    $cond .= " AND user_id <> '".$key."'";
5418
                }
5419
            }
5420
5421
            // Recover user categories
5422
            $sql = "SELECT * FROM $course_user_table
5423
                    WHERE c_id = $courseId AND status = 1 AND relation_type = 0 ".$cond;
5424
            $result = Database::query($sql);
5425
            if (Database::num_rows($result)) {
5426
                $teachersToDelete = Database::store_result($result, 'ASSOC');
5427
                foreach ($teachersToDelete as $data) {
5428
                    $userId = $data['user_id'];
5429
                    $teacherBackup[$userId][$course_code] = $data;
5430
                }
5431
            }
5432
5433
            $sql = "DELETE FROM $course_user_table
5434
                    WHERE c_id = $courseId AND status = 1 AND relation_type = 0 ".$cond;
5435
5436
            Database::query($sql);
5437
        }
5438
5439
        if (count($teachers) > 0) {
5440
            foreach ($teachers as $userId) {
5441
                $userId = intval($userId);
5442
                // We check if the teacher is already subscribed in this course
5443
                $sql = "SELECT 1 FROM $course_user_table
5444
                        WHERE user_id = $userId AND c_id = $courseId";
5445
                $result = Database::query($sql);
5446
                if (Database::num_rows($result)) {
5447
                    $sql = "UPDATE $course_user_table
5448
                            SET status = 1
5449
                            WHERE c_id = $courseId AND user_id = $userId ";
5450
                } else {
5451
                    $userCourseCategory = '0';
5452
                    if (isset($teacherBackup[$userId]) &&
5453
                        isset($teacherBackup[$userId][$course_code])
5454
                    ) {
5455
                        $courseUserData = $teacherBackup[$userId][$course_code];
5456
                        $userCourseCategory = $courseUserData['user_course_cat'];
5457
                        if ($logger) {
5458
                            $logger->debug("Recovering user_course_cat: $userCourseCategory");
5459
                        }
5460
                    }
5461
5462
                    $sql = "INSERT INTO $course_user_table SET
5463
                            c_id = $courseId,
5464
                            user_id = $userId,
5465
                            status = 1,
5466
                            is_tutor = 0,
5467
                            sort = 0,
5468
                            relation_type = 0,
5469
                            user_course_cat = $userCourseCategory
5470
                    ";
5471
                }
5472
                Database::query($sql);
5473
            }
5474
        }
5475
5476
        if ($editTeacherInSessions) {
5477
            $sessions = SessionManager::get_session_by_course($courseId);
5478
            if (!empty($sessions)) {
5479
                if ($logger) {
5480
                    $logger->debug("Edit teachers in sessions");
5481
                }
5482
                foreach ($sessions as $session) {
5483
                    $sessionId = $session['id'];
5484
                    // Remove old and add new
5485
                    if ($deleteSessionTeacherNotInList) {
5486
                        foreach ($teachers as $userId) {
5487
                            if ($logger) {
5488
                                $logger->debug("Set coach #$userId in session #$sessionId of course #$courseId ");
5489
                            }
5490
                            SessionManager::set_coach_to_course_session(
5491
                                $userId,
5492
                                $sessionId,
5493
                                $courseId
5494
                            );
5495
                        }
5496
5497
                        $teachersToDelete = [];
5498
                        if (!empty($alreadyAddedTeachers)) {
5499
                            $teachersToDelete = array_diff(array_keys($alreadyAddedTeachers), $teachers);
5500
                        }
5501
5502
                        if (!empty($teachersToDelete)) {
5503
                            foreach ($teachersToDelete as $userId) {
5504
                                if ($logger) {
5505
                                    $logger->debug("Delete coach #$userId in session #$sessionId of course #$courseId ");
5506
                                }
5507
                                SessionManager::set_coach_to_course_session(
5508
                                    $userId,
5509
                                    $sessionId,
5510
                                    $courseId,
5511
                                    true
5512
                                );
5513
                            }
5514
                        }
5515
                    } else {
5516
                        // Add new teachers only
5517
                        foreach ($teachers as $userId) {
5518
                            if ($logger) {
5519
                                $logger->debug("Add coach #$userId in session #$sessionId of course #$courseId ");
5520
                            }
5521
                            SessionManager::set_coach_to_course_session(
5522
                                $userId,
5523
                                $sessionId,
5524
                                $courseId
5525
                            );
5526
                        }
5527
                    }
5528
                }
5529
            }
5530
        }
5531
    }
5532
5533
    /**
5534
     * Course available settings variables see c_course_setting table.
5535
     *
5536
     * @return array
5537
     */
5538
    public static function getCourseSettingVariables(AppPlugin $appPlugin)
5539
    {
5540
        $pluginCourseSettings = $appPlugin->getAllPluginCourseSettings();
5541
        $courseSettings = [
5542
            // Get allow_learning_path_theme from table
5543
            'allow_learning_path_theme',
5544
            // Get allow_open_chat_window from table
5545
            'allow_open_chat_window',
5546
            'allow_public_certificates',
5547
            // Get allow_user_edit_agenda from table
5548
            'allow_user_edit_agenda',
5549
            // Get allow_user_edit_announcement from table
5550
            'allow_user_edit_announcement',
5551
            // Get allow_user_image_forum from table
5552
            'allow_user_image_forum',
5553
            //Get allow show user list
5554
            'allow_user_view_user_list',
5555
            // Get course_theme from table
5556
            'course_theme',
5557
            //Get allow show user list
5558
            'display_info_advance_inside_homecourse',
5559
            'documents_default_visibility',
5560
            // Get send_mail_setting (work)from table
5561
            'email_alert_manager_on_new_doc',
5562
            // Get send_mail_setting (work)from table
5563
            'email_alert_manager_on_new_quiz',
5564
            // Get send_mail_setting (dropbox) from table
5565
            'email_alert_on_new_doc_dropbox',
5566
            'email_alert_students_on_new_homework',
5567
            // Get send_mail_setting (auth)from table
5568
            'email_alert_to_teacher_on_new_user_in_course',
5569
            'enable_lp_auto_launch',
5570
            'enable_exercise_auto_launch',
5571
            'enable_document_auto_launch',
5572
            'pdf_export_watermark_text',
5573
            'show_system_folders',
5574
            'exercise_invisible_in_session',
5575
            'enable_forum_auto_launch',
5576
            'show_course_in_user_language',
5577
            'email_to_teachers_on_new_work_feedback',
5578
            'student_delete_own_publication',
5579
            'hide_forum_notifications',
5580
            'quiz_question_limit_per_day',
5581
            'subscribe_users_to_forum_notifications',
5582
        ];
5583
5584
        $courseModels = ExerciseLib::getScoreModels();
5585
        if (!empty($courseModels)) {
5586
            $courseSettings[] = 'score_model_id';
5587
        }
5588
5589
        $allowLPReturnLink = api_get_setting('allow_lp_return_link');
5590
        if ('true' === $allowLPReturnLink) {
5591
            $courseSettings[] = 'lp_return_link';
5592
        }
5593
5594
        if (!empty($pluginCourseSettings)) {
5595
            $courseSettings = array_merge(
5596
                $courseSettings,
5597
                $pluginCourseSettings
5598
            );
5599
        }
5600
5601
        return $courseSettings;
5602
    }
5603
5604
    /**
5605
     * @param string       $variable
5606
     * @param string|array $value
5607
     * @param int          $courseId
5608
     *
5609
     * @return bool
5610
     */
5611
    public static function saveCourseConfigurationSetting(AppPlugin $appPlugin, $variable, $value, $courseId)
5612
    {
5613
        $settingList = self::getCourseSettingVariables($appPlugin);
5614
5615
        if (!in_array($variable, $settingList)) {
5616
            return false;
5617
        }
5618
5619
        $courseSettingTable = Database::get_course_table(TABLE_COURSE_SETTING);
5620
5621
        if (is_array($value)) {
5622
            $value = implode(',', $value);
5623
        }
5624
5625
        $settingFromDatabase = self::getCourseSetting($variable, $courseId);
5626
5627
        if (!empty($settingFromDatabase)) {
5628
            // Update
5629
            Database::update(
5630
                $courseSettingTable,
5631
                ['value' => $value],
5632
                ['variable = ? AND c_id = ?' => [$variable, $courseId]]
5633
            );
5634
5635
            if ($settingFromDatabase['value'] != $value) {
5636
                Event::addEvent(
5637
                    LOG_COURSE_SETTINGS_CHANGED,
5638
                    $variable,
5639
                    $settingFromDatabase['value']." -> $value"
5640
                );
5641
            }
5642
        } else {
5643
            // Create
5644
            Database::insert(
5645
                $courseSettingTable,
5646
                [
5647
                    'title' => $variable,
5648
                    'value' => $value,
5649
                    'c_id' => $courseId,
5650
                    'variable' => $variable,
5651
                ]
5652
            );
5653
5654
            Event::addEvent(
5655
                LOG_COURSE_SETTINGS_CHANGED,
5656
                $variable,
5657
                $value
5658
            );
5659
        }
5660
5661
        return true;
5662
    }
5663
5664
    /**
5665
     * Get course setting.
5666
     *
5667
     * @param string $variable
5668
     * @param int    $courseId
5669
     *
5670
     * @return array
5671
     */
5672
    public static function getCourseSetting($variable, $courseId)
5673
    {
5674
        $courseSetting = Database::get_course_table(TABLE_COURSE_SETTING);
5675
        $courseId = (int) $courseId;
5676
        $variable = Database::escape_string($variable);
5677
        $sql = "SELECT variable, value FROM $courseSetting
5678
                WHERE c_id = $courseId AND variable = '$variable'";
5679
        $result = Database::query($sql);
5680
5681
        return Database::fetch_array($result);
5682
    }
5683
5684
    public static function saveSettingChanges($courseInfo, $params)
5685
    {
5686
        if (empty($courseInfo) || empty($params)) {
5687
            return false;
5688
        }
5689
5690
        $userId = api_get_user_id();
5691
        $now = api_get_utc_datetime();
5692
5693
        foreach ($params as $name => $value) {
5694
            $emptyValue = ' - ';
5695
            if (isset($courseInfo[$name]) && $courseInfo[$name] != $value) {
5696
                if ('' !== $courseInfo[$name]) {
5697
                    $emptyValue = $courseInfo[$name];
5698
                }
5699
5700
                $changedTo = $emptyValue.' -> '.$value;
5701
5702
                Event::addEvent(
5703
                    LOG_COURSE_SETTINGS_CHANGED,
5704
                    $name,
5705
                    $changedTo,
5706
                    $now,
5707
                    $userId,
5708
                    $courseInfo['real_id']
5709
                );
5710
            }
5711
        }
5712
5713
        return true;
5714
    }
5715
5716
    /**
5717
     * Get information from the track_e_course_access table.
5718
     *
5719
     * @param int    $courseId
5720
     * @param int    $sessionId
5721
     * @param string $startDate
5722
     * @param string $endDate
5723
     *
5724
     * @return array
5725
     */
5726
    public static function getCourseAccessPerCourseAndSession(
5727
        $courseId,
5728
        $sessionId,
5729
        $startDate,
5730
        $endDate
5731
    ) {
5732
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5733
        $courseId = (int) $courseId;
5734
        $sessionId = (int) $sessionId;
5735
        $startDate = Database::escape_string($startDate);
5736
        $endDate = Database::escape_string($endDate);
5737
5738
        $sql = "SELECT * FROM $table
5739
                WHERE
5740
                    c_id = $courseId AND
5741
                    session_id = $sessionId AND
5742
                    login_course_date BETWEEN '$startDate' AND '$endDate'
5743
                ";
5744
5745
        $result = Database::query($sql);
5746
5747
        return Database::store_result($result);
5748
    }
5749
5750
    /**
5751
     * Get login information from the track_e_course_access table, for any
5752
     * course in the given session.
5753
     *
5754
     * @param int $sessionId
5755
     * @param int $userId
5756
     *
5757
     * @return array
5758
     */
5759
    public static function getFirstCourseAccessPerSessionAndUser($sessionId, $userId)
5760
    {
5761
        $sessionId = (int) $sessionId;
5762
        $userId = (int) $userId;
5763
5764
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5765
        $sql = "SELECT * FROM $table
5766
                WHERE session_id = $sessionId AND user_id = $userId
5767
                ORDER BY login_course_date ASC
5768
                LIMIT 1";
5769
5770
        $result = Database::query($sql);
5771
        $courseAccess = [];
5772
        if (Database::num_rows($result)) {
5773
            $courseAccess = Database::fetch_array($result, 'ASSOC');
5774
        }
5775
5776
        return $courseAccess;
5777
    }
5778
5779
    /**
5780
     * @param int  $courseId
5781
     * @param int  $sessionId
5782
     * @param bool $getAllSessions
5783
     *
5784
     * @return mixed
5785
     */
5786
    public static function getCountForum(
5787
        $courseId,
5788
        $sessionId = 0,
5789
        $getAllSessions = false
5790
    ) {
5791
        $forum = Database::get_course_table(TABLE_FORUM);
5792
        if ($getAllSessions) {
5793
            $sql = "SELECT count(*) as count
5794
                    FROM $forum f
5795
                    WHERE f.c_id = %s";
5796
        } else {
5797
            $sql = "SELECT count(*) as count
5798
                    FROM $forum f
5799
                    WHERE f.c_id = %s and f.session_id = %s";
5800
        }
5801
5802
        $sql = sprintf($sql, intval($courseId), intval($sessionId));
5803
        $result = Database::query($sql);
5804
        $row = Database::fetch_array($result);
5805
5806
        return $row['count'];
5807
    }
5808
5809
    /**
5810
     * @param int $userId
5811
     * @param int $courseId
5812
     * @param int $sessionId
5813
     *
5814
     * @return mixed
5815
     */
5816
    public static function getCountPostInForumPerUser(
5817
        $userId,
5818
        $courseId,
5819
        $sessionId = 0
5820
    ) {
5821
        $forum = Database::get_course_table(TABLE_FORUM);
5822
        $forum_post = Database::get_course_table(TABLE_FORUM_POST);
5823
5824
        $sql = "SELECT count(distinct post_id) as count
5825
                FROM $forum_post p
5826
                INNER JOIN $forum f
5827
                ON f.forum_id = p.forum_id AND f.c_id = p.c_id
5828
                WHERE p.poster_id = %s and f.session_id = %s and p.c_id = %s";
5829
5830
        $sql = sprintf(
5831
            $sql,
5832
            intval($userId),
5833
            intval($sessionId),
5834
            intval($courseId)
5835
        );
5836
5837
        $result = Database::query($sql);
5838
        $row = Database::fetch_array($result);
5839
5840
        return $row['count'];
5841
    }
5842
5843
    /**
5844
     * @param int $userId
5845
     * @param int $courseId
5846
     * @param int $sessionId
5847
     *
5848
     * @return mixed
5849
     */
5850
    public static function getCountForumPerUser(
5851
        $userId,
5852
        $courseId,
5853
        $sessionId = 0
5854
    ) {
5855
        $forum = Database::get_course_table(TABLE_FORUM);
5856
        $forum_post = Database::get_course_table(TABLE_FORUM_POST);
5857
5858
        $sql = "SELECT count(distinct f.forum_id) as count
5859
                FROM $forum_post p
5860
                INNER JOIN $forum f
5861
                ON f.forum_id = p.forum_id AND f.c_id = p.c_id
5862
                WHERE p.poster_id = %s and f.session_id = %s and p.c_id = %s";
5863
5864
        $sql = sprintf(
5865
            $sql,
5866
            intval($userId),
5867
            intval($sessionId),
5868
            intval($courseId)
5869
        );
5870
5871
        $result = Database::query($sql);
5872
        $row = Database::fetch_array($result);
5873
5874
        return $row['count'];
5875
    }
5876
5877
    /**
5878
     * Returns the course name from a given code.
5879
     *
5880
     * @param string $code
5881
     *
5882
     * @return string
5883
     */
5884
    public static function getCourseNameFromCode($code)
5885
    {
5886
        $tbl_main_categories = Database::get_main_table(TABLE_MAIN_COURSE);
5887
        $code = Database::escape_string($code);
5888
        $sql = "SELECT title
5889
                FROM $tbl_main_categories
5890
                WHERE code = '$code'";
5891
        $result = Database::query($sql);
5892
        if ($col = Database::fetch_array($result)) {
5893
            return $col['title'];
5894
        }
5895
    }
5896
5897
    /**
5898
     * Generates a course code from a course title.
5899
     *
5900
     * @todo Such a function might be useful in other places too. It might be moved in the CourseManager class.
5901
     * @todo the function might be upgraded for avoiding code duplications (currently,
5902
     * it might suggest a code that is already in use)
5903
     *
5904
     * @param string $title A course title
5905
     *
5906
     * @return string A proposed course code
5907
     *                +
5908
     * @assert (null,null) === false
5909
     * @assert ('ABC_DEF', null) === 'ABCDEF'
5910
     * @assert ('ABC09*^[%A', null) === 'ABC09A'
5911
     */
5912
    public static function generate_course_code($title)
5913
    {
5914
        return substr(
5915
            preg_replace('/[^A-Z0-9]/', '', strtoupper(api_replace_dangerous_char($title))),
5916
            0,
5917
            self::MAX_COURSE_LENGTH_CODE
5918
        );
5919
    }
5920
5921
    /**
5922
     * this function gets all the users of the course,
5923
     * including users from linked courses.
5924
     *
5925
     * @param $filterByActive
5926
     *
5927
     * @return array
5928
     */
5929
    public static function getCourseUsers($filterByActive = null)
5930
    {
5931
        // This would return only the users from real courses:
5932
        return self::get_user_list_from_course_code(
5933
            api_get_course_id(),
5934
            api_get_session_id(),
5935
            null,
5936
            null,
5937
            null,
5938
            null,
5939
            false,
5940
            false,
5941
            [],
5942
            [],
5943
            [],
5944
            $filterByActive
5945
        );
5946
    }
5947
5948
    /**
5949
     * this function gets all the groups of the course,
5950
     * not including linked courses.
5951
     *
5952
     * @return CGroup[]
5953
     */
5954
    public static function getCourseGroups()
5955
    {
5956
        $sessionId = api_get_session_id();
5957
        $courseCode = api_get_course_id();
5958
        if (0 != $sessionId) {
5959
            $groupList = self::get_group_list_of_course(
5960
                $courseCode,
5961
                $sessionId,
5962
                1
5963
            );
5964
        } else {
5965
            $groupList = self::get_group_list_of_course(
5966
                $courseCode,
5967
                0,
5968
                1
5969
            );
5970
        }
5971
5972
        return $groupList;
5973
    }
5974
5975
    /**
5976
     * @param FormValidator $form
5977
     * @param array         $alreadySelected
5978
     *
5979
     * @return HTML_QuickForm_element
5980
     */
5981
    public static function addUserGroupMultiSelect(&$form, $alreadySelected, $addShortCut = false)
5982
    {
5983
        $userList = self::getCourseUsers(true);
5984
        $groupList = self::getCourseGroups();
5985
5986
        $array = self::buildSelectOptions(
5987
            $groupList,
5988
            $userList,
5989
            $alreadySelected
5990
        );
5991
5992
        $result = [];
5993
        foreach ($array as $content) {
5994
            $result[$content['value']] = $content['content'];
5995
        }
5996
5997
        $multiple = $form->addElement(
5998
            'advmultiselect',
5999
            'users',
6000
            get_lang('Users'),
6001
            $result,
6002
            ['select_all_checkbox' => true, 'id' => 'users']
6003
        );
6004
6005
        $sessionId = api_get_session_id();
6006
        if ($addShortCut && empty($sessionId)) {
6007
            $addStudents = [];
6008
            foreach ($userList as $user) {
6009
                if (STUDENT == $user['status_rel']) {
6010
                    $addStudents[] = $user['user_id'];
6011
                }
6012
            }
6013
            if (!empty($addStudents)) {
6014
                $form->addHtml(
6015
                    '<script>
6016
                    $(function() {
6017
                        $("#add_students").on("click", function() {
6018
                            var addStudents = '.json_encode($addStudents).';
6019
                            $.each(addStudents, function( index, value ) {
6020
                                var option = $("#users option[value=\'USER:"+value+"\']");
6021
                                if (option.val()) {
6022
                                    $("#users_to").append(new Option(option.text(), option.val()))
6023
                                    option.remove();
6024
                                }
6025
                            });
6026
6027
                            return false;
6028
                        });
6029
                    });
6030
                    </script>'
6031
                );
6032
6033
                $form->addLabel(
6034
                    '',
6035
                    Display::url(get_lang('AddStudent'), '#', ['id' => 'add_students', 'class' => 'btn btn-primary'])
6036
                );
6037
            }
6038
        }
6039
6040
        return $multiple;
6041
    }
6042
6043
    /**
6044
     * This function separates the users from the groups
6045
     * users have a value USER:XXX (with XXX the groups id have a value
6046
     *  GROUP:YYY (with YYY the group id).
6047
     *
6048
     * @param array $to Array of strings that define the type and id of each destination
6049
     *
6050
     * @return array Array of groups and users (each an array of IDs)
6051
     */
6052
    public static function separateUsersGroups($to)
6053
    {
6054
        $groupList = [];
6055
        $userList = [];
6056
6057
        foreach ($to as $to_item) {
6058
            if (!empty($to_item)) {
6059
                $parts = explode(':', $to_item);
6060
                $type = isset($parts[0]) ? $parts[0] : '';
6061
                $id = isset($parts[1]) ? $parts[1] : '';
6062
6063
                switch ($type) {
6064
                    case 'GROUP':
6065
                        $groupList[] = (int) $id;
6066
                        break;
6067
                    case 'USER':
6068
                        $userList[] = (int) $id;
6069
                        break;
6070
                }
6071
            }
6072
        }
6073
6074
        $send_to['groups'] = $groupList;
6075
        $send_to['users'] = $userList;
6076
6077
        return $send_to;
6078
    }
6079
6080
    /**
6081
     * Shows the form for sending a message to a specific group or user.
6082
     *
6083
     * @param FormValidator $form
6084
     * @param array         $groupInfo
6085
     * @param array         $to
6086
     *
6087
     * @return HTML_QuickForm_element
6088
     */
6089
    public static function addGroupMultiSelect($form, $groupInfo, $to = [])
6090
    {
6091
        $groupUsers = GroupManager::get_subscribed_users($groupInfo);
6092
        $groupEntity = api_get_group_entity($groupInfo['iid']);
6093
        $array = self::buildSelectOptions([$groupEntity], $groupUsers, $to);
6094
6095
        $result = [];
6096
        foreach ($array as $content) {
6097
            $result[$content['value']] = $content['content'];
6098
        }
6099
6100
        return $form->addElement('advmultiselect', 'users', get_lang('Users'), $result);
6101
    }
6102
6103
    /**
6104
     * this function shows the form for sending a message to a specific group or user.
6105
     *
6106
     * @param CGroup[] $groupList
6107
     * @param array    $userList
6108
     * @param array    $alreadySelected
6109
     *
6110
     * @return array
6111
     */
6112
    public static function buildSelectOptions($groupList = [], $userList = [], $alreadySelected = [])
6113
    {
6114
        if (empty($alreadySelected)) {
6115
            $alreadySelected = [];
6116
        }
6117
6118
        $result = [];
6119
        // adding the groups to the select form
6120
        if ($groupList) {
6121
            foreach ($groupList as $thisGroup) {
6122
                $groupId = $thisGroup->getIid();
6123
                if (is_array($alreadySelected)) {
6124
                    if (!in_array(
6125
                        "GROUP:".$groupId,
6126
                        $alreadySelected
6127
                    )
6128
                    ) {
6129
                        $userCount = $thisGroup->getMembers()->count();
6130
6131
                        // $alreadySelected is the array containing the groups (and users) that are already selected
6132
                        $userLabel = ($userCount > 0) ? get_lang('Users') : get_lang('user');
6133
                        $userDisabled = ($userCount > 0) ? "" : "disabled=disabled";
6134
                        $result[] = [
6135
                            'disabled' => $userDisabled,
6136
                            'value' => "GROUP:".$groupId,
6137
                            // The space before "G" is needed in order to advmultiselect.php js puts groups first
6138
                            'content' => " G: ".$thisGroup->getName()." - ".$userCount." ".$userLabel,
6139
                        ];
6140
                    }
6141
                }
6142
            }
6143
        }
6144
6145
        // adding the individual users to the select form
6146
        if ($userList) {
6147
            foreach ($userList as $user) {
6148
                if (is_array($alreadySelected)) {
6149
                    if (!in_array(
6150
                        "USER:".$user['user_id'],
6151
                        $alreadySelected
6152
                    )
6153
                    ) {
6154
                        // $alreadySelected is the array containing the users (and groups) that are already selected
6155
                        $result[] = [
6156
                            'value' => "USER:".$user['user_id'],
6157
                            'content' => api_get_person_name($user['firstname'], $user['lastname']),
6158
                        ];
6159
                    }
6160
                }
6161
            }
6162
        }
6163
6164
        return $result;
6165
    }
6166
6167
    /**
6168
     * @return array a list (array) of all courses
6169
     */
6170
    public static function get_course_list()
6171
    {
6172
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
6173
6174
        return Database::store_result(Database::query("SELECT *, id as real_id FROM $table"));
6175
    }
6176
6177
    /**
6178
     * Returns course code from a given gradebook category's id.
6179
     *
6180
     * @param int  Category ID
6181
     *
6182
     * @return string Course code
6183
     */
6184
    public static function get_course_by_category($category_id)
6185
    {
6186
        $category_id = (int) $category_id;
6187
        $sql = 'SELECT c_id FROM '.Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY).'
6188
                WHERE id='.$category_id;
6189
        $info = Database::fetch_array(Database::query($sql), 'ASSOC');
6190
6191
        return $info ? $info['c_id'] : false;
6192
    }
6193
6194
    /**
6195
     * This function gets all the courses that are not in a session.
6196
     *
6197
     * @param date Start date
6198
     * @param date End date
6199
     * @param bool $includeClosed Whether to include closed and hidden courses
6200
     *
6201
     * @return array Not-in-session courses
6202
     */
6203
    public static function getCoursesWithoutSession(
6204
        $startDate = null,
6205
        $endDate = null,
6206
        $includeClosed = false
6207
    ) {
6208
        $dateConditional = ($startDate && $endDate) ?
6209
            " WHERE session_id IN (SELECT id FROM ".Database::get_main_table(TABLE_MAIN_SESSION).
6210
            " WHERE access_start_date = '$startDate' AND access_end_date = '$endDate')" : null;
6211
        $visibility = ($includeClosed ? '' : 'visibility NOT IN (0, 4) AND ');
6212
6213
        $sql = "SELECT id, code, title
6214
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
6215
                WHERE $visibility code NOT IN (
6216
                    SELECT DISTINCT course_code
6217
                    FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE).$dateConditional."
6218
                )
6219
                ORDER BY id";
6220
6221
        $result = Database::query($sql);
6222
        $courses = [];
6223
        while ($row = Database::fetch_array($result)) {
6224
            $courses[] = $row;
6225
        }
6226
6227
        return $courses;
6228
    }
6229
6230
    /**
6231
     * Get list of courses based on users of a group for a group admin.
6232
     *
6233
     * @param int $userId The user id
6234
     *
6235
     * @return array
6236
     */
6237
    public static function getCoursesFollowedByGroupAdmin($userId)
6238
    {
6239
        $coursesList = [];
6240
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
6241
        $courseUserTable = Database::get_main_table(TABLE_MAIN_COURSE_USER);
6242
        $userGroup = new UserGroup();
6243
        $userIdList = $userGroup->getGroupUsersByUser($userId);
6244
6245
        if (empty($userIdList)) {
6246
            return [];
6247
        }
6248
6249
        $sql = "SELECT DISTINCT(c.id), c.title
6250
                FROM $courseTable c
6251
                INNER JOIN $courseUserTable cru ON c.id = cru.c_id
6252
                WHERE (
6253
                    cru.user_id IN (".implode(', ', $userIdList).")
6254
                    AND cru.relation_type = 0
6255
                )";
6256
6257
        if (api_is_multiple_url_enabled()) {
6258
            $courseAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
6259
            $accessUrlId = api_get_current_access_url_id();
6260
6261
            if (-1 != $accessUrlId) {
6262
                $sql = "SELECT DISTINCT(c.id), c.title
6263
                        FROM $courseTable c
6264
                        INNER JOIN $courseUserTable cru ON c.id = cru.c_id
6265
                        INNER JOIN $courseAccessUrlTable crau ON c.id = crau.c_id
6266
                        WHERE crau.access_url_id = $accessUrlId
6267
                            AND (
6268
                            cru.id_user IN (".implode(', ', $userIdList).") AND
6269
                            cru.relation_type = 0
6270
                        )";
6271
            }
6272
        }
6273
6274
        $result = Database::query($sql);
6275
        while ($row = Database::fetch_assoc($result)) {
6276
            $coursesList[] = $row;
6277
        }
6278
6279
        return $coursesList;
6280
    }
6281
6282
    /**
6283
     * Direct course link see #5299.
6284
     *
6285
     * You can send to your students an URL like this
6286
     * http://chamilodev.beeznest.com/main/auth/inscription.php?c=ABC&e=3
6287
     * Where "c" is the course code and "e" is the exercise Id, after a successful
6288
     * registration the user will be sent to the course or exercise
6289
     *
6290
     * @param array $form_data
6291
     *
6292
     * @return array
6293
     */
6294
    public static function redirectToCourse($form_data)
6295
    {
6296
        $course_code_redirect = Session::read('course_redirect');
6297
        $_user = api_get_user_info();
6298
        $userId = api_get_user_id();
6299
6300
        if (!empty($course_code_redirect)) {
6301
            $course_info = api_get_course_info($course_code_redirect);
6302
            if (!empty($course_info)) {
6303
                if (in_array(
6304
                    $course_info['visibility'],
6305
                    [COURSE_VISIBILITY_OPEN_PLATFORM, COURSE_VISIBILITY_OPEN_WORLD]
6306
                )
6307
                ) {
6308
                    if (self::is_user_subscribed_in_course($userId, $course_info['code'])) {
6309
                        $form_data['action'] = $course_info['course_public_url'];
6310
                        $form_data['message'] = sprintf(get_lang('You have been registered to course %s'), $course_info['title']);
6311
                        $form_data['button'] = Display::button(
6312
                            'next',
6313
                            get_lang('Go to the course', null, $_user['language']),
6314
                            ['class' => 'btn btn-primary btn-large']
6315
                        );
6316
6317
                        $exercise_redirect = (int) Session::read('exercise_redirect');
6318
                        // Specify the course id as the current context does not
6319
                        // hold a global $_course array
6320
                        $objExercise = new Exercise($course_info['real_id']);
6321
                        $result = $objExercise->read($exercise_redirect);
6322
6323
                        if (!empty($exercise_redirect) && !empty($result)) {
6324
                            $form_data['action'] = api_get_path(WEB_CODE_PATH).
6325
                                'exercise/overview.php?exerciseId='.$exercise_redirect.'&cid='.$course_info['real_id'];
6326
                            $form_data['message'] .= '<br />'.get_lang('Go to the test');
6327
                            $form_data['button'] = Display::button(
6328
                                'next',
6329
                                get_lang('Go', null, $_user['language']),
6330
                                ['class' => 'btn btn-primary btn-large']
6331
                            );
6332
                        }
6333
6334
                        if (!empty($form_data['action'])) {
6335
                            header('Location: '.$form_data['action']);
6336
                            exit;
6337
                        }
6338
                    }
6339
                }
6340
            }
6341
        }
6342
6343
        return $form_data;
6344
    }
6345
6346
    /**
6347
     * Return tab of params to display a course title in the My Courses tab
6348
     * Check visibility, right, and notification icons, and load_dirs option
6349
     * get html course params.
6350
     *
6351
     * @param $courseId
6352
     * @param bool $loadDirs
6353
     *
6354
     * @return array with keys ['right_actions'] ['teachers'] ['notifications']
6355
     */
6356
    public static function getCourseParamsForDisplay($courseId, $loadDirs = false)
6357
    {
6358
        $userId = api_get_user_id();
6359
        $courseId = intval($courseId);
6360
        // Table definitions
6361
        $TABLECOURS = Database::get_main_table(TABLE_MAIN_COURSE);
6362
        $TABLECOURSUSER = Database::get_main_table(TABLE_MAIN_COURSE_USER);
6363
        $TABLE_ACCESS_URL_REL_COURSE = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
6364
        $current_url_id = api_get_current_access_url_id();
6365
6366
        // Get course list auto-register
6367
        $special_course_list = self::get_special_course_list();
6368
6369
        $without_special_courses = '';
6370
        if (!empty($special_course_list)) {
6371
            $without_special_courses = ' AND course.id NOT IN ("'.implode('","', $special_course_list).'")';
6372
        }
6373
6374
        //AND course_rel_user.relation_type<>".COURSE_RELATION_TYPE_RRHH."
6375
        $sql = "SELECT
6376
                    course.id,
6377
                    course.title,
6378
                    course.code,
6379
                    course.subscribe subscr,
6380
                    course.unsubscribe unsubscr,
6381
                    course_rel_user.status status,
6382
                    course_rel_user.sort sort,
6383
                    course_rel_user.user_course_cat user_course_cat
6384
                FROM
6385
                $TABLECOURS course
6386
                INNER JOIN $TABLECOURSUSER course_rel_user
6387
                ON (course.id = course_rel_user.c_id)
6388
                INNER JOIN $TABLE_ACCESS_URL_REL_COURSE url
6389
                ON (url.c_id = course.id)
6390
                WHERE
6391
                    course.id = $courseId AND
6392
                    course_rel_user.user_id = $userId
6393
                    $without_special_courses
6394
                ";
6395
6396
        // If multiple URL access mode is enabled, only fetch courses
6397
        // corresponding to the current URL.
6398
        if (api_get_multiple_access_url() && -1 != $current_url_id) {
6399
            $sql .= " AND url.c_id = course.id AND access_url_id = $current_url_id";
6400
        }
6401
        // Use user's classification for courses (if any).
6402
        $sql .= " ORDER BY course_rel_user.user_course_cat, course_rel_user.sort ASC";
6403
6404
        $result = Database::query($sql);
6405
6406
        // Browse through all courses. We can only have one course because
6407
        // of the  course.id=".intval($courseId) in sql query
6408
        $course = Database::fetch_array($result);
6409
        $course_info = api_get_course_info_by_id($courseId);
6410
        if (empty($course_info)) {
6411
            return '';
6412
        }
6413
6414
        //$course['id_session'] = null;
6415
        $course_info['id_session'] = null;
6416
        $course_info['status'] = $course['status'];
6417
6418
        // For each course, get if there is any notification icon to show
6419
        // (something that would have changed since the user's last visit).
6420
        $show_notification = !api_get_configuration_value('hide_course_notification')
6421
            ? Display::show_notification($course_info)
6422
            : '';
6423
6424
        // New code displaying the user's status in respect to this course.
6425
        $status_icon = Display::return_icon(
6426
            'blackboard.png',
6427
            $course_info['title'],
6428
            [],
6429
            ICON_SIZE_LARGE
6430
        );
6431
6432
        $params = [];
6433
        $params['right_actions'] = '';
6434
6435
        if (api_is_platform_admin()) {
6436
            if ($loadDirs) {
6437
                $params['right_actions'] .= '<a id="document_preview_'.$course_info['real_id'].'_0" class="document_preview" href="javascript:void(0);">'.Display::return_icon('folder.png', get_lang('Documents'), ['align' => 'absmiddle'], ICON_SIZE_SMALL).'</a>';
6438
                $params['right_actions'] .= '<a href="'.api_get_path(WEB_CODE_PATH).'course_info/infocours.php?cid='.$course['real_id'].'">'.
6439
                    Display::return_icon('edit.png', get_lang('Edit'), ['align' => 'absmiddle'], ICON_SIZE_SMALL).
6440
                    '</a>';
6441
                $params['right_actions'] .= Display::div(
6442
                    '',
6443
                    [
6444
                        'id' => 'document_result_'.$course_info['real_id'].'_0',
6445
                        'class' => 'document_preview_container',
6446
                    ]
6447
                );
6448
            } else {
6449
                $params['right_actions'] .=
6450
                    '<a class="btn btn-default btn-sm" title="'.get_lang('Edit').'" href="'.api_get_path(WEB_CODE_PATH).'course_info/infocours.php?cid='.$course['real_id'].'">'.
6451
                    Display::returnFontAwesomeIcon('pencil').'</a>';
6452
            }
6453
        } else {
6454
            if (COURSE_VISIBILITY_CLOSED != $course_info['visibility']) {
6455
                if ($loadDirs) {
6456
                    $params['right_actions'] .= '<a id="document_preview_'.$course_info['real_id'].'_0" class="document_preview" href="javascript:void(0);">'.
6457
                        Display::return_icon('folder.png', get_lang('Documents'), ['align' => 'absmiddle'], ICON_SIZE_SMALL).'</a>';
6458
                    $params['right_actions'] .= Display::div(
6459
                        '',
6460
                        [
6461
                            'id' => 'document_result_'.$course_info['real_id'].'_0',
6462
                            'class' => 'document_preview_container',
6463
                        ]
6464
                    );
6465
                } else {
6466
                    if (COURSEMANAGER == $course_info['status']) {
6467
                        $params['right_actions'] .= '<a
6468
                            class="btn btn-default btn-sm" title="'.get_lang('Edit').'" href="'.api_get_path(WEB_CODE_PATH).'course_info/infocours.php?cid='.$course['real_id'].'">'.
6469
                            Display::returnFontAwesomeIcon('pencil').'</a>';
6470
                    }
6471
                }
6472
            }
6473
        }
6474
6475
        $course_title_url = '';
6476
        if (COURSE_VISIBILITY_CLOSED != $course_info['visibility'] || COURSEMANAGER == $course['status']) {
6477
            $course_title_url = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/?id_session=0';
6478
            $course_title = Display::url($course_info['title'], $course_title_url);
6479
        } else {
6480
            $course_title = $course_info['title'].' '.Display::tag(
6481
                'span',
6482
                get_lang('(the course is currently closed)'),
6483
                ['class' => 'item_closed']
6484
            );
6485
        }
6486
6487
        // Start displaying the course block itself
6488
        if ('true' === api_get_setting('display_coursecode_in_courselist')) {
6489
            $course_title .= ' ('.$course_info['visual_code'].') ';
6490
        }
6491
        $teachers = '';
6492
        if ('true' === api_get_setting('display_teacher_in_courselist')) {
6493
            $teachers = self::getTeacherListFromCourseCodeToString(
6494
                $course['code'],
6495
                self::USER_SEPARATOR,
6496
                true
6497
            );
6498
        }
6499
        $params['link'] = $course_title_url;
6500
        $params['icon'] = $status_icon;
6501
        $params['title'] = $course_title;
6502
        $params['teachers'] = $teachers;
6503
        if (COURSE_VISIBILITY_CLOSED != $course_info['visibility']) {
6504
            $params['notifications'] = $show_notification;
6505
        }
6506
6507
        return $params;
6508
    }
6509
6510
    /**
6511
     * Get the course id based on the original id and field name in the extra fields.
6512
     * Returns 0 if course was not found.
6513
     *
6514
     * @param string $original_course_id_value Original course id
6515
     * @param string $original_course_id_name  Original field name
6516
     *
6517
     * @return int Course id
6518
     */
6519
    public static function get_course_id_from_original_id($original_course_id_value, $original_course_id_name)
6520
    {
6521
        $extraFieldValue = new ExtraFieldValue('course');
6522
        $value = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
6523
            $original_course_id_name,
6524
            $original_course_id_value
6525
        );
6526
6527
        if ($value) {
6528
            return $value['item_id'];
6529
        }
6530
6531
        return 0;
6532
    }
6533
6534
    /**
6535
     * Helper function to create a default gradebook (if necessary) upon course creation.
6536
     *
6537
     * @param int    $modelId    The gradebook model ID
6538
     * @param string $courseCode Course code
6539
     */
6540
    public static function createDefaultGradebook($modelId, $courseCode)
6541
    {
6542
        if ('true' === api_get_setting('gradebook_enable_grade_model')) {
6543
            //Create gradebook_category for the new course and add
6544
            // a gradebook model for the course
6545
            if (isset($modelId) &&
6546
                !empty($modelId) &&
6547
                '-1' != $modelId
6548
            ) {
6549
                GradebookUtils::create_default_course_gradebook(
6550
                    $courseCode,
6551
                    $modelId
6552
                );
6553
            }
6554
        }
6555
    }
6556
6557
    /**
6558
     * Helper function to check if there is a course template and, if so, to
6559
     * copy the template as basis for the new course.
6560
     *
6561
     * @param string $courseCode     Course code
6562
     * @param int    $courseTemplate 0 if no course template is defined
6563
     */
6564
    public static function useTemplateAsBasisIfRequired($courseCode, $courseTemplate)
6565
    {
6566
        $template = api_get_setting('course_creation_use_template');
6567
        $teacherCanSelectCourseTemplate = 'true' === api_get_setting('teacher_can_select_course_template');
6568
        $courseTemplate = isset($courseTemplate) ? intval($courseTemplate) : 0;
6569
6570
        $useTemplate = false;
6571
6572
        if ($teacherCanSelectCourseTemplate && $courseTemplate) {
6573
            $useTemplate = true;
6574
            $originCourse = api_get_course_info_by_id($courseTemplate);
6575
        } elseif (!empty($template)) {
6576
            $useTemplate = true;
6577
            $originCourse = api_get_course_info_by_id($template);
6578
        }
6579
6580
        if ($useTemplate) {
6581
            // Include the necessary libraries to generate a course copy
6582
            // Call the course copy object
6583
            $originCourse['official_code'] = $originCourse['code'];
6584
            $cb = new CourseBuilder(null, $originCourse);
6585
            $course = $cb->build(null, $originCourse['code']);
6586
            $cr = new CourseRestorer($course);
6587
            $cr->set_file_option();
6588
            $cr->restore($courseCode);
6589
        }
6590
    }
6591
6592
    /**
6593
     * Helper method to get the number of users defined with a specific course extra field.
6594
     *
6595
     * @param string $name                 Field title
6596
     * @param string $tableExtraFields     The extra fields table name
6597
     * @param string $tableUserFieldValues The user extra field value table name
6598
     *
6599
     * @return int The number of users with this extra field with a specific value
6600
     */
6601
    public static function getCountRegisteredUsersWithCourseExtraField(
6602
        $name,
6603
        $tableExtraFields = '',
6604
        $tableUserFieldValues = ''
6605
    ) {
6606
        if (empty($tableExtraFields)) {
6607
            $tableExtraFields = Database::get_main_table(TABLE_EXTRA_FIELD);
6608
        }
6609
        if (empty($tableUserFieldValues)) {
6610
            $tableUserFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
6611
        }
6612
6613
        $registered_users_with_extra_field = 0;
6614
        if (!empty($name) && '-' != $name) {
6615
            $extraFieldType = EntityExtraField::COURSE_FIELD_TYPE;
6616
            $name = Database::escape_string($name);
6617
            $sql = "SELECT count(v.item_id) as count
6618
                    FROM $tableUserFieldValues v
6619
                    INNER JOIN $tableExtraFields f
6620
                    ON (f.id = v.field_id)
6621
                    WHERE value = '$name' AND extra_field_type = $extraFieldType";
6622
            $result_count = Database::query($sql);
6623
            if (Database::num_rows($result_count)) {
6624
                $row_count = Database::fetch_array($result_count);
6625
                $registered_users_with_extra_field = $row_count['count'];
6626
            }
6627
        }
6628
6629
        return $registered_users_with_extra_field;
6630
    }
6631
6632
    /**
6633
     * Get the course categories form a course list.
6634
     *
6635
     * @return array
6636
     */
6637
    public static function getCourseCategoriesFromCourseList(array $courseList)
6638
    {
6639
        $allCategories = array_column($courseList, 'category');
6640
        $categories = array_unique($allCategories);
6641
6642
        sort($categories);
6643
6644
        return $categories;
6645
    }
6646
6647
    /**
6648
     * Display the description button of a course in the course catalog.
6649
     *
6650
     * @param array  $course
6651
     * @param string $url
6652
     *
6653
     * @return string HTML string
6654
     */
6655
    public static function returnDescriptionButton($course, $url = '')
6656
    {
6657
        if (empty($course)) {
6658
            return '';
6659
        }
6660
6661
        $class = '';
6662
        if ('true' === api_get_setting('show_courses_descriptions_in_catalog')) {
6663
            $title = $course['title'];
6664
            if (empty($url)) {
6665
                $class = 'ajax';
6666
                $url = api_get_path(WEB_CODE_PATH).
6667
                    'inc/ajax/course_home.ajax.php?a=show_course_information&code='.$course['code'];
6668
            } else {
6669
                if (false !== strpos($url, 'ajax')) {
6670
                    $class = 'ajax';
6671
                }
6672
            }
6673
6674
            return Display::url(
6675
                Display::returnFontAwesomeIcon('info-circle', 'lg'),
6676
                $url,
6677
                [
6678
                    'class' => "$class btn btn-default btn-sm",
6679
                    'data-title' => $title,
6680
                    'title' => get_lang('Description'),
6681
                    'aria-label' => get_lang('Description'),
6682
                    'data-size' => 'lg',
6683
                ]
6684
            );
6685
        }
6686
6687
        return '';
6688
    }
6689
6690
    /**
6691
     * @return int
6692
     */
6693
    public static function getCountOpenCourses()
6694
    {
6695
        $visibility = [
6696
            COURSE_VISIBILITY_REGISTERED,
6697
            COURSE_VISIBILITY_OPEN_PLATFORM,
6698
            COURSE_VISIBILITY_OPEN_WORLD,
6699
        ];
6700
6701
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
6702
        $sql = "SELECT count(id) count
6703
                FROM $table
6704
                WHERE visibility IN (".implode(',', $visibility).")";
6705
        $result = Database::query($sql);
6706
        $row = Database::fetch_array($result);
6707
6708
        return (int) $row['count'];
6709
    }
6710
6711
    /**
6712
     * @return int
6713
     */
6714
    public static function getCountExercisesFromOpenCourse()
6715
    {
6716
        $visibility = [
6717
            COURSE_VISIBILITY_REGISTERED,
6718
            COURSE_VISIBILITY_OPEN_PLATFORM,
6719
            COURSE_VISIBILITY_OPEN_WORLD,
6720
        ];
6721
6722
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
6723
        $tableExercise = Database::get_course_table(TABLE_QUIZ_TEST);
6724
        $sql = "SELECT count(e.iid) count
6725
                FROM $table c
6726
                INNER JOIN $tableExercise e
6727
                ON (c.id = e.c_id)
6728
                WHERE e.active <> -1 AND visibility IN (".implode(',', $visibility).")";
6729
        $result = Database::query($sql);
6730
        $row = Database::fetch_array($result);
6731
6732
        return (int) $row['count'];
6733
    }
6734
6735
    /**
6736
     * retrieves all the courses that the user has already subscribed to.
6737
     *
6738
     * @param int $user_id
6739
     *
6740
     * @return array an array containing all the information of the courses of the given user
6741
     */
6742
    public static function getCoursesByUserCourseCategory($user_id)
6743
    {
6744
        $course = Database::get_main_table(TABLE_MAIN_COURSE);
6745
        $courseRelUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
6746
        $avoidCoursesCondition = CoursesAndSessionsCatalog::getAvoidCourseCondition();
6747
        $visibilityCondition = self::getCourseVisibilitySQLCondition('course', true);
6748
6749
        // Secondly we select the courses that are in a category (user_course_cat<>0) and
6750
        // sort these according to the sort of the category
6751
        $user_id = (int) $user_id;
6752
        $sql = "SELECT
6753
                    course.code k,
6754
                    course.visual_code vc,
6755
                    course.subscribe subscr,
6756
                    course.unsubscribe unsubscr,
6757
                    course.title i,
6758
                    course.tutor_name t,
6759
                    course.category_code cat,
6760
                    course.directory dir,
6761
                    course_rel_user.status status,
6762
                    course_rel_user.sort sort,
6763
                    course_rel_user.user_course_cat user_course_cat
6764
                FROM $course course, $courseRelUser course_rel_user
6765
                WHERE
6766
                    course.id = course_rel_user.c_id AND
6767
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
6768
                    course_rel_user.user_id = '".$user_id."'
6769
                    $avoidCoursesCondition
6770
                    $visibilityCondition
6771
                ORDER BY course_rel_user.sort ASC";
6772
6773
        $result = Database::query($sql);
6774
        $courses = [];
6775
        while ($row = Database::fetch_array($result, 'ASOC')) {
6776
            $courses[] = [
6777
                'code' => $row['k'],
6778
                'visual_code' => $row['vc'],
6779
                'title' => $row['i'],
6780
                'directory' => $row['dir'],
6781
                'status' => $row['status'],
6782
                'tutor' => $row['t'],
6783
                'subscribe' => $row['subscr'],
6784
                'category' => $row['cat'],
6785
                'unsubscribe' => $row['unsubscr'],
6786
                'sort' => $row['sort'],
6787
                'user_course_category' => $row['user_course_cat'],
6788
            ];
6789
        }
6790
6791
        return $courses;
6792
    }
6793
6794
    /**
6795
     * @param string $listType
6796
     *
6797
     * @return string
6798
     */
6799
    public static function getCourseListTabs($listType)
6800
    {
6801
        $tabs = [
6802
            [
6803
                'content' => get_lang('Standard list'),
6804
                'url' => api_get_path(WEB_CODE_PATH).'admin/course_list.php',
6805
            ],
6806
            [
6807
                'content' => get_lang('Management List'),
6808
                'url' => api_get_path(WEB_CODE_PATH).'admin/course_list_admin.php',
6809
            ],
6810
        ];
6811
6812
        $default = 1;
6813
        switch ($listType) {
6814
            case 'simple':
6815
                $default = 1;
6816
                break;
6817
            case 'admin':
6818
                $default = 2;
6819
                break;
6820
        }
6821
6822
        return Display::tabsOnlyLink($tabs, $default);
6823
    }
6824
6825
    /**
6826
     * Check if a specific access-url-related setting is a problem or not.
6827
     *
6828
     * @param array  $_configuration The $_configuration array
6829
     * @param int    $accessUrlId    The access URL ID
6830
     * @param string $param
6831
     * @param string $msgLabel
6832
     *
6833
     * @return bool|string
6834
     */
6835
    private static function checkCreateCourseAccessUrlParam($_configuration, $accessUrlId, $param, $msgLabel)
6836
    {
6837
        if (isset($_configuration[$accessUrlId][$param]) && $_configuration[$accessUrlId][$param] > 0) {
6838
            $num = null;
6839
            switch ($param) {
6840
                case 'hosting_limit_courses':
6841
            $num = self::count_courses($accessUrlId);
6842
                    break;
6843
                case 'hosting_limit_active_courses':
6844
                    $num = self::countActiveCourses($accessUrlId);
6845
                    break;
6846
            }
6847
6848
            if ($num && $num >= $_configuration[$accessUrlId][$param]) {
6849
                api_warn_hosting_contact($param);
6850
6851
                Display::addFlash(
6852
                    Display::return_message($msgLabel)
6853
                );
6854
6855
                return true;
6856
            }
6857
        }
6858
6859
        return false;
6860
    }
6861
6862
    /**
6863
     * Fill course with all necessary items.
6864
     *
6865
     * @param array $courseInfo Course info array
6866
     * @param array $params     Parameters from the course creation form
6867
     * @param int   $authorId
6868
     */
6869
    private static function fillCourse($courseInfo, $params, $authorId = 0)
6870
    {
6871
        $authorId = empty($authorId) ? api_get_user_id() : (int) $authorId;
6872
6873
        AddCourse::fillCourse(
6874
            $courseInfo,
6875
            $params['exemplary_content'],
6876
            $authorId
6877
        );
6878
6879
        if (isset($params['gradebook_model_id'])) {
6880
            self::createDefaultGradebook(
6881
                $params['gradebook_model_id'],
6882
                $courseInfo['code']
6883
            );
6884
        }
6885
6886
        // If parameter defined, copy the contents from a specific
6887
        // template course into this new course
6888
        if (isset($params['course_template'])) {
6889
            self::useTemplateAsBasisIfRequired(
6890
                $courseInfo['id'],
6891
                $params['course_template']
6892
            );
6893
        }
6894
        $params['course_code'] = $courseInfo['code'];
6895
        $params['item_id'] = $courseInfo['real_id'];
6896
6897
        $courseFieldValue = new ExtraFieldValue('course');
6898
        $courseFieldValue->saveFieldValues($params);
6899
    }
6900
}
6901