Passed
Push — 1.11.x ( b7ceb1...b3c700 )
by Yannick
12:37 queued 15s
created

Rest::logout()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 9
rs 10
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\Course;
6
use Chamilo\CoreBundle\Entity\ExtraFieldValues;
7
use Chamilo\CoreBundle\Entity\Session;
8
use Chamilo\CourseBundle\Entity\CLpCategory;
9
use Chamilo\CourseBundle\Entity\CNotebook;
10
use Chamilo\CourseBundle\Entity\Repository\CNotebookRepository;
11
use Chamilo\UserBundle\Entity\User;
12
use Symfony\Component\HttpFoundation\Request as HttpRequest;
13
14
/**
15
 * Class RestApi.
16
 */
17
class Rest extends WebService
18
{
19
    public const SERVICE_NAME = 'MsgREST';
20
    public const EXTRA_FIELD_GCM_REGISTRATION = 'gcm_registration_id';
21
22
    public const GET_AUTH = 'authenticate';
23
    public const SAVE_GCM_ID = 'gcm_id';
24
    public const LOGOUT = 'logout';
25
26
    public const GET_USER_MESSAGES = 'user_messages';
27
    public const GET_USER_MESSAGES_RECEIVED = 'user_messages_received';
28
    public const DELETE_USER_MESSAGE = 'delete_user_message';
29
    public const GET_USER_MESSAGES_SENT = 'user_messages_sent';
30
    public const GET_COUNT_NEW_MESSAGES = 'get_count_new_messages';
31
    public const SET_MESSAGE_READ = 'set_message_read';
32
    public const POST_USER_MESSAGE_READ = 'user_message_read';
33
    public const POST_USER_MESSAGE_UNREAD = 'user_message_unread';
34
    public const SAVE_USER_MESSAGE = 'save_user_message';
35
    public const GET_MESSAGE_USERS = 'message_users';
36
    public const VIEW_MESSAGE = 'view_message';
37
38
    public const GET_USER_COURSES = 'user_courses';
39
    public const GET_USER_COURSES_BY_DATES = 'user_courses_by_dates';
40
    public const GET_USER_SESSIONS = 'user_sessions';
41
42
    public const VIEW_PROFILE = 'view_user_profile';
43
    public const GET_PROFILE = 'user_profile';
44
    public const GET_PROFILES_BY_EXTRA_FIELD = 'users_profiles_by_extra_field';
45
46
    public const VIEW_MY_COURSES = 'view_my_courses';
47
    public const VIEW_COURSE_HOME = 'view_course_home';
48
    public const GET_COURSE_INFO = 'course_info';
49
    public const GET_COURSE_DESCRIPTIONS = 'course_descriptions';
50
    public const GET_COURSE_DOCUMENTS = 'course_documents';
51
    public const GET_COURSE_ANNOUNCEMENTS = 'course_announcements';
52
    public const GET_COURSE_ANNOUNCEMENT = 'course_announcement';
53
    public const GET_COURSE_AGENDA = 'course_agenda';
54
    public const GET_COURSE_NOTEBOOKS = 'course_notebooks';
55
    public const GET_COURSE_FORUM_CATEGORIES = 'course_forumcategories';
56
    public const GET_COURSE_FORUM = 'course_forum';
57
    public const GET_COURSE_FORUM_THREAD = 'course_forumthread';
58
    public const GET_COURSE_LEARNPATHS = 'course_learnpaths';
59
    public const GET_COURSE_LEARNPATH = 'course_learnpath';
60
    public const GET_COURSE_LP_PROGRESS = 'course_lp_progress';
61
    public const GET_COURSE_LINKS = 'course_links';
62
    public const GET_COURSE_WORKS = 'course_works';
63
    public const GET_COURSE_EXERCISES = 'course_exercises';
64
    public const GET_COURSES_DETAILS_BY_EXTRA_FIELD = 'courses_details_by_extra_field';
65
66
    public const SAVE_COURSE_NOTEBOOK = 'save_course_notebook';
67
68
    public const SAVE_FORUM_POST = 'save_forum_post';
69
    public const SAVE_FORUM_THREAD = 'save_forum_thread';
70
    public const SET_THREAD_NOTIFY = 'set_thread_notify';
71
    public const DOWNLOAD_FORUM_ATTACHMENT = 'download_forum_attachment';
72
73
    public const GET_WORK_LIST = 'get_work_list';
74
    public const GET_WORK_STUDENTS_WITHOUT_PUBLICATIONS = 'get_work_students_without_publications';
75
    public const GET_WORK_USERS = 'get_work_users';
76
    public const GET_WORK_STUDENT_LIST = 'get_work_student_list';
77
    public const PUT_WORK_STUDENT_ITEM_VISIBILITY = 'put_course_work_visibility';
78
    public const DELETE_WORK_STUDENT_ITEM = 'delete_work_student_item';
79
    public const DELETE_WORK_CORRECTIONS = 'delete_work_corrections';
80
    public const DOWNLOAD_WORK_FOLDER = 'download_work_folder';
81
    public const DOWNLOAD_WORK_COMMENT_ATTACHMENT = 'download_work_comment_attachment';
82
    public const DOWNLOAD_WORK = 'download_work';
83
84
    public const VIEW_DOCUMENT_IN_FRAME = 'view_document_in_frame';
85
86
    public const VIEW_QUIZ_TOOL = 'view_quiz_tool';
87
88
    public const VIEW_SURVEY_TOOL = 'view_survey_tool';
89
90
    public const CREATE_CAMPUS = 'add_campus';
91
    public const EDIT_CAMPUS = 'edit_campus';
92
    public const DELETE_CAMPUS = 'delete_campus';
93
94
    public const GET_USERS = 'get_users';
95
    public const USERNAME_EXIST = 'username_exist';
96
    public const SAVE_USER = 'save_user';
97
    public const SAVE_USER_GET_APIKEY = 'save_user_get_apikey';
98
    public const SAVE_USER_JSON = 'save_user_json';
99
    public const UPDATE_USER_FROM_USERNAME = 'update_user_from_username';
100
    public const UPDATE_USER_APIKEY = 'update_user_apikey';
101
    public const DELETE_USER = 'delete_user';
102
    public const GET_USERS_API_KEYS = 'get_users_api_keys';
103
    public const GET_USER_API_KEY = 'get_user_api_key';
104
    public const GET_USER_LAST_CONNEXION = 'get_user_last_connexion';
105
    public const GET_USER_TOTAL_CONNEXION_TIME = 'get_user_total_connexion_time';
106
    public const GET_USER_SUB_GROUP = 'get_user_sub_group';
107
108
    public const GET_COURSES = 'get_courses';
109
    public const GET_COURSES_FROM_EXTRA_FIELD = 'get_courses_from_extra_field';
110
    public const SAVE_COURSE = 'save_course';
111
    public const DELETE_COURSE = 'delete_course';
112
    public const GET_SESSION_FROM_EXTRA_FIELD = 'get_session_from_extra_field';
113
    public const GET_SESSION_INFO_FROM_EXTRA_FIELD = 'get_session_info_from_extra_field';
114
    public const SAVE_SESSION = 'save_session';
115
    public const CREATE_SESSION_FROM_MODEL = 'create_session_from_model';
116
    public const UPDATE_SESSION = 'update_session';
117
    public const GET_SESSIONS = 'get_sessions';
118
119
    public const SUBSCRIBE_USER_TO_COURSE = 'subscribe_user_to_course';
120
    public const SUBSCRIBE_USER_TO_COURSE_PASSWORD = 'subscribe_user_to_course_password';
121
    public const UNSUBSCRIBE_USER_FROM_COURSE = 'unsubscribe_user_from_course';
122
    public const GET_USERS_SUBSCRIBED_TO_COURSE = 'get_users_subscribed_to_course';
123
124
    public const ADD_COURSES_SESSION = 'add_courses_session';
125
    public const ADD_USERS_SESSION = 'add_users_session';
126
    public const SUBSCRIBE_USER_TO_SESSION_FROM_USERNAME = 'subscribe_user_to_session_from_username';
127
    public const SUBSCRIBE_USERS_TO_SESSION = 'subscribe_users_to_session';
128
    public const UNSUBSCRIBE_USERS_FROM_SESSION = 'unsubscribe_users_from_session';
129
    public const GET_USERS_SUBSCRIBED_TO_SESSION = 'get_users_subscribed_to_session';
130
131
    public const GET_COURSE_QUIZ_MDL_COMPAT = 'get_course_quiz_mdl_compat';
132
133
    public const UPDATE_USER_PAUSE_TRAINING = 'update_user_pause_training';
134
135
    public const CHECK_CONDITIONAL_LOGIN = 'check_conditional_login';
136
    public const GET_LEGAL_CONDITIONS = 'get_legal_conditions';
137
    public const UPDATE_CONDITION_ACCEPTED = 'update_condition_accepted';
138
    public const GET_TEST_UPDATES_LIST = 'get_test_updates_list';
139
    public const GET_TEST_AVERAGE_RESULTS_LIST = 'get_test_average_results_list';
140
141
    public const GET_GROUPS = 'get_groups';
142
    public const GROUP_EXISTS = 'group_exists';
143
    public const ADD_GROUP = 'add_group';
144
    public const DELETE_GROUP = 'delete_group';
145
    public const GET_GROUP_SUB_USERS = 'get_group_sub_users';
146
    public const GET_GROUP_SUB_COURSES = 'get_group_sub_courses';
147
    public const GET_GROUP_SUB_SESSIONS = 'get_group_sub_sessions';
148
    public const ADD_GROUP_SUB_USER = 'add_group_sub_user';
149
    public const ADD_GROUP_SUB_COURSE = 'add_group_sub_course';
150
    public const ADD_GROUP_SUB_SESSION = 'add_group_sub_session';
151
    public const DELETE_GROUP_SUB_USER = 'delete_group_sub_user';
152
    public const DELETE_GROUP_SUB_COURSE = 'delete_group_sub_course';
153
    public const DELETE_GROUP_SUB_SESSION = 'delete_group_sub_session';
154
    public const GET_AUDIT_ITEMS = 'get_audit_items';
155
156
    /**
157
     * @var Session
158
     */
159
    private $session;
160
161
    /**
162
     * @var Course
163
     */
164
    private $course;
165
166
    /**
167
     * Rest constructor.
168
     *
169
     * @param string $username
170
     * @param string $apiKey
171
     */
172
    public function __construct($username, $apiKey)
173
    {
174
        parent::__construct($username, $apiKey);
175
    }
176
177
    /**
178
     * Get user's username or another field if so configured through $_configuration['webservice_return_user_field'].
179
     *
180
     * @param int $userId
181
     */
182
    private function __getConfiguredUsernameById(int $userId = null): string
183
    {
184
        if (empty($userId)) {
185
            return '';
186
        }
187
        $userField = api_get_configuration_value('webservice_return_user_field');
188
        if (empty($userField)) {
189
            return api_get_user_info($userId)['username'];
190
        }
191
192
        $fieldValue = new ExtraFieldValue('user');
193
        $extraInfo = $fieldValue->get_values_by_handler_and_field_variable($userId, $userField);
194
        if (!empty($extraInfo)) {
195
            return $extraInfo['value'];
196
        } else {
197
            return api_get_user_info($userId)['username'];
198
        }
199
    }
200
201
    /**
202
     * @param string $username
203
     * @param string $apiKeyToValidate
204
     *
205
     * @throws Exception
206
     *
207
     * @return Rest
208
     */
209
    public static function validate($username, $apiKeyToValidate)
210
    {
211
        $apiKey = self::findUserApiKey($username, self::SERVICE_NAME);
212
213
        if ($apiKey != $apiKeyToValidate) {
214
            throw new Exception(get_lang('InvalidApiKey'));
215
        }
216
217
        return new self($username, $apiKey);
218
    }
219
220
    /**
221
     * Create the gcm_registration_id extra field for users.
222
     */
223
    public static function init()
224
    {
225
        $extraField = new ExtraField('user');
226
        $fieldInfo = $extraField->get_handler_field_info_by_field_variable(self::EXTRA_FIELD_GCM_REGISTRATION);
227
228
        if (empty($fieldInfo)) {
229
            $extraField->save(
230
                [
231
                    'variable' => self::EXTRA_FIELD_GCM_REGISTRATION,
232
                    'field_type' => ExtraField::FIELD_TYPE_TEXT,
233
                    'display_text' => self::EXTRA_FIELD_GCM_REGISTRATION,
234
                ]
235
            );
236
        }
237
    }
238
239
    /**
240
     * @param string $encoded
241
     *
242
     * @return array
243
     */
244
    public static function decodeParams($encoded)
245
    {
246
        return json_decode($encoded);
247
    }
248
249
    /**
250
     * Set the current course.
251
     *
252
     * @param int $id
253
     *
254
     * @throws Exception
255
     */
256
    public function setCourse($id)
257
    {
258
        global $_course;
259
260
        if (!$id) {
261
            $this->course = null;
262
263
            ChamiloSession::erase('_real_cid');
264
            ChamiloSession::erase('_cid');
265
            ChamiloSession::erase('_course');
266
267
            return;
268
        }
269
270
        $em = Database::getManager();
271
        /** @var Course $course */
272
        $course = $em->find('ChamiloCoreBundle:Course', $id);
273
274
        if (!$course) {
0 ignored issues
show
introduced by
$course is of type Chamilo\CoreBundle\Entity\Course, thus it always evaluated to true.
Loading history...
275
            throw new Exception(get_lang('NoCourse'));
276
        }
277
278
        $this->course = $course;
279
280
        $courseInfo = api_get_course_info($course->getCode());
281
        $_course = $courseInfo;
282
283
        ChamiloSession::write('_real_cid', $course->getId());
284
        ChamiloSession::write('_cid', $course->getCode());
285
        ChamiloSession::write('_course', $courseInfo);
286
    }
287
288
    /**
289
     * Set the current session.
290
     *
291
     * @param int $id
292
     *
293
     * @throws Exception
294
     */
295
    public function setSession($id)
296
    {
297
        if (!$id) {
298
            $this->session = null;
299
300
            ChamiloSession::erase('session_name');
301
            ChamiloSession::erase('id_session');
302
303
            return;
304
        }
305
306
        $em = Database::getManager();
307
        /** @var Session $session */
308
        $session = $em->find('ChamiloCoreBundle:Session', $id);
309
310
        if (!$session) {
0 ignored issues
show
introduced by
$session is of type Chamilo\CoreBundle\Entity\Session, thus it always evaluated to true.
Loading history...
311
            throw new Exception(get_lang('NoSession'));
312
        }
313
314
        $this->session = $session;
315
316
        ChamiloSession::write('session_name', $session->getName());
317
        ChamiloSession::write('id_session', $session->getId());
318
    }
319
320
    /**
321
     * @param string $registrationId
322
     *
323
     * @return bool
324
     */
325
    public function setGcmId($registrationId)
326
    {
327
        $registrationId = Security::remove_XSS($registrationId);
328
        $extraFieldValue = new ExtraFieldValue('user');
329
330
        return $extraFieldValue->save(
331
            [
332
                'variable' => self::EXTRA_FIELD_GCM_REGISTRATION,
333
                'value' => $registrationId,
334
                'item_id' => $this->user->getId(),
335
            ]
336
        );
337
    }
338
339
    /**
340
     * @param int $lastMessageId
341
     *
342
     * @return array
343
     */
344
    public function getUserMessages($lastMessageId = 0)
345
    {
346
        $lastMessages = MessageManager::getMessagesFromLastReceivedMessage($this->user->getId(), $lastMessageId);
347
        $messages = [];
348
349
        foreach ($lastMessages as $message) {
350
            $hasAttachments = MessageManager::hasAttachments($message['id']);
351
352
            $messages[] = [
353
                'id' => $message['id'],
354
                'title' => $message['title'],
355
                'sender' => [
356
                    'id' => $message['user_id'],
357
                    'lastname' => $message['lastname'],
358
                    'firstname' => $message['firstname'],
359
                    'completeName' => api_get_person_name($message['firstname'], $message['lastname']),
360
                ],
361
                'sendDate' => $message['send_date'],
362
                'content' => $message['content'],
363
                'hasAttachments' => $hasAttachments,
364
                'url' => api_get_path(WEB_CODE_PATH).'messages/view_message.php?'
365
                    .http_build_query(['type' => 1, 'id' => $message['id']]),
366
            ];
367
        }
368
369
        return $messages;
370
    }
371
372
    /**
373
     * @return array
374
     */
375
    public function getUserReceivedMessages()
376
    {
377
        $lastMessages = MessageManager::getReceivedMessages($this->user->getId(), 0);
378
        $messages = [];
379
380
        $webPath = api_get_path(WEB_PATH);
381
382
        foreach ($lastMessages as $message) {
383
            $hasAttachments = MessageManager::hasAttachments($message['id']);
384
            $attachmentList = [];
385
            if ($hasAttachments) {
386
                $attachmentList = MessageManager::getAttachmentList($message['id']);
387
            }
388
            $messages[] = [
389
                'id' => $message['id'],
390
                'title' => $message['title'],
391
                'msgStatus' => $message['msg_status'],
392
                'sender' => [
393
                    'id' => $message['user_id'],
394
                    'lastname' => $message['lastname'],
395
                    'firstname' => $message['firstname'],
396
                    'completeName' => api_get_person_name($message['firstname'], $message['lastname']),
397
                    'pictureUri' => $message['pictureUri'],
398
                ],
399
                'sendDate' => $message['send_date'],
400
                'content' => str_replace('src="/"', $webPath, $message['content']),
401
                'hasAttachments' => $hasAttachments,
402
                'attachmentList' => $attachmentList,
403
                'url' => '',
404
            ];
405
        }
406
407
        return $messages;
408
    }
409
410
    /**
411
     * @return array
412
     */
413
    public function getUserSentMessages()
414
    {
415
        $lastMessages = MessageManager::getSentMessages($this->user->getId(), 0);
416
        $messages = [];
417
418
        foreach ($lastMessages as $message) {
419
            $hasAttachments = MessageManager::hasAttachments($message['id']);
420
421
            $messages[] = [
422
                'id' => $message['id'],
423
                'title' => $message['title'],
424
                'msgStatus' => $message['msg_status'],
425
                'receiver' => [
426
                    'id' => $message['user_id'],
427
                    'lastname' => $message['lastname'],
428
                    'firstname' => $message['firstname'],
429
                    'completeName' => api_get_person_name($message['firstname'], $message['lastname']),
430
                    'pictureUri' => $message['pictureUri'],
431
                ],
432
                'sendDate' => $message['send_date'],
433
                'content' => $message['content'],
434
                'hasAttachments' => $hasAttachments,
435
                'url' => '',
436
            ];
437
        }
438
439
        return $messages;
440
    }
441
442
    /**
443
     * Get the user courses.
444
     *
445
     * @throws Exception
446
     */
447
    public function getUserCourses($userId = 0): array
448
    {
449
        if (!empty($userId)) {
450
            if (!api_is_platform_admin() && $userId != $this->user->getId()) {
451
                self::throwNotAllowedException();
452
            }
453
        }
454
455
        if (empty($userId)) {
456
            $userId = $this->user->getId();
457
        }
458
459
        Event::courseLogout(
460
            [
461
                'uid' => $userId,
462
                'cid' => api_get_course_id(),
463
                'sid' => api_get_session_id(),
464
            ]
465
        );
466
467
        $courses = CourseManager::get_courses_list_by_user_id($userId);
468
        $data = [];
469
470
        $webCodePath = api_get_path(WEB_CODE_PATH).'webservices/api/v2.php?';
471
472
        foreach ($courses as $courseInfo) {
473
            /** @var Course $course */
474
            $course = Database::getManager()->find('ChamiloCoreBundle:Course', $courseInfo['real_id']);
475
            $teachers = CourseManager::getTeacherListFromCourseCodeToString($course->getCode());
476
            $picturePath = CourseManager::getPicturePath($course, true)
477
                ?: Display::return_icon('session_default.png', null, null, null, null, true);
478
479
            $data[] = [
480
                'id' => $course->getId(),
481
                'title' => $course->getTitle(),
482
                'code' => $course->getCode(),
483
                'directory' => $course->getDirectory(),
484
                'urlPicture' => $picturePath,
485
                'teachers' => $teachers,
486
                'isSpecial' => !empty($courseInfo['special_course']),
487
                'url' => $webCodePath.http_build_query(
488
                    [
489
                        'action' => self::VIEW_COURSE_HOME,
490
                        'api_key' => $this->apiKey,
491
                        'username' => $this->user->getUsername(),
492
                        'course' => $course->getId(),
493
                    ]
494
                ),
495
            ];
496
        }
497
498
        return $data;
499
    }
500
501
    /**
502
     * @throws Exception
503
     *
504
     * @return array
505
     */
506
    public function getCourseInfo()
507
    {
508
        $teachers = CourseManager::getTeacherListFromCourseCodeToString($this->course->getCode());
509
        $tools = CourseHome::get_tools_category(
510
            TOOL_STUDENT_VIEW,
511
            $this->course->getId(),
512
            $this->session ? $this->session->getId() : 0
513
        );
514
515
        return [
516
            'id' => $this->course->getId(),
517
            'title' => $this->course->getTitle(),
518
            'code' => $this->course->getCode(),
519
            'directory' => $this->course->getDirectory(),
520
            'urlPicture' => CourseManager::getPicturePath($this->course, true),
521
            'teachers' => $teachers,
522
            'tools' => array_map(
523
                function ($tool) {
524
                    return ['type' => $tool['name']];
525
                },
526
                $tools
527
            ),
528
        ];
529
    }
530
531
    /**
532
     * Get the course descriptions.
533
     *
534
     * @param array $fields A list of extra fields to include in the answer. Searches for the field in the including course
535
     *
536
     * @throws Exception
537
     */
538
    public function getCourseDescriptions($fields = []): array
539
    {
540
        Event::event_access_tool(TOOL_COURSE_DESCRIPTION);
541
542
        // Check the extra fields criteria (whether to add extra field information or not)
543
        $fieldSource = [];
544
        if (count($fields) > 0) {
545
            // For each field, check where to get it from (quiz or course)
546
            $courseExtraField = new ExtraField('course');
547
            foreach ($fields as $fieldName) {
548
                // The field does not exist on the exercise, so use it from the course
549
                $courseFieldExists = $courseExtraField->get_handler_field_info_by_field_variable($fieldName);
550
                if ($courseFieldExists === false) {
551
                    continue;
552
                }
553
                $fieldSource[$fieldName] = ['item_type' => 'course', 'id' => $courseFieldExists['id']];
554
            }
555
        }
556
557
        $courseId = $this->course->getId();
558
        $descriptions = CourseDescription::get_descriptions($courseId);
559
        $results = [];
560
561
        $webPath = api_get_path(WEB_PATH);
562
563
        /** @var CourseDescription $description */
564
        foreach ($descriptions as $description) {
565
            $descriptionDetails = [
566
                'id' => $description->get_description_type(),
567
                'title' => $description->get_title(),
568
                'content' => str_replace('src="/', 'src="'.$webPath, $description->get_content()),
569
            ];
570
            if (count($fieldSource) > 0) {
571
                $extraFieldValuesTable = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
572
                $fieldsSearchString = "SELECT field_id, value FROM $extraFieldValuesTable WHERE item_id = %d AND field_id = %d";
573
                foreach ($fieldSource as $fieldName => $fieldDetails) {
574
                    $itemId = $courseId;
575
                    $result = Database::query(sprintf($fieldsSearchString, $itemId, $fieldDetails['id']));
576
                    if (Database::num_rows($result) > 0) {
577
                        $row = Database::fetch_assoc($result);
578
                        $descriptionDetails['extra_'.$fieldName] = $row['value'];
579
                    } else {
580
                        $descriptionDetails['extra_'.$fieldName] = '';
581
                    }
582
                }
583
            }
584
            $results[] = $descriptionDetails;
585
        }
586
587
        return $results;
588
    }
589
590
    /**
591
     * @param int $directoryId
592
     *
593
     * @throws Exception
594
     *
595
     * @return array
596
     */
597
    public function getCourseDocuments($directoryId = 0)
598
    {
599
        Event::event_access_tool(TOOL_DOCUMENT);
600
601
        /** @var string $path */
602
        $path = '/';
603
        $sessionId = $this->session ? $this->session->getId() : 0;
604
605
        if ($directoryId) {
606
            $directory = DocumentManager::get_document_data_by_id(
607
                $directoryId,
608
                $this->course->getCode(),
609
                false,
610
                $sessionId
611
            );
612
613
            if (!$directory) {
614
                throw new Exception('NoDataAvailable');
615
            }
616
617
            $path = $directory['path'];
618
        }
619
620
        $courseInfo = api_get_course_info_by_id($this->course->getId());
621
        $documents = DocumentManager::getAllDocumentData(
622
            $courseInfo,
623
            $path,
624
            0,
625
            null,
626
            false,
627
            false,
628
            $sessionId
629
        );
630
        $results = [];
631
632
        if (!empty($documents)) {
633
            $webPath = api_get_path(WEB_CODE_PATH).'document/document.php?';
634
635
            /** @var array $document */
636
            foreach ($documents as $document) {
637
                if ($document['visibility'] != '1') {
638
                    continue;
639
                }
640
641
                $icon = $document['filetype'] == 'file'
642
                    ? choose_image($document['path'])
643
                    : chooseFolderIcon($document['path']);
644
645
                $results[] = [
646
                    'id' => $document['id'],
647
                    'type' => $document['filetype'],
648
                    'title' => $document['title'],
649
                    'path' => $document['path'],
650
                    'url' => $webPath.http_build_query(
651
                        [
652
                            'username' => $this->user->getUsername(),
653
                            'api_key' => $this->apiKey,
654
                            'cidReq' => $this->course->getCode(),
655
                            'id_session' => $sessionId,
656
                            'gidReq' => 0,
657
                            'gradebook' => 0,
658
                            'origin' => '',
659
                            'action' => 'download',
660
                            'id' => $document['id'],
661
                        ]
662
                    ),
663
                    'icon' => $icon,
664
                    'size' => format_file_size($document['size']),
665
                ];
666
            }
667
        }
668
669
        return $results;
670
    }
671
672
    /**
673
     * @throws Exception
674
     *
675
     * @return array
676
     */
677
    public function getCourseAnnouncements()
678
    {
679
        Event::event_access_tool(TOOL_ANNOUNCEMENT);
680
681
        $sessionId = $this->session ? $this->session->getId() : 0;
682
683
        $announcements = AnnouncementManager::getAnnouncements(
684
            null,
685
            null,
686
            false,
687
            null,
688
            null,
689
            null,
690
            null,
691
            null,
692
            0,
693
            $this->user->getId(),
694
            $this->course->getId(),
695
            $sessionId
696
        );
697
698
        $announcements = array_map(
699
            function ($announcement) {
700
                return [
701
                    'id' => (int) $announcement['id'],
702
                    'title' => strip_tags($announcement['title']),
703
                    'creatorName' => strip_tags($announcement['username']),
704
                    'date' => strip_tags($announcement['insert_date']),
705
                ];
706
            },
707
            $announcements
708
        );
709
710
        return $announcements;
711
    }
712
713
    /**
714
     * @param int $announcementId
715
     *
716
     * @throws Exception
717
     *
718
     * @return array
719
     */
720
    public function getCourseAnnouncement($announcementId)
721
    {
722
        Event::event_access_tool(TOOL_ANNOUNCEMENT);
723
724
        $sessionId = $this->session ? $this->session->getId() : 0;
725
        $announcement = AnnouncementManager::getAnnouncementInfoById(
726
            $announcementId,
727
            $this->course->getId(),
728
            $this->user->getId()
729
        );
730
731
        if (!$announcement) {
732
            throw new Exception(get_lang('NoAnnouncement'));
733
        }
734
735
        return [
736
            'id' => $announcement['announcement']->getIid(),
737
            'title' => $announcement['announcement']->getTitle(),
738
            'creatorName' => UserManager::formatUserFullName($announcement['item_property']->getInsertUser()),
739
            'date' => api_convert_and_format_date(
740
                $announcement['item_property']->getInsertDate(),
741
                DATE_TIME_FORMAT_LONG_24H
742
            ),
743
            'content' => AnnouncementManager::parseContent(
744
                $this->user->getId(),
745
                $announcement['announcement']->getContent(),
746
                $this->course->getCode(),
747
                $sessionId
748
            ),
749
        ];
750
    }
751
752
    /**
753
     * @throws Exception
754
     *
755
     * @return array
756
     */
757
    public function getCourseAgenda()
758
    {
759
        Event::event_access_tool(TOOL_CALENDAR_EVENT);
760
761
        $sessionId = $this->session ? $this->session->getId() : 0;
762
763
        $agenda = new Agenda(
764
            'course',
765
            $this->user->getId(),
766
            $this->course->getId(),
767
            $sessionId
768
        );
769
        $result = $agenda->parseAgendaFilter(null);
770
771
        $start = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
772
        $start->modify('first day of this month');
773
        $start->setTime(0, 0, 0);
774
        $end = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
775
        $end->modify('last day of this month');
776
        $end->setTime(23, 59, 59);
777
778
        $groupId = current($result['groups']);
779
        $userId = current($result['users']);
780
781
        $events = $agenda->getEvents(
782
            $start->getTimestamp(),
783
            $end->getTimestamp(),
784
            $this->course->getId(),
785
            $groupId,
786
            $userId,
787
            'array'
788
        );
789
790
        if (!is_array($events)) {
791
            return [];
792
        }
793
794
        $webPath = api_get_path(WEB_PATH);
795
796
        return array_map(
797
            function ($event) use ($webPath) {
798
                return [
799
                    'id' => (int) $event['unique_id'],
800
                    'title' => $event['title'],
801
                    'content' => str_replace('src="/', 'src="'.$webPath, $event['description']),
802
                    'startDate' => $event['start_date_localtime'],
803
                    'endDate' => $event['end_date_localtime'],
804
                    'isAllDay' => $event['allDay'] ? true : false,
805
                ];
806
            },
807
            $events
808
        );
809
    }
810
811
    /**
812
     * @throws Exception
813
     *
814
     * @return array
815
     */
816
    public function getCourseNotebooks()
817
    {
818
        Event::event_access_tool(TOOL_NOTEBOOK);
819
820
        $em = Database::getManager();
821
        /** @var CNotebookRepository $notebooksRepo */
822
        $notebooksRepo = $em->getRepository('ChamiloCourseBundle:CNotebook');
823
        $notebooks = $notebooksRepo->findByUser($this->user, $this->course, $this->session);
824
825
        return array_map(
826
            function (CNotebook $notebook) {
827
                return [
828
                    'id' => $notebook->getIid(),
829
                    'title' => $notebook->getTitle(),
830
                    'description' => $notebook->getDescription(),
831
                    'creationDate' => api_format_date(
832
                        $notebook->getCreationDate()->getTimestamp()
833
                    ),
834
                    'updateDate' => api_format_date(
835
                        $notebook->getUpdateDate()->getTimestamp()
836
                    ),
837
                ];
838
            },
839
            $notebooks
840
        );
841
    }
842
843
    /**
844
     * @throws Exception
845
     *
846
     * @return array
847
     */
848
    public function getCourseForumCategories()
849
    {
850
        Event::event_access_tool(TOOL_FORUM);
851
852
        $sessionId = $this->session ? $this->session->getId() : 0;
853
        $webCoursePath = api_get_path(WEB_COURSE_PATH).$this->course->getDirectory().'/upload/forum/images/';
854
855
        require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
856
857
        $categoriesFullData = get_forum_categories('', $this->course->getId(), $sessionId);
858
        $categories = [];
859
        $includeGroupsForums = api_get_setting('display_groups_forum_in_general_tool') === 'true';
860
        $forumsFullData = get_forums('', $this->course->getCode(), $includeGroupsForums, $sessionId);
861
        $forums = [];
862
863
        foreach ($forumsFullData as $forumId => $forumInfo) {
864
            $forum = [
865
                'id' => (int) $forumInfo['iid'],
866
                'catId' => (int) $forumInfo['forum_category'],
867
                'title' => $forumInfo['forum_title'],
868
                'description' => $forumInfo['forum_comment'],
869
                'image' => $forumInfo['forum_image'] ? ($webCoursePath.$forumInfo['forum_image']) : '',
870
                'numberOfThreads' => isset($forumInfo['number_of_threads']) ? intval(
871
                    $forumInfo['number_of_threads']
872
                ) : 0,
873
                'lastPost' => null,
874
            ];
875
876
            $lastPostInfo = get_last_post_information($forumId, false, $this->course->getId(), $sessionId);
877
878
            if ($lastPostInfo) {
879
                $forum['lastPost'] = [
880
                    'date' => api_convert_and_format_date($lastPostInfo['last_post_date']),
881
                    'user' => api_get_person_name(
882
                        $lastPostInfo['last_poster_firstname'],
883
                        $lastPostInfo['last_poster_lastname']
884
                    ),
885
                ];
886
            }
887
888
            $forums[] = $forum;
889
        }
890
891
        foreach ($categoriesFullData as $category) {
892
            $categoryForums = array_filter(
893
                $forums,
894
                function (array $forum) use ($category) {
895
                    if ($forum['catId'] != $category['cat_id']) {
896
                        return false;
897
                    }
898
899
                    return true;
900
                }
901
            );
902
903
            $categories[] = [
904
                'id' => (int) $category['iid'],
905
                'title' => $category['cat_title'],
906
                'catId' => (int) $category['cat_id'],
907
                'description' => $category['cat_comment'],
908
                'forums' => $categoryForums,
909
                'courseId' => $this->course->getId(),
910
            ];
911
        }
912
913
        return $categories;
914
    }
915
916
    /**
917
     * @param int $forumId
918
     *
919
     * @throws Exception
920
     *
921
     * @return array
922
     */
923
    public function getCourseForum($forumId)
924
    {
925
        Event::event_access_tool(TOOL_FORUM);
926
927
        require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
928
929
        $sessionId = $this->session ? $this->session->getId() : 0;
930
        $forumInfo = get_forums($forumId, $this->course->getCode(), true, $sessionId);
931
932
        if (!isset($forumInfo['iid'])) {
933
            throw new Exception(get_lang('NoForum'));
934
        }
935
936
        $webCoursePath = api_get_path(WEB_COURSE_PATH).$this->course->getDirectory().'/upload/forum/images/';
937
        $forum = [
938
            'id' => $forumInfo['iid'],
939
            'title' => $forumInfo['forum_title'],
940
            'description' => $forumInfo['forum_comment'],
941
            'image' => $forumInfo['forum_image'] ? ($webCoursePath.$forumInfo['forum_image']) : '',
942
            'threads' => [],
943
        ];
944
945
        $threads = get_threads($forumInfo['iid'], $this->course->getId(), $sessionId);
946
947
        foreach ($threads as $thread) {
948
            $forum['threads'][] = [
949
                'id' => $thread['iid'],
950
                'title' => $thread['thread_title'],
951
                'lastEditDate' => api_convert_and_format_date($thread['lastedit_date'], DATE_TIME_FORMAT_LONG_24H),
952
                'numberOfReplies' => $thread['thread_replies'],
953
                'numberOfViews' => $thread['thread_views'],
954
                'author' => api_get_person_name($thread['firstname'], $thread['lastname']),
955
            ];
956
        }
957
958
        return $forum;
959
    }
960
961
    /**
962
     * @param int $forumId
963
     * @param int $threadId
964
     *
965
     * @return array
966
     */
967
    public function getCourseForumThread($forumId, $threadId)
968
    {
969
        Event::event_access_tool(TOOL_FORUM);
970
971
        require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
972
973
        $sessionId = $this->session ? $this->session->getId() : 0;
974
        $threadInfo = get_thread_information($forumId, $threadId, $sessionId);
975
976
        $thread = [
977
            'id' => intval($threadInfo['iid']),
978
            'cId' => intval($threadInfo['c_id']),
979
            'title' => $threadInfo['thread_title'],
980
            'forumId' => intval($threadInfo['forum_id']),
981
            'posts' => [],
982
        ];
983
984
        $forumInfo = get_forums($threadInfo['forum_id'], $this->course->getCode(), true, $sessionId);
985
        $postsInfo = getPosts($forumInfo, $threadInfo['iid'], 'ASC');
986
987
        foreach ($postsInfo as $postInfo) {
988
            $thread['posts'][] = [
989
                'id' => $postInfo['iid'],
990
                'title' => $postInfo['post_title'],
991
                'text' => $postInfo['post_text'],
992
                'author' => api_get_person_name($postInfo['firstname'], $postInfo['lastname']),
993
                'date' => api_convert_and_format_date($postInfo['post_date'], DATE_TIME_FORMAT_LONG_24H),
994
                'parentId' => $postInfo['post_parent_id'],
995
                'attachments' => getAttachedFiles(
996
                    $forumId,
997
                    $threadId,
998
                    $postInfo['iid'],
999
                    0,
1000
                    $this->course->getId()
1001
                ),
1002
            ];
1003
        }
1004
1005
        return $thread;
1006
    }
1007
1008
    public function getCourseLinks(): array
1009
    {
1010
        Event::event_access_tool(TOOL_LINK);
1011
1012
        $courseId = $this->course->getId();
1013
        $sessionId = $this->session ? $this->session->getId() : 0;
1014
1015
        $webCodePath = api_get_path(WEB_CODE_PATH);
1016
        $cidReq = api_get_cidreq();
1017
1018
        $categories = array_merge(
1019
            [
1020
                [
1021
                    'iid' => 0,
1022
                    'c_id' => $courseId,
1023
                    'id' => 0,
1024
                    'category_title' => get_lang('NoCategory'),
1025
                    'description' => '',
1026
                    'display_order' => 0,
1027
                    'session_id' => $sessionId,
1028
                    'visibility' => 1,
1029
                ],
1030
            ],
1031
            Link::getLinkCategories($courseId, $sessionId)
1032
        );
1033
1034
        $categories = array_filter(
1035
            $categories,
1036
            function (array $category) {
1037
                return $category['visibility'] != 0;
1038
            }
1039
        );
1040
1041
        return array_map(
1042
            function (array $category) use ($webCodePath, $cidReq, $courseId, $sessionId) {
1043
                $links = array_filter(
1044
                    Link::getLinksPerCategory($category['iid'], $courseId, $sessionId),
1045
                    function (array $link) {
1046
                        return $link['visibility'] != 0;
1047
                    }
1048
                );
1049
1050
                $links = array_map(
1051
                    function (array $link) use ($webCodePath, $cidReq) {
1052
                        return [
1053
                            'id' => (int) $link['id'],
1054
                            'title' => Security::remove_XSS($link['title']),
1055
                            'description' => Security::remove_XSS($link['description']),
1056
                            'visibility' => (int) $link['visibility'],
1057
                            'url' => $webCodePath."link/link_goto.php?$cidReq&link_id=".$link['id'],
1058
                        ];
1059
                    },
1060
                    $links
1061
                );
1062
1063
                return [
1064
                    'id' => (int) $category['iid'],
1065
                    'title' => Security::remove_XSS($category['category_title']),
1066
                    'description' => Security::remove_XSS($category['description']),
1067
                    'visibility' => (int) $category['visibility'],
1068
                    'links' => $links,
1069
                ];
1070
            },
1071
            $categories
1072
        );
1073
    }
1074
1075
    /**
1076
     * It gets the courses and visible tests of a user by dates.
1077
     *
1078
     * @throws Exception
1079
     */
1080
    public function getUserCoursesByDates(int $userId, string $startDate, string $endDate): array
1081
    {
1082
        self::protectAdminEndpoint();
1083
        $userCourses = CourseManager::get_courses_list_by_user_id($userId);
1084
        $courses = [];
1085
        if (!empty($userCourses)) {
1086
            foreach ($userCourses as $course) {
1087
                $courseCode = $course['code'];
1088
                $courseId = $course['real_id'];
1089
                $exercises = Exercise::exerciseGrid(
1090
                    0,
1091
                    '',
1092
                    0,
1093
                    $courseId,
1094
                    0,
1095
                    true,
1096
                    0,
1097
                    0,
1098
                    0,
1099
                    null,
1100
                    false,
1101
                    false
1102
                );
1103
                $trackExercises = Tracking::getUserTrackExerciseByDates(
1104
                    $userId,
1105
                    $courseId,
1106
                    $startDate,
1107
                    $endDate
1108
                );
1109
                $takenExercises = [];
1110
                if (!empty($trackExercises)) {
1111
                    $totalSore = 0;
1112
                    foreach ($trackExercises as $track) {
1113
                        $takenExercises[] = $track['title'];
1114
                        $totalSore += $track['score'];
1115
                    }
1116
                    $avgScore = round($totalSore / count($trackExercises));
1117
                    $takenExercises['avg_score'] = $avgScore;
1118
                }
1119
                $courses[] = [
1120
                    'course_code' => $courseCode,
1121
                    'course_title' => $course['title'],
1122
                    'visible_tests' => $exercises,
1123
                    'taken_tests' => $takenExercises,
1124
                ];
1125
            }
1126
        }
1127
1128
        return $courses;
1129
    }
1130
1131
    /**
1132
     * Get the list of courses from extra field included count of visible exercises.
1133
     *
1134
     * @throws Exception
1135
     */
1136
    public function getCoursesByExtraField(string $fieldName, string $fieldValue): array
1137
    {
1138
        self::protectAdminEndpoint();
1139
        $extraField = new ExtraField('course');
1140
        $extraFieldInfo = $extraField->get_handler_field_info_by_field_variable($fieldName);
1141
1142
        if (empty($extraFieldInfo)) {
1143
            throw new Exception("$fieldName not found");
1144
        }
1145
1146
        $extraFieldValue = new ExtraFieldValue('course');
1147
        $items = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
1148
            $fieldName,
1149
            $fieldValue,
1150
            false,
1151
            false,
1152
            true
1153
        );
1154
1155
        $courses = [];
1156
        foreach ($items as $item) {
1157
            $courseId = $item['item_id'];
1158
            $courses[$courseId] = api_get_course_info_by_id($courseId);
1159
            $exercises = Exercise::exerciseGrid(
1160
                0,
1161
                '',
1162
                0,
1163
                $courseId,
1164
                0,
1165
                true,
1166
                0,
1167
                0,
1168
                0,
1169
                null,
1170
                false,
1171
                false
1172
            );
1173
            $courses[$courseId]['count_visible_tests'] = count($exercises);
1174
        }
1175
1176
        return $courses;
1177
    }
1178
1179
    /**
1180
     * Get the list of users from extra field.
1181
     *
1182
     * @param string $fieldName  The name of the extra_field (as in extra_field.variable) we want to filter on.
1183
     * @param string $fieldValue The value of the extra_field we want to filter on. If a user doesn't have the given extra_field set to that value, it will not be returned.
1184
     * @param int    $active     Additional filter. If 1, only return active users. Otherwise, return them all.
1185
     *
1186
     * @throws Exception
1187
     */
1188
    public function getUsersProfilesByExtraField(string $fieldName, string $fieldValue, int $active = 0): array
1189
    {
1190
        self::protectAdminEndpoint();
1191
        $users = [];
1192
        $extraValues = UserManager::get_extra_user_data_by_value(
1193
            $fieldName,
1194
            $fieldValue
1195
        );
1196
        if (!empty($extraValues)) {
1197
            foreach ($extraValues as $value) {
1198
                $userId = (int) $value;
1199
                $user = api_get_user_entity($userId);
1200
                if ($active && !$user->getActive()) {
1201
                    // If this user is not active, and we only asked for active users, skip to next user.
1202
                    continue;
1203
                }
1204
                $pictureInfo = UserManager::get_user_picture_path_by_id($user->getId(), 'web');
1205
                $users[$userId] = [
1206
                    'pictureUri' => $pictureInfo['dir'].$pictureInfo['file'],
1207
                    'id' => $userId,
1208
                    'status' => $user->getStatus(),
1209
                    'fullName' => UserManager::formatUserFullName($user),
1210
                    'username' => $user->getUsername(),
1211
                    'officialCode' => $user->getOfficialCode(),
1212
                    'phone' => $user->getPhone(),
1213
                    //'extra' => [],
1214
                ];
1215
            }
1216
        }
1217
1218
        return $users;
1219
    }
1220
1221
    /**
1222
     * Get one's own profile.
1223
     */
1224
    public function getUserProfile(): array
1225
    {
1226
        $pictureInfo = UserManager::get_user_picture_path_by_id($this->user->getId(), 'web');
1227
1228
        $result = [
1229
            'pictureUri' => $pictureInfo['dir'].$pictureInfo['file'],
1230
            'id' => $this->user->getId(),
1231
            'status' => $this->user->getStatus(),
1232
            'fullName' => UserManager::formatUserFullName($this->user),
1233
            'username' => $this->user->getUsername(),
1234
            'officialCode' => $this->user->getOfficialCode(),
1235
            'phone' => $this->user->getPhone(),
1236
            'extra' => [],
1237
        ];
1238
1239
        $fieldValue = new ExtraFieldValue('user');
1240
        $extraInfo = $fieldValue->getAllValuesForAnItem($this->user->getId(), true);
1241
1242
        foreach ($extraInfo as $extra) {
1243
            /** @var ExtraFieldValues $extraValue */
1244
            $extraValue = $extra['value'];
1245
            $result['extra'][] = [
1246
                'title' => $extraValue->getField()->getDisplayText(true),
1247
                'value' => $extraValue->getValue(),
1248
            ];
1249
        }
1250
1251
        return $result;
1252
    }
1253
1254
    /**
1255
     * Get one's own (avg) progress in learning paths.
1256
     */
1257
    public function getCourseLpProgress(): array
1258
    {
1259
        $sessionId = $this->session ? $this->session->getId() : 0;
1260
        $userId = $this->user->getId();
1261
1262
        /*$sessionId = $this->session ? $this->session->getId() : 0;
1263
        $courseId = $this->course->getId();*/
1264
1265
        $result = Tracking::getCourseLpProgress($userId, $sessionId);
1266
1267
        return [$result];
1268
    }
1269
1270
    /**
1271
     * @throws Exception
1272
     */
1273
    public function getCourseLearnPaths(): array
1274
    {
1275
        Event::event_access_tool(TOOL_LEARNPATH);
1276
1277
        $sessionId = $this->session ? $this->session->getId() : 0;
1278
        $categoriesTempList = learnpath::getCategories($this->course->getId());
1279
1280
        $categoryNone = new CLpCategory();
1281
        $categoryNone->setId(0);
1282
        $categoryNone->setName(get_lang('WithOutCategory'));
1283
        $categoryNone->setPosition(0);
1284
1285
        $categories = array_merge([$categoryNone], $categoriesTempList);
1286
        $categoryData = [];
1287
1288
        /** @var CLpCategory $category */
1289
        foreach ($categories as $category) {
1290
            $learnPathList = new LearnpathList(
1291
                $this->user->getId(),
1292
                api_get_course_info($this->course->getCode()),
1293
                $sessionId,
1294
                null,
1295
                false,
1296
                $category->getId()
1297
            );
1298
1299
            $flatLpList = $learnPathList->get_flat_list();
1300
1301
            if (empty($flatLpList)) {
1302
                continue;
1303
            }
1304
1305
            $listData = [];
1306
1307
            foreach ($flatLpList as $lpId => $lpDetails) {
1308
                if ($lpDetails['lp_visibility'] == 0) {
1309
                    continue;
1310
                }
1311
1312
                if (!learnpath::is_lp_visible_for_student(
1313
                    $lpId,
1314
                    $this->user->getId(),
1315
                    api_get_course_info($this->course->getCode()),
1316
                    $sessionId
1317
                )) {
1318
                    continue;
1319
                }
1320
1321
                $timeLimits = false;
1322
1323
                // This is an old LP (from a migration 1.8.7) so we do nothing
1324
                if (empty($lpDetails['created_on']) && empty($lpDetails['modified_on'])) {
1325
                    $timeLimits = false;
1326
                }
1327
1328
                // Checking if expired_on is ON
1329
                if (!empty($lpDetails['expired_on'])) {
1330
                    $timeLimits = true;
1331
                }
1332
1333
                if ($timeLimits) {
1334
                    if (!empty($lpDetails['publicated_on']) && !empty($lpDetails['expired_on'])) {
1335
                        $startTime = api_strtotime($lpDetails['publicated_on'], 'UTC');
1336
                        $endTime = api_strtotime($lpDetails['expired_on'], 'UTC');
1337
                        $now = time();
1338
                        $isActiveTime = false;
1339
1340
                        if ($now > $startTime && $endTime > $now) {
1341
                            $isActiveTime = true;
1342
                        }
1343
1344
                        if (!$isActiveTime) {
1345
                            continue;
1346
                        }
1347
                    }
1348
                }
1349
1350
                $progress = learnpath::getProgress($lpId, $this->user->getId(), $this->course->getId(), $sessionId);
1351
1352
                $listData[] = [
1353
                    'id' => $lpId,
1354
                    'title' => Security::remove_XSS($lpDetails['lp_name']),
1355
                    'progress' => $progress,
1356
                    'url' => api_get_path(WEB_CODE_PATH).'webservices/api/v2.php?'.http_build_query(
1357
                        [
1358
                            'hash' => $this->encodeParams(
1359
                                [
1360
                                    'action' => 'course_learnpath',
1361
                                    'lp_id' => $lpId,
1362
                                    'course' => $this->course->getId(),
1363
                                    'session' => $sessionId,
1364
                                ]
1365
                            ),
1366
                        ]
1367
                    ),
1368
                ];
1369
            }
1370
1371
            if (empty($listData)) {
1372
                continue;
1373
            }
1374
1375
            $categoryData[] = [
1376
                'id' => $category->getId(),
1377
                'name' => $category->getName(),
1378
                'learnpaths' => $listData,
1379
            ];
1380
        }
1381
1382
        return $categoryData;
1383
    }
1384
1385
    /**
1386
     * Start login for a user. Then make a redirect to show the learnpath.
1387
     */
1388
    public function showLearningPath(int $lpId)
1389
    {
1390
        $loggedUser['user_id'] = $this->user->getId();
1391
        $loggedUser['status'] = $this->user->getStatus();
1392
        $loggedUser['uidReset'] = true;
1393
        $sessionId = $this->session ? $this->session->getId() : 0;
1394
1395
        ChamiloSession::write('_user', $loggedUser);
1396
        Login::init_user($this->user->getId(), true);
1397
1398
        $url = api_get_path(WEB_CODE_PATH).'lp/lp_controller.php?'.http_build_query(
1399
            [
1400
                'cidReq' => $this->course->getCode(),
1401
                'id_session' => $sessionId,
1402
                'gidReq' => 0,
1403
                'gradebook' => 0,
1404
                'origin' => '',
1405
                'action' => 'view',
1406
                'lp_id' => (int) $lpId,
1407
                'isStudentView' => 'true',
1408
            ]
1409
        );
1410
1411
        header("Location: $url");
1412
        exit;
1413
    }
1414
1415
    /**
1416
     * @param int $forumId
1417
     *
1418
     * @return array
1419
     */
1420
    public function saveForumPost(array $postValues, $forumId)
1421
    {
1422
        Event::event_access_tool(TOOL_FORUM);
1423
1424
        require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
1425
1426
        $forum = get_forums($forumId, $this->course->getCode());
1427
        store_reply($forum, $postValues, $this->course->getId(), $this->user->getId());
1428
1429
        return [
1430
            'registered' => true,
1431
        ];
1432
    }
1433
1434
    /**
1435
     * Get the list of sessions for current user.
1436
     *
1437
     * @return array the sessions list
1438
     */
1439
    public function getUserSessions()
1440
    {
1441
        $data = [];
1442
        $sessionsByCategory = UserManager::get_sessions_by_category($this->user->getId(), false);
1443
1444
        $webCodePath = api_get_path(WEB_CODE_PATH).'webservices/api/v2.php?';
1445
1446
        foreach ($sessionsByCategory as $category) {
1447
            $categorySessions = [];
1448
1449
            foreach ($category['sessions'] as $sessions) {
1450
                $sessionCourses = [];
1451
1452
                foreach ($sessions['courses'] as $course) {
1453
                    $courseInfo = api_get_course_info_by_id($course['real_id']);
1454
                    $teachers = SessionManager::getCoachesByCourseSessionToString(
1455
                        $sessions['session_id'],
1456
                        $course['real_id']
1457
                    );
1458
1459
                    $sessionCourses[] = [
1460
                        'id' => $courseInfo['real_id'],
1461
                        'title' => $courseInfo['title'],
1462
                        'code' => $courseInfo['code'],
1463
                        'directory' => $courseInfo['directory'],
1464
                        'pictureUrl' => $courseInfo['course_image_large'],
1465
                        'urlPicture' => $courseInfo['course_image_large'],
1466
                        'teachers' => $teachers,
1467
                        'url' => $webCodePath.http_build_query(
1468
                            [
1469
                                'action' => self::VIEW_COURSE_HOME,
1470
                                'api_key' => $this->apiKey,
1471
                                'username' => $this->user->getUsername(),
1472
                                'course' => $courseInfo['real_id'],
1473
                                'session' => $sessions['session_id'],
1474
                            ]
1475
                        ),
1476
                    ];
1477
                }
1478
1479
                $sessionBox = Display::getSessionTitleBox($sessions['session_id']);
1480
1481
                $categorySessions[] = [
1482
                    'name' => $sessionBox['title'],
1483
                    'id' => $sessions['session_id'],
1484
                    'date' => $sessionBox['dates'],
1485
                    'duration' => isset($sessionBox['duration']) ? $sessionBox['duration'] : null,
1486
                    'courses' => $sessionCourses,
1487
                ];
1488
            }
1489
1490
            $data[] = [
1491
                'id' => $category['session_category']['id'],
1492
                'name' => $category['session_category']['name'],
1493
                'sessions' => $categorySessions,
1494
            ];
1495
        }
1496
1497
        return $data;
1498
    }
1499
1500
    public function getUsersSubscribedToCourse()
1501
    {
1502
        $users = CourseManager::get_user_list_from_course_code($this->course->getCode());
1503
1504
        $userList = [];
1505
        foreach ($users as $user) {
1506
            $userList[] = [
1507
                'user_id' => $user['user_id'],
1508
                'username' => $user['username'],
1509
                'firstname' => $user['firstname'],
1510
                'lastname' => $user['lastname'],
1511
                'status_rel' => $user['status_rel'],
1512
            ];
1513
        }
1514
1515
        return $userList;
1516
    }
1517
1518
    /**
1519
     * @param string $subject
1520
     * @param string $text
1521
     *
1522
     * @return array
1523
     */
1524
    public function saveUserMessage($subject, $text, array $receivers)
1525
    {
1526
        foreach ($receivers as $userId) {
1527
            MessageManager::send_message($userId, $subject, $text);
1528
        }
1529
1530
        return [
1531
            'sent' => true,
1532
        ];
1533
    }
1534
1535
    /**
1536
     * @param string $search
1537
     *
1538
     * @return array
1539
     */
1540
    public function getMessageUsers($search)
1541
    {
1542
        $repo = UserManager::getRepository();
1543
1544
        $users = $repo->findUsersToSendMessage($this->user->getId(), $search);
1545
        $showEmail = api_get_setting('show_email_addresses') === 'true';
1546
        $data = [];
1547
1548
        /** @var User $user */
1549
        foreach ($users as $user) {
1550
            $userName = UserManager::formatUserFullName($user);
1551
1552
            if ($showEmail) {
1553
                $userName .= " ({$user->getEmail()})";
1554
            }
1555
1556
            $data[] = [
1557
                'id' => $user->getId(),
1558
                'name' => $userName,
1559
            ];
1560
        }
1561
1562
        return $data;
1563
    }
1564
1565
    /**
1566
     * @param string $title
1567
     * @param string $text
1568
     *
1569
     * @return array
1570
     */
1571
    public function saveCourseNotebook($title, $text)
1572
    {
1573
        Event::event_access_tool(TOOL_NOTEBOOK);
1574
1575
        $values = ['note_title' => $title, 'note_comment' => $text];
1576
        $sessionId = $this->session ? $this->session->getId() : 0;
1577
1578
        $noteBookId = NotebookManager::save_note(
1579
            $values,
1580
            $this->user->getId(),
1581
            $this->course->getId(),
1582
            $sessionId
1583
        );
1584
1585
        return [
1586
            'registered' => $noteBookId,
1587
        ];
1588
    }
1589
1590
    /**
1591
     * @param int $forumId
1592
     *
1593
     * @return array
1594
     */
1595
    public function saveForumThread(array $values, $forumId)
1596
    {
1597
        Event::event_access_tool(TOOL_FORUM);
1598
1599
        require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
1600
1601
        $sessionId = $this->session ? $this->session->getId() : 0;
1602
        $forum = get_forums($forumId, $this->course->getCode(), true, $sessionId);
1603
        $courseInfo = api_get_course_info($this->course->getCode());
1604
        $thread = store_thread($forum, $values, $courseInfo, false, $this->user->getId(), $sessionId);
1605
1606
        return [
1607
            'registered' => $thread->getIid(),
1608
        ];
1609
    }
1610
1611
    /**
1612
     * Returns an array of users with id, firstname, lastname, email and username.
1613
     *
1614
     * @param array $params An array of parameters to filter the results (currently supports 'status', 'id_campus' and 'extra_fields')
1615
     *
1616
     * @throws Exception
1617
     */
1618
    public function getUsersCampus(array $params): array
1619
    {
1620
        self::protectAdminEndpoint();
1621
1622
        if ('*' === $params['status']) {
1623
            $conditions = [];
1624
        } else {
1625
            $conditions = [
1626
                'status' => $params['status'],
1627
            ];
1628
        }
1629
        $idCampus = !empty($params['id_campus']) ?? 1;
1630
        $fields = [];
1631
        if (!empty($params['extra_fields'])) {
1632
            //extra_fields must be sent as a comma-separated list of extra_field variable names
1633
            $fields = explode(',', $params['extra_fields']);
1634
        }
1635
        $users = UserManager::get_user_list($conditions, ['firstname'], false, false, $idCampus);
1636
        $list = [];
1637
        foreach ($users as $item) {
1638
            $listTemp = [
1639
                'id' => $item['user_id'],
1640
                'firstname' => $item['firstname'],
1641
                'lastname' => $item['lastname'],
1642
                'email' => $item['email'],
1643
                'username' => $item['username'],
1644
                'active' => $item['active'],
1645
            ];
1646
            foreach ($fields as $field) {
1647
                $field = trim($field);
1648
                $value = UserManager::get_extra_user_data_by_field($item['user_id'], $field);
1649
                if (!empty($value)) {
1650
                    $listTemp[$field] = $value[$field];
1651
                }
1652
            }
1653
            $list[] = $listTemp;
1654
        }
1655
1656
        return $list;
1657
    }
1658
1659
    /**
1660
     * Returns a list of courses in the given URL. If no URL is provided, we assume we are not in a multi-URL setup and
1661
     * return all the courses.
1662
     */
1663
    public function getCoursesCampus(int $campusId = 0): array
1664
    {
1665
        return CourseManager::get_courses_list(
1666
            0, //offset
1667
            0, //howMany
1668
            1, //$orderby = 1
1669
            'ASC',
1670
            -1, //visibility
1671
            null,
1672
            empty($campusId) ? null : $campusId, //$urlId
1673
            true //AlsoSearchCode
1674
        );
1675
    }
1676
1677
    /**
1678
     * Returns a list of sessions in the given URL. If no URL is provided, we assume we are not in a multi-URL setup and
1679
     * return all the sessions.
1680
     *
1681
     * @param int $campusId Optional
1682
     *
1683
     * @throws Exception
1684
     */
1685
    public function getSessionsCampus(int $campusId = 0, bool $getExtraFields = false): array
1686
    {
1687
        self::protectAdminEndpoint();
1688
1689
        $list = SessionManager::get_sessions_list(
1690
            [],
1691
            [],
1692
            null,
1693
            null,
1694
            $campusId
1695
        );
1696
        $shortList = [];
1697
        foreach ($list as $session) {
1698
            $bundle = [
1699
                'id' => $session['id'],
1700
                'name' => $session['name'],
1701
                'access_start_date' => $session['access_start_date'],
1702
                'access_end_date' => $session['access_end_date'],
1703
            ];
1704
            if ($getExtraFields) {
1705
                $extraFieldValues = new ExtraFieldValue('session');
1706
                $extraFields = $extraFieldValues->getAllValuesByItem($session['id']);
1707
                $bundle['extra_fields'] = $extraFields;
1708
            }
1709
            $shortList[] = $bundle;
1710
        }
1711
1712
        return $shortList;
1713
    }
1714
1715
    /**
1716
     * Returns an array of groups with id, group_type, name, description, visibility.
1717
     *
1718
     * @param array $params An array of parameters to filter the results (currently supports 'type')
1719
     *
1720
     * @throws Exception
1721
     */
1722
    public function getGroups(array $params): array
1723
    {
1724
        self::protectAdminEndpoint();
1725
1726
        if ('*' === $params['type']) {
1727
            $conditions = [];
1728
        } else {
1729
            $conditions = ['where' => ['group_type = ?' => $params['type']]];
1730
        }
1731
        $userGroup = new UserGroup();
1732
        $groups = $userGroup->getDataToExport($conditions);
1733
        $list = [];
1734
        /** @var \Chamilo\UserBundle\Entity\Group $item */
1735
        foreach ($groups as $item) {
1736
            $listTemp = [
1737
                'id' => $item['id'],
1738
                'name' => $item['name'],
1739
                'description' => $item['description'],
1740
                'visibility' => $item['visibility'],
1741
                'type' => $item['group_type'],
1742
            ];
1743
            if (in_array($item['group_type'], [0, 1])) {
1744
                $listTemp['type_name'] = ($item['group_type'] == 0) ? 'class' : 'social';
1745
            }
1746
            if (in_array($item['visibility'], [1, 2])) {
1747
                $listTemp['visibility_name'] = ($item['visibility'] == 1) ? 'open' : 'closed';
1748
            }
1749
            $list[] = $listTemp;
1750
        }
1751
1752
        return $list;
1753
    }
1754
1755
    /**
1756
     * @throws Exception
1757
     */
1758
    public function addSession(array $params): array
1759
    {
1760
        self::protectAdminEndpoint();
1761
1762
        $name = $params['name'];
1763
        $coach_username = (int) $params['coach_username'];
1764
        $startDate = $params['access_start_date'];
1765
        $endDate = $params['access_end_date'];
1766
        $displayStartDate = $startDate;
1767
        $displayEndDate = $endDate;
1768
        $description = $params['description'];
1769
        $idUrlCampus = $params['id_campus'];
1770
        $extraFields = isset($params['extra']) ? $params['extra'] : [];
1771
1772
        $return = SessionManager::create_session(
1773
            $name,
1774
            $startDate,
1775
            $endDate,
1776
            $displayStartDate,
1777
            $displayEndDate,
1778
            null,
1779
            null,
1780
            $coach_username,
1781
            null,
1782
            1,
1783
            false,
1784
            null,
1785
            $description,
1786
            1,
1787
            $extraFields,
1788
            null,
1789
            false,
1790
            $idUrlCampus
1791
        );
1792
1793
        if ($return) {
1794
            $out = [
1795
                'status' => true,
1796
                'message' => get_lang('ANewSessionWasCreated'),
1797
                'id_session' => $return,
1798
            ];
1799
        } else {
1800
            $out = [
1801
                'status' => false,
1802
                'message' => get_lang('ErrorOccurred'),
1803
            ];
1804
        }
1805
1806
        return $out;
1807
    }
1808
1809
    public function addCourse(array $courseParam): array
1810
    {
1811
        self::protectAdminEndpoint();
1812
1813
        $idCampus = isset($courseParam['id_campus']) ? $courseParam['id_campus'] : 1;
1814
        $title = isset($courseParam['title']) ? $courseParam['title'] : '';
1815
        $wantedCode = isset($courseParam['wanted_code']) ? $courseParam['wanted_code'] : null;
1816
        $diskQuota = isset($courseParam['disk_quota']) ? $courseParam['disk_quota'] : '100';
1817
        $visibility = isset($courseParam['visibility']) ? (int) $courseParam['visibility'] : null;
1818
        $removeCampusId = $courseParam['remove_campus_id_from_wanted_code'] ?? 0;
1819
        $language = $courseParam['language'] ?? '';
1820
1821
        if (isset($courseParam['visibility'])) {
1822
            if ($courseParam['visibility'] &&
1823
                $courseParam['visibility'] >= 0 &&
1824
                $courseParam['visibility'] <= 3
1825
            ) {
1826
                $visibility = (int) $courseParam['visibility'];
1827
            }
1828
        }
1829
1830
        $params = [];
1831
        $params['title'] = $title;
1832
        $params['wanted_code'] = 'CAMPUS_'.$idCampus.'_'.$wantedCode;
1833
        if (1 === (int) $removeCampusId) {
1834
            $params['wanted_code'] = $wantedCode;
1835
        }
1836
        $params['user_id'] = $this->user->getId();
1837
        $params['visibility'] = $visibility;
1838
        $params['disk_quota'] = $diskQuota;
1839
        $params['course_language'] = $language;
1840
1841
        foreach ($courseParam as $key => $value) {
1842
            if (substr($key, 0, 6) === 'extra_') { //an extra field
1843
                $params[$key] = $value;
1844
            }
1845
        }
1846
1847
        $courseInfo = CourseManager::create_course($params, $params['user_id'], $idCampus);
1848
        $results = [];
1849
        if (!empty($courseInfo)) {
1850
            $results['status'] = true;
1851
            $results['id'] = $courseInfo['real_id'];
1852
            $results['code_course'] = $courseInfo['code'];
1853
            $results['title_course'] = $courseInfo['title'];
1854
            $extraFieldValues = new ExtraFieldValue('course');
1855
            $extraFields = $extraFieldValues->getAllValuesByItem($courseInfo['real_id']);
1856
            $results['extra_fields'] = $extraFields;
1857
            $results['message'] = sprintf(get_lang('CourseXAdded'), $courseInfo['code']);
1858
        } else {
1859
            $results['status'] = false;
1860
            $results['message'] = get_lang('CourseCreationFailed');
1861
        }
1862
1863
        return $results;
1864
    }
1865
1866
    /**
1867
     * @param $userParam
1868
     *
1869
     * @throws Exception
1870
     */
1871
    public function addUser($userParam): array
1872
    {
1873
        self::protectAdminEndpoint();
1874
1875
        $firstName = $userParam['firstname'];
1876
        $lastName = $userParam['lastname'];
1877
        $status = $userParam['status'];
1878
        $email = $userParam['email'];
1879
        $loginName = $userParam['loginname'];
1880
        $password = $userParam['password'];
1881
1882
        $official_code = '';
1883
        $language = '';
1884
        $phone = '';
1885
        $picture_uri = '';
1886
        $auth_source = $userParam['auth_source'] ?? PLATFORM_AUTH_SOURCE;
1887
        $expiration_date = '';
1888
        $active = 1;
1889
        $hr_dept_id = 0;
1890
        $original_user_id_name = $userParam['original_user_id_name'];
1891
        $original_user_id_value = $userParam['original_user_id_value'];
1892
        $sendMail = (empty($userParam['send_mail']) ? false : true);
1893
1894
        $extra_list = isset($userParam['extra']) ? $userParam['extra'] : [];
1895
        if (isset($userParam['language'])) {
1896
            $language = $userParam['language'];
1897
        }
1898
        if (isset($userParam['phone'])) {
1899
            $phone = $userParam['phone'];
1900
        }
1901
        if (isset($userParam['expiration_date'])) {
1902
            $expiration_date = $userParam['expiration_date'];
1903
        }
1904
1905
        // If check_email_duplicates was set, trigger exception (i.e. do not create) if the e-mail is already used
1906
        if ($userParam['check_email_duplicates']) {
1907
            if (!empty($email)) {
1908
                $userFromEmail = api_get_user_info_from_email($email);
1909
                if (!empty($userFromEmail)) {
1910
                    throw new Exception(get_lang('EmailUsedTwice'));
1911
                }
1912
            }
1913
        }
1914
1915
        // Default language.
1916
        if (empty($language)) {
1917
            $language = api_get_setting('platformLanguage');
1918
        }
1919
1920
        // First check whether the login already exists.
1921
        if (!UserManager::is_username_available($loginName)) {
1922
            throw new Exception(get_lang('UserNameNotAvailable'));
1923
        }
1924
1925
        $userId = UserManager::create_user(
1926
            $firstName,
1927
            $lastName,
1928
            $status,
1929
            $email,
1930
            $loginName,
1931
            $password,
1932
            $official_code,
1933
            $language,
1934
            $phone,
1935
            $picture_uri,
1936
            $auth_source,
1937
            $expiration_date,
1938
            $active,
1939
            $hr_dept_id,
1940
            [],
1941
            '',
1942
            $sendMail
1943
        );
1944
1945
        if (empty($userId)) {
1946
            throw new Exception(get_lang('UserNotRegistered'));
1947
        }
1948
1949
        if (api_is_multiple_url_enabled()) {
1950
            if (api_get_current_access_url_id() != -1) {
1951
                UrlManager::add_user_to_url(
1952
                    $userId,
1953
                    api_get_current_access_url_id()
1954
                );
1955
            } else {
1956
                UrlManager::add_user_to_url($userId, 1);
1957
            }
1958
        } else {
1959
            // We add by default the access_url_user table with access_url_id = 1
1960
            UrlManager::add_user_to_url($userId, 1);
1961
        }
1962
1963
        // Save new field label into user_field table.
1964
        UserManager::create_extra_field(
1965
            $original_user_id_name,
1966
            1,
1967
            $original_user_id_name,
1968
            ''
1969
        );
1970
        // Save the external system's id into user_field_value table.
1971
        UserManager::update_extra_field_value(
1972
            $userId,
1973
            $original_user_id_name,
1974
            $original_user_id_value
1975
        );
1976
1977
        if (is_array($extra_list) && count($extra_list) > 0) {
1978
            foreach ($extra_list as $extra) {
1979
                $extra_field_name = $extra['field_name'];
1980
                $extra_field_value = $extra['field_value'];
1981
                // Save new field label into user_field table.
1982
                UserManager::create_extra_field(
1983
                    $extra_field_name,
1984
                    1,
1985
                    $extra_field_name,
1986
                    ''
1987
                );
1988
                // Save the external system's id into user_field_value table.
1989
                UserManager::update_extra_field_value(
1990
                    $userId,
1991
                    $extra_field_name,
1992
                    $extra_field_value
1993
                );
1994
            }
1995
        }
1996
1997
        return [$userId];
1998
    }
1999
2000
    /**
2001
     * @throws Exception
2002
     */
2003
    public function addUserGetApikey(array $userParams): array
2004
    {
2005
        list($userId) = $this->addUser($userParams);
2006
2007
        UserManager::add_api_key($userId, self::SERVICE_NAME);
2008
2009
        $apiKey = UserManager::get_api_keys($userId, self::SERVICE_NAME);
2010
2011
        return [
2012
            'id' => $userId,
2013
            'api_key' => current($apiKey),
2014
        ];
2015
    }
2016
2017
    /**
2018
     * @throws Exception
2019
     */
2020
    public function updateUserApiKey(int $userId, string $oldApiKey): array
2021
    {
2022
        if (!api_is_platform_admin() && $userId != $this->user->getId()) {
2023
            self::throwNotAllowedException();
2024
        }
2025
2026
        if (false === $currentApiKeys = UserManager::get_api_keys($userId, self::SERVICE_NAME)) {
2027
            self::throwNotAllowedException();
2028
        }
2029
2030
        if (current($currentApiKeys) !== $oldApiKey) {
2031
            self::throwNotAllowedException();
2032
        }
2033
2034
        UserManager::update_api_key($userId, self::SERVICE_NAME);
2035
2036
        $apiKey = UserManager::get_api_keys($userId, self::SERVICE_NAME);
2037
2038
        return [
2039
            'api_key' => current($apiKey),
2040
        ];
2041
    }
2042
2043
    /**
2044
     * Subscribe User to Course.
2045
     *
2046
     * @throws Exception
2047
     */
2048
    public function subscribeUserToCourse(array $params): array
2049
    {
2050
        $course_id = $params['course_id'];
2051
        $course_code = $params['course_code'];
2052
        $user_id = $params['user_id'];
2053
        $status = $params['status'] ?? STUDENT;
2054
2055
        if (!api_is_platform_admin() && $user_id != $this->user->getId()) {
2056
            self::throwNotAllowedException();
2057
        }
2058
2059
        if (!$course_id && !$course_code) {
2060
            return [false];
2061
        }
2062
        if (!$course_code) {
2063
            $course_code = CourseManager::get_course_code_from_course_id($course_id);
2064
        }
2065
2066
        if (CourseManager::subscribeUser($user_id, $course_code, $status, 0, 0, false)) {
2067
            return [true];
2068
        }
2069
2070
        return [false];
2071
    }
2072
2073
    /**
2074
     * @throws Exception
2075
     */
2076
    public function subscribeUserToCoursePassword($courseCode, $password)
2077
    {
2078
        $courseInfo = api_get_course_info($courseCode);
2079
2080
        if (empty($courseInfo)) {
2081
            throw new Exception(get_lang('NoCourse'));
2082
        }
2083
2084
        if (sha1($password) === $courseInfo['registration_code']) {
2085
            CourseManager::processAutoSubscribeToCourse($courseCode);
2086
2087
            return;
2088
        }
2089
2090
        throw new Exception(get_lang('CourseRegistrationCodeIncorrect'));
2091
    }
2092
2093
    /**
2094
     * @throws Exception
2095
     */
2096
    public function unSubscribeUserToCourse(array $params): array
2097
    {
2098
        $courseId = $params['course_id'];
2099
        $courseCode = $params['course_code'];
2100
        $userId = $params['user_id'];
2101
2102
        if (!api_is_platform_admin() && $userId != $this->user->getId()) {
2103
            self::throwNotAllowedException();
2104
        }
2105
2106
        if (!$courseId && !$courseCode) {
2107
            return [false];
2108
        }
2109
2110
        if (!$courseCode) {
2111
            $courseCode = CourseManager::get_course_code_from_course_id($courseId);
2112
        }
2113
2114
        if (CourseManager::unsubscribe_user($userId, $courseCode)) {
2115
            return [true];
2116
        }
2117
2118
        return [false];
2119
    }
2120
2121
    public function deleteUserMessage($messageId, $messageType)
2122
    {
2123
        if ($messageType === 'sent') {
2124
            return MessageManager::delete_message_by_user_sender($this->user->getId(), $messageId);
2125
        } else {
2126
            return MessageManager::delete_message_by_user_receiver($this->user->getId(), $messageId);
2127
        }
2128
    }
2129
2130
    /**
2131
     * Set a given message as already read.
2132
     *
2133
     * @param $messageId
2134
     */
2135
    public function setMessageRead(int $messageId)
2136
    {
2137
        // MESSAGE_STATUS_NEW is also used for messages that have been "read"
2138
        MessageManager::update_message_status($this->user->getId(), $messageId, MESSAGE_STATUS_NEW);
2139
    }
2140
2141
    /**
2142
     * Add a group.
2143
     *
2144
     * @param array Params
2145
     */
2146
    public function createGroup($params)
2147
    {
2148
        self::protectAdminEndpoint();
2149
2150
        $name = $params['name'];
2151
        $description = $params['description'];
2152
    }
2153
2154
    /**
2155
     * Add Campus Virtual.
2156
     *
2157
     * @param array Params Campus
2158
     *
2159
     * @return array
2160
     */
2161
    public function createCampusURL($params)
2162
    {
2163
        $urlCampus = Security::remove_XSS($params['url']);
2164
        $description = Security::remove_XSS($params['description']);
2165
2166
        $active = isset($params['active']) ? intval($params['active']) : 0;
2167
        $num = UrlManager::url_exist($urlCampus);
2168
        if ($num == 0) {
2169
            // checking url
2170
            if (substr($urlCampus, strlen($urlCampus) - 1, strlen($urlCampus)) == '/') {
2171
                $idCampus = UrlManager::add($urlCampus, $description, $active, true);
2172
            } else {
2173
                //create
2174
                $idCampus = UrlManager::add($urlCampus.'/', $description, $active, true);
2175
            }
2176
2177
            return [
2178
                'status' => true,
2179
                'id_campus' => $idCampus,
2180
            ];
2181
        }
2182
2183
        return [
2184
            'status' => false,
2185
            'id_campus' => 0,
2186
        ];
2187
    }
2188
2189
    /**
2190
     * Edit Campus Virtual.
2191
     *
2192
     * @param array Params Campus
2193
     *
2194
     * @return array
2195
     */
2196
    public function editCampusURL($params)
2197
    {
2198
        $urlCampus = Security::remove_XSS($params['url']);
2199
        $description = Security::remove_XSS($params['description']);
2200
2201
        $active = isset($params['active']) ? intval($params['active']) : 0;
2202
        $url_id = isset($params['id']) ? intval($params['id']) : 0;
2203
2204
        if (!empty($url_id)) {
2205
            //we can't change the status of the url with id=1
2206
            if ($url_id == 1) {
2207
                $active = 1;
2208
            }
2209
            //checking url
2210
            if (substr($urlCampus, strlen($urlCampus) - 1, strlen($urlCampus)) == '/') {
2211
                UrlManager::update($url_id, $urlCampus, $description, $active);
2212
            } else {
2213
                UrlManager::update($url_id, $urlCampus.'/', $description, $active);
2214
            }
2215
2216
            return [true];
2217
        }
2218
2219
        return [false];
2220
    }
2221
2222
    /**
2223
     * Delete Campus Virtual.
2224
     *
2225
     * @param array Params Campus
2226
     *
2227
     * @return array
2228
     */
2229
    public function deleteCampusURL($params)
2230
    {
2231
        $url_id = isset($params['id']) ? intval($params['id']) : 0;
2232
2233
        $result = UrlManager::delete($url_id);
2234
        if ($result) {
2235
            return [
2236
                'status' => true,
2237
                'message' => get_lang('URLDeleted'),
2238
            ];
2239
        } else {
2240
            return [
2241
                'status' => false,
2242
                'message' => get_lang('Error'),
2243
            ];
2244
        }
2245
    }
2246
2247
    /**
2248
     * @throws Exception
2249
     */
2250
    public function addCoursesSession(array $params): array
2251
    {
2252
        self::protectAdminEndpoint();
2253
2254
        $sessionId = $params['id_session'];
2255
        $courseList = $params['list_courses'];
2256
        $importAssignments = isset($params['import_assignments']) && 1 === (int) $params['import_assignments'];
2257
2258
        $result = SessionManager::add_courses_to_session(
2259
            $sessionId,
2260
            $courseList,
2261
            true,
2262
            false,
2263
            false,
2264
            $importAssignments
2265
        );
2266
2267
        if ($result) {
2268
            return [
2269
                'status' => $result,
2270
                'message' => get_lang('Updated'),
2271
            ];
2272
        }
2273
2274
        return [
2275
            'status' => $result,
2276
            'message' => get_lang('ErrorOccurred'),
2277
        ];
2278
    }
2279
2280
    /**
2281
     * Simple legacy shortcut to subscribeUsersToSession.
2282
     *
2283
     * @throws Exception
2284
     */
2285
    public function addUsersSession(array $params): array
2286
    {
2287
        return self::subscribeUsersToSession($params);
2288
    }
2289
2290
    /**
2291
     * Subscribe a list of users to the given session.
2292
     *
2293
     * @param array $params Containing 'id_session' and 'list_users' entries
2294
     *
2295
     * @throws Exception
2296
     */
2297
    public function subscribeUsersToSession(array $params): array
2298
    {
2299
        $sessionId = $params['id_session'];
2300
        $userList = $params['list_users'];
2301
2302
        if (!is_array($userList)) {
2303
            $userList = [];
2304
        }
2305
2306
        if (!api_is_platform_admin() && !in_array($this->user->getId(), $userList)) {
2307
            self::throwNotAllowedException();
2308
        }
2309
2310
        SessionManager::subscribeUsersToSession(
2311
            $sessionId,
2312
            $userList,
2313
            null,
2314
            false
2315
        );
2316
2317
        return [
2318
            'status' => true,
2319
            'message' => get_lang('UsersAdded'),
2320
        ];
2321
    }
2322
2323
    /**
2324
     * Unsubscribe a given list of users from the given session.
2325
     *
2326
     * @throws Exception
2327
     */
2328
    public function unsubscribeUsersFromSession(array $params): array
2329
    {
2330
        self::protectAdminEndpoint();
2331
2332
        $sessionId = $params['id_session'];
2333
        $userList = $params['list_users'];
2334
2335
        if (!is_array($userList)) {
2336
            $userList = [];
2337
        }
2338
2339
        if (!api_is_platform_admin() && !in_array($this->user->getId(), $userList)) {
2340
            self::throwNotAllowedException();
2341
        }
2342
2343
        foreach ($userList as $userId) {
2344
            SessionManager::unsubscribe_user_from_session(
2345
                $sessionId,
2346
                $userId
2347
            );
2348
        }
2349
2350
        return [
2351
            'status' => true,
2352
            'message' => get_lang('UserUnsubscribed'),
2353
        ];
2354
    }
2355
2356
    /**
2357
     * Creates a session from a model session.
2358
     *
2359
     * @throws Exception
2360
     */
2361
    public function createSessionFromModel(HttpRequest $request): int
2362
    {
2363
        self::protectAdminEndpoint();
2364
2365
        $modelSessionId = $request->request->getInt('modelSessionId');
2366
        $sessionName = $request->request->get('sessionName');
2367
        $startDate = $request->request->get('startDate');
2368
        $endDate = $request->request->get('endDate');
2369
        $extraFields = $request->request->get('extraFields', []);
2370
        $duplicateAgendaContent = $request->request->getBoolean('duplicateAgendaContent');
2371
2372
        if (empty($modelSessionId) || empty($sessionName) || empty($startDate) || empty($endDate)) {
2373
            throw new Exception(get_lang('NoData'));
2374
        }
2375
2376
        if (!SessionManager::isValidId($modelSessionId)) {
2377
            throw new Exception(get_lang('ModelSessionDoesNotExist'));
2378
        }
2379
2380
        $modelSession = SessionManager::fetch($modelSessionId);
2381
2382
        $modelSession['accessUrlId'] = 1;
2383
        if (api_is_multiple_url_enabled()) {
2384
            if (api_get_current_access_url_id() != -1) {
2385
                $modelSession['accessUrlId'] = api_get_current_access_url_id();
2386
            }
2387
        }
2388
2389
        $newSessionId = SessionManager::create_session(
2390
            $sessionName,
2391
            $startDate,
2392
            $endDate,
2393
            $startDate,
2394
            $endDate,
2395
            $startDate,
2396
            $endDate,
2397
            $modelSession['id_coach'],
2398
            $modelSession['session_category_id'],
2399
            $modelSession['visibility'],
2400
            false,
2401
            $modelSession['duration'],
2402
            $modelSession['description'],
2403
            $modelSession['show_description'],
2404
            $extraFields,
2405
            $modelSession['session_admin_id'],
2406
            $modelSession['send_subscription_notification'],
2407
            $modelSession['accessUrlId']
2408
        );
2409
2410
        if (empty($newSessionId)) {
2411
            throw new Exception(get_lang('SessionNotRegistered'));
2412
        }
2413
2414
        if (is_string($newSessionId)) {
2415
            throw new Exception($newSessionId);
2416
        }
2417
2418
        $promotionId = $modelSession['promotion_id'];
2419
        if ($promotionId) {
2420
            $sessionList = array_keys(SessionManager::get_all_sessions_by_promotion($promotionId));
2421
            $sessionList[] = $newSessionId;
2422
            SessionManager::subscribe_sessions_to_promotion($modelSession['promotion_id'], $sessionList);
2423
        }
2424
2425
        $modelExtraFields = [];
2426
        $fields = SessionManager::getFilteredExtraFields($modelSessionId);
2427
        if (is_array($fields) and !empty($fields)) {
2428
            foreach ($fields as $field) {
2429
                $modelExtraFields[$field['variable']] = $field['value'];
2430
            }
2431
        }
2432
        $allExtraFields = array_merge($modelExtraFields, $extraFields);
2433
        foreach ($allExtraFields as $name => $value) {
2434
            // SessionManager::update_session_extra_field_value returns false when no row is changed,
2435
            // which can happen since extra field values are initialized by SessionManager::create_session
2436
            // therefore we do not throw an exception when false is returned
2437
            SessionManager::update_session_extra_field_value($newSessionId, $name, $value);
2438
        }
2439
2440
        $courseList = array_keys(SessionManager::get_course_list_by_session_id($modelSessionId));
2441
        if (is_array($courseList)
2442
            && !empty($courseList)
2443
            && !SessionManager::add_courses_to_session($newSessionId, $courseList)) {
2444
            throw new Exception(get_lang('CoursesNotAddedToSession'));
2445
        }
2446
2447
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
2448
        $courseListOrdered = SessionManager::get_course_list_by_session_id($modelSessionId, null, 'position');
2449
        $count = 0;
2450
        foreach ($courseListOrdered as $course) {
2451
            if ($course['position'] == '') {
2452
                $course['position'] = $count;
2453
            }
2454
            // Saving order.
2455
            $sql = "UPDATE $table SET position = ".$course['position']."
2456
                    WHERE session_id = $newSessionId AND c_id = '".$course['real_id']."'";
2457
            Database::query($sql);
2458
            $count++;
2459
        }
2460
2461
        if ($duplicateAgendaContent) {
2462
            foreach ($courseList as $courseId) {
2463
                SessionManager::importAgendaFromSessionModel($modelSessionId, $newSessionId, $courseId);
2464
            }
2465
        }
2466
2467
        if (api_is_multiple_url_enabled()) {
2468
            if (api_get_current_access_url_id() != -1) {
2469
                UrlManager::add_session_to_url(
2470
                    $newSessionId,
2471
                    api_get_current_access_url_id()
2472
                );
2473
            } else {
2474
                UrlManager::add_session_to_url($newSessionId, 1);
2475
            }
2476
        } else {
2477
            UrlManager::add_session_to_url($newSessionId, 1);
2478
        }
2479
2480
        return $newSessionId;
2481
    }
2482
2483
    /**
2484
     * subscribes a user to a session.
2485
     *
2486
     * @throws Exception
2487
     */
2488
    public function subscribeUserToSessionFromUsername(int $sessionId, string $loginName): bool
2489
    {
2490
        if (!SessionManager::isValidId($sessionId)) {
2491
            throw new Exception(get_lang('SessionNotFound'));
2492
        }
2493
2494
        $userId = UserManager::get_user_id_from_username($loginName);
2495
        if (false === $userId) {
2496
            throw new Exception(get_lang('UserNotFound'));
2497
        }
2498
2499
        if (!api_is_platform_admin() && $userId != $this->user->getId()) {
2500
            self::throwNotAllowedException();
2501
        }
2502
2503
        $subscribed = SessionManager::subscribeUsersToSession(
2504
            $sessionId,
2505
            [$userId],
2506
            SESSION_VISIBLE_READ_ONLY,
2507
            false
2508
        );
2509
        if (!$subscribed) {
2510
            throw new Exception(get_lang('UserNotSubscribed'));
2511
        }
2512
2513
        return true;
2514
    }
2515
2516
    /**
2517
     * Finds the session which has a specific value in a specific extra field and return its ID (only that)
2518
     *
2519
     * @param string $fieldName
2520
     * @param string $fieldValue
2521
     *
2522
     * @return int The matching session id, or an array with details about the session
2523
     * @throws Exception when no session matched or more than one session matched
2524
     *
2525
     */
2526
    public function getSessionFromExtraField(string $fieldName, string $fieldValue)
2527
    {
2528
        // find sessions that have that value in the given field
2529
        $valueModel = new ExtraFieldValue('session');
2530
        $sessionIdList = $valueModel->get_item_id_from_field_variable_and_field_value(
2531
            $fieldName,
2532
            $fieldValue,
2533
            false,
2534
            false,
2535
            true
2536
        );
2537
2538
        // throw if none found
2539
        if (empty($sessionIdList)) {
2540
            throw new Exception(get_lang('NoSessionMatched'));
2541
        }
2542
2543
        // throw if more than one found
2544
        if (count($sessionIdList) > 1) {
2545
            throw new Exception(get_lang('MoreThanOneSessionMatched'));
2546
        }
2547
2548
        // return sessionId
2549
        return intval($sessionIdList[0]['item_id']);
2550
    }
2551
2552
    /**
2553
     * Finds the session which has a specific value in a specific extra field and return its details
2554
     *
2555
     * @param string $fieldName
2556
     * @param string $fieldValue
2557
     *
2558
     * @return array The matching session id, or an array with details about the session
2559
     * @throws Exception when no session matched or more than one session matched
2560
     *
2561
     */
2562
    public function getSessionInfoFromExtraField(string $fieldName, string $fieldValue): array
2563
    {
2564
        $session = [];
2565
        // find sessions that have that value in the given field
2566
        $valueModel = new ExtraFieldValue('session');
2567
        $sessionIdList = $valueModel->get_item_id_from_field_variable_and_field_value(
2568
            $fieldName,
2569
            $fieldValue,
2570
            false,
2571
            false,
2572
            true
2573
        );
2574
2575
        // throw if none found
2576
        if (empty($sessionIdList)) {
2577
            throw new Exception(get_lang('NoSessionMatched'));
2578
        }
2579
2580
        // throw if more than one found
2581
        if (count($sessionIdList) > 1) {
2582
            throw new Exception(get_lang('MoreThanOneSessionMatched'));
2583
        }
2584
2585
        $session = api_get_session_info($sessionIdList[0]['item_id']);
2586
        $bundle = [
2587
            'id' => $session['id'],
2588
            'name' => $session['name'],
2589
            'access_start_date' => $session['access_start_date'],
2590
            'access_end_date' => $session['access_end_date'],
2591
        ];
2592
        $extraFieldValues = new ExtraFieldValue('session');
2593
        $extraFields = $extraFieldValues->getAllValuesByItem($session['id']);
2594
        $bundle['extra_fields'] = $extraFields;
2595
2596
        // return session details, including extra fields that have filter=1
2597
        return $bundle;
2598
    }
2599
2600
    /**
2601
     * Get a list of users subscribed to the given session.
2602
     *
2603
     * @params int $sessionId
2604
     * @params int $moveInfo Whether to return the "moved_*" fields or not
2605
     */
2606
    public function getUsersSubscribedToSession(int $sessionId, int $moveInfo = 0): array
2607
    {
2608
        self::protectAdminEndpoint();
2609
2610
        $users = SessionManager::get_users_by_session($sessionId);
2611
2612
        $userList = [];
2613
        foreach ($users as $user) {
2614
            $userInfo = [
2615
                'user_id' => $user['user_id'],
2616
                'username' => $user['username'],
2617
                'firstname' => $user['firstname'],
2618
                'lastname' => $user['lastname'],
2619
                'status' => $user['relation_type'],
2620
            ];
2621
            if (1 === $moveInfo) {
2622
                $userInfo['moved_to'] = $user['moved_to'];
2623
                $userInfo['moved_status'] = $user['moved_status'];
2624
                $userInfo['moved_at'] = $user['moved_at'];
2625
            }
2626
            $userList[] = $userInfo;
2627
        }
2628
2629
        return $userList;
2630
    }
2631
2632
    /**
2633
     * Updates a user identified by its login name.
2634
     *
2635
     * @throws Exception on failure
2636
     *
2637
     * @todo make a safe version for use by the final user on its account
2638
     */
2639
    public function updateUserFromUserName(array $parameters): bool
2640
    {
2641
        // find user
2642
        $userId = null;
2643
        if (empty($parameters)) {
2644
            throw new Exception('NoData');
2645
        }
2646
        foreach ($parameters as $name => $value) {
2647
            if (strtolower($name) === 'loginname') {
2648
                $userId = UserManager::get_user_id_from_username($value);
2649
                if (false === $userId) {
2650
                    throw new Exception(get_lang('UserNotFound'));
2651
                }
2652
                break;
2653
            }
2654
        }
2655
        if (is_null($userId)) {
2656
            throw new Exception(get_lang('NoData'));
2657
        }
2658
2659
        if (!api_is_platform_admin() && $userId != $this->user->getId()) {
2660
            self::throwNotAllowedException();
2661
        }
2662
2663
        if (!empty($parameters['new_login_name'])) {
2664
            // Make sure the new username, if set, is available
2665
            if (!UserManager::is_username_available($parameters['new_login_name'])) {
2666
                throw new Exception(get_lang('LoginAlreadyTaken'));
2667
            }
2668
        }
2669
2670
        /** @var User $user */
2671
        $user = UserManager::getRepository()->find($userId);
2672
        if (empty($user)) {
2673
            throw new Exception(get_lang('CouldNotLoadUser'));
2674
        }
2675
2676
        // tell the world we are about to update a user
2677
        $hook = HookUpdateUser::create();
2678
        if (!empty($hook)) {
2679
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_PRE);
2680
        }
2681
2682
        // apply submitted modifications
2683
        foreach ($parameters as $name => $value) {
2684
            switch (strtolower($name)) {
2685
                case 'email':
2686
                    $user->setEmail($value);
2687
                    break;
2688
                case 'enabled':
2689
                    $user->setEnabled($value);
2690
                    break;
2691
                case 'lastname':
2692
                    $user->setLastname($value);
2693
                    break;
2694
                case 'firstname':
2695
                    $user->setFirstname($value);
2696
                    break;
2697
                case 'new_login_name':
2698
                    $user->setUsername($value);
2699
                    break;
2700
                case 'phone':
2701
                    $user->setPhone($value);
2702
                    break;
2703
                case 'address':
2704
                    $user->setAddress($value);
2705
                    break;
2706
                case 'roles':
2707
                    $user->setRoles($value);
2708
                    break;
2709
                case 'profile_completed':
2710
                    $user->setProfileCompleted($value);
2711
                    break;
2712
                case 'auth_source':
2713
                    $user->setAuthSource($value);
2714
                    break;
2715
                case 'status':
2716
                    $user->setStatus($value);
2717
                    break;
2718
                case 'official_code':
2719
                    $user->setOfficialCode($value);
2720
                    break;
2721
                case 'picture_uri':
2722
                    $user->setPictureUri($value);
2723
                    break;
2724
                case 'creator_id':
2725
                    $user->setCreatorId($value);
2726
                    break;
2727
                case 'competences':
2728
                    $user->setCompetences($value);
2729
                    break;
2730
                case 'diplomas':
2731
                    $user->setDiplomas($value);
2732
                    break;
2733
                case 'openarea':
2734
                    $user->setOpenArea($value);
2735
                    break;
2736
                case 'teach':
2737
                    $user->setTeach($value);
2738
                    break;
2739
                case 'productions':
2740
                    $user->setProductions($value);
2741
                    break;
2742
                case 'language':
2743
                    $languages = api_get_languages();
2744
                    if (!in_array($value, $languages['folder'])) {
2745
                        throw new Exception(get_lang('LanguageUnavailable'));
2746
                    }
2747
                    $user->setLanguage($value);
2748
                    break;
2749
                case 'registration_date':
2750
                    $user->setRegistrationDate($value);
2751
                    break;
2752
                case 'expiration_date':
2753
                    $user->setExpirationDate(
2754
                        new DateTime(
2755
                            api_get_utc_datetime($value),
2756
                            new DateTimeZone('UTC')
2757
                        )
2758
                    );
2759
                    break;
2760
                case 'active':
2761
                    // see UserManager::update_user() usermanager.lib.php:1205
2762
                    if ($user->getActive() != $value) {
2763
                        $user->setActive($value);
2764
                        Event::addEvent($value ? LOG_USER_ENABLE : LOG_USER_DISABLE, LOG_USER_ID, $userId);
2765
                    }
2766
                    break;
2767
                case 'openid':
2768
                    $user->setOpenId($value);
2769
                    break;
2770
                case 'theme':
2771
                    $user->setTheme($value);
2772
                    break;
2773
                case 'hr_dept_id':
2774
                    $user->setHrDeptId($value);
2775
                    break;
2776
                case 'extra':
2777
                    if (is_array($value)) {
2778
                        if (count($value) > 0) {
2779
                            if (is_array($value[0])) {
2780
                                foreach ($value as $field) {
2781
                                    $fieldName = $field['field_name'];
2782
                                    $fieldValue = $field['field_value'];
2783
                                    if (!isset($fieldName) || !isset($fieldValue) ||
2784
                                        !UserManager::update_extra_field_value($userId, $fieldName, $fieldValue)) {
2785
                                        throw new Exception(get_lang('CouldNotUpdateExtraFieldValue').': '.print_r($field, true));
2786
                                    }
2787
                                }
2788
                            } else {
2789
                                foreach ($value as $fieldName => $fieldValue) {
2790
                                    if (!UserManager::update_extra_field_value($userId, $fieldName, $fieldValue)) {
2791
                                        throw new Exception(get_lang('CouldNotUpdateExtraFieldValue').': '.$fieldName);
2792
                                    }
2793
                                }
2794
                            }
2795
                        }
2796
                    }
2797
                    break;
2798
                case 'username':
2799
                case 'api_key':
2800
                case 'action':
2801
                case 'loginname':
2802
                    break;
2803
                case 'email_canonical':
2804
                case 'locked':
2805
                case 'expired':
2806
                case 'credentials_expired':
2807
                case 'credentials_expire_at':
2808
                case 'expires_at':
2809
                case 'salt':
2810
                case 'last_login':
2811
                case 'created_at':
2812
                case 'updated_at':
2813
                case 'confirmation_token':
2814
                case 'password_requested_at':
2815
                case 'password': // see UserManager::update_user usermanager.lib.php:1182
2816
                case 'username_canonical':
2817
                default:
2818
                    throw new Exception(get_lang('UnsupportedUpdate')." '$name'");
2819
            }
2820
        }
2821
2822
        // save modifications
2823
        UserManager::getManager()->updateUser($user, true);
2824
2825
        // tell the world we just updated this user
2826
        if (!empty($hook)) {
2827
            $hook->setEventData(['user' => $user]);
2828
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_POST);
2829
        }
2830
2831
        // invalidate cache for this user
2832
        $cacheAvailable = api_get_configuration_value('apc');
2833
        if ($cacheAvailable === true) {
2834
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$userId;
2835
            if (apcu_exists($apcVar)) {
2836
                apcu_delete($apcVar);
2837
            }
2838
        }
2839
2840
        return true;
2841
    }
2842
2843
    /**
2844
     * Returns whether a user login name exists.
2845
     *
2846
     * @param string $loginname the user login name
2847
     *
2848
     * @return bool whether the user login name exists
2849
     */
2850
    public function usernameExist($loginname)
2851
    {
2852
        return false !== api_get_user_info_from_username($loginname);
2853
    }
2854
2855
    /**
2856
     * Returns whether a user group name exists.
2857
     *
2858
     * @param string $name the group name
2859
     *
2860
     * @return bool whether the group name exists
2861
     */
2862
    public function groupExists($name)
2863
    {
2864
        $userGroup = new UserGroup();
2865
2866
        return false !== $userGroup->usergroup_exists($name);
2867
    }
2868
2869
    /**
2870
     * This service roughly matches what the call to MDL's API core_course_get_contents function returns.
2871
     *
2872
     * @return array
2873
     */
2874
    public function getCourseQuizMdlCompat()
2875
    {
2876
        $userId = $this->user->getId();
2877
        $courseId = $this->course->getId();
2878
        $sessionId = $this->session ? $this->session->getId() : 0;
2879
2880
        $toolVisibility = CourseHome::getToolVisibility(TOOL_QUIZ, $courseId, $sessionId);
2881
2882
        $json = [
2883
            "id" => $this->course->getId(),
2884
            "name" => get_lang('Exercises'),
2885
            "visible" => (int) $toolVisibility,
2886
            "summary" => '',
2887
            "summaryformat" => 1,
2888
            "section" => 1,
2889
            "hiddenbynumsections" => 0,
2890
            "uservisible" => $toolVisibility,
2891
            "modules" => [],
2892
        ];
2893
2894
        $quizIcon = Display::return_icon('quiz.png', '', [], ICON_SIZE_SMALL, false, true);
2895
2896
        $json['modules'] = array_map(
2897
            function (array $exercise) use ($quizIcon) {
2898
                return [
2899
                    'id' => (int) $exercise['id'],
2900
                    'url' => $exercise['url'],
2901
                    'name' => $exercise['name'],
2902
                    'instance' => 1,
2903
                    'visible' => 1,
2904
                    'uservisible' => true,
2905
                    'visibleoncoursepage' => 0,
2906
                    'modicon' => $quizIcon,
2907
                    'modname' => 'quiz',
2908
                    'modplural' => get_lang('Exercises'),
2909
                    'availability' => null,
2910
                    'indent' => 0,
2911
                    'onclick' => '',
2912
                    'afterlink' => null,
2913
                    'customdata' => "",
2914
                    'noviewlink' => false,
2915
                    'completion' => (int) ($exercise[1] > 0),
2916
                ];
2917
            },
2918
            Exercise::exerciseGrid(0, '', $userId, $courseId, $sessionId, true)
2919
        );
2920
2921
        return [$json];
2922
    }
2923
2924
    /**
2925
     * @throws Exception
2926
     */
2927
    public function updateSession(array $params): array
2928
    {
2929
        self::protectAdminEndpoint();
2930
2931
        $id = $params['session_id'];
2932
        $reset = $params['reset'] ?? null;
2933
        $name = $params['name'] ?? null;
2934
        $coachId = isset($params['id_coach']) ? (int) $params['id_coach'] : null;
2935
        $sessionCategoryId = isset($params['session_category_id']) ? (int) $params['session_category_id'] : null;
2936
        $description = $params['description'] ?? null;
2937
        $showDescription = $params['show_description'] ?? null;
2938
        $duration = $params['duration'] ?? null;
2939
        $visibility = $params['visibility'] ?? null;
2940
        $promotionId = $params['promotion_id'] ?? null;
2941
        $displayStartDate = $params['display_start_date'] ?? null;
2942
        $displayEndDate = $params['display_end_date'] ?? null;
2943
        $accessStartDate = $params['access_start_date'] ?? null;
2944
        $accessEndDate = $params['access_end_date'] ?? null;
2945
        $coachStartDate = $params['coach_access_start_date'] ?? null;
2946
        $coachEndDate = $params['coach_access_end_date'] ?? null;
2947
        $sendSubscriptionNotification = $params['send_subscription_notification'] ?? null;
2948
        $extraFields = $params['extra'] ?? [];
2949
2950
        $reset = (bool) $reset;
2951
        $visibility = (int) $visibility;
2952
        $tblSession = Database::get_main_table(TABLE_MAIN_SESSION);
2953
2954
        if (!SessionManager::isValidId($id)) {
2955
            throw new Exception(get_lang('NoData'));
2956
        }
2957
2958
        if (!empty($accessStartDate) && !api_is_valid_date($accessStartDate, 'Y-m-d H:i') &&
2959
            !api_is_valid_date($accessStartDate, 'Y-m-d H:i:s')
2960
        ) {
2961
            throw new Exception(get_lang('InvalidDate'));
2962
        }
2963
2964
        if (!empty($accessEndDate) && !api_is_valid_date($accessEndDate, 'Y-m-d H:i') &&
2965
            !api_is_valid_date($accessEndDate, 'Y-m-d H:i:s')
2966
        ) {
2967
            throw new Exception(get_lang('InvalidDate'));
2968
        }
2969
2970
        if (!empty($accessStartDate) && !empty($accessEndDate) && $accessStartDate >= $accessEndDate) {
2971
            throw new Exception(get_lang('InvalidDate'));
2972
        }
2973
2974
        $values = [];
2975
2976
        if ($reset) {
2977
            $values['name'] = $name;
2978
            $values['id_coach'] = $coachId;
2979
            $values['session_category_id'] = $sessionCategoryId;
2980
            $values['description'] = $description;
2981
            $values['show_description'] = $showDescription;
2982
            $values['duration'] = $duration;
2983
            $values['visibility'] = $visibility;
2984
            $values['promotion_id'] = $promotionId;
2985
            $values['display_start_date'] = !empty($displayStartDate) ? api_get_utc_datetime($displayStartDate) : null;
2986
            $values['display_end_date'] = !empty($displayEndDate) ? api_get_utc_datetime($displayEndDate) : null;
2987
            $values['access_start_date'] = !empty($accessStartDate) ? api_get_utc_datetime($accessStartDate) : null;
2988
            $values['access_end_date'] = !empty($accessEndDate) ? api_get_utc_datetime($accessEndDate) : null;
2989
            $values['coach_access_start_date'] = !empty($coachStartDate) ? api_get_utc_datetime($coachStartDate) : null;
2990
            $values['coach_access_end_date'] = !empty($coachEndDate) ? api_get_utc_datetime($coachEndDate) : null;
2991
            $values['send_subscription_notification'] = $sendSubscriptionNotification;
2992
        } else {
2993
            if (!empty($name)) {
2994
                $values['name'] = $name;
2995
            }
2996
2997
            if (!empty($coachId)) {
2998
                $values['id_coach'] = $coachId;
2999
            }
3000
3001
            if (!empty($sessionCategoryId)) {
3002
                $values['session_category_id'] = $sessionCategoryId;
3003
            }
3004
3005
            if (!empty($description)) {
3006
                $values['description'] = $description;
3007
            }
3008
3009
            if (!empty($showDescription)) {
3010
                $values['show_description'] = $showDescription;
3011
            }
3012
3013
            if (!empty($duration)) {
3014
                $values['duration'] = $duration;
3015
            }
3016
3017
            if (!empty($visibility)) {
3018
                $values['visibility'] = $visibility;
3019
            }
3020
3021
            if (!empty($promotionId)) {
3022
                $values['promotion_id'] = $promotionId;
3023
            }
3024
3025
            if (!empty($displayStartDate)) {
3026
                $values['display_start_date'] = api_get_utc_datetime($displayStartDate);
3027
            }
3028
3029
            if (!empty($displayEndDate)) {
3030
                $values['display_end_date'] = api_get_utc_datetime($displayEndDate);
3031
            }
3032
3033
            if (!empty($accessStartDate)) {
3034
                $values['access_start_date'] = api_get_utc_datetime($accessStartDate);
3035
            }
3036
3037
            if (!empty($accessEndDate)) {
3038
                $values['access_end_date'] = api_get_utc_datetime($accessEndDate);
3039
            }
3040
3041
            if (!empty($coachStartDate)) {
3042
                $values['coach_access_start_date'] = api_get_utc_datetime($coachStartDate);
3043
            }
3044
3045
            if (!empty($coachEndDate)) {
3046
                $values['coach_access_end_date'] = api_get_utc_datetime($coachEndDate);
3047
            }
3048
3049
            if (!empty($sendSubscriptionNotification)) {
3050
                $values['send_subscription_notification'] = $sendSubscriptionNotification;
3051
            }
3052
        }
3053
3054
        Database::update(
3055
            $tblSession,
3056
            $values,
3057
            ['id = ?' => $id]
3058
        );
3059
3060
        if (!empty($extraFields)) {
3061
            $extraFields['item_id'] = $id;
3062
            $sessionFieldValue = new ExtraFieldValue('session');
3063
            $sessionFieldValue->saveFieldValues($extraFields);
3064
        }
3065
3066
        return [
3067
            'status' => true,
3068
            'message' => get_lang('Updated'),
3069
            'id_session' => $id,
3070
        ];
3071
    }
3072
3073
    public function checkConditionalLogin(): bool
3074
    {
3075
        $file = api_get_path(SYS_CODE_PATH).'auth/conditional_login/conditional_login.php';
3076
3077
        if (!file_exists($file)) {
3078
            return true;
3079
        }
3080
3081
        include_once $file;
3082
3083
        if (!isset($login_conditions)) {
3084
            return true;
3085
        }
3086
3087
        foreach ($login_conditions as $condition) {
3088
            //If condition fails we redirect to the URL defined by the condition
3089
            if (!isset($condition['conditional_function'])) {
3090
                continue;
3091
            }
3092
3093
            $function = $condition['conditional_function'];
3094
            $result = $function(['user_id' => $this->user->getId()]);
3095
3096
            if ($result == false) {
3097
                return false;
3098
            }
3099
        }
3100
3101
        return true;
3102
    }
3103
3104
    public function getLegalConditions(): array
3105
    {
3106
        $language = api_get_language_id(
3107
            api_get_interface_language()
3108
        );
3109
3110
        $termPreview = LegalManager::get_last_condition($language);
3111
3112
        if ($termPreview) {
3113
            return $termPreview;
3114
        }
3115
3116
        $language = api_get_language_id(
3117
            api_get_setting('platformLanguage')
3118
        );
3119
3120
        $termPreview = LegalManager::get_last_condition($language);
3121
3122
        if ($termPreview) {
3123
            return $termPreview;
3124
        }
3125
3126
        $language = api_get_language_id('english');
3127
3128
        return LegalManager::get_last_condition($language);
3129
    }
3130
3131
    public function updateConditionAccepted()
3132
    {
3133
        $legalAcceptType = $_POST['legal_accept_type'] ?? null;
3134
3135
        $condArray = explode(':', $legalAcceptType);
3136
        $condArray = array_map('intval', $condArray);
3137
3138
        if (empty($condArray[0]) || empty($condArray[1])) {
3139
            return;
3140
        }
3141
3142
        $conditionToSave = intval($condArray[0]).':'.intval($condArray[1]).':'.time();
3143
3144
        LegalManager::sendEmailToUserBoss(
3145
            $this->user->getId(),
3146
            $conditionToSave
3147
        );
3148
    }
3149
3150
    /**
3151
     * Get the list of test with last user attempt and his datetime.
3152
     *
3153
     * @param array $fields A list of extra fields to include in the answer. Searches for the field in exercise, then in course
3154
     *
3155
     * @throws Exception
3156
     */
3157
    public function getTestUpdatesList($fields = []): array
3158
    {
3159
        self::protectAdminEndpoint();
3160
3161
        $tableCQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
3162
        $tableTrackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3163
        $tableUser = Database::get_main_table(TABLE_MAIN_USER);
3164
        $resultArray = [];
3165
3166
        // Check the extra fields criteria (whether to add extra field information or not)
3167
        $fieldSource = [];
3168
        $extraFieldValuesTable = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3169
        $fieldsSearchString = "SELECT field_id, value FROM $extraFieldValuesTable WHERE item_id = %d AND field_id = %d";
3170
        if (count($fields) > 0) {
3171
            // For each field, check where to get it from (quiz or course)
3172
            $quizExtraField = new ExtraField('exercise');
3173
            $courseExtraField = new ExtraField('course');
3174
            foreach ($fields as $fieldName) {
3175
                $fieldExists = $quizExtraField->get_handler_field_info_by_field_variable($fieldName);
3176
                if ($fieldExists === false) {
3177
                    // The field does not exist on the exercise, so use it from the course
3178
                    $courseFieldExists = $courseExtraField->get_handler_field_info_by_field_variable($fieldName);
3179
                    if ($courseFieldExists === false) {
3180
                        continue;
3181
                    }
3182
                    $fieldSource[$fieldName] = ['item_type' => 'course', 'id' => $courseFieldExists['id']];
3183
                } else {
3184
                    $fieldSource[$fieldName] = ['item_type' => 'exercise', 'id' => $fieldExists['id']];
3185
                }
3186
            }
3187
        }
3188
3189
        $sql = "
3190
            SELECT q.iid AS id,
3191
                q.title,
3192
                MAX(a.start_date) AS last_attempt_time,
3193
                u.username AS last_attempt_username,
3194
                q.c_id
3195
            FROM $tableCQuiz q
3196
            JOIN $tableTrackExercises a ON q.iid = a.exe_exo_id
3197
            JOIN $tableUser u ON a.exe_user_id = u.id
3198
            GROUP BY q.iid
3199
        ";
3200
3201
        $result = Database::query($sql);
3202
        if (Database::num_rows($result) > 0) {
3203
            while ($row = Database::fetch_assoc($result)) {
3204
                // Check the whole extra fields thing
3205
                if (count($fieldSource) > 0) {
3206
                    foreach ($fieldSource as $fieldName => $fieldDetails) {
3207
                        if ($fieldDetails['item_type'] == 'course') {
3208
                            $itemId = $row['c_id'];
3209
                        } else {
3210
                            $itemId = $row['id'];
3211
                        }
3212
                        $fieldResult = Database::query(sprintf($fieldsSearchString, $itemId, $fieldDetails['id']));
3213
                        if (Database::num_rows($fieldResult) > 0) {
3214
                            $fieldRow = Database::fetch_assoc($fieldResult);
3215
                            $row['extra_'.$fieldName] = $fieldRow['value'];
3216
                        } else {
3217
                            $row['extra_'.$fieldName] = '';
3218
                        }
3219
                    }
3220
                }
3221
                // Get item authoring data
3222
                $itemProps = api_get_last_item_property_info($row['c_id'], 'quiz', $row['id']);
3223
                $row['created_by'] = $this->__getConfiguredUsernameById($itemProps['insert_user_id']);
3224
                if ($itemProps['insert_user_id'] == $itemProps['lastedit_user_id']) {
3225
                    $row['updated_by'] = $row['created_by'];
3226
                } else {
3227
                    $row['updated_by'] = $this->__getConfiguredUsernameById($itemProps['lastedit_user_id']);
3228
                }
3229
                $resultArray[] = $row;
3230
            }
3231
        }
3232
3233
        return $resultArray;
3234
    }
3235
3236
    /**
3237
     * Get tests results data
3238
     * Not support sessions
3239
     * By default, is successful if score greater than 50%.
3240
     *
3241
     * @throws Exception
3242
     *
3243
     * @return array e.g: [ { "id": 4, "title": "aiken", "updated_by": "-", "type": "1", "completion": 0 } ]
3244
     */
3245
    public function getTestAverageResultsList(array $ids = [], ?array $fields = []): array
3246
    {
3247
        self::protectAdminEndpoint();
3248
        $tableTrackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3249
        $tableCQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
3250
        $tableCourseRelUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3251
3252
        $resultArray = [];
3253
        $countUsersInCourses = [];
3254
        $extraArray = [];
3255
3256
        if (!empty($ids)) {
3257
            if (!is_array($ids)) {
3258
                $ids = [$ids];
3259
            }
3260
            if (!is_array($fields)) {
3261
                $fields = [$fields];
3262
            }
3263
            if (!empty($fields)) {
3264
                foreach ($fields as $field) {
3265
                    $extraArray['extra_'.$field] = '';
3266
                }
3267
            }
3268
3269
            $queryUsersInCourses = "
3270
                SELECT c_id, count(*)
3271
                FROM $tableCourseRelUser
3272
                GROUP BY c_id
3273
                ORDER BY c_id;
3274
            ";
3275
3276
            $resultUsersInCourses = Database::query($queryUsersInCourses);
3277
            while ($row = Database::fetch_array($resultUsersInCourses)) {
3278
                $countUsersInCourses[$row[0]] = $row[1];
3279
            }
3280
3281
            foreach ($ids as $item) {
3282
                $item = (int) $item;
3283
3284
                $queryCQuiz = "
3285
                    SELECT c_id,
3286
                        title,
3287
                        feedback_type,
3288
                        pass_percentage
3289
                    FROM $tableCQuiz
3290
                    WHERE iid = $item";
3291
3292
                $resultCQuiz = Database::query($queryCQuiz);
3293
                if (Database::num_rows($resultCQuiz) <= 0) {
3294
                    continue;
3295
                }
3296
                $row = Database::fetch_assoc($resultCQuiz);
3297
3298
                $cId = $row['c_id'];
3299
                $title = $row['title'];
3300
                $type = Exercise::getFeedbackTypeLiteral($row['feedback_type']);
3301
                $passPercentage = empty($row['pass_percentage']) ? 0.5 : $row['pass_percentage'];
3302
3303
                // Get item authoring data
3304
                $itemProps = api_get_last_item_property_info($row['c_id'], 'quiz', $item);
3305
                $createdBy = $this->__getConfiguredUsernameById($itemProps['insert_user_id']);
3306
                if ($itemProps['insert_user_id'] == $itemProps['lastedit_user_id']) {
3307
                    $updatedBy = $createdBy;
3308
                } else {
3309
                    $updatedBy = $this->__getConfiguredUsernameById($itemProps['lastedit_user_id']);
3310
                }
3311
3312
                $sql = "
3313
                    SELECT a.exe_exo_id AS id,
3314
                           a.exe_user_id,
3315
                           MAX(a.start_date),
3316
                           a.exe_result,
3317
                           a.exe_weighting
3318
                    FROM $tableTrackExercises a
3319
                    WHERE a.exe_exo_id = $item
3320
                    GROUP BY a.exe_exo_id, a.exe_user_id
3321
                ";
3322
3323
                $result = Database::query($sql);
3324
                if (Database::num_rows($result) > 0) {
3325
                    $countAttempts = 0;
3326
                    $countSuccess = 0;
3327
                    $scoreSum = 0;
3328
3329
                    while ($row = Database::fetch_assoc($result)) {
3330
3331
                        // If test is badly configured, with all questions at score 0
3332
                        if ($row['exe_weighting'] == 0) {
3333
                            continue;
3334
                        }
3335
                        $score = $row['exe_result'] / $row['exe_weighting'];
3336
                        if ($score >= $passPercentage) {
3337
                            $countSuccess++;
3338
                        }
3339
                        $scoreSum += $score;
3340
                        $countAttempts++;
3341
                    }
3342
                    $completionMethod = 'Success on users count';
3343
                    if ($countAttempts === 0) {
3344
                        // In some cases, there are no attempts at all. Return 0 completion & score.
3345
                        $averageScore = 0;
3346
                        $completion = 0;
3347
                    } else {
3348
                        $averageScore = round(($scoreSum / $countAttempts) * 100, 2);
3349
                        if (empty($countUsersInCourses[$cId])) {
3350
                            // Users might have all been unsubscribed from the course since taking the test
3351
                            $completion = $countSuccess / $countAttempts;
3352
                            $completionMethod = 'Success on attempts count';
3353
                        } else {
3354
                            $completion = $countSuccess / $countUsersInCourses[$cId];
3355
                        }
3356
                    }
3357
                    $params = [
3358
                        'id' => $item,
3359
                        'title' => $title,
3360
                        'created_by' => $createdBy,
3361
                        'updated_by' => $updatedBy,
3362
                        'type' => $type,
3363
                        'completion' => $completion,
3364
                        'completion_method' => $completionMethod,
3365
                        'number_of_last_attempts' => $countAttempts,
3366
                        'average_score_in_percent' => $averageScore,
3367
                    ];
3368
                    foreach ($extraArray as $name => $value) {
3369
                        $params[$name] = $value;
3370
                    }
3371
                    $resultArray[] = $params;
3372
                }
3373
            }
3374
        }
3375
3376
        return $resultArray;
3377
    }
3378
3379
    public function logout()
3380
    {
3381
        online_logout($this->user->getId());
3382
3383
        Event::courseLogout(
3384
            [
3385
                'uid' => $this->user->getId(),
3386
                'cid' => $this->course ? $this->course->getId() : 0,
3387
                'sid' => $this->session ? $this->session->getId() : 0,
3388
            ]
3389
        );
3390
    }
3391
3392
    /**
3393
     * @throws Exception
3394
     */
3395
    public function setThreadNotify(int $threadId): string
3396
    {
3397
        require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
3398
3399
        $result = set_notification(
3400
            'thread',
3401
            $threadId,
3402
            false,
3403
            api_get_user_info($this->user->getId()),
3404
            api_get_course_info($this->course->getCode())
3405
        );
3406
3407
        if (false === $result) {
3408
            self::throwNotAllowedException();
3409
        }
3410
3411
        return $result;
3412
    }
3413
3414
    public function getCourseWorks(): array
3415
    {
3416
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3417
3418
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3419
3420
        $isAllowedToEdit = $this->user->getStatus() !== STUDENT;
3421
3422
        $courseId = $this->course->getId();
3423
        $sessionId = $this->session ? $this->session->getId() : 0;
3424
3425
        $courseInfo = api_get_course_info_by_id($this->course->getId());
3426
3427
        $works = array_filter(
3428
            getWorkListTeacherData($courseId, $sessionId, 0, 0, 0, 'title', 'ASC', ''),
3429
            function (array $work) use ($isAllowedToEdit, $courseInfo, $courseId, $sessionId) {
3430
                if (!$isAllowedToEdit
3431
                    && !userIsSubscribedToWork($this->user->getId(), $work['id'], $courseId)
3432
                ) {
3433
                    return false;
3434
                }
3435
3436
                $visibility = api_get_item_visibility($courseInfo, 'work', $work['id'], $sessionId);
3437
3438
                if (!$isAllowedToEdit && $visibility != 1) {
3439
                    return false;
3440
                }
3441
3442
                return true;
3443
            }
3444
        );
3445
3446
        return array_map(
3447
            function (array $work) use ($isAllowedToEdit, $courseInfo) {
3448
                $work['type'] = 'work.png';
3449
3450
                if (!$isAllowedToEdit) {
3451
                    $workList = get_work_user_list(
3452
                        0,
3453
                        1000,
3454
                        null,
3455
                        null,
3456
                        $work['id'],
3457
                        ' AND u.id = '.$this->user->getId()
3458
                    );
3459
3460
                    $count = getTotalWorkComment($workList, $courseInfo);
3461
                    $lastWork = getLastWorkStudentFromParentByUser($this->user->getId(), $work, $courseInfo);
3462
3463
                    $work['feedback'] = ' '.Display::label('0 '.get_lang('Feedback'), 'warning');
3464
3465
                    if (!empty($count)) {
3466
                        $work['feedback'] = ' '.Display::label($count.' '.get_lang('Feedback'), 'info');
3467
                    }
3468
3469
                    $work['last_upload'] = '';
3470
3471
                    if (!empty($lastWork)) {
3472
                        $work['last_upload'] = !empty($lastWork['qualification'])
3473
                            ? $lastWork['qualification_rounded'].' - '
3474
                            : '';
3475
                        $work['last_upload'] .= api_get_local_time($lastWork['sent_date']);
3476
                    }
3477
                }
3478
3479
                return $work;
3480
            },
3481
            $works
3482
        );
3483
    }
3484
3485
    /**
3486
     * Returns a list of exercises in the given course. The given course is received through generic param at instanciation.
3487
     *
3488
     * @param array $fields A list of extra fields to include in the answer. Searches for the field in exercise, then in course
3489
     */
3490
    public function getCourseExercises($fields = []): array
3491
    {
3492
        Event::event_access_tool(TOOL_QUIZ);
3493
3494
        $sessionId = $this->session ? $this->session->getId() : 0;
3495
        $courseInfo = api_get_course_info_by_id($this->course->getId());
3496
3497
        // Check the extra fields criteria (whether to add extra field information or not)
3498
        $fieldSource = [];
3499
        if (count($fields) > 0) {
3500
            // For each field, check where to get it from (quiz or course)
3501
            $quizExtraField = new ExtraField('exercise');
3502
            $courseExtraField = new ExtraField('course');
3503
            foreach ($fields as $fieldName) {
3504
                $fieldExists = $quizExtraField->get_handler_field_info_by_field_variable($fieldName);
3505
                if ($fieldExists === false) {
3506
                    // The field does not exist on the exercise, so use it from the course
3507
                    $courseFieldExists = $courseExtraField->get_handler_field_info_by_field_variable($fieldName);
3508
                    if ($courseFieldExists === false) {
3509
                        continue;
3510
                    }
3511
                    $fieldSource[$fieldName] = ['item_type' => 'course', 'id' => $courseFieldExists['id']];
3512
                } else {
3513
                    $fieldSource[$fieldName] = ['item_type' => 'exercise', 'id' => $fieldExists['id']];
3514
                }
3515
            }
3516
        }
3517
        $list = ExerciseLib::get_all_exercises($courseInfo, $sessionId);
3518
3519
        // Now check the whole extra fields thing
3520
        if (count($fieldSource) > 0) {
3521
            $extraFieldValuesTable = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3522
            $fieldsSearchString = "SELECT field_id, value FROM $extraFieldValuesTable WHERE item_id = %d AND field_id = %d";
3523
            foreach ($list as $id => $exercise) {
3524
                foreach ($fieldSource as $fieldName => $fieldDetails) {
3525
                    if ($fieldDetails['item_type'] == 'course') {
3526
                        $itemId = $exercise['c_id'];
3527
                    } else {
3528
                        $itemId = $exercise['iid'];
3529
                    }
3530
                    $result = Database::query(sprintf($fieldsSearchString, $itemId, $fieldDetails['id']));
3531
                    if (Database::num_rows($result) > 0) {
3532
                        $row = Database::fetch_assoc($result);
3533
                        $list[$id]['extra_'.$fieldName] = $row['value'];
3534
                    } else {
3535
                        $list[$id]['extra_'.$fieldName] = '';
3536
                    }
3537
                }
3538
            }
3539
        }
3540
        foreach ($list as $id => $row) {
3541
            // Get item authoring data
3542
            $itemProps = api_get_last_item_property_info($row['c_id'], 'quiz', $row['iid']);
3543
            $createdBy = $this->__getConfiguredUsernameById($itemProps['insert_user_id']);
3544
            if ($itemProps['insert_user_id'] == $itemProps['lastedit_user_id']) {
3545
                $updatedBy = $createdBy;
3546
            } else {
3547
                $updatedBy = $this->__getConfiguredUsernameById($itemProps['lastedit_user_id']);
3548
            }
3549
            $list[$id]['created_by'] = $createdBy;
3550
            $list[$id]['updated_by'] = $updatedBy;
3551
        }
3552
3553
        return $list;
3554
    }
3555
3556
    /**
3557
     * @throws Exception
3558
     */
3559
    public function putCourseWorkVisibility(int $workId, int $status): bool
3560
    {
3561
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3562
3563
        $courseInfo = api_get_course_info_by_id($this->course->getId());
3564
3565
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3566
3567
        switch ($status) {
3568
            case 1:
3569
                return makeVisible($workId, $courseInfo);
3570
            case 0:
3571
                return makeInvisible($workId, $courseInfo);
3572
            default:
3573
                throw new Exception(get_lang('ActionNotAllowed'));
3574
        }
3575
    }
3576
3577
    public function deleteWorkStudentItem(int $workId): string
3578
    {
3579
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3580
3581
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3582
3583
        $courseInfo = api_get_course_info_by_id($this->course->getId());
3584
3585
        $fileDeleted = deleteWorkItem($workId, $courseInfo);
3586
3587
        if ($fileDeleted) {
3588
            return get_lang('TheDocumentHasBeenDeleted');
3589
        }
3590
3591
        return get_lang('YouAreNotAllowedToDeleteThisDocument');
3592
    }
3593
3594
    public function deleteWorkCorrections(int $workId): string
3595
    {
3596
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3597
3598
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3599
3600
        $courseInfo = api_get_course_info_by_id($this->course->getId());
3601
3602
        $result = get_work_user_list(null, null, null, null, $workId);
3603
3604
        if ($result) {
3605
            foreach ($result as $item) {
3606
                $workInfo = get_work_data_by_id($item['id']);
3607
3608
                deleteCorrection($courseInfo, $workInfo);
3609
            }
3610
        }
3611
3612
        return get_lang('Deleted');
3613
    }
3614
3615
    public function getWorkList(int $workId): array
3616
    {
3617
        $isAllowedToEdit = api_is_allowed_to_edit();
3618
3619
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3620
3621
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3622
3623
        $userId = $this->user->getId();
3624
        $courseId = $this->course->getId();
3625
        $sessionId = $this->session ? $this->session->getId() : 0;
3626
3627
        $courseInfo = api_get_course_info_by_id($courseId);
3628
        $webPath = api_get_path(WEB_PATH);
3629
3630
        $whereCondition = !$isAllowedToEdit ? " AND u.id = $userId" : '';
3631
3632
        $works = get_work_user_list(
3633
            0,
3634
            0,
3635
            'title',
3636
            'asc',
3637
            $workId,
3638
            $whereCondition,
3639
            null,
3640
            false,
3641
            $courseId,
3642
            $sessionId
3643
        );
3644
3645
        return array_map(
3646
            function (array $work) use ($courseInfo, $webPath) {
3647
                $itemId = $work['id'];
3648
                $count = getWorkCommentCount($itemId, $courseInfo);
3649
3650
                $work['feedback'] = $count.' '.Display::returnFontAwesomeIcon('comments-o');
3651
                $work['feedback_clean'] = $count;
3652
3653
                $workInfo = get_work_data_by_id($itemId);
3654
                $commentsTmp = getWorkComments($workInfo);
3655
                $comments = [];
3656
3657
                foreach ($commentsTmp as $comment) {
3658
                    $comment['comment'] = str_replace('src="/', 'src="'.$webPath.'app/', $comment['comment']);
3659
                    $comments[] = $comment;
3660
                }
3661
3662
                $work['comments'] = $comments;
3663
3664
                if (empty($workInfo['qualificator_id'])) {
3665
                    $qualificator_id = Display::label(get_lang('NotRevised'), 'warning');
3666
                } else {
3667
                    $qualificator_id = Display::label(get_lang('Revised'), 'success');
3668
                }
3669
3670
                $work['qualificator_id'] = $qualificator_id;
3671
3672
                return $work;
3673
            },
3674
            $works
3675
        );
3676
    }
3677
3678
    public function getWorkStudentsWithoutPublications(int $workId): array
3679
    {
3680
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3681
3682
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3683
3684
        return get_list_users_without_publication($workId);
3685
    }
3686
3687
    public function getWorkUsers(int $workId): array
3688
    {
3689
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3690
3691
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3692
3693
        $courseId = $this->course->getId();
3694
        $sessionId = $this->session ? $this->session->getId() : 0;
3695
        $courseInfo = api_get_course_info_by_id($courseId);
3696
3697
        $items = getAllUserToWork($workId, $courseId);
3698
        $usersAdded = [];
3699
        $result = [
3700
            'users_added' => [],
3701
            'users_to_add' => [],
3702
        ];
3703
3704
        if (!empty($items)) {
3705
            foreach ($items as $data) {
3706
                $usersAdded[] = $data['user_id'];
3707
3708
                $userInfo = api_get_user_info($data['user_id']);
3709
3710
                $result['users_added'][] = [
3711
                    'user_id' => (int) $data['user_id'],
3712
                    'complete_name_with_username' => $userInfo['complete_name_with_username'],
3713
                ];
3714
            }
3715
        }
3716
3717
        if (empty($sessionId)) {
3718
            $status = STUDENT;
3719
        } else {
3720
            $status = 0;
3721
        }
3722
3723
        $userList = CourseManager::get_user_list_from_course_code(
3724
            $courseInfo['code'],
3725
            $sessionId,
3726
            null,
3727
            null,
3728
            $status
3729
        );
3730
3731
        $userToAddList = [];
3732
        foreach ($userList as $user) {
3733
            if (!in_array($user['user_id'], $usersAdded)) {
3734
                $userToAddList[] = $user;
3735
            }
3736
        }
3737
3738
        if (!empty($userToAddList)) {
3739
            foreach ($userToAddList as $user) {
3740
                $userName = api_get_person_name($user['firstname'], $user['lastname']).' ('.$user['username'].') ';
3741
3742
                $result['users_to_add'][] = [
3743
                    'user_id' => (int) $user['user_id'],
3744
                    'complete_name_with_username' => $userName,
3745
                ];
3746
            }
3747
        }
3748
3749
        return $result;
3750
    }
3751
3752
    public function getWorkStudentList(int $workId): array
3753
    {
3754
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3755
3756
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3757
3758
        $courseId = $this->course->getId();
3759
        $courseCode = $this->course->getCode();
3760
        $sessionId = $this->session ? $this->session->getId() : 0;
3761
3762
        $myFolderData = get_work_data_by_id($workId);
3763
3764
        $workParents = [];
3765
3766
        if (empty($myFolderData)) {
3767
            $workParents = getWorkList($workId, $myFolderData);
3768
        }
3769
3770
        $workIdList = [];
3771
3772
        if (!empty($workParents)) {
3773
            foreach ($workParents as $work) {
3774
                $workIdList[] = $work->id;
3775
            }
3776
        }
3777
3778
        $userList = getWorkUserList(
3779
            $courseCode,
3780
            $sessionId,
3781
            0,
3782
            0,
3783
            null,
3784
            null,
3785
            null
3786
        );
3787
3788
        return array_map(
3789
            function ($userId) use ($courseId, $sessionId, $workParents, $workIdList) {
3790
                $user = api_get_user_info($userId);
3791
3792
                $userWorks = 0;
3793
3794
                if (!empty($workIdList)) {
3795
                    $userWorks = getUniqueStudentAttempts(
3796
                        $workIdList,
3797
                        0,
3798
                        $courseId,
3799
                        $sessionId,
3800
                        $user['user_id']
3801
                    );
3802
                }
3803
3804
                $works = $userWorks." / ".count($workParents);
3805
3806
                return [
3807
                    'id' => $userId,
3808
                    'complete_name' => api_get_person_name($user['firstname'], $user['lastname']),
3809
                    'works' => $works,
3810
                ];
3811
            },
3812
            $userList
3813
        );
3814
    }
3815
3816
    public function viewUserProfile(int $userId)
3817
    {
3818
        $url = api_get_path(WEB_CODE_PATH).'social/profile.php';
3819
3820
        if ($userId) {
3821
            $url .= '?'.http_build_query(['u' => $userId]);
3822
        }
3823
3824
        header("Location: $url");
3825
        exit;
3826
    }
3827
3828
    public function viewCourseHome()
3829
    {
3830
        $url = api_get_course_url($this->course->getCode(), $this->session ? $this->session->getId() : 0);
3831
3832
        header("Location: $url");
3833
        exit;
3834
    }
3835
3836
    public function viewDocumentInFrame(int $documentId)
3837
    {
3838
        $courseCode = $this->course->getCode();
3839
        $sessionId = $this->session ? $this->session->getId() : 0;
3840
3841
        $url = api_get_path(WEB_CODE_PATH).'document/showinframes.php?'
3842
            .http_build_query(
3843
                [
3844
                    'cidReq' => $courseCode,
3845
                    'id_session' => $sessionId,
3846
                    'gidReq' => 0,
3847
                    'gradebook' => 0,
3848
                    'origin' => self::SERVICE_NAME,
3849
                    'id' => $documentId,
3850
                ]
3851
            );
3852
3853
        header("Location: $url");
3854
        exit;
3855
    }
3856
3857
    public function viewQuizTool()
3858
    {
3859
        $courseCode = $this->course->getCode();
3860
        $sessionId = $this->session ? $this->session->getId() : 0;
3861
3862
        $url = api_get_path(WEB_CODE_PATH).'exercise/exercise.php?'
3863
            .http_build_query(
3864
                [
3865
                    'cidReq' => $courseCode,
3866
                    'id_session' => $sessionId,
3867
                    'gidReq' => 0,
3868
                    'gradebook' => 0,
3869
                    'origin' => self::SERVICE_NAME,
3870
                ]
3871
            );
3872
3873
        header("Location: $url");
3874
        exit;
3875
    }
3876
3877
    public function viewSurveyTool()
3878
    {
3879
        $courseCode = $this->course->getCode();
3880
        $sessionId = $this->session ? $this->session->getId() : 0;
3881
3882
        $url = api_get_path(WEB_CODE_PATH).'survey/survey_list.php?'
3883
            .http_build_query(
3884
                [
3885
                    'cidReq' => $courseCode,
3886
                    'id_session' => $sessionId,
3887
                    'gidReq' => 0,
3888
                    'gradebook' => 0,
3889
                    'origin' => self::SERVICE_NAME,
3890
                ]
3891
            );
3892
3893
        header("Location: $url");
3894
        exit;
3895
    }
3896
3897
    public function viewMessage(int $messageId)
3898
    {
3899
        $url = api_get_path(WEB_CODE_PATH).'messages/view_message.php?'.http_build_query(['id' => $messageId]);
3900
3901
        header("Location: $url");
3902
        exit;
3903
    }
3904
3905
    public function downloadForumPostAttachment(string $path)
3906
    {
3907
        $courseCode = $this->course->getCode();
3908
        $sessionId = $this->session ? $this->session->getId() : 0;
3909
3910
        $url = api_get_path(WEB_CODE_PATH).'forum/download.php?'
3911
            .http_build_query(
3912
                [
3913
                    'cidReq' => $courseCode,
3914
                    'id_session' => $sessionId,
3915
                    'gidReq' => 0,
3916
                    'gradebook' => 0,
3917
                    'origin' => self::SERVICE_NAME,
3918
                    'file' => Security::remove_XSS($path),
3919
                ]
3920
            );
3921
3922
        header("Location: $url");
3923
        exit;
3924
    }
3925
3926
    public function downloadWorkFolder(int $workId)
3927
    {
3928
        $cidReq = api_get_cidreq();
3929
        $url = api_get_path(WEB_CODE_PATH)."work/downloadfolder.inc.php?id=$workId&$cidReq";
3930
3931
        header("Location: $url");
3932
        exit;
3933
    }
3934
3935
    public function downloadWorkCommentAttachment(int $commentId)
3936
    {
3937
        $cidReq = api_get_cidreq();
3938
        $url = api_get_path(WEB_CODE_PATH)."work/download_comment_file.php?comment_id=$commentId&$cidReq";
3939
3940
        header("Location: $url");
3941
        exit;
3942
    }
3943
3944
    public function downloadWork(int $workId, bool $isCorrection = false)
3945
    {
3946
        $cidReq = api_get_cidreq();
3947
        $url = api_get_path(WEB_CODE_PATH)."work/download.php?$cidReq&"
3948
            .http_build_query(
3949
                [
3950
                    'id' => $workId,
3951
                    'correction' => $isCorrection ? 1 : null,
3952
                ]
3953
            );
3954
3955
        header("Location: $url");
3956
        exit;
3957
    }
3958
3959
    /**
3960
     * @throws Exception
3961
     */
3962
    public function getAllUsersApiKeys(int $page, int $length, bool $force = false, ?int $urlId = null): array
3963
    {
3964
        if (false === api_get_configuration_value('webservice_enable_adminonly_api')
3965
            || !UserManager::is_admin($this->user->getId())
3966
        ) {
3967
            self::throwNotAllowedException();
3968
        }
3969
3970
        $limitOffset = ($page - 1) * $length;
3971
3972
        $currentUrlId = $urlId ?: api_get_current_access_url_id();
3973
3974
        $data = [];
3975
        $data['total'] = UserManager::get_number_of_users(0, $currentUrlId);
3976
        $data['list'] = array_map(
3977
            function (array $user) use ($force) {
3978
                $apiKeys = UserManager::get_api_keys($user['id'], self::SERVICE_NAME);
3979
                $apiKey = $apiKeys ? current($apiKeys) : null;
3980
3981
                if ($force && empty($apiKey)) {
3982
                    $apiKey = self::generateApiKeyForUser((int) $user['id']);
3983
                }
3984
3985
                return [
3986
                    'id' => (int) $user['id'],
3987
                    'username' => $user['username'],
3988
                    'api_key' => $apiKey,
3989
                ];
3990
            },
3991
            $users = UserManager::get_user_list([], [], $limitOffset, $length, $currentUrlId)
3992
        );
3993
        $data['length'] = count($users);
3994
3995
        if ($page * $length < $data['total']) {
3996
            $nextPageQueryParams = [
3997
                'page' => $page + 1,
3998
                'per_page' => $length,
3999
                'url_id' => $urlId,
4000
            ];
4001
4002
            $data['next'] = $this->generateUrl($nextPageQueryParams);
4003
        }
4004
4005
        return $data;
4006
    }
4007
4008
    /**
4009
     * @throws Exception
4010
     */
4011
    public function getUserApiKey(string $username, bool $force = false): array
4012
    {
4013
        if (false === api_get_configuration_value('webservice_enable_adminonly_api')
4014
            || !UserManager::is_admin($this->user->getId())
4015
        ) {
4016
            self::throwNotAllowedException();
4017
        }
4018
4019
        $userInfo = api_get_user_info_from_username($username);
4020
4021
        if (empty($userInfo)) {
4022
            throw new Exception(get_lang('UserNotFound'));
4023
        }
4024
4025
        $apiKeys = UserManager::get_api_keys($userInfo['id'], self::SERVICE_NAME);
4026
        $apiKey = $apiKeys ? current($apiKeys) : null;
4027
4028
        if ($force && empty($apiKey)) {
4029
            $apiKey = self::generateApiKeyForUser((int) $userInfo['id']);
4030
        }
4031
4032
        return [
4033
            'id' => $userInfo['id'],
4034
            'username' => $userInfo['username'],
4035
            'api_key' => $apiKey,
4036
        ];
4037
    }
4038
4039
    /**
4040
     * @throws Exception
4041
     */
4042
    public function getUserLastConnexion(string $username): array
4043
    {
4044
        $userInfo = api_get_user_info_from_username($username);
4045
4046
        if (empty($userInfo)) {
4047
            throw new Exception(get_lang('UserNotFound'));
4048
        }
4049
4050
        $lastConnexionDate = Tracking::get_last_connection_date($userInfo['id']);
4051
4052
        return [
4053
            'id' => $userInfo['id'],
4054
            'username' => $userInfo['username'],
4055
            'last_connexion_date' => $lastConnexionDate,
4056
        ];
4057
    }
4058
4059
    /**
4060
     * @throws Exception
4061
     */
4062
    public function getUserTotalConnexionTime(string $username): array
4063
    {
4064
        $userInfo = api_get_user_info_from_username($username);
4065
4066
        if (empty($userInfo)) {
4067
            throw new Exception(get_lang('UserNotFound'));
4068
        }
4069
4070
        $totalConnexionTimeInSecond = Tracking::get_time_spent_on_the_platform($userInfo['id'], 'ever');
4071
        $totalConnexionTime = api_time_to_hms($totalConnexionTimeInSecond);
4072
4073
        return [
4074
            'id' => $userInfo['id'],
4075
            'username' => $userInfo['username'],
4076
            'total_connexion_time' => $totalConnexionTime,
4077
        ];
4078
    }
4079
4080
    public static function isAllowedByRequest(bool $inpersonate = false): bool
4081
    {
4082
        $username = $_GET['username'] ?? null;
4083
        $apiKey = $_GET['api_key'] ?? null;
4084
4085
        if (empty($username) || empty($apiKey)) {
4086
            return false;
4087
        }
4088
4089
        try {
4090
            $restApi = self::validate($username, $apiKey);
4091
        } catch (Exception $e) {
4092
            return false;
4093
        }
4094
4095
        if ($inpersonate) {
4096
            Login::init_user($restApi->getUser()->getId(), true);
4097
        }
4098
4099
        return (bool) $restApi;
4100
    }
4101
4102
    public function viewMyCourses()
4103
    {
4104
        $url = api_get_path(WEB_PATH).'user_portal.php?'
4105
            .http_build_query(['nosession' => 'true']);
4106
4107
        header("Location: $url");
4108
        exit;
4109
    }
4110
4111
    /**
4112
     * Create a group/class.
4113
     *
4114
     * @param $params
4115
     *
4116
     * @throws Exception
4117
     */
4118
    public function addGroup($params): array
4119
    {
4120
        self::protectAdminEndpoint();
4121
4122
        if (!empty($params['type'])) {
4123
            $params['group_type'] = $params['type'];
4124
        }
4125
4126
        // First check wether the login already exists.
4127
        $userGroup = new UserGroup();
4128
        if ($userGroup->usergroup_exists($params['name'])) {
4129
            throw new Exception($params['name'].' '.get_lang('AlreadyExists'));
4130
        }
4131
4132
        $groupId = $userGroup->save($params);
4133
4134
        if (empty($groupId)) {
4135
            throw new Exception(get_lang('NotRegistered'));
4136
        }
4137
4138
        return [$groupId];
4139
    }
4140
4141
    /**
4142
     * Delete a group/class.
4143
     *
4144
     * @throws Exception
4145
     *
4146
     * @return bool
4147
     */
4148
    public function deleteGroup(int $id): array
4149
    {
4150
        self::protectAdminEndpoint();
4151
4152
        if (empty($id)) {
4153
            return false;
4154
        }
4155
4156
        // First check wether the login already exists.
4157
        $userGroup = new UserGroup();
4158
        if (!$userGroup->delete($id)) {
4159
            throw new Exception(get_lang('NotDeleted'));
4160
        }
4161
4162
        return [$id];
4163
    }
4164
4165
    /**
4166
     * Get the list of users subscribed to the given group/class.
4167
     *
4168
     * @return array The list of users (userID => [firstname, lastname, relation_type]
4169
     */
4170
    public function getGroupSubscribedUsers(int $groupId): array
4171
    {
4172
        $userGroup = new UserGroup();
4173
4174
        return $userGroup->get_all_users_by_group($groupId);
4175
    }
4176
4177
    /**
4178
     * Get the list of courses to which the given group/class is subscribed.
4179
     *
4180
     * @return array The list of courses (ID => [title]
4181
     */
4182
    public function getGroupSubscribedCourses(int $groupId): array
4183
    {
4184
        $userGroup = new UserGroup();
4185
4186
        return $userGroup->get_courses_by_usergroup($groupId, true);
4187
    }
4188
4189
    /**
4190
     * Get the list of sessions to which the given group/class is subscribed.
4191
     *
4192
     * @return array The list of courses (ID => [title]
4193
     */
4194
    public function getGroupSubscribedSessions(int $groupId): array
4195
    {
4196
        $userGroup = new UserGroup();
4197
4198
        return $userGroup->get_sessions_by_usergroup($groupId, true);
4199
    }
4200
4201
    /**
4202
     * Add a new user to the given group/class.
4203
     *
4204
     * @param int $relationType (1:admin, 2:reader, etc. See GROUP_USER_PERMISSION_ constants in api.lib.php)
4205
     *
4206
     * @return array One item array containing true on success, false otherwise
4207
     */
4208
    public function addGroupSubscribedUser(int $groupId, int $userId, int $relationType = 2): array
4209
    {
4210
        $userGroup = new UserGroup();
4211
4212
        if (!$userGroup->groupExists($groupId) or !$userGroup->userExists($userId)) {
4213
            throw new Exception('user_id or group_id does not exist');
4214
        }
4215
4216
        return [$userGroup->add_user_to_group($userId, $groupId, $relationType)];
4217
    }
4218
4219
    /**
4220
     * Get the list of group/class IDs to which the user belongs.
4221
     *
4222
     * @return array Array containing the group IDs like ['groups' => [1, 2, 3]]
4223
     */
4224
    public function getUserSubGroup(int $userId): array
4225
    {
4226
        $userGroup = new UserGroup();
4227
4228
        $res = $userGroup->get_usergroup_by_user($userId);
4229
4230
        return ['groups' => $res];
4231
    }
4232
4233
    /**
4234
     * Add a new course to which the given group/class is subscribed.
4235
     *
4236
     * @return array One item array containing the ID of the course on success, nothing on failure
4237
     */
4238
    public function addGroupSubscribedCourse(int $groupId, int $courseId): array
4239
    {
4240
        $userGroup = new UserGroup();
4241
4242
        return [$userGroup->subscribe_courses_to_usergroup($groupId, [$courseId], false)];
4243
    }
4244
4245
    /**
4246
     * Add a new session to which the given group/class is subscribed.
4247
     *
4248
     * @return array One item array containing the ID of the session on success, nothing on failure
4249
     */
4250
    public function addGroupSubscribedSession(int $groupId, int $sessionId): array
4251
    {
4252
        $userGroup = new UserGroup();
4253
4254
        return [$userGroup->subscribe_sessions_to_usergroup($groupId, [$sessionId], false)];
4255
    }
4256
4257
    /**
4258
     * Remove a user from the given group/class.
4259
     *
4260
     * @return array One item array containing true on success, false otherwise
4261
     */
4262
    public function deleteGroupSubscribedUser(int $groupId, int $userId): array
4263
    {
4264
        $userGroup = new UserGroup();
4265
4266
        return [$userGroup->delete_user_rel_group($userId, $groupId)];
4267
    }
4268
4269
    /**
4270
     * Remove a course to which the given group/class is subscribed.
4271
     *
4272
     * @return array One item array containing true on success, false otherwise
4273
     */
4274
    public function deleteGroupSubscribedCourse(int $groupId, int $courseId): array
4275
    {
4276
        $userGroup = new UserGroup();
4277
4278
        return [$userGroup->unsubscribe_courses_from_usergroup($groupId, [$courseId])];
4279
    }
4280
4281
    /**
4282
     * Remove a session to which the given group/class is subscribed.
4283
     *
4284
     * @return array One item array containing true on success, false otherwise
4285
     */
4286
    public function deleteGroupSubscribedSession(int $groupId, int $sessionId): array
4287
    {
4288
        $userGroup = new UserGroup();
4289
4290
        return [$userGroup->unsubscribeSessionsFromUserGroup($groupId, [$sessionId], false)];
4291
    }
4292
4293
    /**
4294
     * Get audit items from track_e_default.
4295
     *
4296
     * @throws Exception
4297
     */
4298
    public function getAuditItems(
4299
        string $defaultEventType,
4300
        ?int $cId = null,
4301
        ?int $sessionId = null,
4302
        ?string $afterDate = null,
4303
        ?string $beforeDate = null,
4304
        ?int $userId = null,
4305
        int $offset = 0,
4306
        int $limit = 100
4307
    ): array {
4308
        self::protectAdminEndpoint();
4309
4310
        return Event::getAuditItems(
4311
            $defaultEventType,
4312
            $cId,
4313
            $sessionId,
4314
            $afterDate,
4315
            $beforeDate,
4316
            $userId,
4317
            $offset,
4318
            $limit
4319
        );
4320
    }
4321
4322
    /**
4323
     * Generate an API key for webservices access for the given user ID.
4324
     */
4325
    protected static function generateApiKeyForUser(int $userId): string
4326
    {
4327
        UserManager::add_api_key($userId, self::SERVICE_NAME);
4328
4329
        $apiKeys = UserManager::get_api_keys($userId, self::SERVICE_NAME);
4330
4331
        return current($apiKeys);
4332
    }
4333
4334
    /**
4335
     * Encode the given parameters (structured array) in JSON format.
4336
     *
4337
     * @param array $additionalParams Optional
4338
     *
4339
     * @return string
4340
     */
4341
    private function encodeParams(array $additionalParams = [])
4342
    {
4343
        $params = array_merge(
4344
            $additionalParams,
4345
            [
4346
                'api_key' => $this->apiKey,
4347
                'username' => $this->user->getUsername(),
4348
            ]
4349
        );
4350
4351
        return json_encode($params);
4352
    }
4353
4354
    /**
4355
     * Helper generating a query URL (to the current script) from an array of parameters
4356
     * (course, session, api_key and username) commonly used in webservice calls.
4357
     */
4358
    private function generateUrl(array $additionalParams = []): string
4359
    {
4360
        $queryParams = [
4361
            'course' => $this->course ? $this->course->getId() : null,
4362
            'session' => $this->session ? $this->session->getId() : null,
4363
            'api_key' => $this->apiKey,
4364
            'username' => $this->user->getUsername(),
4365
        ];
4366
4367
        return api_get_self().'?'
4368
            .http_build_query(array_merge($queryParams, $additionalParams));
4369
    }
4370
}
4371