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

CourseManager::getCoursesFollowedByUser()   F

Complexity

Conditions 14
Paths 480

Size

Total Lines 107
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
/* 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