Passed
Push — 1.11.x ( 7c94fa...ea5a07 )
by Julito
11:53 queued 15s
created

CourseManager::get_courses_list()   F

Complexity

Conditions 22
Paths 6912

Size

Total Lines 110
Code Lines 67

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
eloc 67
c 0
b 0
f 0
nc 6912
nop 10
dl 0
loc 110
rs 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

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