Passed
Push — master ( 447a4c...2f567c )
by Julito
09:40
created

CourseManager::hasPicture()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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