Passed
Push — 1.11.x ( bf773f...a00d57 )
by Julito
13:09 queued 12s
created

CourseManager::get_courses_list()   F

Complexity

Conditions 22
Paths 6912

Size

Total Lines 110
Code Lines 67

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

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