Passed
Push — 1.11.x ( 47505b...220ec2 )
by Angel Fernando Quiroz
09:45 queued 11s
created

CourseManager::autoSubscribeToCourse()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 7
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 13
rs 10
1
<?php
2
3
/* For licensing terms, see /license.txt*/
4
5
use Chamilo\CoreBundle\Entity\Course;
6
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
7
use Chamilo\CoreBundle\Entity\Repository\SequenceResourceRepository;
8
use Chamilo\CoreBundle\Entity\SequenceResource;
9
use Chamilo\CourseBundle\Component\CourseCopy\CourseBuilder;
10
use Chamilo\CourseBundle\Component\CourseCopy\CourseRestorer;
11
use ChamiloSession as Session;
12
use Doctrine\Common\Collections\Criteria;
13
14
/**
15
 * Class CourseManager.
16
 *
17
 * This is the course library for Chamilo.
18
 *
19
 * All main course functions should be placed here.
20
 *
21
 * Many functions of this library deal with providing support for
22
 * virtual/linked/combined courses (this was already used in several universities
23
 * but not available in standard Chamilo).
24
 *
25
 * There are probably some places left with the wrong code.
26
 */
27
class CourseManager
28
{
29
    const MAX_COURSE_LENGTH_CODE = 40;
30
    /** This constant is used to show separate user names in the course
31
     * list (userportal), footer, etc */
32
    const USER_SEPARATOR = ' |';
33
    const COURSE_FIELD_TYPE_CHECKBOX = 10;
34
    public $columns = [];
35
36
    /**
37
     * Creates a course.
38
     *
39
     * @param array $params      Columns in the main.course table.
40
     * @param int   $authorId    Optional.
41
     * @param int   $accessUrlId Optional.
42
     *
43
     * @return mixed false if the course was not created, array with the course info
44
     */
45
    public static function create_course($params, $authorId = 0, $accessUrlId = 0)
46
    {
47
        global $_configuration;
48
49
        $hook = HookCreateCourse::create();
50
51
        // Check portal limits
52
        $accessUrlId = empty($accessUrlId)
53
            ? (api_get_multiple_access_url() ? api_get_current_access_url_id() : 1)
54
            : $accessUrlId;
55
56
        $authorId = empty($authorId) ? api_get_user_id() : (int) $authorId;
57
58
        if (isset($_configuration[$accessUrlId]) && is_array($_configuration[$accessUrlId])) {
59
            $return = self::checkCreateCourseAccessUrlParam(
60
                $_configuration,
61
                $accessUrlId,
62
                'hosting_limit_courses',
63
                'PortalCoursesLimitReached'
64
            );
65
            if ($return != false) {
66
                return $return;
67
            }
68
            $return = self::checkCreateCourseAccessUrlParam(
69
                $_configuration,
70
                $accessUrlId,
71
                'hosting_limit_active_courses',
72
                'PortalActiveCoursesLimitReached'
73
            );
74
            if ($return != false) {
75
                return $return;
76
            }
77
        }
78
79
        if (empty($params['title'])) {
80
            return false;
81
        }
82
83
        if (empty($params['wanted_code'])) {
84
            $params['wanted_code'] = $params['title'];
85
            // Check whether the requested course code has already been occupied.
86
            $substring = api_substr($params['title'], 0, self::MAX_COURSE_LENGTH_CODE);
87
            if ($substring === false || empty($substring)) {
88
                return false;
89
            } else {
90
                $params['wanted_code'] = self::generate_course_code($substring);
91
            }
92
        }
93
94
        // Create the course keys
95
        $keys = AddCourse::define_course_keys($params['wanted_code']);
96
        $params['exemplary_content'] = isset($params['exemplary_content']) ? $params['exemplary_content'] : false;
97
98
        if (count($keys)) {
99
            $params['code'] = $keys['currentCourseCode'];
100
            $params['visual_code'] = $keys['currentCourseId'];
101
            $params['directory'] = $keys['currentCourseRepository'];
102
            $course_info = api_get_course_info($params['code']);
103
            if (empty($course_info)) {
104
                $course_id = AddCourse::register_course($params, $accessUrlId);
105
                $course_info = api_get_course_info_by_id($course_id);
106
107
                if ($hook) {
108
                    $hook->setEventData(['course_info' => $course_info]);
109
                    $hook->notifyCreateCourse(HOOK_EVENT_TYPE_POST);
110
                }
111
112
                if (!empty($course_info)) {
113
                    self::fillCourse($course_info, $params, $authorId);
114
115
                    return $course_info;
116
                }
117
            }
118
        }
119
120
        return false;
121
    }
122
123
    /**
124
     * Returns all the information of a given course code.
125
     *
126
     * @param string $course_code , the course code
127
     *
128
     * @return array with all the fields of the course table
129
     *
130
     * @deprecated Use api_get_course_info() instead
131
     *
132
     * @author Patrick Cool <[email protected]>, Ghent University
133
     * @assert ('') === false
134
     */
135
    public static function get_course_information($course_code)
136
    {
137
        return Database::fetch_array(
138
            Database::query(
139
                "SELECT *, id as real_id FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
140
                WHERE code = '".Database::escape_string($course_code)."'"
141
            ),
142
            'ASSOC'
143
        );
144
    }
145
146
    /**
147
     * Returns a list of courses. Should work with quickform syntax.
148
     *
149
     * @param int    $from               Offset (from the 7th = '6'). Optional.
150
     * @param int    $howmany            Number of results we want. Optional.
151
     * @param int    $orderby            The column we want to order it by. Optional, defaults to first column.
152
     * @param string $orderdirection     The direction of the order (ASC or DESC). Optional, defaults to ASC.
153
     * @param int    $visibility         the visibility of the course, or all by default
154
     * @param string $startwith          If defined, only return results for which the course *title* begins with this
155
     *                                   string
156
     * @param string $urlId              The Access URL ID, if using multiple URLs
157
     * @param bool   $alsoSearchCode     An extension option to indicate that we also want to search for course codes
158
     *                                   (not *only* titles)
159
     * @param array  $conditionsLike
160
     * @param array  $onlyThisCourseList
161
     *
162
     * @return array
163
     */
164
    public static function get_courses_list(
165
        $from = 0,
166
        $howmany = 0,
167
        $orderby = 'title',
168
        $orderdirection = 'ASC',
169
        $visibility = -1,
170
        $startwith = '',
171
        $urlId = null,
172
        $alsoSearchCode = false,
173
        $conditionsLike = [],
174
        $onlyThisCourseList = []
175
    ) {
176
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
177
        $sql = "SELECT course.*, course.id as real_id
178
                FROM $courseTable course ";
179
180
        if (!empty($urlId)) {
181
            $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
182
            $sql .= " INNER JOIN $table url ON (url.c_id = course.id) ";
183
        }
184
185
        $visibility = (int) $visibility;
186
187
        if (!empty($startwith)) {
188
            $sql .= "WHERE (title LIKE '".Database::escape_string($startwith)."%' ";
189
            if ($alsoSearchCode) {
190
                $sql .= "OR code LIKE '".Database::escape_string($startwith)."%' ";
191
            }
192
            $sql .= ') ';
193
            if ($visibility !== -1) {
194
                $sql .= " AND visibility = $visibility ";
195
            }
196
        } else {
197
            $sql .= 'WHERE 1 ';
198
            if ($visibility !== -1) {
199
                $sql .= " AND visibility = $visibility ";
200
            }
201
        }
202
203
        if (!empty($urlId)) {
204
            $urlId = (int) $urlId;
205
            $sql .= " AND access_url_id = $urlId";
206
        }
207
208
        if (!empty($onlyThisCourseList)) {
209
            $onlyThisCourseList = array_map('intval', $onlyThisCourseList);
210
            $onlyThisCourseList = implode("','", $onlyThisCourseList);
211
            $sql .= " AND course.id IN ('$onlyThisCourseList') ";
212
        }
213
214
        $allowedFields = [
215
            'title',
216
            'code',
217
        ];
218
219
        if (count($conditionsLike) > 0) {
220
            $sql .= ' AND ';
221
            $temp_conditions = [];
222
            foreach ($conditionsLike as $field => $value) {
223
                if (!in_array($field, $allowedFields)) {
224
                    continue;
225
                }
226
                $field = Database::escape_string($field);
227
                $value = Database::escape_string($value);
228
                $simple_like = false;
229
                if ($simple_like) {
230
                    $temp_conditions[] = $field." LIKE '$value%'";
231
                } else {
232
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
233
                }
234
            }
235
            $condition = ' AND ';
236
            if (!empty($temp_conditions)) {
237
                $sql .= implode(' '.$condition.' ', $temp_conditions);
238
            }
239
        }
240
241
        if (empty($orderby)) {
242
            $sql .= ' ORDER BY title ';
243
        } else {
244
            if (in_array($orderby, ['title'])) {
245
                $sql .= " ORDER BY `".Database::escape_string($orderby)."` ";
246
            } else {
247
                $sql .= ' ORDER BY title ';
248
            }
249
        }
250
251
        $orderdirection = strtoupper($orderdirection);
252
        if (!in_array($orderdirection, ['ASC', 'DESC'])) {
253
            $sql .= 'ASC';
254
        } else {
255
            $sql .= $orderdirection === 'ASC' ? 'ASC' : 'DESC';
256
        }
257
258
        if (!empty($howmany) && is_int($howmany) and $howmany > 0) {
259
            $sql .= ' LIMIT '.(int) $howmany;
260
        } else {
261
            $sql .= ' LIMIT 1000000'; //virtually no limit
262
        }
263
        if (!empty($from)) {
264
            $from = intval($from);
265
            $sql .= ' OFFSET '.intval($from);
266
        } else {
267
            $sql .= ' OFFSET 0';
268
        }
269
270
        $data = [];
271
        $res = Database::query($sql);
272
        if (Database::num_rows($res) > 0) {
273
            while ($row = Database::fetch_array($res, 'ASSOC')) {
274
                $data[] = $row;
275
            }
276
        }
277
278
        return $data;
279
    }
280
281
    /**
282
     * Returns the status of a user in a course, which is COURSEMANAGER or STUDENT.
283
     *
284
     * @param int $userId
285
     * @param int $courseId
286
     *
287
     * @return int|bool the status of the user in that course (or false if the user is not in that course)
288
     */
289
    public static function getUserInCourseStatus($userId, $courseId)
290
    {
291
        $courseId = (int) $courseId;
292
        $userId = (int) $userId;
293
        if (empty($courseId)) {
294
            return false;
295
        }
296
297
        $result = Database::fetch_array(
298
            Database::query(
299
                "SELECT status FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
300
                WHERE
301
                    c_id  = $courseId AND
302
                    user_id = $userId"
303
            )
304
        );
305
306
        if (empty($result['status'])) {
307
            return false;
308
        }
309
310
        return $result['status'];
311
    }
312
313
    /**
314
     * @param int $userId
315
     * @param int $courseId
316
     *
317
     * @return mixed
318
     */
319
    public static function getUserCourseInfo($userId, $courseId)
320
    {
321
        $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
322
                WHERE
323
                    c_id  = ".intval($courseId)." AND
324
                    user_id = ".intval($userId);
325
        $result = Database::fetch_array(Database::query($sql));
326
327
        return $result;
328
    }
329
330
    /**
331
     * @param int  $userId
332
     * @param int  $courseId
333
     * @param bool $isTutor
334
     *
335
     * @return bool
336
     */
337
    public static function updateUserCourseTutor($userId, $courseId, $isTutor)
338
    {
339
        $table = Database::escape_string(TABLE_MAIN_COURSE_USER);
340
341
        $courseId = intval($courseId);
342
        $isTutor = intval($isTutor);
343
344
        $sql = "UPDATE $table SET is_tutor = '".$isTutor."'
345
			    WHERE
346
				    user_id = ".$userId." AND
347
				    c_id = ".$courseId;
348
349
        $result = Database::query($sql);
350
351
        if (Database::affected_rows($result) > 0) {
352
            return true;
353
        } else {
354
            return false;
355
        }
356
    }
357
358
    /**
359
     * @param int $userId
360
     * @param int $courseId
361
     *
362
     * @return mixed
363
     */
364
    public static function get_tutor_in_course_status($userId, $courseId)
365
    {
366
        $userId = (int) $userId;
367
        $courseId = (int) $courseId;
368
369
        $result = Database::fetch_array(
370
            Database::query(
371
                "SELECT is_tutor
372
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
373
                WHERE
374
                    c_id = $courseId AND
375
                    user_id = $userId"
376
            )
377
        );
378
379
        if ($result) {
380
            return $result['is_tutor'];
381
        }
382
383
        return false;
384
    }
385
386
    /**
387
     * Unsubscribe one or more users from a course.
388
     *
389
     * @param   mixed   user_id or an array with user ids
390
     * @param   string  course code
391
     * @param   int     session id
392
     *
393
     * @return bool
394
     *
395
     * @assert ('', '') === false
396
     */
397
    public static function unsubscribe_user($user_id, $course_code, $session_id = 0)
398
    {
399
        if (empty($user_id)) {
400
            return false;
401
        }
402
        if (!is_array($user_id)) {
403
            $user_id = [$user_id];
404
        }
405
406
        if (count($user_id) == 0) {
407
            return false;
408
        }
409
410
        if (!empty($session_id)) {
411
            $session_id = (int) $session_id;
412
        } else {
413
            $session_id = api_get_session_id();
414
        }
415
416
        if (empty($course_code)) {
417
            return false;
418
        }
419
420
        $userList = [];
421
        // Cleaning the $user_id variable
422
        if (is_array($user_id)) {
423
            $new_user_id_list = [];
424
            foreach ($user_id as $my_user_id) {
425
                $new_user_id_list[] = (int) $my_user_id;
426
            }
427
            $new_user_id_list = array_filter($new_user_id_list);
428
            $userList = $new_user_id_list;
429
            $user_ids = implode(',', $new_user_id_list);
430
        } else {
431
            $user_ids = (int) $user_id;
432
            $userList[] = $user_id;
433
        }
434
435
        $course_info = api_get_course_info($course_code);
436
        $course_id = $course_info['real_id'];
437
438
        // Unsubscribe user from all groups in the course.
439
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_GROUP_USER)."
440
                WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
441
        Database::query($sql);
442
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_GROUP_TUTOR)."
443
                WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
444
        Database::query($sql);
445
446
        // Erase user student publications (works) in the course - by André Boivin
447
        if (!empty($userList)) {
448
            require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
449
            foreach ($userList as $userId) {
450
                // Getting all work from user
451
                $workList = getWorkPerUser($userId);
452
                if (!empty($workList)) {
453
                    foreach ($workList as $work) {
454
                        $work = $work['work'];
455
                        // Getting user results
456
                        if (!empty($work->user_results)) {
457
                            foreach ($work->user_results as $workSent) {
458
                                deleteWorkItem($workSent['id'], $course_info);
459
                            }
460
                        }
461
                    }
462
                }
463
            }
464
        }
465
466
        // Unsubscribe user from all blogs in the course.
467
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_BLOGS_REL_USER)."
468
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
469
        Database::query($sql);
470
471
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_BLOGS_TASKS_REL_USER)."
472
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
473
        Database::query($sql);
474
475
        // Deleting users in forum_notification and mailqueue course tables
476
        $sql = "DELETE FROM  ".Database::get_course_table(TABLE_FORUM_NOTIFICATION)."
477
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
478
        Database::query($sql);
479
480
        $sql = "DELETE FROM ".Database::get_course_table(TABLE_FORUM_MAIL_QUEUE)."
481
                WHERE c_id = $course_id AND user_id IN ($user_ids)";
482
        Database::query($sql);
483
484
        // Unsubscribe user from the course.
485
        if (!empty($session_id)) {
486
            foreach ($userList as $uid) {
487
                SessionManager::unSubscribeUserFromCourseSession($uid, $course_id, $session_id);
488
489
                // check if a user is register in the session with other course
490
                $sql = "SELECT user_id FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
491
                        WHERE session_id = $session_id AND user_id = $uid";
492
                $rs = Database::query($sql);
493
494
                if (Database::num_rows($rs) == 0) {
495
                    SessionManager::unsubscribe_user_from_session($uid, $session_id);
496
                }
497
            }
498
499
            Event::addEvent(
500
                LOG_UNSUBSCRIBE_USER_FROM_COURSE,
501
                LOG_COURSE_CODE,
502
                $course_code,
503
                api_get_utc_datetime(),
504
                $user_id,
505
                $course_id,
506
                $session_id
507
            );
508
        } else {
509
            $sql = "DELETE FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
510
                    WHERE
511
                        user_id IN ($user_ids) AND
512
                        relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND
513
                        c_id = $course_id";
514
            Database::query($sql);
515
516
            // add event to system log
517
            $user_id = api_get_user_id();
518
519
            Event::addEvent(
520
                LOG_UNSUBSCRIBE_USER_FROM_COURSE,
521
                LOG_COURSE_CODE,
522
                $course_code,
523
                api_get_utc_datetime(),
524
                $user_id,
525
                $course_id
526
            );
527
528
            foreach ($userList as $userId) {
529
                $userInfo = api_get_user_info($userId);
530
                Event::addEvent(
531
                    LOG_UNSUBSCRIBE_USER_FROM_COURSE,
532
                    LOG_USER_OBJECT,
533
                    $userInfo,
534
                    api_get_utc_datetime(),
535
                    $user_id,
536
                    $course_id
537
                );
538
            }
539
        }
540
541
        $subscriptionSettings = learnpath::getSubscriptionSettings();
542
        if ($subscriptionSettings['allow_add_users_to_lp_category']) {
543
            $em = Database::getManager();
544
            $repo = $em->getRepository('ChamiloCourseBundle:CLpCategory');
545
546
            if (api_get_configuration_value('allow_session_lp_category')) {
547
                //$criteria = ['cId' => $course_id, 'sessionId' => $session_id];
548
                $table = Database::get_course_table('lp_category');
549
                $conditionSession = api_get_session_condition($session_id, true);
550
                $sql = "SELECT * FROM $table WHERE c_id = $course_id $conditionSession";
551
                $result = Database::query($sql);
552
                $categories = [];
553
                if (Database::num_rows($result)) {
554
                    while ($row = Database::fetch_array($result)) {
555
                        $categories[] = $repo->find($row['iid']);
556
                    }
557
                }
558
            } else {
559
                $criteria = ['cId' => $course_id];
560
                $categories = $repo->findBy($criteria);
561
            }
562
            if (!empty($categories)) {
563
                /** @var \Chamilo\CourseBundle\Entity\CLpCategory $category */
564
                foreach ($categories as $category) {
565
                    if ($category->getUsers()->count() > 0) {
566
                        foreach ($userList as $uid) {
567
                            $user = api_get_user_entity($uid);
568
                            $criteria = Criteria::create()->where(
569
                                Criteria::expr()->eq('user', $user)
570
                            );
571
                            $userCategory = $category->getUsers()->matching($criteria)->first();
572
                            if ($userCategory) {
573
                                $category->removeUsers($userCategory);
574
                            }
575
                        }
576
                        $em->persist($category);
577
                        $em->flush();
578
                    }
579
                }
580
            }
581
        }
582
583
        if (api_get_configuration_value('catalog_course_subscription_in_user_s_session')) {
584
            // Also unlink the course from the users' currently accessible sessions
585
            /** @var Course $course */
586
            $course = Database::getManager()->getRepository('ChamiloCoreBundle:Course')->findOneBy([
587
                'code' => $course_code,
588
            ]);
589
590
            if (null === $course) {
591
                return false;
592
            }
593
            /** @var Chamilo\UserBundle\Entity\User $user */
594
            foreach (UserManager::getRepository()->matching(
595
                Criteria::create()->where(Criteria::expr()->in('id', $userList))
596
            ) as $user) {
597
                foreach ($user->getCurrentlyAccessibleSessions() as $session) {
598
                    $session->removeCourse($course);
599
                    // unsubscribe user from course within this session
600
                    SessionManager::unSubscribeUserFromCourseSession($user->getId(), $course->getId(), $session->getId());
601
                }
602
            }
603
            try {
604
                Database::getManager()->flush();
605
            } catch (\Doctrine\ORM\OptimisticLockException $exception) {
606
                Display::addFlash(
607
                    Display::return_message(
608
                        get_lang('InternalDatabaseError').': '.$exception->getMessage(),
609
                        'warning'
610
                    )
611
                );
612
613
                return false;
614
            }
615
        }
616
617
        return true;
618
    }
619
620
    /**
621
     * @throws Exception
622
     */
623
    public static function processAutoSubscribeToCourse(string $courseCode, int $status = STUDENT)
624
    {
625
        if (api_is_anonymous()) {
626
            throw new Exception(get_lang('NotAllowed'));
627
        }
628
629
        $course = Database::getManager()->getRepository('ChamiloCoreBundle:Course')->findOneBy(['code' => $courseCode]);
630
631
        if (null === $course) {
632
            throw new Exception(get_lang('NotAllowed'));
633
        }
634
635
        $visibility = (int) $course->getVisibility();
636
637
        if (in_array($visibility, [COURSE_VISIBILITY_CLOSED, COURSE_VISIBILITY_HIDDEN])) {
638
            throw new Exception(get_lang('SubscribingNotAllowed'));
639
        }
640
641
        // Private course can allow auto subscription
642
        if (COURSE_VISIBILITY_REGISTERED === $visibility && false === $course->getSubscribe()) {
643
            throw new Exception(get_lang('SubscribingNotAllowed'));
644
        }
645
646
        $userId = api_get_user_id();
647
648
        if (api_get_configuration_value('catalog_course_subscription_in_user_s_session')) {
649
            $user = api_get_user_entity($userId);
650
            $sessions = $user->getCurrentlyAccessibleSessions();
651
            if (empty($sessions)) {
652
                // user has no accessible session
653
                if ($user->getStudentSessions()) {
654
                    // user has ancient or future student session(s) but not available now
655
                    throw new Exception(get_lang('CanNotSubscribeToCourseUserSessionExpired'));
656
                }
657
                // user has no session at all, create one starting now
658
                $numberOfDays = api_get_configuration_value('user_s_session_duration') ?: 3 * 365;
659
                try {
660
                    $duration = new DateInterval(sprintf('P%dD', $numberOfDays));
661
                } catch (Exception $exception) {
662
                    throw new Exception(
663
                        get_lang('WrongNumberOfDays').': '.$numberOfDays.': '.$exception->getMessage()
664
                    );
665
                }
666
                $endDate = new DateTime();
667
                $endDate->add($duration);
668
                $session = new \Chamilo\CoreBundle\Entity\Session();
669
                $session->setName(
670
                    sprintf(get_lang('FirstnameLastnameCourses'), $user->getFirstname(), $user->getLastname())
671
                );
672
                $session->setAccessEndDate($endDate);
673
                $session->setCoachAccessEndDate($endDate);
674
                $session->setDisplayEndDate($endDate);
675
                $session->setSendSubscriptionNotification(false);
676
                $session->setSessionAdminId(api_get_configuration_value('session_automatic_creation_user_id') ?: 1);
677
                $session->addUserInSession(0, $user);
678
                Database::getManager()->persist($session);
679
                try {
680
                    Database::getManager()->flush();
681
                } catch (\Doctrine\ORM\OptimisticLockException $exception) {
682
                    throw new Exception(
683
                        get_lang('InternalDatabaseError').': '.$exception->getMessage()
684
                    );
685
                }
686
                $accessUrlRelSession = new \Chamilo\CoreBundle\Entity\AccessUrlRelSession();
687
                $accessUrlRelSession->setAccessUrlId(api_get_current_access_url_id());
688
                $accessUrlRelSession->setSessionId($session->getId());
689
                Database::getManager()->persist($accessUrlRelSession);
690
                try {
691
                    Database::getManager()->flush();
692
                } catch (\Doctrine\ORM\OptimisticLockException $exception) {
693
                    throw new Exception(
694
                        get_lang('InternalDatabaseError').': '.$exception->getMessage()
695
                    );
696
                }
697
            } else {
698
                // user has at least one accessible session, let's use it
699
                $session = $sessions[0];
700
            }
701
            // add chosen course to the user session
702
            $session->addCourse($course);
703
            Database::getManager()->persist($session);
704
            try {
705
                Database::getManager()->flush();
706
            } catch (\Doctrine\ORM\OptimisticLockException $exception) {
707
                throw new Exception(
708
                    get_lang('InternalDatabaseError').': '.$exception->getMessage()
709
                );
710
            }
711
            // subscribe user to course within this session
712
            SessionManager::subscribe_users_to_session_course([$userId], $session->getId(), $course->getCode());
713
        }
714
715
        self::subscribeUser($userId, $course->getCode(), $status, 0);
716
    }
717
718
    /**
719
     * @param string $courseCode
720
     * @param int    $status
721
     *
722
     * @return bool
723
     */
724
    public static function autoSubscribeToCourse($courseCode, $status = STUDENT): bool
725
    {
726
        try {
727
            self::processAutoSubscribeToCourse($courseCode, $status);
728
        } catch (Exception $e) {
729
            Display::addFlash(
730
                Display::return_message($e->getMessage(), 'warning')
731
            );
732
733
            return false;
734
        }
735
736
        return true;
737
    }
738
739
    /**
740
     * Subscribe a user to a course. No checks are performed here to see if
741
     * course subscription is allowed.
742
     *
743
     * @param int    $userId
744
     * @param string $courseCode
745
     * @param int    $status                 (STUDENT, COURSEMANAGER, COURSE_ADMIN, NORMAL_COURSE_MEMBER)
746
     * @param int    $sessionId
747
     * @param int    $userCourseCategoryId
748
     * @param bool   $checkTeacherPermission
749
     *
750
     * @return bool True on success, false on failure
751
     *
752
     * @assert ('', '') === false
753
     */
754
    public static function subscribeUser(
755
        $userId,
756
        $courseCode,
757
        $status = STUDENT,
758
        $sessionId = 0,
759
        $userCourseCategoryId = 0,
760
        $checkTeacherPermission = true
761
    ) {
762
        $userId = (int) $userId;
763
        $status = (int) $status;
764
765
        if (empty($userId) || empty($courseCode)) {
766
            return false;
767
        }
768
769
        $courseInfo = api_get_course_info($courseCode);
770
771
        if (empty($courseInfo)) {
772
            Display::addFlash(Display::return_message(get_lang('CourseDoesNotExist'), 'warning'));
773
774
            return false;
775
        }
776
777
        $userInfo = api_get_user_info($userId);
778
779
        if (empty($userInfo)) {
780
            Display::addFlash(Display::return_message(get_lang('UserDoesNotExist'), 'warning'));
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
                Display::addFlash(Display::return_message(get_lang('AlreadyRegisteredToCourse'), 'warning'));
808
809
                return false;
810
            }
811
812
            if ($checkTeacherPermission && !api_is_course_admin()) {
813
                // Check in advance whether subscription is allowed or not for this course.
814
                if ((int) $courseInfo['subscribe'] === SUBSCRIBE_NOT_ALLOWED) {
815
                    Display::addFlash(Display::return_message(get_lang('SubscriptionNotAllowed'), 'warning'));
816
817
                    return false;
818
                }
819
            }
820
821
            if (STUDENT === $status) {
822
                // Check if max students per course extra field is set
823
                $extraFieldValue = new ExtraFieldValue('course');
824
                $value = $extraFieldValue->get_values_by_handler_and_field_variable(
825
                    $courseId,
826
                    'max_subscribed_students'
827
                );
828
829
                if (!empty($value) && isset($value['value'])) {
830
                    $maxStudents = $value['value'];
831
                    if ($maxStudents !== '') {
832
                        $maxStudents = (int) $maxStudents;
833
                        $count = self::get_user_list_from_course_code(
834
                            $courseCode,
835
                            0,
836
                            null,
837
                            null,
838
                            STUDENT,
839
                            true,
840
                            false
841
                        );
842
843
                        if ($count >= $maxStudents) {
844
                            Display::addFlash(Display::return_message(get_lang('MaxNumberSubscribedStudentsReached'), 'warning'));
845
846
                            return false;
847
                        }
848
                    }
849
                }
850
            }
851
852
            $maxSort = api_max_sort_value('0', $userId) + 1;
853
854
            self::insertUserInCourse(
855
                $userId,
856
                $courseId,
857
                ['status' => $status, 'sort' => $maxSort, 'user_course_cat' => $userCourseCategoryId]
858
            );
859
860
            Display::addFlash(
861
                Display::return_message(
862
                    sprintf(
863
                        get_lang('UserXAddedToCourseX'),
864
                        $userInfo['complete_name_with_username'],
865
                        $courseInfo['title']
866
                    )
867
                )
868
            );
869
870
            $send = api_get_course_setting('email_alert_to_teacher_on_new_user_in_course', $courseInfo);
871
872
            if ($send == 1) {
873
                self::email_to_tutor(
874
                    $userId,
875
                    $courseInfo['real_id'],
876
                    false
877
                );
878
            } elseif ($send == 2) {
879
                self::email_to_tutor(
880
                    $userId,
881
                    $courseInfo['real_id'],
882
                    true
883
                );
884
            }
885
886
            $subscribe = (int) api_get_course_setting('subscribe_users_to_forum_notifications', $courseInfo);
887
            if ($subscribe === 1) {
888
                require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
889
                $forums = get_forums(0, $courseCode, true, $sessionId);
890
                foreach ($forums as $forum) {
891
                    $forumId = $forum['iid'];
892
                    set_notification('forum', $forumId, false, $userInfo, $courseInfo);
893
                }
894
            }
895
        }
896
897
        return true;
898
    }
899
900
    /**
901
     * Get the course id based on the original id and field name in the
902
     * extra fields. Returns 0 if course was not found.
903
     *
904
     * @param string $original_course_id_value
905
     * @param string $original_course_id_name
906
     *
907
     * @return int Course id
908
     *
909
     * @assert ('', '') === false
910
     */
911
    public static function get_course_code_from_original_id(
912
        $original_course_id_value,
913
        $original_course_id_name
914
    ) {
915
        $t_cfv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
916
        $table_field = Database::get_main_table(TABLE_EXTRA_FIELD);
917
        $extraFieldType = EntityExtraField::COURSE_FIELD_TYPE;
918
        $original_course_id_value = Database::escape_string($original_course_id_value);
919
        $original_course_id_name = Database::escape_string($original_course_id_name);
920
921
        $sql = "SELECT item_id
922
                FROM $table_field cf
923
                INNER JOIN $t_cfv cfv
924
                ON cfv.field_id=cf.id
925
                WHERE
926
                    variable = '$original_course_id_name' AND
927
                    value = '$original_course_id_value' AND
928
                    cf.extra_field_type = $extraFieldType
929
                ";
930
        $res = Database::query($sql);
931
        $row = Database::fetch_object($res);
932
        if ($row) {
933
            return $row->item_id;
934
        } else {
935
            return 0;
936
        }
937
    }
938
939
    /**
940
     * Gets the course code from the course id. Returns null if course id was not found.
941
     *
942
     * @param int $id Course id
943
     *
944
     * @return string Course code
945
     * @assert ('') === false
946
     */
947
    public static function get_course_code_from_course_id($id)
948
    {
949
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
950
        $id = intval($id);
951
        $sql = "SELECT code FROM $table WHERE id = $id ";
952
        $res = Database::query($sql);
953
        $row = Database::fetch_object($res);
954
        if ($row) {
955
            return $row->code;
956
        } else {
957
            return null;
958
        }
959
    }
960
961
    /**
962
     * Add the user $userId visibility to the course $courseCode in the catalogue.
963
     *
964
     * @author David Nos (https://github.com/dnos)
965
     *
966
     * @param int    $userId     the id of the user
967
     * @param string $courseCode the course code
968
     * @param int    $visible    (optional) The course visibility in the catalogue to the user (1=visible, 0=invisible)
969
     *
970
     * @return bool true if added succesfully, false otherwise
971
     */
972
    public static function addUserVisibilityToCourseInCatalogue(
973
        $userId,
974
        $courseCode,
975
        $visible = 1
976
    ) {
977
        $debug = false;
978
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
979
        $courseUserTable = Database::get_main_table(TABLE_MAIN_COURSE_CATALOGUE_USER);
980
        $visible = (int) $visible;
981
        if (empty($userId) || empty($courseCode) || ($userId != strval(intval($userId)))) {
982
            return false;
983
        }
984
985
        $courseCode = Database::escape_string($courseCode);
986
        $courseInfo = api_get_course_info($courseCode);
987
        $courseId = $courseInfo['real_id'];
988
989
        // Check in advance whether the user has already been registered on the platform.
990
        $sql = "SELECT status FROM ".$userTable." WHERE user_id = $userId ";
991
        if (Database::num_rows(Database::query($sql)) == 0) {
992
            if ($debug) {
993
                error_log('The user has not been registered to the platform');
994
            }
995
996
            return false; // The user has not been registered to the platform.
997
        }
998
999
        // Check whether the user has already been registered to the course visibility in the catalogue.
1000
        $sql = "SELECT * FROM $courseUserTable
1001
                WHERE
1002
                    user_id = $userId AND
1003
                    visible = $visible AND
1004
                    c_id = $courseId";
1005
        if (Database::num_rows(Database::query($sql)) > 0) {
1006
            if ($debug) {
1007
                error_log('The user has been already registered to the course visibility in the catalogue');
1008
            }
1009
1010
            return true; // The visibility of the user to the course in the catalogue does already exist.
1011
        }
1012
1013
        // Register the user visibility to course in catalogue.
1014
        $params = [
1015
            'user_id' => $userId,
1016
            'c_id' => $courseId,
1017
            'visible' => $visible,
1018
        ];
1019
        $insertId = Database::insert($courseUserTable, $params);
1020
1021
        return $insertId;
1022
    }
1023
1024
    /**
1025
     * Remove the user $userId visibility to the course $courseCode in the catalogue.
1026
     *
1027
     * @author David Nos (https://github.com/dnos)
1028
     *
1029
     * @param int    $userId     the id of the user
1030
     * @param string $courseCode the course code
1031
     * @param int    $visible    (optional) The course visibility in the catalogue to the user (1=visible, 0=invisible)
1032
     *
1033
     * @return bool true if removed succesfully or register not found, false otherwise
1034
     */
1035
    public static function removeUserVisibilityToCourseInCatalogue(
1036
        $userId,
1037
        $courseCode,
1038
        $visible = 1
1039
    ) {
1040
        $courseUserTable = Database::get_main_table(TABLE_MAIN_COURSE_CATALOGUE_USER);
1041
1042
        if (empty($userId) || empty($courseCode) || ($userId != strval(intval($userId)))) {
1043
            return false;
1044
        }
1045
1046
        $courseCode = Database::escape_string($courseCode);
1047
        $courseInfo = api_get_course_info($courseCode);
1048
        $courseId = $courseInfo['real_id'];
1049
1050
        // Check whether the user has already been registered to the course visibility in the catalogue.
1051
        $sql = "SELECT * FROM $courseUserTable
1052
                WHERE
1053
                    user_id = $userId AND
1054
                    visible = $visible AND
1055
                    c_id = $courseId";
1056
        if (Database::num_rows(Database::query($sql)) > 0) {
1057
            $cond = [
1058
                'user_id = ? AND c_id = ? AND visible = ? ' => [
1059
                    $userId,
1060
                    $courseId,
1061
                    $visible,
1062
                ],
1063
            ];
1064
1065
            return Database::delete($courseUserTable, $cond);
1066
        } else {
1067
            return true; // Register does not exist
1068
        }
1069
    }
1070
1071
    /**
1072
     * @param string $code
1073
     *
1074
     * @return bool if there already are one or more courses
1075
     *              with the same code OR visual_code (visualcode), false otherwise
1076
     */
1077
    public static function course_code_exists($code)
1078
    {
1079
        $code = Database::escape_string($code);
1080
        $sql = "SELECT COUNT(*) as number
1081
                FROM ".Database::get_main_table(TABLE_MAIN_COURSE)."
1082
                WHERE code = '$code' OR visual_code = '$code'";
1083
        $result = Database::fetch_array(Database::query($sql));
1084
1085
        return $result['number'] > 0;
1086
    }
1087
1088
    /**
1089
     * @param int    $user_id
1090
     * @param string $startsWith Optional
1091
     *
1092
     * @return array an array with the course info of all the courses (real and virtual)
1093
     *               of which the current user is course admin
1094
     */
1095
    public static function get_course_list_of_user_as_course_admin($user_id, $startsWith = '')
1096
    {
1097
        if ($user_id != strval(intval($user_id))) {
1098
            return [];
1099
        }
1100
1101
        // Definitions database tables and variables
1102
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
1103
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1104
        $user_id = intval($user_id);
1105
        $data = [];
1106
1107
        $sql = "SELECT
1108
                    course.code,
1109
                    course.title,
1110
                    course.id,
1111
                    course.id as real_id,
1112
                    course.category_code
1113
                FROM $tbl_course_user as course_rel_user
1114
                INNER JOIN $tbl_course as course
1115
                ON course.id = course_rel_user.c_id
1116
                WHERE
1117
                    course_rel_user.user_id = $user_id AND
1118
                    course_rel_user.status = 1
1119
        ";
1120
1121
        if (api_get_multiple_access_url()) {
1122
            $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
1123
            $access_url_id = api_get_current_access_url_id();
1124
            if ($access_url_id != -1) {
1125
                $sql = "
1126
                    SELECT
1127
                        course.code,
1128
                        course.title,
1129
                        course.id,
1130
                        course.id as real_id
1131
                    FROM $tbl_course_user as course_rel_user
1132
                    INNER JOIN $tbl_course as course
1133
                    ON course.id = course_rel_user.c_id
1134
                    INNER JOIN $tbl_course_rel_access_url course_rel_url
1135
                    ON (course_rel_url.c_id = course.id)
1136
                    WHERE
1137
                        access_url_id = $access_url_id  AND
1138
                        course_rel_user.user_id = $user_id AND
1139
                        course_rel_user.status = 1
1140
                ";
1141
            }
1142
        }
1143
1144
        if (!empty($startsWith)) {
1145
            $startsWith = Database::escape_string($startsWith);
1146
1147
            $sql .= " AND (course.title LIKE '$startsWith%' OR course.code LIKE '$startsWith%')";
1148
        }
1149
1150
        $sql .= ' ORDER BY course.title';
1151
1152
        $result_nb_cours = Database::query($sql);
1153
        if (Database::num_rows($result_nb_cours) > 0) {
1154
            while ($row = Database::fetch_array($result_nb_cours, 'ASSOC')) {
1155
                $data[$row['id']] = $row;
1156
            }
1157
        }
1158
1159
        return $data;
1160
    }
1161
1162
    /**
1163
     * @param int   $userId
1164
     * @param array $courseInfo
1165
     *
1166
     * @return bool|null
1167
     */
1168
    public static function isUserSubscribedInCourseAsDrh($userId, $courseInfo)
1169
    {
1170
        $userId = intval($userId);
1171
1172
        if (!api_is_drh()) {
1173
            return false;
1174
        }
1175
1176
        if (empty($courseInfo) || empty($userId)) {
1177
            return false;
1178
        }
1179
1180
        $courseId = intval($courseInfo['real_id']);
1181
        $table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1182
1183
        $sql = "SELECT * FROM $table
1184
                WHERE
1185
                    user_id = $userId AND
1186
                    relation_type = ".COURSE_RELATION_TYPE_RRHH." AND
1187
                    c_id = $courseId";
1188
1189
        $result = Database::fetch_array(Database::query($sql));
1190
1191
        if (!empty($result)) {
1192
            // The user has been registered in this course.
1193
            return true;
1194
        }
1195
    }