Passed
Pull Request — 1.11.x (#6662)
by Angel Fernando Quiroz
09:40
created

CourseManager::getCoursesFollowedByUser()   F

Complexity

Conditions 15
Paths 960

Size

Total Lines 111
Code Lines 66

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 66
c 0
b 0
f 0
nc 960
nop 11
dl 0
loc 111
rs 1.8055

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