CourseManager::get_student_list_from_course_code()   F
last analyzed

Complexity

Conditions 22
Paths 1577

Size

Total Lines 127
Code Lines 70

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 70
c 0
b 0
f 0
dl 0
loc 127
rs 0
cc 22
nc 1577
nop 11

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

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