Passed
Push — 1.11.x ( 47505b...220ec2 )
by Angel Fernando Quiroz
09:45 queued 11s
created

CourseManager::getCoursesFollowedByUser()   F

Complexity

Conditions 15
Paths 960

Size

Total Lines 111
Code Lines 66

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 66
c 0
b 0
f 0
nc 960
nop 11
dl 0
loc 111
rs 1.8055

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