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

CourseManager::is_course_teacher()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

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