Passed
Push — 1.11.x ( 04948c...424272 )
by Julito
11:00
created

CourseManager::addUserGroupMultiSelect()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 60
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 31
nc 6
nop 3
dl 0
loc 60
rs 8.4906
c 0
b 0
f 0

How to fix   Long Method   

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:

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