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

CourseManager::getCoursesFollowedByUser()   F

Complexity

Conditions 14
Paths 480

Size

Total Lines 107
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 64
nc 480
nop 10
dl 0
loc 107
rs 2.8222
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* For licensing terms, see /license.txt*/
4
5
use Chamilo\CoreBundle\Entity\Course;
6
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
7
use Chamilo\CoreBundle\Entity\Repository\SequenceResourceRepository;
8
use Chamilo\CoreBundle\Entity\SequenceResource;
9
use Chamilo\CourseBundle\Component\CourseCopy\CourseBuilder;
10
use Chamilo\CourseBundle\Component\CourseCopy\CourseRestorer;
11
use ChamiloSession as Session;
12
use Doctrine\Common\Collections\Criteria;
13
14
/**
15
 * Class CourseManager.
16
 *
17
 * This is the course library for Chamilo.
18
 *
19
 * All main course functions should be placed here.
20
 *
21
 * Many functions of this library deal with providing support for
22
 * virtual/linked/combined courses (this was already used in several universities
23
 * but not available in standard Chamilo).
24
 *
25
 * There are probably some places left with the wrong code.
26
 */
27
class CourseManager
28
{
29
    const MAX_COURSE_LENGTH_CODE = 40;
30
    /** This constant is used to show separate user names in the course
31
     * list (userportal), footer, etc */
32
    const USER_SEPARATOR = ' |';
33
    const COURSE_FIELD_TYPE_CHECKBOX = 10;
34
    public $columns = [];
35
36
    /**
37
     * Creates a course.
38
     *
39
     * @param array $params      Columns in the main.course table.
40
     * @param int   $authorId    Optional.
41
     * @param int   $accessUrlId Optional.
42
     *
43
     * @return mixed false if the course was not created, array with the course info
44
     */
45
    public static function create_course($params, $authorId = 0, $accessUrlId = 0)
46
    {
47
        global $_configuration;
48
49
        $hook = HookCreateCourse::create();
50
51
        // Check portal limits
52
        $accessUrlId = empty($accessUrlId)
53
            ? (api_get_multiple_access_url() ? api_get_current_access_url_id() : 1)
54
            : $accessUrlId;
55
56
        $authorId = empty($authorId) ? api_get_user_id() : (int) $authorId;
57
58
        if (isset($_configuration[$accessUrlId]) && is_array($_configuration[$accessUrlId])) {
59
            $return = self::checkCreateCourseAccessUrlParam(
60
                $_configuration,
61
                $accessUrlId,
62
                'hosting_limit_courses',
63
                'PortalCoursesLimitReached'
64
            );
65
            if ($return != false) {
66
                return $return;
67
            }
68
            $return = self::checkCreateCourseAccessUrlParam(
69
                $_configuration,
70
                $accessUrlId,
71
                'hosting_limit_active_courses',
72
                'PortalActiveCoursesLimitReached'
73
            );
74
            if ($return != false) {
75
                return $return;
76
            }
77
        }
78
79
        if (empty($params['title'])) {
80
            return false;
81
        }
82
83
        if (empty($params['wanted_code'])) {
84
            $params['wanted_code'] = $params['title'];
85
            // Check whether the requested course code has already been occupied.
86
            $substring = api_substr($params['title'], 0, self::MAX_COURSE_LENGTH_CODE);
87
            if ($substring === false || empty($substring)) {
88
                return false;
89
            } else {
90
                $params['wanted_code'] = self::generate_course_code($substring);
91
            }
92
        }
93
94
        // Create the course keys
95
        $keys = AddCourse::define_course_keys($params['wanted_code']);
96
        $params['exemplary_content'] = isset($params['exemplary_content']) ? $params['exemplary_content'] : false;
97
98
        if (count($keys)) {
99
            $params['code'] = $keys['currentCourseCode'];
100
            $params['visual_code'] = $keys['currentCourseId'];
101
            $params['directory'] = $keys['currentCourseRepository'];
102
            $course_info = api_get_course_info($params['code']);
103
            if (empty($course_info)) {
104
                $course_id = AddCourse::register_course($params, $accessUrlId);
105
                $course_info = api_get_course_info_by_id($course_id);
106
107
                if ($hook) {
108
                    $hook->setEventData(['course_info' => $course_info]);
109
                    $hook->notifyCreateCourse(HOOK_EVENT_TYPE_POST);
110
                }
111
112
                if (!empty($course_info)) {
113
                    self::fillCourse($course_info, $params, $authorId);
114
115
                    return $course_info;
116
                }
117
            }
118
        }
119
120
        return false;
121
    }
122
123
    /**
124
     * Returns all the information of a given course code.
125
     *
126
     * @param string $course_code , the course code
127
     *
128
     * @return array with all the fields of the course table
129
     *
130
     * @deprecated Use api_get_course_info() instead
131
     *
132
     * @author Patrick Cool <[email protected]>, Ghent University
133
     * @assert ('') === false
134
     */
135
    public static function get_course_information($course_code)
136
    {
137
        return Database::fetch_array(
138
            Database::query(
139
                "SELECT *, id as real_id FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
140
                WHERE code = '".Database::escape_string($course_code)."'"
141
            ),
142
            'ASSOC'
143
        );
144
    }
145
146
    /**
147
     * Returns a list of courses. Should work with quickform syntax.
148
     *
149
     * @param int    $from               Offset (from the 7th = '6'). Optional.
150
     * @param int    $howmany            Number of results we want. Optional.
151
     * @param int    $orderby            The column we want to order it by. Optional, defaults to first column.
152
     * @param string $orderdirection     The direction of the order (ASC or DESC). Optional, defaults to ASC.
153
     * @param int    $visibility         the visibility of the course, or all by default
154
     * @param string $startwith          If defined, only return results for which the course *title* begins with this string
155
     * @param string $urlId              The Access URL ID, if using multiple URLs
156
     * @param bool   $alsoSearchCode     An extension option to indicate that we also want to search for course codes (not *only* titles)
157
     * @param array  $conditionsLike
158
     * @param array  $onlyThisCourseList
159
     *
160
     * @return array
161
     */
162
    public static function get_courses_list(
163
        $from = 0,
164
        $howmany = 0,
165
        $orderby = 1,
166
        $orderdirection = 'ASC',
167
        $visibility = -1,
168
        $startwith = '',
169
        $urlId = null,
170
        $alsoSearchCode = false,
171
        $conditionsLike = [],
172
        $onlyThisCourseList = []
173
    ) {
174
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
175
        $sql = "SELECT course.*, course.id as real_id
176
                FROM $courseTable course ";
177
178
        if (!empty($urlId)) {
179
            $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
180
            $sql .= " INNER JOIN $table url ON (url.c_id = course.id) ";
181
        }
182
183
        $visibility = (int) $visibility;
184
185
        if (!empty($startwith)) {
186
            $sql .= "WHERE (title LIKE '".Database::escape_string($startwith)."%' ";
187
            if ($alsoSearchCode) {
188
                $sql .= "OR code LIKE '".Database::escape_string($startwith)."%' ";
189
            }
190
            $sql .= ') ';
191
            if ($visibility !== -1) {
192
                $sql .= " AND visibility = $visibility ";
193
            }
194
        } else {
195
            $sql .= 'WHERE 1 ';
196
            if ($visibility !== -1) {
197
                $sql .= " AND visibility = $visibility ";
198
            }
199
        }
200
201
        if (!empty($urlId)) {
202
            $urlId = (int) $urlId;
203
            $sql .= " AND access_url_id = $urlId";
204
        }
205
206
        if (!empty($onlyThisCourseList)) {
207
            $onlyThisCourseList = array_map('intval', $onlyThisCourseList);
208
            $onlyThisCourseList = implode("','", $onlyThisCourseList);
209
            $sql .= " AND course.id IN ('$onlyThisCourseList') ";
210
        }
211
212
        $allowedFields = [
213
            'title',
214
            'code',
215
        ];
216
217
        if (count($conditionsLike) > 0) {
218
            $sql .= ' AND ';
219
            $temp_conditions = [];
220
            foreach ($conditionsLike as $field => $value) {
221
                if (!in_array($field, $allowedFields)) {
222
                    continue;
223
                }
224
                $field = Database::escape_string($field);
225
                $value = Database::escape_string($value);
226
                $simple_like = false;
227
                if ($simple_like) {
228
                    $temp_conditions[] = $field." LIKE '$value%'";
229
                } else {
230
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
231
                }
232
            }
233
            $condition = ' AND ';
234
            if (!empty($temp_conditions)) {
235
                $sql .= implode(' '.$condition.' ', $temp_conditions);
236
            }
237
        }
238
239
        if (!empty($orderby)) {
240
            $sql .= " ORDER BY ".Database::escape_string($orderby)." ";
241
        } else {
242
            $sql .= ' ORDER BY 1 ';
243
        }
244
245
        if (!in_array($orderdirection, ['ASC', 'DESC'])) {
246
            $sql .= 'ASC';
247
        } else {
248
            $sql .= ($orderdirection == 'ASC' ? 'ASC' : 'DESC');
249
        }
250
251
        if (!empty($howmany) && is_int($howmany) and $howmany > 0) {
252
            $sql .= ' LIMIT '.Database::escape_string($howmany);
253
        } else {
254
            $sql .= ' LIMIT 1000000'; //virtually no limit
255
        }
256
        if (!empty($from)) {
257
            $from = intval($from);
258
            $sql .= ' OFFSET '.intval($from);
259
        } else {
260
            $sql .= ' OFFSET 0';
261
        }
262
263
        $data = [];
264
        $res = Database::query($sql);
265
        if (Database::num_rows($res) > 0) {
266
            while ($row = Database::fetch_array($res, 'ASSOC')) {
267
                $data[] = $row;
268
            }
269
        }
270
271
        return $data;
272
    }
273
274
    /**
275
     * Returns the status of a user in a course, which is COURSEMANAGER or STUDENT.
276
     *
277
     * @param int $userId
278
     * @param int $courseId
279
     *
280
     * @return int|bool the status of the user in that course (or false if the user is not in that course)
281
     */
282
    public static function getUserInCourseStatus($userId, $courseId)
283
    {
284
        $courseId = (int) $courseId;
285
        $userId = (int) $userId;
286
        if (empty($courseId)) {
287
            return false;
288
        }
289
290
        $result = Database::fetch_array(
291
            Database::query(
292
                "SELECT status FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
293
                WHERE
294
                    c_id  = $courseId AND
295
                    user_id = $userId"
296
            )
297
        );
298
299
        if (empty($result['status'])) {
300
            return false;
301
        }
302
303
        return $result['status'];
304
    }
305
306
    /**
307
     * @param int $userId
308
     * @param int $courseId
309
     *
310
     * @return mixed
311
     */
312
    public static function getUserCourseInfo($userId, $courseId)
313
    {
314
        $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
315
                WHERE
316
                    c_id  = ".intval($courseId)." AND
317
                    user_id = ".intval($userId);
318
        $result = Database::fetch_array(Database::query($sql));
319
320
        return $result;
321
    }
322
323
    /**
324
     * @param int  $userId
325
     * @param int  $courseId
326
     * @param bool $isTutor
327
     *
328
     * @return bool
329
     */
330
    public static function updateUserCourseTutor($userId, $courseId, $isTutor)
331
    {
332
        $table = Database::escape_string(TABLE_MAIN_COURSE_USER);
333
334
        $courseId = intval($courseId);
335
        $isTutor = intval($isTutor);
336
337
        $sql = "UPDATE $table SET is_tutor = '".$isTutor."'
338
			    WHERE
339
				    user_id = ".$userId." AND
340
				    c_id = ".$courseId;
341
342
        $result = Database::query($sql);
343
344
        if (Database::affected_rows($result) > 0) {
345
            return true;
346
        } else {
347
            return false;
348
        }
349
    }
350
351
    /**
352
     * @param int $userId
353
     * @param int $courseId
354
     *
355
     * @return mixed
356
     */
357
    public static function get_tutor_in_course_status($userId, $courseId)
358
    {
359
        $userId = intval($userId);
360
        $courseId = intval($courseId);
361
        $result = Database::fetch_array(
362
            Database::query(
363
                "SELECT is_tutor
364
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
365
                WHERE
366
                    c_id = $courseId AND
367
                    user_id = $userId"
368
            )
369
        );
370
371
        return $result['is_tutor'];
372
    }
373
374
    /**
375
     * Unsubscribe one or more users from a course.
376
     *
377
     * @param   mixed   user_id or an array with user ids
378
     * @param   string  course code
379
     * @param   int     session id
380
     *
381
     * @return bool
382
     *
383
     * @assert ('', '') === false
384
     */
385
    public static function unsubscribe_user($user_id, $course_code, $session_id = 0)
386
    {
387
        if (empty($user_id)) {
388
            return false;
389
        }
390
        if (!is_array($user_id)) {
391
            $user_id = [$user_id];
392
        }
393
394
        if (count($user_id) == 0) {
395
            return false;
396
        }
397
398
        if (!empty($session_id)) {
399
            $session_id = (int) $session_id;
400
        } else {
401
            $session_id = api_get_session_id();
402
        }
403
404
        if (empty($course_code)) {
405
            return false;
406
        }
407
408
        $userList = [];
409
        // Cleaning the $user_id variable
410
        if (is_array($user_id)) {
411
            $new_user_id_list = [];
412
            foreach ($user_id as $my_user_id) {
413
                $new_user_id_list[] = (int) $my_user_id;
414
            }
415
            $new_user_id_list = array_filter($new_user_id_list);
416
            $userList = $new_user_id_list;
417
            $user_ids = implode(',', $new_user_id_list);
418
        } else {
419
            $user_ids = (int) $user_id;
420
            $userList[] = $user_id;
421
        }
422
423
        $course_info = api_get_course_info($course_code);
424
        $course_id = $course_info['real_id'];
425
426
        // Unsubscribe user from all groups in the course.
427
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_GROUP_USER)."
428
                WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
429
        Database::query($sql);
430
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_GROUP_TUTOR)."
431
                WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
432
        Database::query($sql);
433
434
        // Erase user student publications (works) in the course - by André Boivin
435
        if (!empty($userList)) {
436
            require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
437
            foreach ($userList as $userId) {
438
                // Getting all work from user
439
                $workList = getWorkPerUser($userId);
440
                if (!empty($workList)) {
441
                    foreach ($workList as $work) {
442
                        $work = $work['work'];
443
                        // Getting user results
444
                        if (!empty($work->user_results)) {
445
                            foreach ($work->user_results as $workSent) {
446
                                deleteWorkItem($workSent['id'], $course_info);
447
                            }
448
                        }
449
                    }
450
                }
451
            }
452
        }
453
454
        // Unsubscribe user from all blogs in the course.
455
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_BLOGS_REL_USER)."
456
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
457
        Database::query($sql);
458
459
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_BLOGS_TASKS_REL_USER)."
460
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
461
        Database::query($sql);
462
463
        // Deleting users in forum_notification and mailqueue course tables
464
        $sql = "DELETE FROM  ".Database::get_course_table(TABLE_FORUM_NOTIFICATION)."
465
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
466
        Database::query($sql);
467
468
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_FORUM_MAIL_QUEUE)."
469
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
470
        Database::query($sql);
471
472
        // Unsubscribe user from the course.
473
        if (!empty($session_id)) {
474
            foreach ($userList as $uid) {
475
                SessionManager::unSubscribeUserFromCourseSession($uid, $course_id, $session_id);
476
477
                // check if a user is register in the session with other course
478
                $sql = "SELECT user_id FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
479
                        WHERE session_id = $session_id AND user_id = $uid";
480
                $rs = Database::query($sql);
481
482
                if (Database::num_rows($rs) == 0) {
483
                    SessionManager::unsubscribe_user_from_session($uid, $session_id);
484
                }
485
            }
486
487
            Event::addEvent(
488
                LOG_UNSUBSCRIBE_USER_FROM_COURSE,
489
                LOG_COURSE_CODE,
490
                $course_code,
491
                api_get_utc_datetime(),
492
                $user_id,
493
                $course_id,
494
                $session_id
495
            );
496
        } else {
497
            $sql = "DELETE FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
498
                    WHERE
499
                        user_id IN ($user_ids) AND
500
                        relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
501
                        c_id = $course_id";
502
            Database::query($sql);
503
504
            // add event to system log
505
            $user_id = api_get_user_id();
506
507
            Event::addEvent(
508
                LOG_UNSUBSCRIBE_USER_FROM_COURSE,
509
                LOG_COURSE_CODE,
510
                $course_code,
511
                api_get_utc_datetime(),
512
                $user_id,
513
                $course_id
514
            );
515
516
            foreach ($userList as $userId) {
517
                $userInfo = api_get_user_info($userId);
518
                Event::addEvent(
519
                    LOG_UNSUBSCRIBE_USER_FROM_COURSE,
520
                    LOG_USER_OBJECT,
521
                    $userInfo,
522
                    api_get_utc_datetime(),
523
                    $user_id,
524
                    $course_id
525
                );
526
            }
527
        }
528
        if (api_get_configuration_value('catalog_course_subscription_in_user_s_session')) {
529
            // Also unlink the course from the users' currently accessible sessions
530
            /** @var Course $course */
531
            $course = Database::getManager()->getRepository('ChamiloCoreBundle:Course')->findOneBy([
532
                'code' => $course_code,
533
            ]);
534
            if (is_null($course)) {
535
                return false;
536
            }
537
            /** @var Chamilo\UserBundle\Entity\User $user */
538
            foreach (UserManager::getRepository()->matching(
539
                Criteria::create()->where(Criteria::expr()->in('id', $userList))
540
            ) as $user) {
541
                foreach ($user->getCurrentlyAccessibleSessions() as $session) {
542
                    $session->removeCourse($course);
543
                    // unsubscribe user from course within this session
544
                    SessionManager::unSubscribeUserFromCourseSession($user->getId(), $course->getId(), $session->getId());
545
                }
546
            }
547
            try {
548
                Database::getManager()->flush();
549
            } catch (\Doctrine\ORM\OptimisticLockException $exception) {
550
                Display::addFlash(
551
                    Display::return_message(
552
                        get_lang('InternalDatabaseError').': '.$exception->getMessage(),
553
                        'warning'
554
                    )
555
                );
556
557
                return false;
558
            }
559
        }
560
561
        return true;
562
    }
563
564
    /**
565
     * @param string $courseCode
566
     * @param int    $status
567
     *
568
     * @return bool
569
     */
570
    public static function autoSubscribeToCourse($courseCode, $status = STUDENT)
571
    {
572
        if (api_is_anonymous()) {
573
            return false;
574
        }
575
576
        $course = Database::getManager()->getRepository('ChamiloCoreBundle:Course')->findOneBy(['code' => $courseCode]);
577
578
        if (null === $course) {
579
            return false;
580
        }
581
582
        $visibility = (int) $course->getVisibility();
583
584
        if (in_array($visibility, [
585
                COURSE_VISIBILITY_CLOSED,
586
                //COURSE_VISIBILITY_REGISTERED,
587
                COURSE_VISIBILITY_HIDDEN,
588
        ])) {
589
            Display::addFlash(
590
                Display::return_message(
591
                    get_lang('SubscribingNotAllowed'),
592
                    'warning'
593
                )
594
            );
595
596
            return false;
597
        }
598
599
        // Private course can allow auto subscription
600
        if (COURSE_VISIBILITY_REGISTERED === $visibility && false === $course->getSubscribe()) {
601
            Display::addFlash(
602
                Display::return_message(
603
                    get_lang('SubscribingNotAllowed'),
604
                    'warning'
605
                )
606
            );
607
608
            return false;
609
        }
610
611
        $userId = api_get_user_id();
612
613
        if (api_get_configuration_value('catalog_course_subscription_in_user_s_session')) {
614
            /**
615
             * @var Chamilo\UserBundle\Entity\User
616
             */
617
            $user = UserManager::getRepository()->find($userId);
618
            $sessions = $user->getCurrentlyAccessibleSessions();
619
            if (empty($sessions)) {
620
                // user has no accessible session
621
                if ($user->getStudentSessions()) {
622
                    // user has ancient or future student session(s) but not available now
623
                    Display::addFlash(
624
                        Display::return_message(
625
                            get_lang('CanNotSubscribeToCourseUserSessionExpired'),
626
                            'warning'
627
                        )
628
                    );
629
630
                    return false;
631
                }
632
                // user has no session at all, create one starting now
633
                $numberOfDays = api_get_configuration_value('user_s_session_duration') ?: 3 * 365;
634
                try {
635
                    $duration = new DateInterval(sprintf('P%dD', $numberOfDays));
636
                } catch (Exception $exception) {
637
                    Display::addFlash(
638
                        Display::return_message(
639
                            get_lang('WrongNumberOfDays').': '.$numberOfDays.': '.$exception->getMessage(),
640
                            'warning'
641
                        )
642
                    );
643
644
                    return false;
645
                }
646
                $endDate = new DateTime();
647
                $endDate->add($duration);
648
                $session = new \Chamilo\CoreBundle\Entity\Session();
649
                $session->setName(
650
                    sprintf(get_lang('FirstnameLastnameCourses'), $user->getFirstname(), $user->getLastname())
651
                );
652
                $session->setAccessEndDate($endDate);
653
                $session->setCoachAccessEndDate($endDate);
654
                $session->setDisplayEndDate($endDate);
655
                $session->setSendSubscriptionNotification(false);
656
                $session->setSessionAdminId(api_get_configuration_value('session_automatic_creation_user_id') ?: 1);
657
                $session->addUserInSession(0, $user);
658
                Database::getManager()->persist($session);
659
                try {
660
                    Database::getManager()->flush();
661
                } catch (\Doctrine\ORM\OptimisticLockException $exception) {
662
                    Display::addFlash(
663
                        Display::return_message(
664
                            get_lang('InternalDatabaseError').': '.$exception->getMessage(),
665
                            'warning'
666
                        )
667
                    );
668
669
                    return false;
670
                }
671
                $accessUrlRelSession = new \Chamilo\CoreBundle\Entity\AccessUrlRelSession();
672
                $accessUrlRelSession->setAccessUrlId(api_get_current_access_url_id());
673
                $accessUrlRelSession->setSessionId($session->getId());
674
                Database::getManager()->persist($accessUrlRelSession);
675
                try {
676
                    Database::getManager()->flush();
677
                } catch (\Doctrine\ORM\OptimisticLockException $exception) {
678
                    Display::addFlash(
679
                        Display::return_message(
680
                            get_lang('InternalDatabaseError').': '.$exception->getMessage(),
681
                            'warning'
682
                        )
683
                    );
684
685
                    return false;
686
                }
687
            } else {
688
                // user has at least one accessible session, let's use it
689
                $session = $sessions[0];
690
            }
691
            // add chosen course to the user session
692
            $session->addCourse($course);
693
            Database::getManager()->persist($session);
694
            try {
695
                Database::getManager()->flush();
696
            } catch (\Doctrine\ORM\OptimisticLockException $exception) {
697
                Display::addFlash(
698
                    Display::return_message(
699
                        get_lang('InternalDatabaseError').': '.$exception->getMessage(),
700
                        'warning'
701
                    )
702
                );
703
704
                return false;
705
            }
706
            // subscribe user to course within this session
707
            SessionManager::subscribe_users_to_session_course([$userId], $session->getId(), $course->getCode());
708
709
            return true;
710
        }
711
712
        return self::subscribeUser($userId, $course->getCode(), $status, 0);
713
    }
714
715
    /**
716
     * Subscribe a user to a course. No checks are performed here to see if
717
     * course subscription is allowed.
718
     *
719
     * @param int    $userId
720
     * @param string $courseCode
721
     * @param int    $status                 (STUDENT, COURSEMANAGER, COURSE_ADMIN, NORMAL_COURSE_MEMBER)
722
     * @param int    $sessionId
723
     * @param int    $userCourseCategoryId
724
     * @param bool   $checkTeacherPermission
725
     *
726
     * @return bool True on success, false on failure
727
     *
728
     * @assert ('', '') === false
729
     */
730
    public static function subscribeUser(
731
        $userId,
732
        $courseCode,
733
        $status = STUDENT,
734
        $sessionId = 0,
735
        $userCourseCategoryId = 0,
736
        $checkTeacherPermission = true
737
    ) {
738
        $userId = (int) $userId;
739
        $status = (int) $status;
740
741
        if (empty($userId) || empty($courseCode)) {
742
            return false;
743
        }
744
745
        $courseInfo = api_get_course_info($courseCode);
746
747
        if (empty($courseInfo)) {
748
            Display::addFlash(Display::return_message(get_lang('CourseDoesNotExist'), 'warning'));
749
750
            return false;
751
        }
752
753
        $userInfo = api_get_user_info($userId);
754
755
        if (empty($userInfo)) {
756
            Display::addFlash(Display::return_message(get_lang('UserDoesNotExist'), 'warning'));
757
758
            return false;
759
        }
760
761
        $courseId = $courseInfo['real_id'];
762
        $courseCode = $courseInfo['code'];
763
        $userCourseCategoryId = (int) $userCourseCategoryId;
764
        $sessionId = empty($sessionId) ? api_get_session_id() : (int) $sessionId;
765
        $status = $status === STUDENT || $status === COURSEMANAGER ? $status : STUDENT;
766
        $courseUserTable = Database::get_main_table(TABLE_MAIN_COURSE_USER);
767
768
        if (!empty($sessionId)) {
769
            SessionManager::subscribe_users_to_session_course(
770
                [$userId],
771
                $sessionId,
772
                $courseCode
773
            );
774
775
            // Add event to the system log
776
            Event::addEvent(
777
                LOG_SUBSCRIBE_USER_TO_COURSE,
778
                LOG_COURSE_CODE,
779
                $courseCode,
780
                api_get_utc_datetime(),
781
                api_get_user_id(),
782
                $courseId,
783
                $sessionId
784
            );
785
            Event::addEvent(
786
                LOG_SUBSCRIBE_USER_TO_COURSE,
787
                LOG_USER_OBJECT,
788
                $userInfo,
789
                api_get_utc_datetime(),
790
                api_get_user_id(),
791
                $courseId,
792
                $sessionId
793
            );
794
795
            return true;
796
        } else {
797
            // Check whether the user has not been already subscribed to the course.
798
            $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
799
                    WHERE
800
                        user_id = $userId AND
801
                        relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
802
                        c_id = $courseId
803
                    ";
804
            if (Database::num_rows(Database::query($sql)) > 0) {
805
                Display::addFlash(Display::return_message(get_lang('AlreadyRegisteredToCourse'), 'warning'));
806
807
                return false;
808
            }
809
810
            if ($checkTeacherPermission && !api_is_course_admin()) {
811
                // Check in advance whether subscription is allowed or not for this course.
812
                if ((int) $courseInfo['subscribe'] === SUBSCRIBE_NOT_ALLOWED) {
813
                    Display::addFlash(Display::return_message(get_lang('SubscriptionNotAllowed'), 'warning'));
814
815
                    return false;
816
                }
817
            }
818
819
            if (STUDENT === $status) {
820
                // Check if max students per course extra field is set
821
                $extraFieldValue = new ExtraFieldValue('course');
822
                $value = $extraFieldValue->get_values_by_handler_and_field_variable(
823
                    $courseId,
824
                    'max_subscribed_students'
825
                );
826
827
                if (!empty($value) && isset($value['value'])) {
828
                    $maxStudents = $value['value'];
829
                    if ($maxStudents !== '') {
830
                        $maxStudents = (int) $maxStudents;
831
                        $count = self::get_user_list_from_course_code(
832
                            $courseCode,
833
                            0,
834
                            null,
835
                            null,
836
                            STUDENT,
837
                            true,
838
                            false
839
                        );
840
841
                        if ($count >= $maxStudents) {
842
                            Display::addFlash(Display::return_message(get_lang('MaxNumberSubscribedStudentsReached'), 'warning'));
843
844
                            return false;
845
                        }
846
                    }
847
                }
848
            }
849
850
            $maxSort = api_max_sort_value('0', $userId);
851
            $params = [
852
                'c_id' => $courseId,
853
                'user_id' => $userId,
854
                'status' => $status,
855
                'sort' => $maxSort + 1,
856
                'relation_type' => 0,
857
                'user_course_cat' => (int) $userCourseCategoryId,
858
            ];
859
            $insertId = Database::insert($courseUserTable, $params);
860
861
            if ($insertId) {
862
                Display::addFlash(
863
                    Display::return_message(
864
                        sprintf(
865
                            get_lang('UserXAddedToCourseX'),
866
                            $userInfo['complete_name_with_username'],
867
                            $courseInfo['title']
868
                        )
869
                    )
870
                );
871
872
                $send = api_get_course_setting('email_alert_to_teacher_on_new_user_in_course', $courseInfo);
873
874
                if ($send == 1) {
875
                    self::email_to_tutor(
876
                        $userId,
877
                        $courseInfo['real_id'],
878
                        false
879
                    );
880
                } elseif ($send == 2) {
881
                    self::email_to_tutor(
882
                        $userId,
883
                        $courseInfo['real_id'],
884
                        true
885
                    );
886
                }
887
888
                $subscribe = (int) api_get_course_setting('subscribe_users_to_forum_notifications', $courseInfo);
889
                if ($subscribe === 1) {
890
                    require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
891
                    $forums = get_forums(0, $courseCode, true, $sessionId);
892
                    foreach ($forums as $forum) {
893
                        $forumId = $forum['iid'];
894
                        set_notification('forum', $forumId, false, $userInfo, $courseInfo);
895
                    }
896
                }
897
898
                // Add event to the system log
899
                Event::addEvent(
900
                    LOG_SUBSCRIBE_USER_TO_COURSE,
901
                    LOG_COURSE_CODE,
902
                    $courseCode,
903
                    api_get_utc_datetime(),
904
                    api_get_user_id(),
905
                    $courseId
906
                );
907
908
                Event::addEvent(
909
                    LOG_SUBSCRIBE_USER_TO_COURSE,
910
                    LOG_USER_OBJECT,
911
                    $userInfo,
912
                    api_get_utc_datetime(),
913
                    api_get_user_id(),
914
                    $courseId
915
                );
916
917
                return true;
918
            }
919
920
            return false;
921
        }
922
    }
923
924
    /**
925
     * Get the course id based on the original id and field name in the
926
     * extra fields. Returns 0 if course was not found.
927
     *
928
     * @param string $original_course_id_value
929
     * @param string $original_course_id_name
930
     *
931
     * @return int Course id
932
     *
933
     * @assert ('', '') === false
934
     */
935
    public static function get_course_code_from_original_id(
936
        $original_course_id_value,
937
        $original_course_id_name
938
    ) {
939
        $t_cfv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
940
        $table_field = Database::get_main_table(TABLE_EXTRA_FIELD);
941
        $extraFieldType = EntityExtraField::COURSE_FIELD_TYPE;
942
        $original_course_id_value = Database::escape_string($original_course_id_value);
943
        $original_course_id_name = Database::escape_string($original_course_id_name);
944
945
        $sql = "SELECT item_id
946
                FROM $table_field cf
947
                INNER JOIN $t_cfv cfv
948
                ON cfv.field_id=cf.id
949
                WHERE
950
                    variable = '$original_course_id_name' AND
951
                    value = '$original_course_id_value' AND
952
                    cf.extra_field_type = $extraFieldType
953
                ";
954
        $res = Database::query($sql);
955
        $row = Database::fetch_object($res);
956
        if ($row) {
957
            return $row->item_id;
958
        } else {
959
            return 0;
960
        }
961
    }
962
963
    /**
964
     * Gets the course code from the course id. Returns null if course id was not found.
965
     *
966
     * @param int $id Course id
967
     *
968
     * @return string Course code
969
     * @assert ('') === false
970
     */
971
    public static function get_course_code_from_course_id($id)
972
    {
973
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
974
        $id = intval($id);
975
        $sql = "SELECT code FROM $table WHERE id = $id ";
976
        $res = Database::query($sql);
977
        $row = Database::fetch_object($res);
978
        if ($row) {
979
            return $row->code;
980
        } else {
981
            return null;
982
        }
983
    }
984
985
    /**
986
     * Add the user $userId visibility to the course $courseCode in the catalogue.
987
     *
988
     * @author David Nos (https://github.com/dnos)
989
     *
990
     * @param int    $userId     the id of the user
991
     * @param string $courseCode the course code
992
     * @param int    $visible    (optional) The course visibility in the catalogue to the user (1=visible, 0=invisible)
993
     *
994
     * @return bool true if added succesfully, false otherwise
995
     */
996
    public static function addUserVisibilityToCourseInCatalogue(
997
        $userId,
998
        $courseCode,
999
        $visible = 1
1000
    ) {
1001
        $debug = false;
1002
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1003
        $courseUserTable = Database::get_main_table(TABLE_MAIN_COURSE_CATALOGUE_USER);
1004
        $visible = (int) $visible;
1005
        if (empty($userId) || empty($courseCode) || ($userId != strval(intval($userId)))) {
1006
            return false;
1007
        }
1008
1009
        $courseCode = Database::escape_string($courseCode);
1010
        $courseInfo = api_get_course_info($courseCode);
1011
        $courseId = $courseInfo['real_id'];
1012
1013
        // Check in advance whether the user has already been registered on the platform.
1014
        $sql = "SELECT status FROM ".$userTable." WHERE user_id = $userId ";
1015
        if (Database::num_rows(Database::query($sql)) == 0) {
1016
            if ($debug) {
1017
                error_log('The user has not been registered to the platform');
1018
            }
1019
1020
            return false; // The user has not been registered to the platform.
1021
        }
1022
1023
        // Check whether the user has already been registered to the course visibility in the catalogue.
1024
        $sql = "SELECT * FROM $courseUserTable
1025
                WHERE
1026
                    user_id = $userId AND
1027
                    visible = $visible AND
1028
                    c_id = $courseId";
1029
        if (Database::num_rows(Database::query($sql)) > 0) {
1030
            if ($debug) {
1031
                error_log('The user has been already registered to the course visibility in the catalogue');
1032
            }
1033
1034
            return true; // The visibility of the user to the course in the catalogue does already exist.
1035
        }
1036
1037
        // Register the user visibility to course in catalogue.
1038
        $params = [
1039
            'user_id' => $userId,
1040
            'c_id' => $courseId,
1041
            'visible' => $visible,
1042
        ];
1043
        $insertId = Database::insert($courseUserTable, $params);
1044
1045
        return $insertId;
1046
    }
1047
1048
    /**
1049
     * Remove the user $userId visibility to the course $courseCode in the catalogue.
1050
     *
1051
     * @author David Nos (https://github.com/dnos)
1052
     *
1053
     * @param int    $userId     the id of the user
1054
     * @param string $courseCode the course code
1055
     * @param int    $visible    (optional) The course visibility in the catalogue to the user (1=visible, 0=invisible)
1056
     *
1057
     * @return bool true if removed succesfully or register not found, false otherwise
1058
     */
1059
    public static function removeUserVisibilityToCourseInCatalogue(
1060
        $userId,
1061
        $courseCode,
1062
        $visible = 1
1063
    ) {
1064
        $courseUserTable = Database::get_main_table(TABLE_MAIN_COURSE_CATALOGUE_USER);
1065
1066
        if (empty($userId) || empty($courseCode) || ($userId != strval(intval($userId)))) {
1067
            return false;
1068
        }
1069
1070
        $courseCode = Database::escape_string($courseCode);
1071
        $courseInfo = api_get_course_info($courseCode);
1072
        $courseId = $courseInfo['real_id'];
1073
1074
        // Check whether the user has already been registered to the course visibility in the catalogue.
1075
        $sql = "SELECT * FROM $courseUserTable
1076
                WHERE
1077
                    user_id = $userId AND
1078
                    visible = $visible AND
1079
                    c_id = $courseId";
1080
        if (Database::num_rows(Database::query($sql)) > 0) {
1081
            $cond = [
1082
                'user_id = ? AND c_id = ? AND visible = ? ' => [
1083
                    $userId,
1084
                    $courseId,
1085
                    $visible,
1086
                ],
1087
            ];
1088
1089
            return Database::delete($courseUserTable, $cond);
1090
        } else {
1091
            return true; // Register does not exist
1092
        }
1093
    }
1094
1095
    /**
1096
     * @param string $code
1097
     *
1098
     * @return bool if there already are one or more courses
1099
     *              with the same code OR visual_code (visualcode), false otherwise
1100
     */
1101
    public static function course_code_exists($code)
1102
    {
1103
        $code = Database::escape_string($code);
1104
        $sql = "SELECT COUNT(*) as number
1105
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
1106
                WHERE code = '$code' OR visual_code = '$code'";
1107
        $result = Database::fetch_array(Database::query($sql));
1108
1109
        return $result['number'] > 0;
1110
    }
1111
1112
    /**
1113
     * @param int    $user_id
1114
     * @param string $startsWith Optional
1115
     *
1116
     * @return array an array with the course info of all the courses (real and virtual)
1117
     *               of which the current user is course admin
1118
     */
1119
    public static function get_course_list_of_user_as_course_admin($user_id, $startsWith = '')
1120
    {
1121
        if ($user_id != strval(intval($user_id))) {
1122
            return [];
1123
        }
1124
1125
        // Definitions database tables and variables
1126
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
1127
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1128
        $user_id = intval($user_id);
1129
        $data = [];
1130
1131
        $sql = "SELECT
1132
                    course.code,
1133
                    course.title,
1134
                    course.id,
1135
                    course.id as real_id,
1136
                    course.category_code
1137
                FROM $tbl_course_user as course_rel_user
1138
                INNER JOIN $tbl_course as course
1139
                ON course.id = course_rel_user.c_id
1140
                WHERE
1141
                    course_rel_user.user_id = $user_id AND
1142
                    course_rel_user.status = 1
1143
        ";
1144
1145
        if (api_get_multiple_access_url()) {
1146
            $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
1147
            $access_url_id = api_get_current_access_url_id();
1148
            if ($access_url_id != -1) {
1149
                $sql = "
1150
                    SELECT
1151
                        course.code,
1152
                        course.title,
1153
                        course.id,
1154
                        course.id as real_id
1155
                    FROM $tbl_course_user as course_rel_user
1156
                    INNER JOIN $tbl_course as course
1157
                    ON course.id = course_rel_user.c_id
1158
                    INNER JOIN $tbl_course_rel_access_url course_rel_url
1159
                    ON (course_rel_url.c_id = course.id)
1160
                    WHERE
1161
                        access_url_id = $access_url_id  AND
1162
                        course_rel_user.user_id = $user_id AND
1163
                        course_rel_user.status = 1
1164
                ";
1165
            }
1166
        }
1167
1168
        if (!empty($startsWith)) {
1169
            $startsWith = Database::escape_string($startsWith);
1170
1171
            $sql .= " AND (course.title LIKE '$startsWith%' OR course.code LIKE '$startsWith%')";
1172
        }
1173
1174
        $sql .= ' ORDER BY course.title';
1175
1176
        $result_nb_cours = Database::query($sql);
1177
        if (Database::num_rows($result_nb_cours) > 0) {
1178
            while ($row = Database::fetch_array($result_nb_cours, 'ASSOC')) {
1179
                $data[$row['id']] = $row;
1180
            }
1181
        }
1182
1183
        return $data;
1184
    }
1185
1186
    /**
1187
     * @param int   $userId
1188
     * @param array $courseInfo
1189
     *
1190
     * @return bool|null
1191
     */
1192
    public static function isUserSubscribedInCourseAsDrh($userId, $courseInfo)
1193
    {
1194
        $userId = intval($userId);
1195
1196
        if (!api_is_drh()) {
1197
            return false;
1198
        }
1199
1200
        if (empty($courseInfo) || empty($userId)) {
1201
            return false;
1202
        }
1203
1204
        $courseId = intval($courseInfo['real_id']);
1205
        $table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1206
1207
        $sql = "SELECT * FROM $table
1208
                WHERE
1209
                    user_id = $userId AND
1210
                    relation_type = ".COURSE_RELATION_TYPE_RRHH." AND
1211
                    c_id = $courseId";
1212
1213
        $result = Database::fetch_array(Database::query($sql));
1214
1215
        if (!empty($result)) {
1216
            // The user has been registered in this course.
1217
            return true;
1218
        }
1219
    }
1220
1221
    /**
1222
     * Check if user is subscribed inside a course.
1223
     *
1224
     * @param int    $user_id
1225
     * @param string $course_code  , if this parameter is null, it'll check for all courses
1226
     * @param bool   $in_a_session True for checking inside sessions too, by default is not checked
1227
     * @param int    $session_id
1228
     *
1229
     * @return bool $session_id true if the user is registered in the course, false otherwise
1230
     */
1231
    public static function is_user_subscribed_in_course(
1232
        $user_id,
1233
        $course_code = null,
1234
        $in_a_session = false,
1235
        $session_id = 0
1236
    ) {
1237
        $user_id = (int) $user_id;
1238
        $session_id = (int) $session_id;
1239
1240
        if (api_get_configuration_value('catalog_course_subscription_in_user_s_session')) {
1241
            // with this option activated, only check whether the course is in one of the users' sessions
1242
            $course = Database::getManager()->getRepository('ChamiloCoreBundle:Course')->findOneBy([
1243
                'code' => $course_code,
1244
            ]);
1245
            if (is_null($course)) {
1246
                return false;
1247
            }
1248
            /**
1249
             * @var \Chamilo\UserBundle\Entity\User
1250
             */
1251
            $user = UserManager::getRepository()->find($user_id);
1252
            if (is_null($user)) {
1253
                return false;
1254
            }
1255
            foreach ($user->getStudentSessions() as $session) {
1256
                if ($session->isRelatedToCourse($course)) {
1257
                    return true;
1258
                }
1259
            }
1260
1261
            return false;
1262
        }
1263
1264
        if (empty($session_id)) {
1265
            $session_id = api_get_session_id();
1266
        }
1267
1268
        $condition_course = '';
1269
        if (isset($course_code)) {
1270
            $courseInfo = api_get_course_info($course_code);
1271
            if (empty($courseInfo)) {
1272
                return false;
1273
            }
1274
            $courseId = $courseInfo['real_id'];
1275
            $condition_course = " AND c_id = $courseId";
1276
        }
1277
1278
        $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
1279
                WHERE
1280
                    user_id = $user_id AND
1281
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
1282
                    $condition_course ";
1283
1284
        $result = Database::fetch_array(Database::query($sql));
1285
1286
        if (!empty($result)) {
1287
            // The user has been registered in this course.
1288
            return true;
1289
        }
1290
1291
        if (!$in_a_session) {
1292
            // The user has not been registered in this course.
1293
            return false;
1294
        }
1295
1296
        $tableSessionCourseUser = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1297
        $sql = "SELECT 1 FROM $tableSessionCourseUser
1298
                WHERE user_id = $user_id AND session_id = $session_id $condition_course";
1299
1300
        if (Database::num_rows(Database::query($sql)) > 0) {
1301
            return true;
1302
        }
1303
1304
        $sql = "SELECT 1 FROM $tableSessionCourseUser
1305
                WHERE user_id = $user_id AND session_id = $session_id AND status = 2 $condition_course";
1306
1307
        if (Database::num_rows(Database::query($sql)) > 0) {
1308
            return true;
1309
        }
1310
1311
        $sql = 'SELECT 1 FROM '.Database::get_main_table(TABLE_MAIN_SESSION).
1312
              " WHERE id = $session_id AND id_coach = $user_id";
1313
1314
        if (Database::num_rows(Database::query($sql)) > 0) {
1315
            return true;
1316
        }
1317
1318
        return false;
1319
    }
1320
1321
    /**
1322
     * Is the user a teacher in the given course?
1323
     *
1324
     * @param int    $user_id     , the id (int) of the user
1325
     * @param string $course_code , the course code
1326
     *
1327
     * @return bool if the user is a teacher in the course, false otherwise
1328
     */
1329
    public static function is_course_teacher($user_id, $course_code)
1330
    {
1331
        if ($user_id != strval(intval($user_id))) {
1332
            return false;
1333
        }
1334
1335
        $courseInfo = api_get_course_info($course_code);
1336
        if (empty($courseInfo)) {
1337
            return false;
1338
        }
1339
        $courseId = $courseInfo['real_id'];
1340
        $sql = "SELECT status FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
1341
                WHERE c_id = $courseId AND user_id = $user_id ";
1342
        $result = Database::query($sql);
1343
1344
        if (Database::num_rows($result) > 0) {
1345
            return Database::result($result, 0, 'status') == 1;
1346
        }
1347
1348
        return false;
1349
    }
1350
1351
    /**
1352
     *    Is the user subscribed in the real course or linked courses?
1353
     *
1354
     * @param int the id of the user
1355
     * @param int $courseId
1356
     *
1357
     * @deprecated linked_courses definition doesn't exists
1358
     *
1359
     * @return bool if the user is registered in the real course or linked courses, false otherwise
1360
     */
1361
    public static function is_user_subscribed_in_real_or_linked_course($user_id, $courseId, $session_id = 0)
1362
    {
1363
        if ($user_id != strval(intval($user_id))) {
1364
            return false;
1365
        }
1366
1367
        $courseId = intval($courseId);
1368
        $session_id = intval($session_id);
1369
1370
        if (empty($session_id)) {
1371
            $result = Database::fetch_array(
1372
                Database::query(
1373
                    "SELECT *
1374
                    FROM ".Database::get_main_table(TABLE_MAIN_COURSE)." course
1375
                    LEFT JOIN ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." course_user
1376
                    ON course.id = course_user.c_id
1377
                    WHERE
1378
                        course_user.user_id = $user_id AND
1379
                        course_user.relation_type<>".COURSE_RELATION_TYPE_RRHH." AND
1380
                        ( course.id = $courseId)"
1381
                )
1382
            );
1383
1384
            return !empty($result);
1385
        }
1386
1387
        // From here we trust session id.
1388
        // Is he/she subscribed to the session's course?
1389
        // A user?
1390
        if (Database::num_rows(Database::query("SELECT user_id
1391
                FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
1392
                WHERE session_id = $session_id
1393
                AND user_id = $user_id"))
1394
        ) {
1395
            return true;
1396
        }
1397
1398
        // A course coach?
1399
        if (Database::num_rows(Database::query("SELECT user_id
1400
                FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
1401
                WHERE session_id = $session_id
1402
                AND user_id = $user_id AND status = 2
1403
                AND c_id = $courseId"))
1404
        ) {
1405
            return true;
1406
        }
1407
1408
        // A session coach?
1409
        if (Database::num_rows(Database::query("SELECT id_coach
1410
                FROM ".Database::get_main_table(TABLE_MAIN_SESSION)." AS session
1411
                WHERE session.id = $session_id
1412
                AND id_coach = $user_id"))
1413
        ) {
1414
            return true;
1415
        }
1416
1417
        return false;
1418
    }
1419
1420
    /**
1421
     * Return user info array of all users registered in a course
1422
     * This only returns the users that are registered in this actual course, not linked courses.
1423
     *
1424
     * @param string    $course_code
1425
     * @param int       $sessionId
1426
     * @param string    $limit
1427
     * @param string    $order_by         the field to order the users by.
1428
     *                                    Valid values are 'lastname', 'firstname', 'username', 'email', 'official_code' OR a part of a SQL statement
1429
     *                                    that starts with ORDER BY ...
1430
     * @param int|null  $filter_by_status if using the session_id: 0 or 2 (student, coach),
1431
     *                                    if using session_id = 0 STUDENT or COURSEMANAGER
1432
     * @param bool|null $return_count
1433
     * @param bool      $add_reports
1434
     * @param bool      $resumed_report
1435
     * @param array     $extra_field
1436
     * @param array     $courseCodeList
1437
     * @param array     $userIdList
1438
     * @param string    $filterByActive
1439
     * @param array     $sessionIdList
1440
     * @param string    $searchByKeyword
1441
     *
1442
     * @return array|int
1443
     */
1444
    public static function get_user_list_from_course_code(
1445
        $course_code = null,
1446
        $sessionId = 0,
1447
        $limit = null,
1448
        $order_by = null,
1449
        $filter_by_status = null,
1450
        $return_count = null,
1451
        $add_reports = false,
1452
        $resumed_report = false,
1453
        $extra_field = [],
1454
        $courseCodeList = [],
1455
        $userIdList = [],
1456
        $filterByActive = null,
1457
        $sessionIdList = [],
1458
        $searchByKeyword = '',
1459
        $options = []
1460
    ) {
1461
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
1462
        $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
1463
1464
        $sessionId = (int) $sessionId;
1465
        $course_code = Database::escape_string($course_code);
1466
        $courseInfo = api_get_course_info($course_code);
1467
        $courseId = 0;
1468
        if (!empty($courseInfo)) {
1469
            $courseId = $courseInfo['real_id'];
1470
        }
1471
1472
        $where = [];
1473
        if (empty($order_by)) {
1474
            $order_by = 'user.lastname, user.firstname';
1475
            if (api_is_western_name_order()) {
1476
                $order_by = 'user.firstname, user.lastname';
1477
            }
1478
        }
1479
1480
        // if the $order_by does not contain 'ORDER BY'
1481
        // we have to check if it is a valid field that can be sorted on
1482
        if (!strstr($order_by, 'ORDER BY')) {
1483
            if (!empty($order_by)) {
1484
                $order_by = "ORDER BY $order_by";
1485
            } else {
1486
                $order_by = '';
1487
            }
1488
        }
1489
1490
        $filter_by_status_condition = null;
1491
        $sqlInjectWhere = '';
1492
        $whereExtraField = '';
1493
        $injectExtraFields = ' , ';
1494
        $sqlInjectJoins = '';
1495
        if (!empty($options)) {
1496
            $extraFieldModel = new ExtraField('user');
1497
            $conditions = $extraFieldModel->parseConditions($options, 'user');
1498
            if (!empty($conditions)) {
1499
                $injectExtraFields = $conditions['inject_extra_fields'];
1500
1501
                if (!empty($injectExtraFields)) {
1502
                    $injectExtraFields = ', '.$injectExtraFields;
1503
                } else {
1504
                    $injectExtraFields = ' , ';
1505
                }
1506
                $sqlInjectJoins = $conditions['inject_joins'];
1507
                $whereExtraField = $conditions['where'];
1508
            }
1509
        }
1510
1511
        if (!empty($sessionId) || !empty($sessionIdList)) {
1512
            $sql = 'SELECT DISTINCT
1513
                        user.user_id,
1514
                        user.email,
1515
                        session_course_user.status as status_session,
1516
                        session_id,
1517
                        user.*,
1518
                        course.*,
1519
                        course.id AS c_id
1520
                         '.$injectExtraFields.'
1521
                        session.name as session_name
1522
                    ';
1523
            if ($return_count) {
1524
                $sql = ' SELECT COUNT(user.user_id) as count';
1525
            }
1526
1527
            $sessionCondition = " session_course_user.session_id = $sessionId";
1528
            if (!empty($sessionIdList)) {
1529
                $sessionIdListToString = implode("','", array_map('intval', $sessionIdList));
1530
                $sessionCondition = " session_course_user.session_id IN ('$sessionIdListToString') ";
1531
            }
1532
1533
            $courseCondition = " course.id = $courseId";
1534
            if (!empty($courseCodeList)) {
1535
                $courseCodeListForSession = array_map(['Database', 'escape_string'], $courseCodeList);
1536
                $courseCodeListForSession = implode("','", $courseCodeListForSession);
1537
                $courseCondition = " course.code IN ('$courseCodeListForSession')  ";
1538
            }
1539
1540
            $sql .= ' FROM '.Database::get_main_table(TABLE_MAIN_USER).' as user ';
1541
            $sql .= "LEFT JOIN ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)." as session_course_user
1542
                    ON
1543
                        user.id = session_course_user.user_id AND
1544
                        $sessionCondition
1545
                    INNER JOIN $course_table course
1546
                    ON session_course_user.c_id = course.id AND
1547
                    $courseCondition
1548
                    INNER JOIN $sessionTable session
1549
                    ON session_course_user.session_id = session.id
1550
                    $sqlInjectJoins
1551
                   ";
1552
            $where[] = ' session_course_user.c_id IS NOT NULL ';
1553
1554
            // 2 = coach
1555
            // 0 = student
1556
            if (isset($filter_by_status)) {
1557
                $filter_by_status = (int) $filter_by_status;
1558
                $filter_by_status_condition = " session_course_user.status = $filter_by_status AND ";
1559
            }
1560
        } else {
1561
            if ($return_count) {
1562
                $sql = " SELECT COUNT(*) as count";
1563
            } else {
1564
                if (empty($course_code)) {
1565
                    $sql = 'SELECT DISTINCT
1566
                                course.title,
1567
                                course.code,
1568
                                course.id AS c_id,
1569
                                course_rel_user.status as status_rel,
1570
                                user.id as user_id,
1571
                                user.email,
1572
                                course_rel_user.is_tutor
1573
                                '.$injectExtraFields.'
1574
                                user.*';
1575
                } else {
1576
                    $sql = 'SELECT DISTINCT
1577
                                course_rel_user.status as status_rel,
1578
                                user.id as user_id,
1579
                                user.email,
1580
                                course_rel_user.is_tutor
1581
                                '.$injectExtraFields.'
1582
                                user.*';
1583
                }
1584
            }
1585
1586
            $sql .= " FROM ".Database::get_main_table(TABLE_MAIN_USER)." as user
1587
                      LEFT JOIN ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." as course_rel_user
1588
                      ON
1589
                            user.id = course_rel_user.user_id AND
1590
                            course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
1591
                       INNER JOIN $course_table course
1592
                       ON (course_rel_user.c_id = course.id)
1593
                       $sqlInjectJoins
1594
                       ";
1595
1596
            if (!empty($course_code)) {
1597
                $sql .= " AND course_rel_user.c_id = $courseId";
1598
            }
1599
            $where[] = ' course_rel_user.c_id IS NOT NULL ';
1600
1601
            if (isset($filter_by_status) && is_numeric($filter_by_status)) {
1602
                $filter_by_status = (int) $filter_by_status;
1603
                $filter_by_status_condition = " course_rel_user.status = $filter_by_status AND ";
1604
            }
1605
        }
1606
1607
        $multiple_access_url = api_get_multiple_access_url();
1608
        if ($multiple_access_url) {
1609
            $sql .= ' LEFT JOIN '.Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER).' au
1610
                      ON (au.user_id = user.id) ';
1611
        }
1612
1613
        $extraFieldWasAdded = false;
1614
        if ($return_count && $resumed_report) {
1615
            foreach ($extra_field as $extraField) {
1616
                $extraFieldInfo = UserManager::get_extra_field_information_by_name($extraField);
1617
                if (!empty($extraFieldInfo)) {
1618
                    $fieldValuesTable = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1619
                    $sql .= " LEFT JOIN $fieldValuesTable as ufv
1620
                            ON (
1621
                                user.id = ufv.item_id AND
1622
                                (field_id = ".$extraFieldInfo['id']." OR field_id IS NULL)
1623
                            )";
1624
                    $extraFieldWasAdded = true;
1625
                }
1626
            }
1627
        }
1628
1629
        $sql .= " WHERE
1630
            $filter_by_status_condition
1631
            ".implode(' OR ', $where);
1632
1633
        if ($multiple_access_url) {
1634
            $current_access_url_id = api_get_current_access_url_id();
1635
            $sql .= " AND (access_url_id =  $current_access_url_id ) ";
1636
        }
1637
1638
        if ($return_count && $resumed_report && $extraFieldWasAdded) {
1639
            $sql .= ' AND field_id IS NOT NULL GROUP BY value ';
1640
        }
1641
1642
        if (!empty($courseCodeList)) {
1643
            $courseCodeList = array_map(['Database', 'escape_string'], $courseCodeList);
1644
            $courseCodeList = implode('","', $courseCodeList);
1645
            if (empty($sessionIdList)) {
1646
                $sql .= ' AND course.code IN ("'.$courseCodeList.'")';
1647
            }
1648
        }
1649
1650
        if (!empty($userIdList)) {
1651
            $userIdList = array_map('intval', $userIdList);
1652
            $userIdList = implode('","', $userIdList);
1653
            $sql .= ' AND user.id IN ("'.$userIdList.'")';
1654
        }
1655
1656
        if (isset($filterByActive)) {
1657
            $filterByActive = (int) $filterByActive;
1658
            $sql .= " AND user.active = $filterByActive";
1659
        }
1660
1661
        if (!empty($searchByKeyword)) {
1662
            $searchByKeyword = Database::escape_string($searchByKeyword);
1663
            $sql .= " AND (
1664
                        user.firstname LIKE '$searchByKeyword' OR
1665
                        user.username LIKE '$searchByKeyword' OR
1666
                        user.lastname LIKE '$searchByKeyword'
1667
                    ) ";
1668
        }
1669
1670
        $sql .= $whereExtraField;
1671
        $sql .= " $order_by $limit";
1672
1673
        $rs = Database::query($sql);
1674
        $users = [];
1675
1676
        $extra_fields = UserManager::get_extra_fields(
1677
            0,
1678
            100,
1679
            null,
1680
            null,
1681
            true,
1682
            true
1683
        );
1684
1685
        $counter = 1;
1686
        $count_rows = Database::num_rows($rs);
1687
1688
        if ($return_count && $resumed_report) {
1689
            return $count_rows;
1690
        }
1691
        $table_user_field_value = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1692
        $tableExtraField = Database::get_main_table(TABLE_EXTRA_FIELD);
1693
        if ($count_rows) {
1694
            while ($user = Database::fetch_array($rs)) {
1695
                if ($return_count) {
1696
                    return $user['count'];
1697
                }
1698
1699
                $report_info = [];
1700
                $user_info = $user;
1701
                $user_info['status'] = $user['status'];
1702
                if (isset($user['is_tutor'])) {
1703
                    $user_info['is_tutor'] = $user['is_tutor'];
1704
                }
1705
                if (!empty($sessionId)) {
1706
                    $user_info['status_session'] = $user['status_session'];
1707
                }
1708
1709
                $sessionId = isset($user['session_id']) ? $user['session_id'] : 0;
1710
                $course_code = isset($user['code']) ? $user['code'] : null;
1711
                $sessionName = isset($user['session_name']) ? ' ('.$user['session_name'].') ' : '';
1712
1713
                if ($add_reports) {
1714
                    if ($resumed_report) {
1715
                        $extra = [];
1716
                        if (!empty($extra_fields)) {
1717
                            foreach ($extra_fields as $extra) {
1718
                                if (in_array($extra['1'], $extra_field)) {
1719
                                    $user_data = UserManager::get_extra_user_data_by_field(
1720
                                        $user['user_id'],
1721
                                        $extra['1']
1722
                                    );
1723
                                    break;
1724
                                }
1725
                            }
1726
                        }
1727
1728
                        $row_key = '-1';
1729
                        $name = '-';
1730
                        if (!empty($extra)) {
1731
                            if (!empty($user_data[$extra['1']])) {
1732
                                $row_key = $user_data[$extra['1']];
1733
                                $name = $user_data[$extra['1']];
1734
                                $users[$row_key]['extra_'.$extra['1']] = $name;
1735
                            }
1736
                        }
1737
1738
                        if (empty($users[$row_key])) {
1739
                            $users[$row_key] = [];
1740
                        }
1741
1742
                        if (!array_key_exists('training_hours', $users[$row_key])) {
1743
                            $users[$row_key]['training_hours'] = 0;
1744
                        }
1745
1746
                        $users[$row_key]['training_hours'] += Tracking::get_time_spent_on_the_course(
1747
                            $user['user_id'],
1748
                            $courseId,
1749
                            $sessionId
1750
                        );
1751
1752
                        if (!array_key_exists('count_users', $users[$row_key])) {
1753
                            $users[$row_key]['count_users'] = 0;
1754
                        }
1755
1756
                        $users[$row_key]['count_users'] += $counter;
1757
1758
                        $registered_users_with_extra_field = self::getCountRegisteredUsersWithCourseExtraField(
1759
                            $name,
1760
                            $tableExtraField,
1761
                            $table_user_field_value
1762
                        );
1763
1764
                        $users[$row_key]['count_users_registered'] = $registered_users_with_extra_field;
1765
                        $users[$row_key]['average_hours_per_user'] = $users[$row_key]['training_hours'] / $users[$row_key]['count_users'];
1766
1767
                        $category = Category:: load(
1768
                            null,
1769
                            null,
1770
                            $course_code,
1771
                            null,
1772
                            null,
1773
                            $sessionId
1774
                        );
1775
1776
                        if (!isset($users[$row_key]['count_certificates'])) {
1777
                            $users[$row_key]['count_certificates'] = 0;
1778
                        }
1779
1780
                        if (isset($category[0]) && $category[0]->is_certificate_available($user['user_id'])) {
1781
                            $users[$row_key]['count_certificates']++;
1782
                        }
1783
1784
                        foreach ($extra_fields as $extra) {
1785
                            if ($extra['1'] === 'ruc') {
1786
                                continue;
1787
                            }
1788
1789
                            if (!isset($users[$row_key][$extra['1']])) {
1790
                                $user_data = UserManager::get_extra_user_data_by_field($user['user_id'], $extra['1']);
1791
                                if (!empty($user_data[$extra['1']])) {
1792
                                    $users[$row_key][$extra['1']] = $user_data[$extra['1']];
1793
                                }
1794
                            }
1795
                        }
1796
                    } else {
1797
                        $report_info['course'] = $user['title'].$sessionName;
1798
                        $report_info['user'] = api_get_person_name($user['firstname'], $user['lastname']);
1799
                        $report_info['email'] = $user['email'];
1800
                        $report_info['time'] = api_time_to_hms(
1801
                            Tracking::get_time_spent_on_the_course(
1802
                                $user['user_id'],
1803
                                empty($user['c_id']) ? $courseId : $user['c_id'],
1804
                                $sessionId
1805
                            )
1806
                        );
1807
1808
                        $category = Category:: load(
1809
                            null,
1810
                            null,
1811
                            $course_code,
1812
                            null,
1813
                            null,
1814
                            $sessionId
1815
                        );
1816
1817
                        $report_info['certificate'] = Display::label(get_lang('No'));
1818
                        if (isset($category[0]) && $category[0]->is_certificate_available($user['user_id'])) {
1819
                            $report_info['certificate'] = Display::label(get_lang('Yes'), 'success');
1820
                        }
1821
1822
                        $progress = intval(
1823
                            Tracking::get_avg_student_progress(
1824
                                $user['user_id'],
1825
                                $course_code,
1826
                                [],
1827
                                $sessionId
1828
                            )
1829
                        );
1830
1831
                        $report_info['progress_100'] = $progress == 100 ? Display::label(get_lang('Yes'), 'success') : Display::label(get_lang('No'));
1832
                        $report_info['progress'] = $progress."%";
1833
1834
                        foreach ($extra_fields as $extra) {
1835
                            $user_data = UserManager::get_extra_user_data_by_field($user['user_id'], $extra['1']);
1836
                            $report_info[$extra['1']] = $user_data[$extra['1']];
1837
                        }
1838
                        $report_info['user_id'] = $user['user_id'];
1839
                        $users[] = $report_info;
1840
                    }
1841
                } else {
1842
                    $users[$user['user_id']] = $user_info;
1843
                }
1844
            }
1845
        }
1846
1847
        return $users;
1848
    }
1849
1850
    /**
1851
     * @param bool  $resumed_report
1852
     * @param array $extra_field
1853
     * @param array $courseCodeList
1854
     * @param array $userIdList
1855
     * @param array $sessionIdList
1856
     * @param array $options
1857
     *
1858
     * @return array|int
1859
     */
1860
    public static function get_count_user_list_from_course_code(
1861
        $resumed_report = false,
1862
        $extra_field = [],
1863
        $courseCodeList = [],
1864
        $userIdList = [],
1865
        $sessionIdList = [],
1866
        $options = []
1867
    ) {
1868
        return self::get_user_list_from_course_code(
1869
            null,
1870
            0,
1871
            null,
1872
            null,
1873
            null,
1874
            true,
1875
            false,
1876
            $resumed_report,
1877
            $extra_field,
1878
            $courseCodeList,
1879
            $userIdList,
1880
            null,
1881
            $sessionIdList,
1882
            null,
1883
            $options
1884
        );
1885
    }
1886
1887
    /**
1888
     * Gets subscribed users in a course or in a course/session.
1889
     *
1890
     * @param string $course_code
1891
     * @param int    $session_id
1892
     *
1893
     * @return int
1894
     */
1895
    public static function get_users_count_in_course(
1896
        $course_code,
1897
        $session_id = 0,
1898
        $status = null
1899
    ) {
1900
        // variable initialisation
1901
        $session_id = (int) $session_id;
1902
        $course_code = Database::escape_string($course_code);
1903
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
1904
        $tblSessionCourseUser = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1905
        $tblCourseUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1906
        $tblUrlUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1907
1908
        $courseInfo = api_get_course_info($course_code);
1909
        $courseId = $courseInfo['real_id'];
1910
1911
        $sql = "
1912
            SELECT DISTINCT count(user.id) as count
1913
            FROM $tblUser as user
1914
        ";
1915
        $where = [];
1916
        if (!empty($session_id)) {
1917
            $sql .= "
1918
                LEFT JOIN $tblSessionCourseUser as session_course_user
1919
                    ON user.user_id = session_course_user.user_id
1920
                    AND session_course_user.c_id = $courseId
1921
                    AND session_course_user.session_id = $session_id
1922
            ";
1923
1924
            $where[] = ' session_course_user.c_id IS NOT NULL ';
1925
        } else {
1926
            $sql .= "
1927
                LEFT JOIN $tblCourseUser as course_rel_user
1928
                    ON user.user_id = course_rel_user.user_id
1929
                    AND course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
1930
                    AND course_rel_user.c_id = $courseId
1931
            ";
1932
            $where[] = ' course_rel_user.c_id IS NOT NULL ';
1933
        }
1934
1935
        $multiple_access_url = api_get_multiple_access_url();
1936
        if ($multiple_access_url) {
1937
            $sql .= " LEFT JOIN $tblUrlUser au ON (au.user_id = user.user_id) ";
1938
        }
1939
1940
        $sql .= ' WHERE '.implode(' OR ', $where);
1941
1942
        if ($multiple_access_url) {
1943
            $current_access_url_id = api_get_current_access_url_id();
1944
            $sql .= " AND (access_url_id =  $current_access_url_id ) ";
1945
        }
1946
        $rs = Database::query($sql);
1947
        $count = 0;
1948
        if (Database::num_rows($rs)) {
1949
            $user = Database::fetch_array($rs);
1950
            $count = $user['count'];
1951
        }
1952
1953
        return $count;
1954
    }
1955
1956
    /**
1957
     * Get a list of coaches of a course and a session.
1958
     *
1959
     * @param string $course_code
1960
     * @param int    $session_id
1961
     * @param bool   $addGeneralCoach
1962
     *
1963
     * @return array List of users
1964
     */
1965
    public static function get_coach_list_from_course_code(
1966
        $course_code,
1967
        $session_id,
1968
        $addGeneralCoach = true
1969
    ) {
1970
        if (empty($course_code) || empty($session_id)) {
1971
            return [];
1972
        }
1973
1974
        $course_code = Database::escape_string($course_code);
1975
        $courseInfo = api_get_course_info($course_code);
1976
        $courseId = $courseInfo['real_id'];
1977
        $session_id = (int) $session_id;
1978
        $users = [];
1979
1980
        // We get the coach for the given course in a given session.
1981
        $sql = 'SELECT user_id FROM '.Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER).
1982
               " WHERE session_id = $session_id AND c_id = $courseId AND status = 2";
1983
        $rs = Database::query($sql);
1984
        while ($user = Database::fetch_array($rs)) {
1985
            $userInfo = api_get_user_info($user['user_id']);
1986
            if ($userInfo) {
1987
                $users[$user['user_id']] = $userInfo;
1988
            }
1989
        }
1990
1991
        if ($addGeneralCoach) {
1992
            $table = Database::get_main_table(TABLE_MAIN_SESSION);
1993
            // We get the session coach.
1994
            $sql = "SELECT id_coach FROM $table WHERE id = $session_id";
1995
            $rs = Database::query($sql);
1996
            $session_id_coach = Database::result($rs, 0, 'id_coach');
1997
            if (is_int($session_id_coach)) {
1998
                $userInfo = api_get_user_info($session_id_coach);
1999
                if ($userInfo) {
2000
                    $users[$session_id_coach] = $userInfo;
2001
                }
2002
            }
2003
        }
2004
2005
        return $users;
2006
    }
2007
2008
    /**
2009
     *  Return user info array of all users registered in a course
2010
     *  This only returns the users that are registered in this actual course, not linked courses.
2011
     *
2012
     * @param string $course_code
2013
     * @param bool   $with_session
2014
     * @param int    $sessionId
2015
     * @param string $date_from
2016
     * @param string $date_to
2017
     * @param bool   $includeInvitedUsers Whether include the invited users
2018
     * @param int    $groupId
2019
     * @param bool   $getCount
2020
     * @param int    $start
2021
     * @param int    $limit
2022
     *
2023
     * @return array with user id
2024
     */
2025
    public static function get_student_list_from_course_code(
2026
        $course_code,
2027
        $with_session = false,
2028
        $sessionId = 0,
2029
        $date_from = null,
2030
        $date_to = null,
2031
        $includeInvitedUsers = true,
2032
        $groupId = 0,
2033
        $getCount = false,
2034
        $start = 0,
2035
        $limit = 0
2036
    ) {
2037
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
2038
        $sessionId = (int) $sessionId;
2039
        $courseInfo = api_get_course_info($course_code);
2040
        if (empty($courseInfo)) {
2041
            return [];
2042
        }
2043
        $courseId = $courseInfo['real_id'];
2044
        $students = [];
2045
2046
        $limitCondition = '';
2047
        if (isset($start) && isset($limit) && !empty($limit)) {
2048
            $start = (int) $start;
2049
            $limit = (int) $limit;
2050
            $limitCondition = " LIMIT $start, $limit";
2051
        }
2052
2053
        $select = '*';
2054
        if ($getCount) {
2055
            $select = 'count(u.id) as count';
2056
        }
2057
2058
        if (empty($sessionId)) {
2059
            if (empty($groupId)) {
2060
                // students directly subscribed to the course
2061
                $sql = "SELECT $select
2062
                        FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." cu
2063
                        INNER JOIN $userTable u
2064
                        ON cu.user_id = u.user_id
2065
                        WHERE c_id = $courseId AND cu.status = ".STUDENT;
2066
2067
                if (!$includeInvitedUsers) {
2068
                    $sql .= " AND u.status != ".INVITEE;
2069
                }
2070
                $sql .= $limitCondition;
2071
                $rs = Database::query($sql);
2072
2073
                if ($getCount) {
2074
                    $row = Database::fetch_array($rs);
2075
2076
                    return (int) $row['count'];
2077
                }
2078
2079
                while ($student = Database::fetch_array($rs)) {
2080
                    $students[$student['user_id']] = $student;
2081
                }
2082
            } else {
2083
                $students = GroupManager::get_users(
2084
                    $groupId,
2085
                    false,
2086
                    $start,
2087
                    $limit,
2088
                    $getCount,
2089
                    $courseInfo['real_id']
2090
                );
2091
                $students = array_flip($students);
2092
            }
2093
        }
2094
2095
        // students subscribed to the course through a session
2096
        if ($with_session) {
2097
            $joinSession = '';
2098
            //Session creation date
2099
            if (!empty($date_from) && !empty($date_to)) {
2100
                $joinSession = "INNER JOIN ".Database::get_main_table(TABLE_MAIN_SESSION)." s";
2101
            }
2102
2103
            $sql = "SELECT $select
2104
                      FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)." scu
2105
                      $joinSession
2106
                      INNER JOIN $userTable u
2107
                      ON scu.user_id = u.user_id
2108
                      WHERE scu.c_id = $courseId AND scu.status <> 2";
2109
2110
            if (!empty($date_from) && !empty($date_to)) {
2111
                $date_from = Database::escape_string($date_from);
2112
                $date_to = Database::escape_string($date_to);
2113
                $sql .= " AND s.access_start_date >= '$date_from' AND s.access_end_date <= '$date_to'";
2114
            }
2115
2116
            if ($sessionId != 0) {
2117
                $sql .= " AND scu.session_id = $sessionId";
2118
            }
2119
2120
            if (!$includeInvitedUsers) {
2121
                $sql .= " AND u.status != ".INVITEE;
2122
            }
2123
            $sql .= $limitCondition;
2124
2125
            $rs = Database::query($sql);
2126
2127
            if ($getCount) {
2128
                $row = Database::fetch_array($rs);
2129
2130
                return (int) $row['count'];
2131
            }
2132
2133
            while ($student = Database::fetch_array($rs)) {
2134
                $students[$student['user_id']] = $student;
2135
            }
2136
        }
2137
2138
        return $students;
2139
    }
2140
2141
    /**
2142
     * Return user info array of all teacher-users registered in a course
2143
     * This only returns the users that are registered in this actual course, not linked courses.
2144
     *
2145
     * @param string $course_code
2146
     *
2147
     * @return array with user id
2148
     */
2149
    public static function get_teacher_list_from_course_code($course_code)
2150
    {
2151
        $courseInfo = api_get_course_info($course_code);
2152
        $courseId = $courseInfo['real_id'];
2153
        if (empty($courseId)) {
2154
            return false;
2155
        }
2156
2157
        $sql = "SELECT DISTINCT
2158
                    u.id as user_id,
2159
                    u.lastname,
2160
                    u.firstname,
2161
                    u.email,
2162
                    u.username,
2163
                    u.status
2164
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." cu
2165
                INNER JOIN ".Database::get_main_table(TABLE_MAIN_USER)." u
2166
                ON (cu.user_id = u.id)
2167
                WHERE
2168
                    cu.c_id = $courseId AND
2169
                    cu.status = 1 ";
2170
        $rs = Database::query($sql);
2171
        $teachers = [];
2172
        while ($teacher = Database::fetch_array($rs)) {
2173
            $teachers[$teacher['user_id']] = $teacher;
2174
        }
2175
2176
        return $teachers;
2177
    }
2178
2179
    /**
2180
     * Return user info array of all teacher-users registered in a course
2181
     * This only returns the users that are registered in this actual course, not linked courses.
2182
     *
2183
     * @param int  $courseId
2184
     * @param bool $loadAvatars
2185
     *
2186
     * @return array with user id
2187
     */
2188
    public static function getTeachersFromCourse($courseId, $loadAvatars = true)
2189
    {
2190
        $courseId = (int) $courseId;
2191
2192
        if (empty($courseId)) {
2193
            return false;
2194
        }
2195
2196
        $sql = "SELECT DISTINCT
2197
                    u.id as user_id,
2198
                    u.lastname,
2199
                    u.firstname,
2200
                    u.email,
2201
                    u.username,
2202
                    u.status
2203
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." cu
2204
                INNER JOIN ".Database::get_main_table(TABLE_MAIN_USER)." u
2205
                ON (cu.user_id = u.id)
2206
                WHERE
2207
                    cu.c_id = $courseId AND
2208
                    cu.status = 1 ";
2209
        $rs = Database::query($sql);
2210
        $listTeachers = [];
2211
        $teachers = [];
2212
        $url = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&course_id='.$courseId;
2213
        while ($teacher = Database::fetch_array($rs)) {
2214
            $teachers['id'] = $teacher['user_id'];
2215
            $teachers['lastname'] = $teacher['lastname'];
2216
            $teachers['firstname'] = $teacher['firstname'];
2217
            $teachers['email'] = $teacher['email'];
2218
            $teachers['username'] = $teacher['username'];
2219
            $teachers['status'] = $teacher['status'];
2220
            $teachers['fullname'] = api_get_person_name($teacher['firstname'], $teacher['lastname']);
2221
            $teachers['avatar'] = '';
2222
            if ($loadAvatars) {
2223
                $userPicture = UserManager::getUserPicture($teacher['user_id'], USER_IMAGE_SIZE_SMALL);
2224
                $teachers['avatar'] = $userPicture;
2225
            }
2226
            $teachers['url'] = $url.'&user_id='.$teacher['user_id'];
2227
            $listTeachers[] = $teachers;
2228
        }
2229
2230
        return $listTeachers;
2231
    }
2232
2233
    /**
2234
     * Returns a string list of teachers assigned to the given course.
2235
     *
2236
     * @param string $course_code
2237
     * @param string $separator           between teachers names
2238
     * @param bool   $add_link_to_profile Whether to add a link to the teacher's profile
2239
     * @param bool   $orderList
2240
     *
2241
     * @return string List of teachers teaching the course
2242
     */
2243
    public static function getTeacherListFromCourseCodeToString(
2244
        $course_code,
2245
        $separator = self::USER_SEPARATOR,
2246
        $add_link_to_profile = false,
2247
        $orderList = false
2248
    ) {
2249
        $teacher_list = self::get_teacher_list_from_course_code($course_code);
2250
        $html = '';
2251
        $list = [];
2252
        if (!empty($teacher_list)) {
2253
            foreach ($teacher_list as $teacher) {
2254
                $teacher_name = api_get_person_name(
2255
                    $teacher['firstname'],
2256
                    $teacher['lastname']
2257
                );
2258
                if ($add_link_to_profile) {
2259
                    $url = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$teacher['user_id'];
2260
                    $teacher_name = Display::url(
2261
                        $teacher_name,
2262
                        $url,
2263
                        [
2264
                            'class' => 'ajax',
2265
                            'data-title' => $teacher_name,
2266
                        ]
2267
                    );
2268
                }
2269
                $list[] = $teacher_name;
2270
            }
2271
2272
            if (!empty($list)) {
2273
                if ($orderList === true) {
2274
                    $html .= '<ul class="user-teacher">';
2275
                    foreach ($list as $teacher) {
2276
                        $html .= '<li>';
2277
                        $html .= Display::return_icon('teacher.png', '', null, ICON_SIZE_TINY);
2278
                        $html .= ' '.$teacher;
2279
                        $html .= '</li>';
2280
                    }
2281
                    $html .= '</ul>';
2282
                } else {
2283
                    $html .= array_to_string($list, $separator);
2284
                }
2285
            }
2286
        }
2287
2288
        return $html;
2289
    }
2290
2291
    /**
2292
     * This function returns information about coachs from a course in session.
2293
     *
2294
     * @param int $session_id
2295
     * @param int $courseId
2296
     *
2297
     * @return array containing user_id, lastname, firstname, username
2298
     */
2299
    public static function get_coachs_from_course($session_id = 0, $courseId = 0)
2300
    {
2301
        if (!empty($session_id)) {
2302
            $session_id = intval($session_id);
2303
        } else {
2304
            $session_id = api_get_session_id();
2305
        }
2306
2307
        if (!empty($courseId)) {
2308
            $courseId = intval($courseId);
2309
        } else {
2310
            $courseId = api_get_course_int_id();
2311
        }
2312
2313
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2314
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2315
2316
        $sql = "SELECT DISTINCT
2317
                    u.id as user_id,
2318
                    u.lastname,
2319
                    u.firstname,
2320
                    u.username
2321
                FROM $tbl_user u
2322
                INNER JOIN $tbl_session_course_user scu
2323
                ON (u.id = scu.user_id)
2324
                WHERE
2325
                    scu.session_id = $session_id AND
2326
                    scu.c_id = $courseId AND
2327
                    scu.status = 2";
2328
        $rs = Database::query($sql);
2329
2330
        $coaches = [];
2331
        if (Database::num_rows($rs) > 0) {
2332
            while ($row = Database::fetch_array($rs)) {
2333
                $completeName = api_get_person_name($row['firstname'], $row['lastname']);
2334
                $coaches[] = $row + ['full_name' => $completeName];
2335
            }
2336
        }
2337
2338
        return $coaches;
2339
    }
2340
2341
    /**
2342
     * @param int    $session_id
2343
     * @param int    $courseId
2344
     * @param string $separator
2345
     * @param bool   $add_link_to_profile
2346
     * @param bool   $orderList
2347
     *
2348
     * @return string
2349
     */
2350
    public static function get_coachs_from_course_to_string(
2351
        $session_id = 0,
2352
        $courseId = 0,
2353
        $separator = self::USER_SEPARATOR,
2354
        $add_link_to_profile = false,
2355
        $orderList = false
2356
    ) {
2357
        $coachList = self::get_coachs_from_course($session_id, $courseId);
2358
        $course_coachs = [];
2359
        if (!empty($coachList)) {
2360
            foreach ($coachList as $coach_course) {
2361
                $coach_name = $coach_course['full_name'];
2362
                if ($add_link_to_profile) {
2363
                    $url = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$coach_course['user_id'].'&course_id='.$courseId.'&session_id='.$session_id;
2364
                    $coach_name = Display::url(
2365
                        $coach_name,
2366
                        $url,
2367
                        [
2368
                            'class' => 'ajax',
2369
                            'data-title' => $coach_name,
2370
                        ]
2371
                    );
2372
                }
2373
                $course_coachs[] = $coach_name;
2374
            }
2375
        }
2376
2377
        $html = '';
2378
        if (!empty($course_coachs)) {
2379
            if ($orderList === true) {
2380
                $html .= '<ul class="user-coachs">';
2381
                foreach ($course_coachs as $coachs) {
2382
                    $html .= Display::tag(
2383
                        'li',
2384
                        Display::return_icon(
2385
                            'teacher.png',
2386
                            get_lang('Coach'),
2387
                            null,
2388
                            ICON_SIZE_TINY
2389
                        ).' '.$coachs
2390
                    );
2391
                }
2392
                $html .= '</ul>';
2393
            } else {
2394
                $html = array_to_string($course_coachs, $separator);
2395
            }
2396
        }
2397
2398
        return $html;
2399
    }
2400
2401
    /**
2402
     * Get the list of groups from the course.
2403
     *
2404
     * @param string $course_code
2405
     * @param int    $session_id         Session ID (optional)
2406
     * @param int    $in_get_empty_group get empty groups (optional)
2407
     *
2408
     * @return array List of groups info
2409
     */
2410
    public static function get_group_list_of_course(
2411
        $course_code,
2412
        $session_id = 0,
2413
        $in_get_empty_group = 0
2414
    ) {
2415
        $course_info = api_get_course_info($course_code);
2416
2417
        if (empty($course_info)) {
2418
            return [];
2419
        }
2420
        $course_id = $course_info['real_id'];
2421
2422
        if (empty($course_id)) {
2423
            return [];
2424
        }
2425
2426
        $session_id != 0 ? $session_condition = ' WHERE g.session_id IN(1,'.intval($session_id).')' : $session_condition = ' WHERE g.session_id = 0';
2427
        if ($in_get_empty_group == 0) {
2428
            // get only groups that are not empty
2429
            $sql = "SELECT DISTINCT g.id, g.iid, g.name
2430
                    FROM ".Database::get_course_table(TABLE_GROUP)." AS g
2431
                    INNER JOIN ".Database::get_course_table(TABLE_GROUP_USER)." gu
2432
                    ON (g.id = gu.group_id AND g.c_id = $course_id AND gu.c_id = $course_id)
2433
                    $session_condition
2434
                    ORDER BY g.name";
2435
        } else {
2436
            // get all groups even if they are empty
2437
            $sql = "SELECT g.id, g.name, g.iid
2438
                    FROM ".Database::get_course_table(TABLE_GROUP)." AS g
2439
                    $session_condition
2440
                    AND c_id = $course_id";
2441
        }
2442
2443
        $result = Database::query($sql);
2444
        $groupList = [];
2445
        while ($groupData = Database::fetch_array($result)) {
2446
            $groupData['userNb'] = GroupManager::number_of_students($groupData['id'], $course_id);
2447
            $groupList[$groupData['iid']] = $groupData;
2448
        }
2449
2450
        return $groupList;
2451
    }
2452
2453
    /**
2454
     * Delete a course
2455
     * This function deletes a whole course-area from the platform. When the
2456
     * given course is a virtual course, the database and directory will not be
2457
     * deleted.
2458
     * When the given course is a real course, also all virtual courses refering
2459
     * to the given course will be deleted.
2460
     * Considering the fact that we remove all traces of the course in the main
2461
     * database, it makes sense to remove all tracking as well (if stats databases exist)
2462
     * so that a new course created with this code would not use the remains of an older
2463
     * course.
2464
     *
2465
     * @param string $code The code of the course to delete
2466
     *
2467
     * @todo When deleting a virtual course: unsubscribe users from that virtual
2468
     * course from the groups in the real course if they are not subscribed in
2469
     * that real course.
2470
     */
2471
    public static function delete_course($code)
2472
    {
2473
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
2474
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2475
        $table_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
2476
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2477
        $table_course_survey = Database::get_main_table(TABLE_MAIN_SHARED_SURVEY);
2478
        $table_course_survey_question = Database::get_main_table(TABLE_MAIN_SHARED_SURVEY_QUESTION);
2479
        $table_course_survey_question_option = Database::get_main_table(TABLE_MAIN_SHARED_SURVEY_QUESTION_OPTION);
2480
        $table_course_rel_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2481
2482
        $table_stats_hotpots = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTPOTATOES);
2483
        $table_stats_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2484
        $table_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2485
        $table_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
2486
        $table_stats_lastaccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
2487
        $table_stats_course_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2488
        $table_stats_online = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
2489
        $table_stats_default = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
2490
        $table_stats_downloads = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
2491
        $table_stats_links = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
2492
        $table_stats_uploads = Database::get_main_table(TABLE_STATISTIC_TRACK_E_UPLOADS);
2493
2494
        if (empty($code)) {
2495
            return false;
2496
        }
2497
2498
        $codeFiltered = Database::escape_string($code);
2499
        $sql = "SELECT * FROM $table_course
2500
                WHERE code = '$codeFiltered'";
2501
        $res = Database::query($sql);
2502
2503
        if (Database::num_rows($res) == 0) {
2504
            return false;
2505
        }
2506
2507
        $course = Database::fetch_array($res);
2508
        $courseId = $course['id']; // int
2509
2510
        /** @var SequenceResourceRepository $repo */
2511
        $repo = Database::getManager()->getRepository('ChamiloCoreBundle:SequenceResource');
2512
        $sequenceResource = $repo->findRequirementForResource(
2513
            $courseId,
2514
            SequenceResource::COURSE_TYPE
2515
        );
2516
2517
        if ($sequenceResource) {
2518
            Display::addFlash(
2519
                Display::return_message(
2520
                    get_lang('ThereIsASequenceResourceLinkedToThisCourseYouNeedToDeleteItFirst'),
2521
                    'error'
2522
                )
2523
            );
2524
2525
            return false;
2526
        }
2527
2528
        $count = 0;
2529
        if (api_is_multiple_url_enabled()) {
2530
            $url_id = 1;
2531
            if (api_get_current_access_url_id() != -1) {
2532
                $url_id = api_get_current_access_url_id();
2533
            }
2534
            UrlManager::delete_url_rel_course($courseId, $url_id);
2535
            $count = UrlManager::getCountUrlRelCourse($courseId);
2536
        }
2537
2538
        if ($count == 0) {
2539
            self::create_database_dump($code);
2540
2541
            $course_tables = AddCourse::get_course_tables();
2542
2543
            // Cleaning group categories
2544
            $groupCategories = GroupManager::get_categories($course['code']);
2545
            if (!empty($groupCategories)) {
2546
                foreach ($groupCategories as $category) {
2547
                    GroupManager::delete_category($category['id'], $course['code']);
2548
                }
2549
            }
2550
2551
            // Cleaning groups
2552
            $groups = GroupManager::get_groups($courseId);
2553
            if (!empty($groups)) {
2554
                foreach ($groups as $group) {
2555
                    GroupManager::deleteGroup($group, $course['code']);
2556
                }
2557
            }
2558
2559
            // Cleaning c_x tables
2560
            if (!empty($courseId)) {
2561
                foreach ($course_tables as $table) {
2562
                    $table = Database::get_course_table($table);
2563
                    $sql = "DELETE FROM $table WHERE c_id = $courseId ";
2564
                    Database::query($sql);
2565
                }
2566
            }
2567
2568
            $course_dir = api_get_path(SYS_COURSE_PATH).$course['directory'];
2569
            $archive_dir = api_get_path(SYS_ARCHIVE_PATH).$course['directory'].'_'.time();
2570
            if (is_dir($course_dir)) {
2571
                rename($course_dir, $archive_dir);
2572
            }
2573
2574
            Category::deleteFromCourse($course['code']);
2575
2576
            // Unsubscribe all users from the course
2577
            $sql = "DELETE FROM $table_course_user WHERE c_id = $courseId";
2578
            Database::query($sql);
2579
            // Delete the course from the sessions tables
2580
            $sql = "DELETE FROM $table_session_course WHERE c_id = $courseId";
2581
            Database::query($sql);
2582
            $sql = "DELETE FROM $table_session_course_user WHERE c_id = $courseId";
2583
            Database::query($sql);
2584
2585
            // Delete from Course - URL
2586
            $sql = "DELETE FROM $table_course_rel_url WHERE c_id = $courseId";
2587
            Database::query($sql);
2588
2589
            $sql = "SELECT survey_id FROM $table_course_survey WHERE course_code = '$codeFiltered'";
2590
            $result_surveys = Database::query($sql);
2591
            while ($surveys = Database::fetch_array($result_surveys)) {
2592
                $survey_id = $surveys[0]; //int
2593
                $sql = "DELETE FROM $table_course_survey_question WHERE survey_id = $survey_id";
2594
                Database::query($sql);
2595
                $sql = "DELETE FROM $table_course_survey_question_option WHERE survey_id = $survey_id";
2596
                Database::query($sql);
2597
                $sql = "DELETE FROM $table_course_survey WHERE survey_id = $survey_id";
2598
                Database::query($sql);
2599
            }
2600
2601
            // Delete the course from the stats tables
2602
            $sql = "DELETE FROM $table_stats_hotpots WHERE c_id = $courseId";
2603
            Database::query($sql);
2604
            $sql = "DELETE FROM $table_stats_attempt WHERE c_id = $courseId";
2605
            Database::query($sql);
2606
            $sql = "DELETE FROM $table_stats_exercises WHERE c_id = $courseId";
2607
            Database::query($sql);
2608
            $sql = "DELETE FROM $table_stats_access WHERE c_id = $courseId";
2609
            Database::query($sql);
2610
            $sql = "DELETE FROM $table_stats_lastaccess WHERE c_id = $courseId";
2611
            Database::query($sql);
2612
            $sql = "DELETE FROM $table_stats_course_access WHERE c_id = $courseId";
2613
            Database::query($sql);
2614
            $sql = "DELETE FROM $table_stats_online WHERE c_id = $courseId";
2615
            Database::query($sql);
2616
            // Do not delete rows from track_e_default as these include course
2617
            // creation and other important things that do not take much space
2618
            // but give information on the course history
2619
            //$sql = "DELETE FROM $table_stats_default WHERE c_id = $courseId";
2620
            //Database::query($sql);
2621
            $sql = "DELETE FROM $table_stats_downloads WHERE c_id = $courseId";
2622
            Database::query($sql);
2623
            $sql = "DELETE FROM $table_stats_links WHERE c_id = $courseId";
2624
            Database::query($sql);
2625
            $sql = "DELETE FROM $table_stats_uploads WHERE c_id = $courseId";
2626
            Database::query($sql);
2627
2628
            // Update ticket
2629
            $table = Database::get_main_table(TABLE_TICKET_TICKET);
2630
            $sql = "UPDATE $table SET course_id = NULL WHERE course_id = $courseId";
2631
            Database::query($sql);
2632
2633
            $repo->deleteResource(
2634
                $courseId,
2635
                SequenceResource::COURSE_TYPE
2636
            );
2637
2638
            // Class
2639
            $table = Database::get_main_table(TABLE_USERGROUP_REL_COURSE);
2640
            $sql = "DELETE FROM $table
2641
                    WHERE course_id = $courseId";
2642
            Database::query($sql);
2643
2644
            // Skills
2645
            $table = Database::get_main_table(TABLE_MAIN_SKILL_REL_USER);
2646
            $argumentation = Database::escape_string(sprintf(get_lang('SkillFromCourseXDeletedSinceThen'), $course['code']));
2647
            $sql = "UPDATE $table SET course_id = NULL, session_id = NULL, argumentation = '$argumentation'
2648
                    WHERE course_id = $courseId";
2649
            Database::query($sql);
2650
2651
            if (api_get_configuration_value('allow_skill_rel_items')) {
2652
                $sql = "DELETE FROM skill_rel_course WHERE c_id = $courseId";
2653
                Database::query($sql);
2654
            }
2655
2656
            // Deletes all groups, group-users, group-tutors information
2657
            // To prevent fK mix up on some tables
2658
            GroupManager::deleteAllGroupsFromCourse($courseId);
2659
2660
            $app_plugin = new AppPlugin();
2661
            $app_plugin->performActionsWhenDeletingItem('course', $courseId);
2662
2663
            // Delete the course from the database
2664
            $sql = "DELETE FROM $table_course WHERE id = $courseId";
2665
            Database::query($sql);
2666
2667
            // delete extra course fields
2668
            $extraFieldValues = new ExtraFieldValue('course');
2669
            $extraFieldValues->deleteValuesByItem($courseId);
2670
2671
            // Add event to system log
2672
            Event::addEvent(
2673
                LOG_COURSE_DELETE,
2674
                LOG_COURSE_CODE,
2675
                $code,
2676
                api_get_utc_datetime(),
2677
                api_get_user_id(),
2678
                $courseId
2679
            );
2680
2681
            return true;
2682
        }
2683
    }
2684
2685
    /**
2686
     * Creates a file called mysql_dump.sql in the course folder.
2687
     *
2688
     * @param string $course_code The code of the course
2689
     *
2690
     * @todo Implementation for single database
2691
     */
2692
    public static function create_database_dump($course_code)
2693
    {
2694
        $sql_dump = '';
2695
        $course_code = Database::escape_string($course_code);
2696
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
2697
        $sql = "SELECT * FROM $table_course WHERE code = '$course_code'";
2698
        $res = Database::query($sql);
2699
        $course = Database::fetch_array($res);
2700
2701
        $course_tables = AddCourse::get_course_tables();
2702
2703
        if (!empty($course['id'])) {
2704
            //Cleaning c_x tables
2705
            foreach ($course_tables as $table) {
2706
                $table = Database::get_course_table($table);
2707
                $sql = "SELECT * FROM $table WHERE c_id = {$course['id']} ";
2708
                $res_table = Database::query($sql);
2709
2710
                while ($row = Database::fetch_array($res_table, 'ASSOC')) {
2711
                    $row_to_save = [];
2712
                    foreach ($row as $key => $value) {
2713
                        $row_to_save[$key] = $key."='".Database::escape_string($row[$key])."'";
2714
                    }
2715
                    $sql_dump .= "\nINSERT INTO $table SET ".implode(', ', $row_to_save).';';
2716
                }
2717
            }
2718
        }
2719
2720
        if (is_dir(api_get_path(SYS_COURSE_PATH).$course['directory'])) {
2721
            $file_name = api_get_path(SYS_COURSE_PATH).$course['directory'].'/mysql_dump.sql';
2722
            $handle = fopen($file_name, 'a+');
2723
            if ($handle !== false) {
2724
                fwrite($handle, $sql_dump);
2725
                fclose($handle);
2726
            } else {
2727
                //TODO trigger exception in a try-catch
2728
            }
2729
        }
2730
    }
2731
2732
    /**
2733
     * Sort courses for a specific user ??
2734
     *
2735
     * @param int    $user_id     User ID
2736
     * @param string $course_code Course code
2737
     *
2738
     * @return int Minimum course order
2739
     *
2740
     * @todo Review documentation
2741
     */
2742
    public static function userCourseSort($user_id, $course_code)
2743
    {
2744
        if ($user_id != strval(intval($user_id))) {
2745
            return false;
2746
        }
2747
2748
        $course_code = Database::escape_string($course_code);
2749
        $TABLECOURSE = Database::get_main_table(TABLE_MAIN_COURSE);
2750
        $TABLECOURSUSER = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2751
2752
        $course_title = Database::result(
2753
            Database::query(
2754
                "SELECT title FROM $TABLECOURSE WHERE code = '$course_code'"
2755
            ),
2756
            0,
2757
            0
2758
        );
2759
        if ($course_title === false) {
2760
            $course_title = '';
2761
        }
2762
2763
        $sql = "SELECT course.code as code, course.title as title, cu.sort as sort
2764
                FROM $TABLECOURSUSER as cu, $TABLECOURSE as course
2765
                WHERE   course.id = cu.c_id AND user_id = $user_id AND
2766
                        cu.relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
2767
                        user_course_cat = 0
2768
                ORDER BY cu.sort";
2769
        $result = Database::query($sql);
2770
2771
        $course_title_precedent = '';
2772
        $counter = 0;
2773
        $course_found = false;
2774
        $course_sort = 1;
2775
2776
        if (Database::num_rows($result) > 0) {
2777
            while ($courses = Database::fetch_array($result)) {
2778
                if ($course_title_precedent == '') {
2779
                    $course_title_precedent = $courses['title'];
2780
                }
2781
                if (api_strcasecmp($course_title_precedent, $course_title) < 0) {
2782
                    $course_found = true;
2783
                    if (!empty($courses['sort'])) {
2784
                        $course_sort = $courses['sort'];
2785
                    }
2786
                    if ($counter == 0) {
2787
                        $sql = "UPDATE $TABLECOURSUSER
2788
                                SET sort = sort+1
2789
                                WHERE
2790
                                    user_id= $user_id AND
2791
                                    relation_type <> ".COURSE_RELATION_TYPE_RRHH."
2792
                                    AND user_course_cat = 0
2793
                                    AND sort > $course_sort";
2794
                        $course_sort++;
2795
                    } else {
2796
                        $sql = "UPDATE $TABLECOURSUSER SET sort = sort+1
2797
                                WHERE
2798
                                    user_id= $user_id AND
2799
                                    relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
2800
                                    user_course_cat = 0 AND
2801
                                    sort >= $course_sort";
2802
                    }
2803
                    Database::query($sql);
2804
                    break;
2805
                } else {
2806
                    $course_title_precedent = $courses['title'];
2807
                }
2808
                $counter++;
2809
            }
2810
2811
            // We must register the course in the beginning of the list
2812
            if (!$course_found) {
2813
                $course_sort = Database::result(
2814
                    Database::query(
2815
                        'SELECT min(sort) as min_sort FROM '.$TABLECOURSUSER.' WHERE user_id = "'.$user_id.'" AND user_course_cat="0"'
2816
                    ),
2817
                    0,
2818
                    0
2819
                );
2820
                Database::query("UPDATE $TABLECOURSUSER SET sort = sort+1 WHERE user_id = $user_id AND user_course_cat = 0");
2821
            }
2822
        }
2823
2824
        return $course_sort;
2825
    }
2826
2827
    /**
2828
     * check if course exists.
2829
     *
2830
     * @param string $courseCode
2831
     *
2832
     * @return int if exists, false else
2833
     */
2834
    public static function course_exists($courseCode)
2835
    {
2836
        $courseCode = Database::escape_string($courseCode);
2837
        $sql = "SELECT 1 FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
2838
                WHERE code = '$courseCode'";
2839
2840
        return Database::num_rows(Database::query($sql));
2841
    }
2842
2843
    /**
2844
     * Send an email to tutor after the auth-suscription of a student in your course.
2845
     *
2846
     * @author Carlos Vargas <[email protected]>, Dokeos Latino
2847
     *
2848
     * @param int    $user_id            the id of the user
2849
     * @param string $courseId           the course code
2850
     * @param bool   $send_to_tutor_also
2851
     *
2852
     * @return false|null we return the message that is displayed when the action is successful
2853
     */
2854
    public static function email_to_tutor($user_id, $courseId, $send_to_tutor_also = false)
2855
    {
2856
        $user_id = (int) $user_id;
2857
        $courseId = (int) $courseId;
2858
        $information = api_get_course_info_by_id($courseId);
2859
        $course_code = $information['code'];
2860
        $student = api_get_user_info($user_id);
2861
2862
        $name_course = $information['title'];
2863
        $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
2864
                WHERE c_id = $courseId";
2865
2866
        // TODO: Ivan: This is a mistake, please, have a look at it. Intention here is diffcult to be guessed.
2867
        //if ($send_to_tutor_also = true)
2868
        // Proposed change:
2869
        if ($send_to_tutor_also) {
2870
            $sql .= ' AND is_tutor = 1';
2871
        } else {
2872
            $sql .= ' AND status = 1';
2873
        }
2874
2875
        $result = Database::query($sql);
2876
        while ($row = Database::fetch_array($result)) {
2877
            $tutor = api_get_user_info($row['user_id']);
2878
            $emailto = $tutor['email'];
2879
            $emailsubject = get_lang('NewUserInTheCourse').': '.$name_course;
2880
            $emailbody = get_lang('Dear').': '.api_get_person_name($tutor['firstname'], $tutor['lastname'])."\n";
2881
            $emailbody .= get_lang('MessageNewUserInTheCourse').': '.$name_course."\n";
2882
            $emailbody .= get_lang('UserName').': '.$student['username']."\n";
2883
            if (api_is_western_name_order()) {
2884
                $emailbody .= get_lang('FirstName').': '.$student['firstname']."\n";
2885
                $emailbody .= get_lang('LastName').': '.$student['lastname']."\n";
2886
            } else {
2887
                $emailbody .= get_lang('LastName').': '.$student['lastname']."\n";
2888
                $emailbody .= get_lang('FirstName').': '.$student['firstname']."\n";
2889
            }
2890
            $emailbody .= get_lang('Email').': <a href="mailto:'.$student['email'].'">'.$student['email']."</a>\n\n";
2891
            $recipient_name = api_get_person_name(
2892
                $tutor['firstname'],
2893
                $tutor['lastname'],
2894
                null,
2895
                PERSON_NAME_EMAIL_ADDRESS
2896
            );
2897
            $sender_name = api_get_person_name(
2898
                api_get_setting('administratorName'),
2899
                api_get_setting('administratorSurname'),
2900
                null,
2901
                PERSON_NAME_EMAIL_ADDRESS
2902
            );
2903
            $email_admin = api_get_setting('emailAdministrator');
2904
2905
            $additionalParameters = [
2906
                'smsType' => SmsPlugin::NEW_USER_SUBSCRIBED_COURSE,
2907
                'userId' => $tutor['user_id'],
2908
                'userUsername' => $student['username'],
2909
                'courseCode' => $course_code,
2910
            ];
2911
            api_mail_html(
2912
                $recipient_name,
2913
                $emailto,
2914
                $emailsubject,
2915
                $emailbody,
2916
                $sender_name,
2917
                $email_admin,
2918
                null,
2919
                null,
2920
                null,
2921
                $additionalParameters
2922
            );
2923
        }
2924
    }
2925
2926
    /**
2927
     * Get the list of course IDs with the special_course field
2928
     * set to 1. This function is access_url aware.
2929
     *
2930
     * @return array
2931
     */
2932
    public static function get_special_course_list()
2933
    {
2934
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
2935
        $tbl_course_field = Database::get_main_table(TABLE_EXTRA_FIELD);
2936
        $tbl_course_field_value = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2937
        $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2938
2939
        $extraFieldType = EntityExtraField::COURSE_FIELD_TYPE;
2940
        // Set the return list
2941
        $courseList = [];
2942
2943
        // Get special course field
2944
        $sql = "SELECT id FROM $tbl_course_field
2945
                WHERE extra_field_type = $extraFieldType AND
2946
                variable = 'special_course'";
2947
        $result = Database::query($sql);
2948
2949
        if (Database::num_rows($result) > 0) {
2950
            $row = Database::fetch_assoc($result);
2951
            // Get list of special courses (appear to all)
2952
            // Note: The value is better indexed as string, so
2953
            // using '1' instead of integer is more efficient
2954
            $sql = "SELECT DISTINCT(item_id) as cid
2955
                FROM $tbl_course_field_value
2956
                WHERE field_id = ".$row['id']." AND value = '1'";
2957
            $result = Database::query($sql);
2958
            while ($row = Database::fetch_assoc($result)) {
2959
                $courseList[] = $row['cid'];
2960
            }
2961
            if (count($courseList) < 1) {
2962
                return $courseList;
2963
            }
2964
            if (api_get_multiple_access_url()) {
2965
                //we filter the courses by the active URL
2966
                $coursesSelect = '';
2967
                if (count($courseList) == 1) {
2968
                    $coursesSelect = $courseList[0];
2969
                } else {
2970
                    $coursesSelect = implode(',', $courseList);
2971
                }
2972
                $access_url_id = api_get_current_access_url_id();
2973
                if ($access_url_id != -1) {
2974
                    $sql = "SELECT c_id FROM $tbl_url_course
2975
                            WHERE access_url_id = $access_url_id
2976
                            AND c_id IN ($coursesSelect)";
2977
                    $result = Database::query($sql);
2978
                    while ($row = Database::fetch_assoc($result)) {
2979
                        $courseList[] = $row['c_id'];
2980
                    }
2981
                }
2982
            }
2983
        }
2984
2985
        return $courseList;
2986
    }
2987
2988
    /**
2989
     * Get the course codes that have been restricted in the catalogue, and if byUserId is set
2990
     * then the courses that the user is allowed or not to see in catalogue.
2991
     *
2992
     * @param bool $allowed  Either if the courses have some users that are or are not allowed to see in catalogue
2993
     * @param int  $byUserId if the courses are or are not allowed to see to the user
2994
     *
2995
     * @return array Course codes allowed or not to see in catalogue by some user or the user
2996
     */
2997
    public static function getCatalogCourseList($allowed = true, $byUserId = -1)
2998
    {
2999
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
3000
        $tblCourseRelUserCatalogue = Database::get_main_table(TABLE_MAIN_COURSE_CATALOGUE_USER);
3001
        $visibility = $allowed ? 1 : 0;
3002
3003
        // Restriction by user id
3004
        $currentUserRestriction = '';
3005
        if ($byUserId > 0) {
3006
            $byUserId = (int) $byUserId;
3007
            $currentUserRestriction = " AND tcruc.user_id = $byUserId ";
3008
        }
3009
3010
        //we filter the courses from the URL
3011
        $joinAccessUrl = '';
3012
        $whereAccessUrl = '';
3013
        if (api_get_multiple_access_url()) {
3014
            $accessUrlId = api_get_current_access_url_id();
3015
            if ($accessUrlId != -1) {
3016
                $tblUrlCourse = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3017
                $joinAccessUrl = "LEFT JOIN $tblUrlCourse url_rel_course
3018
                                  ON url_rel_course.c_id = c.id ";
3019
                $whereAccessUrl = " AND access_url_id = $accessUrlId ";
3020
            }
3021
        }
3022
3023
        // get course list auto-register
3024
        $sql = "SELECT DISTINCT(c.code)
3025
                FROM $tblCourseRelUserCatalogue tcruc
3026
                INNER JOIN $courseTable c
3027
                ON (c.id = tcruc.c_id) $joinAccessUrl
3028
                WHERE tcruc.visible = $visibility $currentUserRestriction $whereAccessUrl";
3029
3030
        $result = Database::query($sql);
3031
        $courseList = [];
3032
3033
        if (Database::num_rows($result) > 0) {
3034
            while ($resultRow = Database::fetch_array($result)) {
3035
                $courseList[] = $resultRow['code'];
3036
            }
3037
        }
3038
3039
        return $courseList;
3040
    }
3041
3042
    /**
3043
     * Get list of courses for a given user.
3044
     *
3045
     * @param int   $user_id
3046
     * @param bool  $include_sessions                   Whether to include courses from session or not
3047
     * @param bool  $adminGetsAllCourses                If the user is platform admin,
3048
     *                                                  whether he gets all the courses or just his. Note: This does *not* include all sessions
3049
     * @param bool  $loadSpecialCourses
3050
     * @param array $skipCourseList                     List of course ids to skip
3051
     * @param bool  $useUserLanguageFilterIfAvailable
3052
     * @param bool  $showCoursesSessionWithDifferentKey
3053
     *
3054
     * @return array List of codes and db name
3055
     *
3056
     * @author isaac flores paz
3057
     */
3058
    public static function get_courses_list_by_user_id(
3059
        $user_id,
3060
        $include_sessions = false,
3061
        $adminGetsAllCourses = false,
3062
        $loadSpecialCourses = true,
3063
        $skipCourseList = [],
3064
        $useUserLanguageFilterIfAvailable = true,
3065
        $showCoursesSessionWithDifferentKey = false
3066
    ) {
3067
        $user_id = 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