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