Passed
Pull Request — 1.11.x (#5023)
by Yannick
11:34
created

Rest::viewMyCourses()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 7
rs 10
cc 1
nc 1
nop 0
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
113
    public const GET_SESSION_FROM_EXTRA_FIELD = 'get_session_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): 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
            $shortList[] = [
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
        }
1705
1706
        return $shortList;
1707
    }
1708
1709
    /**
1710
     * Returns an array of groups with id, group_type, name, description, visibility.
1711
     *
1712
     * @param array $params An array of parameters to filter the results (currently supports 'type')
1713
     *
1714
     * @throws Exception
1715
     */
1716
    public function getGroups(array $params): array
1717
    {
1718
        self::protectAdminEndpoint();
1719
1720
        if ('*' === $params['type']) {
1721
            $conditions = [];
1722
        } else {
1723
            $conditions = ['where' => ['group_type = ?' => $params['type']]];
1724
        }
1725
        $userGroup = new UserGroup();
1726
        $groups = $userGroup->getDataToExport($conditions);
1727
        $list = [];
1728
        /** @var \Chamilo\UserBundle\Entity\Group $item */
1729
        foreach ($groups as $item) {
1730
            $listTemp = [
1731
                'id' => $item['id'],
1732
                'name' => $item['name'],
1733
                'description' => $item['description'],
1734
                'visibility' => $item['visibility'],
1735
                'type' => $item['group_type'],
1736
            ];
1737
            if (in_array($item['group_type'], [0, 1])) {
1738
                $listTemp['type_name'] = ($item['group_type'] == 0) ? 'class' : 'social';
1739
            }
1740
            if (in_array($item['visibility'], [1, 2])) {
1741
                $listTemp['visibility_name'] = ($item['visibility'] == 1) ? 'open' : 'closed';
1742
            }
1743
            $list[] = $listTemp;
1744
        }
1745
1746
        return $list;
1747
    }
1748
1749
    /**
1750
     * @throws Exception
1751
     */
1752
    public function addSession(array $params): array
1753
    {
1754
        self::protectAdminEndpoint();
1755
1756
        $name = $params['name'];
1757
        $coach_username = (int) $params['coach_username'];
1758
        $startDate = $params['access_start_date'];
1759
        $endDate = $params['access_end_date'];
1760
        $displayStartDate = $startDate;
1761
        $displayEndDate = $endDate;
1762
        $description = $params['description'];
1763
        $idUrlCampus = $params['id_campus'];
1764
        $extraFields = isset($params['extra']) ? $params['extra'] : [];
1765
1766
        $return = SessionManager::create_session(
1767
            $name,
1768
            $startDate,
1769
            $endDate,
1770
            $displayStartDate,
1771
            $displayEndDate,
1772
            null,
1773
            null,
1774
            $coach_username,
1775
            null,
1776
            1,
1777
            false,
1778
            null,
1779
            $description,
1780
            1,
1781
            $extraFields,
1782
            null,
1783
            false,
1784
            $idUrlCampus
1785
        );
1786
1787
        if ($return) {
1788
            $out = [
1789
                'status' => true,
1790
                'message' => get_lang('ANewSessionWasCreated'),
1791
                'id_session' => $return,
1792
            ];
1793
        } else {
1794
            $out = [
1795
                'status' => false,
1796
                'message' => get_lang('ErrorOccurred'),
1797
            ];
1798
        }
1799
1800
        return $out;
1801
    }
1802
1803
    public function addCourse(array $courseParam): array
1804
    {
1805
        self::protectAdminEndpoint();
1806
1807
        $idCampus = isset($courseParam['id_campus']) ? $courseParam['id_campus'] : 1;
1808
        $title = isset($courseParam['title']) ? $courseParam['title'] : '';
1809
        $wantedCode = isset($courseParam['wanted_code']) ? $courseParam['wanted_code'] : null;
1810
        $diskQuota = isset($courseParam['disk_quota']) ? $courseParam['disk_quota'] : '100';
1811
        $visibility = isset($courseParam['visibility']) ? (int) $courseParam['visibility'] : null;
1812
        $removeCampusId = $courseParam['remove_campus_id_from_wanted_code'] ?? 0;
1813
        $language = $courseParam['language'] ?? '';
1814
1815
        if (isset($courseParam['visibility'])) {
1816
            if ($courseParam['visibility'] &&
1817
                $courseParam['visibility'] >= 0 &&
1818
                $courseParam['visibility'] <= 3
1819
            ) {
1820
                $visibility = (int) $courseParam['visibility'];
1821
            }
1822
        }
1823
1824
        $params = [];
1825
        $params['title'] = $title;
1826
        $params['wanted_code'] = 'CAMPUS_'.$idCampus.'_'.$wantedCode;
1827
        if (1 === (int) $removeCampusId) {
1828
            $params['wanted_code'] = $wantedCode;
1829
        }
1830
        $params['user_id'] = $this->user->getId();
1831
        $params['visibility'] = $visibility;
1832
        $params['disk_quota'] = $diskQuota;
1833
        $params['course_language'] = $language;
1834
1835
        foreach ($courseParam as $key => $value) {
1836
            if (substr($key, 0, 6) === 'extra_') { //an extra field
1837
                $params[$key] = $value;
1838
            }
1839
        }
1840
1841
        $courseInfo = CourseManager::create_course($params, $params['user_id'], $idCampus);
1842
        $results = [];
1843
        if (!empty($courseInfo)) {
1844
            $results['status'] = true;
1845
            $results['id'] = $courseInfo['real_id'];
1846
            $results['code_course'] = $courseInfo['code'];
1847
            $results['title_course'] = $courseInfo['title'];
1848
            $extraFieldValues = new ExtraFieldValue('course');
1849
            $extraFields = $extraFieldValues->getAllValuesByItem($courseInfo['real_id']);
1850
            $results['extra_fields'] = $extraFields;
1851
            $results['message'] = sprintf(get_lang('CourseXAdded'), $courseInfo['code']);
1852
        } else {
1853
            $results['status'] = false;
1854
            $results['message'] = get_lang('CourseCreationFailed');
1855
        }
1856
1857
        return $results;
1858
    }
1859
1860
    /**
1861
     * @param $userParam
1862
     *
1863
     * @throws Exception
1864
     */
1865
    public function addUser($userParam): array
1866
    {
1867
        self::protectAdminEndpoint();
1868
1869
        $firstName = $userParam['firstname'];
1870
        $lastName = $userParam['lastname'];
1871
        $status = $userParam['status'];
1872
        $email = $userParam['email'];
1873
        $loginName = $userParam['loginname'];
1874
        $password = $userParam['password'];
1875
1876
        $official_code = '';
1877
        $language = '';
1878
        $phone = '';
1879
        $picture_uri = '';
1880
        $auth_source = $userParam['auth_source'] ?? PLATFORM_AUTH_SOURCE;
1881
        $expiration_date = '';
1882
        $active = 1;
1883
        $hr_dept_id = 0;
1884
        $original_user_id_name = $userParam['original_user_id_name'];
1885
        $original_user_id_value = $userParam['original_user_id_value'];
1886
        $sendMail = (empty($userParam['send_mail']) ? false : true);
1887
1888
        $extra_list = isset($userParam['extra']) ? $userParam['extra'] : [];
1889
        if (isset($userParam['language'])) {
1890
            $language = $userParam['language'];
1891
        }
1892
        if (isset($userParam['phone'])) {
1893
            $phone = $userParam['phone'];
1894
        }
1895
        if (isset($userParam['expiration_date'])) {
1896
            $expiration_date = $userParam['expiration_date'];
1897
        }
1898
1899
        // Default language.
1900
        if (empty($language)) {
1901
            $language = api_get_setting('platformLanguage');
1902
        }
1903
1904
        // First check wether the login already exists.
1905
        if (!UserManager::is_username_available($loginName)) {
1906
            throw new Exception(get_lang('UserNameNotAvailable'));
1907
        }
1908
1909
        $userId = UserManager::create_user(
1910
            $firstName,
1911
            $lastName,
1912
            $status,
1913
            $email,
1914
            $loginName,
1915
            $password,
1916
            $official_code,
1917
            $language,
1918
            $phone,
1919
            $picture_uri,
1920
            $auth_source,
1921
            $expiration_date,
1922
            $active,
1923
            $hr_dept_id,
1924
            [],
1925
            '',
1926
            $sendMail
1927
        );
1928
1929
        if (empty($userId)) {
1930
            throw new Exception(get_lang('UserNotRegistered'));
1931
        }
1932
1933
        if (api_is_multiple_url_enabled()) {
1934
            if (api_get_current_access_url_id() != -1) {
1935
                UrlManager::add_user_to_url(
1936
                    $userId,
1937
                    api_get_current_access_url_id()
1938
                );
1939
            } else {
1940
                UrlManager::add_user_to_url($userId, 1);
1941
            }
1942
        } else {
1943
            // We add by default the access_url_user table with access_url_id = 1
1944
            UrlManager::add_user_to_url($userId, 1);
1945
        }
1946
1947
        // Save new field label into user_field table.
1948
        UserManager::create_extra_field(
1949
            $original_user_id_name,
1950
            1,
1951
            $original_user_id_name,
1952
            ''
1953
        );
1954
        // Save the external system's id into user_field_value table.
1955
        UserManager::update_extra_field_value(
1956
            $userId,
1957
            $original_user_id_name,
1958
            $original_user_id_value
1959
        );
1960
1961
        if (is_array($extra_list) && count($extra_list) > 0) {
1962
            foreach ($extra_list as $extra) {
1963
                $extra_field_name = $extra['field_name'];
1964
                $extra_field_value = $extra['field_value'];
1965
                // Save new field label into user_field table.
1966
                UserManager::create_extra_field(
1967
                    $extra_field_name,
1968
                    1,
1969
                    $extra_field_name,
1970
                    ''
1971
                );
1972
                // Save the external system's id into user_field_value table.
1973
                UserManager::update_extra_field_value(
1974
                    $userId,
1975
                    $extra_field_name,
1976
                    $extra_field_value
1977
                );
1978
            }
1979
        }
1980
1981
        return [$userId];
1982
    }
1983
1984
    /**
1985
     * @throws Exception
1986
     */
1987
    public function addUserGetApikey(array $userParams): array
1988
    {
1989
        list($userId) = $this->addUser($userParams);
1990
1991
        UserManager::add_api_key($userId, self::SERVICE_NAME);
1992
1993
        $apiKey = UserManager::get_api_keys($userId, self::SERVICE_NAME);
1994
1995
        return [
1996
            'id' => $userId,
1997
            'api_key' => current($apiKey),
1998
        ];
1999
    }
2000
2001
    /**
2002
     * @throws Exception
2003
     */
2004
    public function updateUserApiKey(int $userId, string $oldApiKey): array
2005
    {
2006
        if (!api_is_platform_admin() && $userId != $this->user->getId()) {
2007
            self::throwNotAllowedException();
2008
        }
2009
2010
        if (false === $currentApiKeys = UserManager::get_api_keys($userId, self::SERVICE_NAME)) {
2011
            self::throwNotAllowedException();
2012
        }
2013
2014
        if (current($currentApiKeys) !== $oldApiKey) {
2015
            self::throwNotAllowedException();
2016
        }
2017
2018
        UserManager::update_api_key($userId, self::SERVICE_NAME);
2019
2020
        $apiKey = UserManager::get_api_keys($userId, self::SERVICE_NAME);
2021
2022
        return [
2023
            'api_key' => current($apiKey),
2024
        ];
2025
    }
2026
2027
    /**
2028
     * Subscribe User to Course.
2029
     *
2030
     * @throws Exception
2031
     */
2032
    public function subscribeUserToCourse(array $params): array
2033
    {
2034
        $course_id = $params['course_id'];
2035
        $course_code = $params['course_code'];
2036
        $user_id = $params['user_id'];
2037
        $status = $params['status'] ?? STUDENT;
2038
2039
        if (!api_is_platform_admin() && $user_id != $this->user->getId()) {
2040
            self::throwNotAllowedException();
2041
        }
2042
2043
        if (!$course_id && !$course_code) {
2044
            return [false];
2045
        }
2046
        if (!$course_code) {
2047
            $course_code = CourseManager::get_course_code_from_course_id($course_id);
2048
        }
2049
2050
        if (CourseManager::subscribeUser($user_id, $course_code, $status, 0, 0, false)) {
2051
            return [true];
2052
        }
2053
2054
        return [false];
2055
    }
2056
2057
    /**
2058
     * @throws Exception
2059
     */
2060
    public function subscribeUserToCoursePassword($courseCode, $password)
2061
    {
2062
        $courseInfo = api_get_course_info($courseCode);
2063
2064
        if (empty($courseInfo)) {
2065
            throw new Exception(get_lang('NoCourse'));
2066
        }
2067
2068
        if (sha1($password) === $courseInfo['registration_code']) {
2069
            CourseManager::processAutoSubscribeToCourse($courseCode);
2070
2071
            return;
2072
        }
2073
2074
        throw new Exception(get_lang('CourseRegistrationCodeIncorrect'));
2075
    }
2076
2077
    /**
2078
     * @throws Exception
2079
     */
2080
    public function unSubscribeUserToCourse(array $params): array
2081
    {
2082
        $courseId = $params['course_id'];
2083
        $courseCode = $params['course_code'];
2084
        $userId = $params['user_id'];
2085
2086
        if (!api_is_platform_admin() && $userId != $this->user->getId()) {
2087
            self::throwNotAllowedException();
2088
        }
2089
2090
        if (!$courseId && !$courseCode) {
2091
            return [false];
2092
        }
2093
2094
        if (!$courseCode) {
2095
            $courseCode = CourseManager::get_course_code_from_course_id($courseId);
2096
        }
2097
2098
        if (CourseManager::unsubscribe_user($userId, $courseCode)) {
2099
            return [true];
2100
        }
2101
2102
        return [false];
2103
    }
2104
2105
    public function deleteUserMessage($messageId, $messageType)
2106
    {
2107
        if ($messageType === 'sent') {
2108
            return MessageManager::delete_message_by_user_sender($this->user->getId(), $messageId);
2109
        } else {
2110
            return MessageManager::delete_message_by_user_receiver($this->user->getId(), $messageId);
2111
        }
2112
    }
2113
2114
    /**
2115
     * Set a given message as already read.
2116
     *
2117
     * @param $messageId
2118
     */
2119
    public function setMessageRead(int $messageId)
2120
    {
2121
        // MESSAGE_STATUS_NEW is also used for messages that have been "read"
2122
        MessageManager::update_message_status($this->user->getId(), $messageId, MESSAGE_STATUS_NEW);
2123
    }
2124
2125
    /**
2126
     * Add a group.
2127
     *
2128
     * @param array Params
2129
     */
2130
    public function createGroup($params)
2131
    {
2132
        self::protectAdminEndpoint();
2133
2134
        $name = $params['name'];
2135
        $description = $params['description'];
2136
    }
2137
2138
    /**
2139
     * Add Campus Virtual.
2140
     *
2141
     * @param array Params Campus
2142
     *
2143
     * @return array
2144
     */
2145
    public function createCampusURL($params)
2146
    {
2147
        $urlCampus = Security::remove_XSS($params['url']);
2148
        $description = Security::remove_XSS($params['description']);
2149
2150
        $active = isset($params['active']) ? intval($params['active']) : 0;
2151
        $num = UrlManager::url_exist($urlCampus);
2152
        if ($num == 0) {
2153
            // checking url
2154
            if (substr($urlCampus, strlen($urlCampus) - 1, strlen($urlCampus)) == '/') {
2155
                $idCampus = UrlManager::add($urlCampus, $description, $active, true);
2156
            } else {
2157
                //create
2158
                $idCampus = UrlManager::add($urlCampus.'/', $description, $active, true);
2159
            }
2160
2161
            return [
2162
                'status' => true,
2163
                'id_campus' => $idCampus,
2164
            ];
2165
        }
2166
2167
        return [
2168
            'status' => false,
2169
            'id_campus' => 0,
2170
        ];
2171
    }
2172
2173
    /**
2174
     * Edit Campus Virtual.
2175
     *
2176
     * @param array Params Campus
2177
     *
2178
     * @return array
2179
     */
2180
    public function editCampusURL($params)
2181
    {
2182
        $urlCampus = Security::remove_XSS($params['url']);
2183
        $description = Security::remove_XSS($params['description']);
2184
2185
        $active = isset($params['active']) ? intval($params['active']) : 0;
2186
        $url_id = isset($params['id']) ? intval($params['id']) : 0;
2187
2188
        if (!empty($url_id)) {
2189
            //we can't change the status of the url with id=1
2190
            if ($url_id == 1) {
2191
                $active = 1;
2192
            }
2193
            //checking url
2194
            if (substr($urlCampus, strlen($urlCampus) - 1, strlen($urlCampus)) == '/') {
2195
                UrlManager::update($url_id, $urlCampus, $description, $active);
2196
            } else {
2197
                UrlManager::update($url_id, $urlCampus.'/', $description, $active);
2198
            }
2199
2200
            return [true];
2201
        }
2202
2203
        return [false];
2204
    }
2205
2206
    /**
2207
     * Delete Campus Virtual.
2208
     *
2209
     * @param array Params Campus
2210
     *
2211
     * @return array
2212
     */
2213
    public function deleteCampusURL($params)
2214
    {
2215
        $url_id = isset($params['id']) ? intval($params['id']) : 0;
2216
2217
        $result = UrlManager::delete($url_id);
2218
        if ($result) {
2219
            return [
2220
                'status' => true,
2221
                'message' => get_lang('URLDeleted'),
2222
            ];
2223
        } else {
2224
            return [
2225
                'status' => false,
2226
                'message' => get_lang('Error'),
2227
            ];
2228
        }
2229
    }
2230
2231
    /**
2232
     * @throws Exception
2233
     */
2234
    public function addCoursesSession(array $params): array
2235
    {
2236
        self::protectAdminEndpoint();
2237
2238
        $sessionId = $params['id_session'];
2239
        $courseList = $params['list_courses'];
2240
        $importAssignments = isset($params['import_assignments']) && 1 === (int) $params['import_assignments'];
2241
2242
        $result = SessionManager::add_courses_to_session(
2243
            $sessionId,
2244
            $courseList,
2245
            true,
2246
            false,
2247
            false,
2248
            $importAssignments
2249
        );
2250
2251
        if ($result) {
2252
            return [
2253
                'status' => $result,
2254
                'message' => get_lang('Updated'),
2255
            ];
2256
        }
2257
2258
        return [
2259
            'status' => $result,
2260
            'message' => get_lang('ErrorOccurred'),
2261
        ];
2262
    }
2263
2264
    /**
2265
     * Simple legacy shortcut to subscribeUsersToSession.
2266
     *
2267
     * @throws Exception
2268
     */
2269
    public function addUsersSession(array $params): array
2270
    {
2271
        return self::subscribeUsersToSession($params);
2272
    }
2273
2274
    /**
2275
     * Subscribe a list of users to the given session.
2276
     *
2277
     * @param array $params Containing 'id_session' and 'list_users' entries
2278
     *
2279
     * @throws Exception
2280
     */
2281
    public function subscribeUsersToSession(array $params): array
2282
    {
2283
        $sessionId = $params['id_session'];
2284
        $userList = $params['list_users'];
2285
2286
        if (!is_array($userList)) {
2287
            $userList = [];
2288
        }
2289
2290
        if (!api_is_platform_admin() && !in_array($this->user->getId(), $userList)) {
2291
            self::throwNotAllowedException();
2292
        }
2293
2294
        SessionManager::subscribeUsersToSession(
2295
            $sessionId,
2296
            $userList,
2297
            null,
2298
            false
2299
        );
2300
2301
        return [
2302
            'status' => true,
2303
            'message' => get_lang('UsersAdded'),
2304
        ];
2305
    }
2306
2307
    /**
2308
     * Unsubscribe a given list of users from the given session.
2309
     *
2310
     * @throws Exception
2311
     */
2312
    public function unsubscribeUsersFromSession(array $params): array
2313
    {
2314
        self::protectAdminEndpoint();
2315
2316
        $sessionId = $params['id_session'];
2317
        $userList = $params['list_users'];
2318
2319
        if (!is_array($userList)) {
2320
            $userList = [];
2321
        }
2322
2323
        if (!api_is_platform_admin() && !in_array($this->user->getId(), $userList)) {
2324
            self::throwNotAllowedException();
2325
        }
2326
2327
        foreach ($userList as $userId) {
2328
            SessionManager::unsubscribe_user_from_session(
2329
                $sessionId,
2330
                $userId
2331
            );
2332
        }
2333
2334
        return [
2335
            'status' => true,
2336
            'message' => get_lang('UserUnsubscribed'),
2337
        ];
2338
    }
2339
2340
    /**
2341
     * Creates a session from a model session.
2342
     *
2343
     * @throws Exception
2344
     */
2345
    public function createSessionFromModel(HttpRequest $request): int
2346
    {
2347
        self::protectAdminEndpoint();
2348
2349
        $modelSessionId = $request->request->getInt('modelSessionId');
2350
        $sessionName = $request->request->get('sessionName');
2351
        $startDate = $request->request->get('startDate');
2352
        $endDate = $request->request->get('endDate');
2353
        $extraFields = $request->request->get('extraFields', []);
2354
        $duplicateAgendaContent = $request->request->getBoolean('duplicateAgendaContent');
2355
2356
        if (empty($modelSessionId) || empty($sessionName) || empty($startDate) || empty($endDate)) {
2357
            throw new Exception(get_lang('NoData'));
2358
        }
2359
2360
        if (!SessionManager::isValidId($modelSessionId)) {
2361
            throw new Exception(get_lang('ModelSessionDoesNotExist'));
2362
        }
2363
2364
        $modelSession = SessionManager::fetch($modelSessionId);
2365
2366
        $modelSession['accessUrlId'] = 1;
2367
        if (api_is_multiple_url_enabled()) {
2368
            if (api_get_current_access_url_id() != -1) {
2369
                $modelSession['accessUrlId'] = api_get_current_access_url_id();
2370
            }
2371
        }
2372
2373
        $newSessionId = SessionManager::create_session(
2374
            $sessionName,
2375
            $startDate,
2376
            $endDate,
2377
            $startDate,
2378
            $endDate,
2379
            $startDate,
2380
            $endDate,
2381
            $modelSession['id_coach'],
2382
            $modelSession['session_category_id'],
2383
            $modelSession['visibility'],
2384
            false,
2385
            $modelSession['duration'],
2386
            $modelSession['description'],
2387
            $modelSession['show_description'],
2388
            $extraFields,
2389
            $modelSession['session_admin_id'],
2390
            $modelSession['send_subscription_notification'],
2391
            $modelSession['accessUrlId']
2392
        );
2393
2394
        if (empty($newSessionId)) {
2395
            throw new Exception(get_lang('SessionNotRegistered'));
2396
        }
2397
2398
        if (is_string($newSessionId)) {
2399
            throw new Exception($newSessionId);
2400
        }
2401
2402
        $promotionId = $modelSession['promotion_id'];
2403
        if ($promotionId) {
2404
            $sessionList = array_keys(SessionManager::get_all_sessions_by_promotion($promotionId));
2405
            $sessionList[] = $newSessionId;
2406
            SessionManager::subscribe_sessions_to_promotion($modelSession['promotion_id'], $sessionList);
2407
        }
2408
2409
        $modelExtraFields = [];
2410
        $fields = SessionManager::getFilteredExtraFields($modelSessionId);
2411
        if (is_array($fields) and !empty($fields)) {
2412
            foreach ($fields as $field) {
2413
                $modelExtraFields[$field['variable']] = $field['value'];
2414
            }
2415
        }
2416
        $allExtraFields = array_merge($modelExtraFields, $extraFields);
2417
        foreach ($allExtraFields as $name => $value) {
2418
            // SessionManager::update_session_extra_field_value returns false when no row is changed,
2419
            // which can happen since extra field values are initialized by SessionManager::create_session
2420
            // therefore we do not throw an exception when false is returned
2421
            SessionManager::update_session_extra_field_value($newSessionId, $name, $value);
2422
        }
2423
2424
        $courseList = array_keys(SessionManager::get_course_list_by_session_id($modelSessionId));
2425
        if (is_array($courseList)
2426
            && !empty($courseList)
2427
            && !SessionManager::add_courses_to_session($newSessionId, $courseList)) {
2428
            throw new Exception(get_lang('CoursesNotAddedToSession'));
2429
        }
2430
2431
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
2432
        $courseListOrdered = SessionManager::get_course_list_by_session_id($modelSessionId, null, 'position');
2433
        $count = 0;
2434
        foreach ($courseListOrdered as $course) {
2435
            if ($course['position'] == '') {
2436
                $course['position'] = $count;
2437
            }
2438
            // Saving order.
2439
            $sql = "UPDATE $table SET position = ".$course['position']."
2440
                    WHERE session_id = $newSessionId AND c_id = '".$course['real_id']."'";
2441
            Database::query($sql);
2442
            $count++;
2443
        }
2444
2445
        if ($duplicateAgendaContent) {
2446
            foreach ($courseList as $courseId) {
2447
                SessionManager::importAgendaFromSessionModel($modelSessionId, $newSessionId, $courseId);
2448
            }
2449
        }
2450
2451
        if (api_is_multiple_url_enabled()) {
2452
            if (api_get_current_access_url_id() != -1) {
2453
                UrlManager::add_session_to_url(
2454
                    $newSessionId,
2455
                    api_get_current_access_url_id()
2456
                );
2457
            } else {
2458
                UrlManager::add_session_to_url($newSessionId, 1);
2459
            }
2460
        } else {
2461
            UrlManager::add_session_to_url($newSessionId, 1);
2462
        }
2463
2464
        return $newSessionId;
2465
    }
2466
2467
    /**
2468
     * subscribes a user to a session.
2469
     *
2470
     * @throws Exception
2471
     */
2472
    public function subscribeUserToSessionFromUsername(int $sessionId, string $loginName): bool
2473
    {
2474
        if (!SessionManager::isValidId($sessionId)) {
2475
            throw new Exception(get_lang('SessionNotFound'));
2476
        }
2477
2478
        $userId = UserManager::get_user_id_from_username($loginName);
2479
        if (false === $userId) {
2480
            throw new Exception(get_lang('UserNotFound'));
2481
        }
2482
2483
        if (!api_is_platform_admin() && $userId != $this->user->getId()) {
2484
            self::throwNotAllowedException();
2485
        }
2486
2487
        $subscribed = SessionManager::subscribeUsersToSession(
2488
            $sessionId,
2489
            [$userId],
2490
            SESSION_VISIBLE_READ_ONLY,
2491
            false
2492
        );
2493
        if (!$subscribed) {
2494
            throw new Exception(get_lang('UserNotSubscribed'));
2495
        }
2496
2497
        return true;
2498
    }
2499
2500
    /**
2501
     * finds the session which has a specific value in a specific extra field.
2502
     *
2503
     * @param $fieldName
2504
     * @param $fieldValue
2505
     *
2506
     * @throws Exception when no session matched or more than one session matched
2507
     *
2508
     * @return int, the matching session id
2509
     */
2510
    public function getSessionFromExtraField($fieldName, $fieldValue)
2511
    {
2512
        // find sessions that that have value in field
2513
        $valueModel = new ExtraFieldValue('session');
2514
        $sessionIdList = $valueModel->get_item_id_from_field_variable_and_field_value(
2515
            $fieldName,
2516
            $fieldValue,
2517
            false,
2518
            false,
2519
            true
2520
        );
2521
2522
        // throw if none found
2523
        if (empty($sessionIdList)) {
2524
            throw new Exception(get_lang('NoSessionMatched'));
2525
        }
2526
2527
        // throw if more than one found
2528
        if (count($sessionIdList) > 1) {
2529
            throw new Exception(get_lang('MoreThanOneSessionMatched'));
2530
        }
2531
2532
        // return sessionId
2533
        return intval($sessionIdList[0]['item_id']);
2534
    }
2535
2536
    /**
2537
     * Get a list of users subscribed to the given session.
2538
     *
2539
     * @params int $sessionId
2540
     * @params int $moveInfo Whether to return the "moved_*" fields or not
2541
     */
2542
    public function getUsersSubscribedToSession(int $sessionId, int $moveInfo = 0): array
2543
    {
2544
        self::protectAdminEndpoint();
2545
2546
        $users = SessionManager::get_users_by_session($sessionId);
2547
2548
        $userList = [];
2549
        foreach ($users as $user) {
2550
            $userInfo = [
2551
                'user_id' => $user['user_id'],
2552
                'username' => $user['username'],
2553
                'firstname' => $user['firstname'],
2554
                'lastname' => $user['lastname'],
2555
                'status' => $user['relation_type'],
2556
            ];
2557
            if (1 === $moveInfo) {
2558
                $userInfo['moved_to'] = $user['moved_to'];
2559
                $userInfo['moved_status'] = $user['moved_status'];
2560
                $userInfo['moved_at'] = $user['moved_at'];
2561
            }
2562
            $userList[] = $userInfo;
2563
        }
2564
2565
        return $userList;
2566
    }
2567
2568
    /**
2569
     * Updates a user identified by its login name.
2570
     *
2571
     * @throws Exception on failure
2572
     */
2573
    public function updateUserFromUserName(array $parameters): bool
2574
    {
2575
        // find user
2576
        $userId = null;
2577
        if (empty($parameters)) {
2578
            throw new Exception('NoData');
2579
        }
2580
        foreach ($parameters as $name => $value) {
2581
            if (strtolower($name) === 'loginname') {
2582
                $userId = UserManager::get_user_id_from_username($value);
2583
                if (false === $userId) {
2584
                    throw new Exception(get_lang('UserNotFound'));
2585
                }
2586
                break;
2587
            }
2588
        }
2589
        if (is_null($userId)) {
2590
            throw new Exception(get_lang('NoData'));
2591
        }
2592
2593
        if (!api_is_platform_admin() && $userId != $this->user->getId()) {
2594
            self::throwNotAllowedException();
2595
        }
2596
2597
        /** @var User $user */
2598
        $user = UserManager::getRepository()->find($userId);
2599
        if (empty($user)) {
2600
            throw new Exception(get_lang('CouldNotLoadUser'));
2601
        }
2602
2603
        // tell the world we are about to update a user
2604
        $hook = HookUpdateUser::create();
2605
        if (!empty($hook)) {
2606
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_PRE);
2607
        }
2608
2609
        // apply submitted modifications
2610
        foreach ($parameters as $name => $value) {
2611
            switch (strtolower($name)) {
2612
                case 'email':
2613
                    $user->setEmail($value);
2614
                    break;
2615
                case 'enabled':
2616
                    $user->setEnabled($value);
2617
                    break;
2618
                case 'lastname':
2619
                    $user->setLastname($value);
2620
                    break;
2621
                case 'firstname':
2622
                    $user->setFirstname($value);
2623
                    break;
2624
                case 'phone':
2625
                    $user->setPhone($value);
2626
                    break;
2627
                case 'address':
2628
                    $user->setAddress($value);
2629
                    break;
2630
                case 'roles':
2631
                    $user->setRoles($value);
2632
                    break;
2633
                case 'profile_completed':
2634
                    $user->setProfileCompleted($value);
2635
                    break;
2636
                case 'auth_source':
2637
                    $user->setAuthSource($value);
2638
                    break;
2639
                case 'status':
2640
                    $user->setStatus($value);
2641
                    break;
2642
                case 'official_code':
2643
                    $user->setOfficialCode($value);
2644
                    break;
2645
                case 'picture_uri':
2646
                    $user->setPictureUri($value);
2647
                    break;
2648
                case 'creator_id':
2649
                    $user->setCreatorId($value);
2650
                    break;
2651
                case 'competences':
2652
                    $user->setCompetences($value);
2653
                    break;
2654
                case 'diplomas':
2655
                    $user->setDiplomas($value);
2656
                    break;
2657
                case 'openarea':
2658
                    $user->setOpenArea($value);
2659
                    break;
2660
                case 'teach':
2661
                    $user->setTeach($value);
2662
                    break;
2663
                case 'productions':
2664
                    $user->setProductions($value);
2665
                    break;
2666
                case 'language':
2667
                    $languages = api_get_languages();
2668
                    if (!in_array($value, $languages['folder'])) {
2669
                        throw new Exception(get_lang('LanguageUnavailable'));
2670
                    }
2671
                    $user->setLanguage($value);
2672
                    break;
2673
                case 'registration_date':
2674
                    $user->setRegistrationDate($value);
2675
                    break;
2676
                case 'expiration_date':
2677
                    $user->setExpirationDate(
2678
                        new DateTime(
2679
                            api_get_utc_datetime($value),
2680
                            new DateTimeZone('UTC')
2681
                        )
2682
                    );
2683
                    break;
2684
                case 'active':
2685
                    // see UserManager::update_user() usermanager.lib.php:1205
2686
                    if ($user->getActive() != $value) {
2687
                        $user->setActive($value);
2688
                        Event::addEvent($value ? LOG_USER_ENABLE : LOG_USER_DISABLE, LOG_USER_ID, $userId);
2689
                    }
2690
                    break;
2691
                case 'openid':
2692
                    $user->setOpenId($value);
2693
                    break;
2694
                case 'theme':
2695
                    $user->setTheme($value);
2696
                    break;
2697
                case 'hr_dept_id':
2698
                    $user->setHrDeptId($value);
2699
                    break;
2700
                case 'extra':
2701
                    if (is_array($value)) {
2702
                        if (count($value) > 0) {
2703
                            if (is_array($value[0])) {
2704
                                foreach ($value as $field) {
2705
                                    $fieldName = $field['field_name'];
2706
                                    $fieldValue = $field['field_value'];
2707
                                    if (!isset($fieldName) || !isset($fieldValue) ||
2708
                                        !UserManager::update_extra_field_value($userId, $fieldName, $fieldValue)) {
2709
                                        throw new Exception(get_lang('CouldNotUpdateExtraFieldValue').': '.print_r($field, true));
2710
                                    }
2711
                                }
2712
                            } else {
2713
                                foreach ($value as $fieldName => $fieldValue) {
2714
                                    if (!UserManager::update_extra_field_value($userId, $fieldName, $fieldValue)) {
2715
                                        throw new Exception(get_lang('CouldNotUpdateExtraFieldValue').': '.$fieldName);
2716
                                    }
2717
                                }
2718
                            }
2719
                        }
2720
                    }
2721
                    break;
2722
                case 'username':
2723
                case 'api_key':
2724
                case 'action':
2725
                case 'loginname':
2726
                    break;
2727
                case 'email_canonical':
2728
                case 'locked':
2729
                case 'expired':
2730
                case 'credentials_expired':
2731
                case 'credentials_expire_at':
2732
                case 'expires_at':
2733
                case 'salt':
2734
                case 'last_login':
2735
                case 'created_at':
2736
                case 'updated_at':
2737
                case 'confirmation_token':
2738
                case 'password_requested_at':
2739
                case 'password': // see UserManager::update_user usermanager.lib.php:1182
2740
                case 'username_canonical':
2741
                default:
2742
                    throw new Exception(get_lang('UnsupportedUpdate')." '$name'");
2743
            }
2744
        }
2745
2746
        // save modifications
2747
        UserManager::getManager()->updateUser($user, true);
2748
2749
        // tell the world we just updated this user
2750
        if (!empty($hook)) {
2751
            $hook->setEventData(['user' => $user]);
2752
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_POST);
2753
        }
2754
2755
        // invalidate cache for this user
2756
        $cacheAvailable = api_get_configuration_value('apc');
2757
        if ($cacheAvailable === true) {
2758
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$userId;
2759
            if (apcu_exists($apcVar)) {
2760
                apcu_delete($apcVar);
2761
            }
2762
        }
2763
2764
        return true;
2765
    }
2766
2767
    /**
2768
     * Returns whether a user login name exists.
2769
     *
2770
     * @param string $loginname the user login name
2771
     *
2772
     * @return bool whether the user login name exists
2773
     */
2774
    public function usernameExist($loginname)
2775
    {
2776
        return false !== api_get_user_info_from_username($loginname);
2777
    }
2778
2779
    /**
2780
     * Returns whether a user group name exists.
2781
     *
2782
     * @param string $name the group name
2783
     *
2784
     * @return bool whether the group name exists
2785
     */
2786
    public function groupExists($name)
2787
    {
2788
        $userGroup = new UserGroup();
2789
2790
        return false !== $userGroup->usergroup_exists($name);
2791
    }
2792
2793
    /**
2794
     * This service roughly matches what the call to MDL's API core_course_get_contents function returns.
2795
     *
2796
     * @return array
2797
     */
2798
    public function getCourseQuizMdlCompat()
2799
    {
2800
        $userId = $this->user->getId();
2801
        $courseId = $this->course->getId();
2802
        $sessionId = $this->session ? $this->session->getId() : 0;
2803
2804
        $toolVisibility = CourseHome::getToolVisibility(TOOL_QUIZ, $courseId, $sessionId);
2805
2806
        $json = [
2807
            "id" => $this->course->getId(),
2808
            "name" => get_lang('Exercises'),
2809
            "visible" => (int) $toolVisibility,
2810
            "summary" => '',
2811
            "summaryformat" => 1,
2812
            "section" => 1,
2813
            "hiddenbynumsections" => 0,
2814
            "uservisible" => $toolVisibility,
2815
            "modules" => [],
2816
        ];
2817
2818
        $quizIcon = Display::return_icon('quiz.png', '', [], ICON_SIZE_SMALL, false, true);
2819
2820
        $json['modules'] = array_map(
2821
            function (array $exercise) use ($quizIcon) {
2822
                return [
2823
                    'id' => (int) $exercise['id'],
2824
                    'url' => $exercise['url'],
2825
                    'name' => $exercise['name'],
2826
                    'instance' => 1,
2827
                    'visible' => 1,
2828
                    'uservisible' => true,
2829
                    'visibleoncoursepage' => 0,
2830
                    'modicon' => $quizIcon,
2831
                    'modname' => 'quiz',
2832
                    'modplural' => get_lang('Exercises'),
2833
                    'availability' => null,
2834
                    'indent' => 0,
2835
                    'onclick' => '',
2836
                    'afterlink' => null,
2837
                    'customdata' => "",
2838
                    'noviewlink' => false,
2839
                    'completion' => (int) ($exercise[1] > 0),
2840
                ];
2841
            },
2842
            Exercise::exerciseGrid(0, '', $userId, $courseId, $sessionId, true)
2843
        );
2844
2845
        return [$json];
2846
    }
2847
2848
    /**
2849
     * @throws Exception
2850
     */
2851
    public function updateSession(array $params): array
2852
    {
2853
        self::protectAdminEndpoint();
2854
2855
        $id = $params['session_id'];
2856
        $reset = $params['reset'] ?? null;
2857
        $name = $params['name'] ?? null;
2858
        $coachId = isset($params['id_coach']) ? (int) $params['id_coach'] : null;
2859
        $sessionCategoryId = isset($params['session_category_id']) ? (int) $params['session_category_id'] : null;
2860
        $description = $params['description'] ?? null;
2861
        $showDescription = $params['show_description'] ?? null;
2862
        $duration = $params['duration'] ?? null;
2863
        $visibility = $params['visibility'] ?? null;
2864
        $promotionId = $params['promotion_id'] ?? null;
2865
        $displayStartDate = $params['display_start_date'] ?? null;
2866
        $displayEndDate = $params['display_end_date'] ?? null;
2867
        $accessStartDate = $params['access_start_date'] ?? null;
2868
        $accessEndDate = $params['access_end_date'] ?? null;
2869
        $coachStartDate = $params['coach_access_start_date'] ?? null;
2870
        $coachEndDate = $params['coach_access_end_date'] ?? null;
2871
        $sendSubscriptionNotification = $params['send_subscription_notification'] ?? null;
2872
        $extraFields = $params['extra'] ?? [];
2873
2874
        $reset = (bool) $reset;
2875
        $visibility = (int) $visibility;
2876
        $tblSession = Database::get_main_table(TABLE_MAIN_SESSION);
2877
2878
        if (!SessionManager::isValidId($id)) {
2879
            throw new Exception(get_lang('NoData'));
2880
        }
2881
2882
        if (!empty($accessStartDate) && !api_is_valid_date($accessStartDate, 'Y-m-d H:i') &&
2883
            !api_is_valid_date($accessStartDate, 'Y-m-d H:i:s')
2884
        ) {
2885
            throw new Exception(get_lang('InvalidDate'));
2886
        }
2887
2888
        if (!empty($accessEndDate) && !api_is_valid_date($accessEndDate, 'Y-m-d H:i') &&
2889
            !api_is_valid_date($accessEndDate, 'Y-m-d H:i:s')
2890
        ) {
2891
            throw new Exception(get_lang('InvalidDate'));
2892
        }
2893
2894
        if (!empty($accessStartDate) && !empty($accessEndDate) && $accessStartDate >= $accessEndDate) {
2895
            throw new Exception(get_lang('InvalidDate'));
2896
        }
2897
2898
        $values = [];
2899
2900
        if ($reset) {
2901
            $values['name'] = $name;
2902
            $values['id_coach'] = $coachId;
2903
            $values['session_category_id'] = $sessionCategoryId;
2904
            $values['description'] = $description;
2905
            $values['show_description'] = $showDescription;
2906
            $values['duration'] = $duration;
2907
            $values['visibility'] = $visibility;
2908
            $values['promotion_id'] = $promotionId;
2909
            $values['display_start_date'] = !empty($displayStartDate) ? api_get_utc_datetime($displayStartDate) : null;
2910
            $values['display_end_date'] = !empty($displayEndDate) ? api_get_utc_datetime($displayEndDate) : null;
2911
            $values['access_start_date'] = !empty($accessStartDate) ? api_get_utc_datetime($accessStartDate) : null;
2912
            $values['access_end_date'] = !empty($accessEndDate) ? api_get_utc_datetime($accessEndDate) : null;
2913
            $values['coach_access_start_date'] = !empty($coachStartDate) ? api_get_utc_datetime($coachStartDate) : null;
2914
            $values['coach_access_end_date'] = !empty($coachEndDate) ? api_get_utc_datetime($coachEndDate) : null;
2915
            $values['send_subscription_notification'] = $sendSubscriptionNotification;
2916
        } else {
2917
            if (!empty($name)) {
2918
                $values['name'] = $name;
2919
            }
2920
2921
            if (!empty($coachId)) {
2922
                $values['id_coach'] = $coachId;
2923
            }
2924
2925
            if (!empty($sessionCategoryId)) {
2926
                $values['session_category_id'] = $sessionCategoryId;
2927
            }
2928
2929
            if (!empty($description)) {
2930
                $values['description'] = $description;
2931
            }
2932
2933
            if (!empty($showDescription)) {
2934
                $values['show_description'] = $showDescription;
2935
            }
2936
2937
            if (!empty($duration)) {
2938
                $values['duration'] = $duration;
2939
            }
2940
2941
            if (!empty($visibility)) {
2942
                $values['visibility'] = $visibility;
2943
            }
2944
2945
            if (!empty($promotionId)) {
2946
                $values['promotion_id'] = $promotionId;
2947
            }
2948
2949
            if (!empty($displayStartDate)) {
2950
                $values['display_start_date'] = api_get_utc_datetime($displayStartDate);
2951
            }
2952
2953
            if (!empty($displayEndDate)) {
2954
                $values['display_end_date'] = api_get_utc_datetime($displayEndDate);
2955
            }
2956
2957
            if (!empty($accessStartDate)) {
2958
                $values['access_start_date'] = api_get_utc_datetime($accessStartDate);
2959
            }
2960
2961
            if (!empty($accessEndDate)) {
2962
                $values['access_end_date'] = api_get_utc_datetime($accessEndDate);
2963
            }
2964
2965
            if (!empty($coachStartDate)) {
2966
                $values['coach_access_start_date'] = api_get_utc_datetime($coachStartDate);
2967
            }
2968
2969
            if (!empty($coachEndDate)) {
2970
                $values['coach_access_end_date'] = api_get_utc_datetime($coachEndDate);
2971
            }
2972
2973
            if (!empty($sendSubscriptionNotification)) {
2974
                $values['send_subscription_notification'] = $sendSubscriptionNotification;
2975
            }
2976
        }
2977
2978
        Database::update(
2979
            $tblSession,
2980
            $values,
2981
            ['id = ?' => $id]
2982
        );
2983
2984
        if (!empty($extraFields)) {
2985
            $extraFields['item_id'] = $id;
2986
            $sessionFieldValue = new ExtraFieldValue('session');
2987
            $sessionFieldValue->saveFieldValues($extraFields);
2988
        }
2989
2990
        return [
2991
            'status' => true,
2992
            'message' => get_lang('Updated'),
2993
            'id_session' => $id,
2994
        ];
2995
    }
2996
2997
    public function checkConditionalLogin(): bool
2998
    {
2999
        $file = api_get_path(SYS_CODE_PATH).'auth/conditional_login/conditional_login.php';
3000
3001
        if (!file_exists($file)) {
3002
            return true;
3003
        }
3004
3005
        include_once $file;
3006
3007
        if (!isset($login_conditions)) {
3008
            return true;
3009
        }
3010
3011
        foreach ($login_conditions as $condition) {
3012
            //If condition fails we redirect to the URL defined by the condition
3013
            if (!isset($condition['conditional_function'])) {
3014
                continue;
3015
            }
3016
3017
            $function = $condition['conditional_function'];
3018
            $result = $function(['user_id' => $this->user->getId()]);
3019
3020
            if ($result == false) {
3021
                return false;
3022
            }
3023
        }
3024
3025
        return true;
3026
    }
3027
3028
    public function getLegalConditions(): array
3029
    {
3030
        $language = api_get_language_id(
3031
            api_get_interface_language()
3032
        );
3033
3034
        $termPreview = LegalManager::get_last_condition($language);
3035
3036
        if ($termPreview) {
3037
            return $termPreview;
3038
        }
3039
3040
        $language = api_get_language_id(
3041
            api_get_setting('platformLanguage')
3042
        );
3043
3044
        $termPreview = LegalManager::get_last_condition($language);
3045
3046
        if ($termPreview) {
3047
            return $termPreview;
3048
        }
3049
3050
        $language = api_get_language_id('english');
3051
3052
        return LegalManager::get_last_condition($language);
3053
    }
3054
3055
    public function updateConditionAccepted()
3056
    {
3057
        $legalAcceptType = $_POST['legal_accept_type'] ?? null;
3058
3059
        $condArray = explode(':', $legalAcceptType);
3060
        $condArray = array_map('intval', $condArray);
3061
3062
        if (empty($condArray[0]) || empty($condArray[1])) {
3063
            return;
3064
        }
3065
3066
        $conditionToSave = intval($condArray[0]).':'.intval($condArray[1]).':'.time();
3067
3068
        LegalManager::sendEmailToUserBoss(
3069
            $this->user->getId(),
3070
            $conditionToSave
3071
        );
3072
    }
3073
3074
    /**
3075
     * Get the list of test with last user attempt and his datetime.
3076
     *
3077
     * @param array $fields A list of extra fields to include in the answer. Searches for the field in exercise, then in course
3078
     *
3079
     * @throws Exception
3080
     */
3081
    public function getTestUpdatesList($fields = []): array
3082
    {
3083
        self::protectAdminEndpoint();
3084
3085
        $tableCQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
3086
        $tableTrackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3087
        $tableUser = Database::get_main_table(TABLE_MAIN_USER);
3088
        $resultArray = [];
3089
3090
        // Check the extra fields criteria (whether to add extra field information or not)
3091
        $fieldSource = [];
3092
        $extraFieldValuesTable = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3093
        $fieldsSearchString = "SELECT field_id, value FROM $extraFieldValuesTable WHERE item_id = %d AND field_id = %d";
3094
        if (count($fields) > 0) {
3095
            // For each field, check where to get it from (quiz or course)
3096
            $quizExtraField = new ExtraField('exercise');
3097
            $courseExtraField = new ExtraField('course');
3098
            foreach ($fields as $fieldName) {
3099
                $fieldExists = $quizExtraField->get_handler_field_info_by_field_variable($fieldName);
3100
                if ($fieldExists === false) {
3101
                    // The field does not exist on the exercise, so use it from the course
3102
                    $courseFieldExists = $courseExtraField->get_handler_field_info_by_field_variable($fieldName);
3103
                    if ($courseFieldExists === false) {
3104
                        continue;
3105
                    }
3106
                    $fieldSource[$fieldName] = ['item_type' => 'course', 'id' => $courseFieldExists['id']];
3107
                } else {
3108
                    $fieldSource[$fieldName] = ['item_type' => 'exercise', 'id' => $fieldExists['id']];
3109
                }
3110
            }
3111
        }
3112
3113
        $sql = "
3114
            SELECT q.iid AS id,
3115
                q.title,
3116
                MAX(a.start_date) AS last_attempt_time,
3117
                u.username AS last_attempt_username,
3118
                q.c_id
3119
            FROM $tableCQuiz q
3120
            JOIN $tableTrackExercises a ON q.iid = a.exe_exo_id
3121
            JOIN $tableUser u ON a.exe_user_id = u.id
3122
            GROUP BY q.iid
3123
        ";
3124
3125
        $result = Database::query($sql);
3126
        if (Database::num_rows($result) > 0) {
3127
            while ($row = Database::fetch_assoc($result)) {
3128
                // Check the whole extra fields thing
3129
                if (count($fieldSource) > 0) {
3130
                    foreach ($fieldSource as $fieldName => $fieldDetails) {
3131
                        if ($fieldDetails['item_type'] == 'course') {
3132
                            $itemId = $row['c_id'];
3133
                        } else {
3134
                            $itemId = $row['id'];
3135
                        }
3136
                        $fieldResult = Database::query(sprintf($fieldsSearchString, $itemId, $fieldDetails['id']));
3137
                        if (Database::num_rows($fieldResult) > 0) {
3138
                            $fieldRow = Database::fetch_assoc($fieldResult);
3139
                            $row['extra_'.$fieldName] = $fieldRow['value'];
3140
                        } else {
3141
                            $row['extra_'.$fieldName] = '';
3142
                        }
3143
                    }
3144
                }
3145
                // Get item authoring data
3146
                $itemProps = api_get_last_item_property_info($row['c_id'], 'quiz', $row['id']);
3147
                $row['created_by'] = $this->__getConfiguredUsernameById($itemProps['insert_user_id']);
3148
                if ($itemProps['insert_user_id'] == $itemProps['lastedit_user_id']) {
3149
                    $row['updated_by'] = $row['created_by'];
3150
                } else {
3151
                    $row['updated_by'] = $this->__getConfiguredUsernameById($itemProps['lastedit_user_id']);
3152
                }
3153
                $resultArray[] = $row;
3154
            }
3155
        }
3156
3157
        return $resultArray;
3158
    }
3159
3160
    /**
3161
     * Get tests results data
3162
     * Not support sessions
3163
     * By default, is successful if score greater than 50%.
3164
     *
3165
     * @throws Exception
3166
     *
3167
     * @return array e.g: [ { "id": 4, "title": "aiken", "updated_by": "-", "type": "1", "completion": 0 } ]
3168
     */
3169
    public function getTestAverageResultsList(array $ids = [], ?array $fields = []): array
3170
    {
3171
        self::protectAdminEndpoint();
3172
        $tableTrackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3173
        $tableCQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
3174
        $tableCourseRelUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3175
3176
        $resultArray = [];
3177
        $countUsersInCourses = [];
3178
        $extraArray = [];
3179
3180
        if (!empty($ids)) {
3181
            if (!is_array($ids)) {
3182
                $ids = [$ids];
3183
            }
3184
            if (!is_array($fields)) {
3185
                $fields = [$fields];
3186
            }
3187
            if (!empty($fields)) {
3188
                foreach ($fields as $field) {
3189
                    $extraArray['extra_'.$field] = '';
3190
                }
3191
            }
3192
3193
            $queryUsersInCourses = "
3194
                SELECT c_id, count(*)
3195
                FROM $tableCourseRelUser
3196
                GROUP BY c_id
3197
                ORDER BY c_id;
3198
            ";
3199
3200
            $resultUsersInCourses = Database::query($queryUsersInCourses);
3201
            while ($row = Database::fetch_array($resultUsersInCourses)) {
3202
                $countUsersInCourses[$row[0]] = $row[1];
3203
            }
3204
3205
            foreach ($ids as $item) {
3206
                $item = (int) $item;
3207
3208
                $queryCQuiz = "
3209
                    SELECT c_id,
3210
                        title,
3211
                        feedback_type,
3212
                        pass_percentage
3213
                    FROM $tableCQuiz
3214
                    WHERE iid = $item";
3215
3216
                $resultCQuiz = Database::query($queryCQuiz);
3217
                if (Database::num_rows($resultCQuiz) <= 0) {
3218
                    continue;
3219
                }
3220
                $row = Database::fetch_assoc($resultCQuiz);
3221
3222
                $cId = $row['c_id'];
3223
                $title = $row['title'];
3224
                $type = Exercise::getFeedbackTypeLiteral($row['feedback_type']);
3225
                $passPercentage = empty($row['pass_percentage']) ? 0.5 : $row['pass_percentage'];
3226
3227
                // Get item authoring data
3228
                $itemProps = api_get_last_item_property_info($row['c_id'], 'quiz', $item);
3229
                $createdBy = $this->__getConfiguredUsernameById($itemProps['insert_user_id']);
3230
                if ($itemProps['insert_user_id'] == $itemProps['lastedit_user_id']) {
3231
                    $updatedBy = $createdBy;
3232
                } else {
3233
                    $updatedBy = $this->__getConfiguredUsernameById($itemProps['lastedit_user_id']);
3234
                }
3235
3236
                $sql = "
3237
                    SELECT a.exe_exo_id AS id,
3238
                           a.exe_user_id,
3239
                           MAX(a.start_date),
3240
                           a.exe_result,
3241
                           a.exe_weighting
3242
                    FROM $tableTrackExercises a
3243
                    WHERE a.exe_exo_id = $item
3244
                    GROUP BY a.exe_exo_id, a.exe_user_id
3245
                ";
3246
3247
                $result = Database::query($sql);
3248
                if (Database::num_rows($result) > 0) {
3249
                    $countAttempts = 0;
3250
                    $countSuccess = 0;
3251
                    $scoreSum = 0;
3252
3253
                    while ($row = Database::fetch_assoc($result)) {
3254
3255
                        // If test is badly configured, with all questions at score 0
3256
                        if ($row['exe_weighting'] == 0) {
3257
                            continue;
3258
                        }
3259
                        $score = $row['exe_result'] / $row['exe_weighting'];
3260
                        if ($score >= $passPercentage) {
3261
                            $countSuccess++;
3262
                        }
3263
                        $scoreSum += $score;
3264
                        $countAttempts++;
3265
                    }
3266
                    $completionMethod = 'Success on users count';
3267
                    if ($countAttempts === 0) {
3268
                        // In some cases, there are no attempts at all. Return 0 completion & score.
3269
                        $averageScore = 0;
3270
                        $completion = 0;
3271
                    } else {
3272
                        $averageScore = round(($scoreSum / $countAttempts) * 100, 2);
3273
                        if (empty($countUsersInCourses[$cId])) {
3274
                            // Users might have all been unsubscribed from the course since taking the test
3275
                            $completion = $countSuccess / $countAttempts;
3276
                            $completionMethod = 'Success on attempts count';
3277
                        } else {
3278
                            $completion = $countSuccess / $countUsersInCourses[$cId];
3279
                        }
3280
                    }
3281
                    $params = [
3282
                        'id' => $item,
3283
                        'title' => $title,
3284
                        'created_by' => $createdBy,
3285
                        'updated_by' => $updatedBy,
3286
                        'type' => $type,
3287
                        'completion' => $completion,
3288
                        'completion_method' => $completionMethod,
3289
                        'number_of_last_attempts' => $countAttempts,
3290
                        'average_score_in_percent' => $averageScore,
3291
                    ];
3292
                    foreach ($extraArray as $name => $value) {
3293
                        $params[$name] = $value;
3294
                    }
3295
                    $resultArray[] = $params;
3296
                }
3297
            }
3298
        }
3299
3300
        return $resultArray;
3301
    }
3302
3303
    public function logout()
3304
    {
3305
        online_logout($this->user->getId());
3306
3307
        Event::courseLogout(
3308
            [
3309
                'uid' => $this->user->getId(),
3310
                'cid' => $this->course ? $this->course->getId() : 0,
3311
                'sid' => $this->session ? $this->session->getId() : 0,
3312
            ]
3313
        );
3314
    }
3315
3316
    /**
3317
     * @throws Exception
3318
     */
3319
    public function setThreadNotify(int $threadId): string
3320
    {
3321
        require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
3322
3323
        $result = set_notification(
3324
            'thread',
3325
            $threadId,
3326
            false,
3327
            api_get_user_info($this->user->getId()),
3328
            api_get_course_info($this->course->getCode())
3329
        );
3330
3331
        if (false === $result) {
3332
            self::throwNotAllowedException();
3333
        }
3334
3335
        return $result;
3336
    }
3337
3338
    public function getCourseWorks(): array
3339
    {
3340
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3341
3342
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3343
3344
        $isAllowedToEdit = $this->user->getStatus() !== STUDENT;
3345
3346
        $courseId = $this->course->getId();
3347
        $sessionId = $this->session ? $this->session->getId() : 0;
3348
3349
        $courseInfo = api_get_course_info_by_id($this->course->getId());
3350
3351
        $works = array_filter(
3352
            getWorkListTeacherData($courseId, $sessionId, 0, 0, 0, 'title', 'ASC', ''),
3353
            function (array $work) use ($isAllowedToEdit, $courseInfo, $courseId, $sessionId) {
3354
                if (!$isAllowedToEdit
3355
                    && !userIsSubscribedToWork($this->user->getId(), $work['id'], $courseId)
3356
                ) {
3357
                    return false;
3358
                }
3359
3360
                $visibility = api_get_item_visibility($courseInfo, 'work', $work['id'], $sessionId);
3361
3362
                if (!$isAllowedToEdit && $visibility != 1) {
3363
                    return false;
3364
                }
3365
3366
                return true;
3367
            }
3368
        );
3369
3370
        return array_map(
3371
            function (array $work) use ($isAllowedToEdit, $courseInfo) {
3372
                $work['type'] = 'work.png';
3373
3374
                if (!$isAllowedToEdit) {
3375
                    $workList = get_work_user_list(
3376
                        0,
3377
                        1000,
3378
                        null,
3379
                        null,
3380
                        $work['id'],
3381
                        ' AND u.id = '.$this->user->getId()
3382
                    );
3383
3384
                    $count = getTotalWorkComment($workList, $courseInfo);
3385
                    $lastWork = getLastWorkStudentFromParentByUser($this->user->getId(), $work, $courseInfo);
3386
3387
                    $work['feedback'] = ' '.Display::label('0 '.get_lang('Feedback'), 'warning');
3388
3389
                    if (!empty($count)) {
3390
                        $work['feedback'] = ' '.Display::label($count.' '.get_lang('Feedback'), 'info');
3391
                    }
3392
3393
                    $work['last_upload'] = '';
3394
3395
                    if (!empty($lastWork)) {
3396
                        $work['last_upload'] = !empty($lastWork['qualification'])
3397
                            ? $lastWork['qualification_rounded'].' - '
3398
                            : '';
3399
                        $work['last_upload'] .= api_get_local_time($lastWork['sent_date']);
3400
                    }
3401
                }
3402
3403
                return $work;
3404
            },
3405
            $works
3406
        );
3407
    }
3408
3409
    /**
3410
     * Returns a list of exercises in the given course. The given course is received through generic param at instanciation.
3411
     *
3412
     * @param array $fields A list of extra fields to include in the answer. Searches for the field in exercise, then in course
3413
     */
3414
    public function getCourseExercises($fields = []): array
3415
    {
3416
        Event::event_access_tool(TOOL_QUIZ);
3417
3418
        $sessionId = $this->session ? $this->session->getId() : 0;
3419
        $courseInfo = api_get_course_info_by_id($this->course->getId());
3420
3421
        // Check the extra fields criteria (whether to add extra field information or not)
3422
        $fieldSource = [];
3423
        if (count($fields) > 0) {
3424
            // For each field, check where to get it from (quiz or course)
3425
            $quizExtraField = new ExtraField('exercise');
3426
            $courseExtraField = new ExtraField('course');
3427
            foreach ($fields as $fieldName) {
3428
                $fieldExists = $quizExtraField->get_handler_field_info_by_field_variable($fieldName);
3429
                if ($fieldExists === false) {
3430
                    // The field does not exist on the exercise, so use it from the course
3431
                    $courseFieldExists = $courseExtraField->get_handler_field_info_by_field_variable($fieldName);
3432
                    if ($courseFieldExists === false) {
3433
                        continue;
3434
                    }
3435
                    $fieldSource[$fieldName] = ['item_type' => 'course', 'id' => $courseFieldExists['id']];
3436
                } else {
3437
                    $fieldSource[$fieldName] = ['item_type' => 'exercise', 'id' => $fieldExists['id']];
3438
                }
3439
            }
3440
        }
3441
        $list = ExerciseLib::get_all_exercises($courseInfo, $sessionId);
3442
3443
        // Now check the whole extra fields thing
3444
        if (count($fieldSource) > 0) {
3445
            $extraFieldValuesTable = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3446
            $fieldsSearchString = "SELECT field_id, value FROM $extraFieldValuesTable WHERE item_id = %d AND field_id = %d";
3447
            foreach ($list as $id => $exercise) {
3448
                foreach ($fieldSource as $fieldName => $fieldDetails) {
3449
                    if ($fieldDetails['item_type'] == 'course') {
3450
                        $itemId = $exercise['c_id'];
3451
                    } else {
3452
                        $itemId = $exercise['iid'];
3453
                    }
3454
                    $result = Database::query(sprintf($fieldsSearchString, $itemId, $fieldDetails['id']));
3455
                    if (Database::num_rows($result) > 0) {
3456
                        $row = Database::fetch_assoc($result);
3457
                        $list[$id]['extra_'.$fieldName] = $row['value'];
3458
                    } else {
3459
                        $list[$id]['extra_'.$fieldName] = '';
3460
                    }
3461
                }
3462
            }
3463
        }
3464
        foreach ($list as $id => $row) {
3465
            // Get item authoring data
3466
            $itemProps = api_get_last_item_property_info($row['c_id'], 'quiz', $row['iid']);
3467
            $createdBy = $this->__getConfiguredUsernameById($itemProps['insert_user_id']);
3468
            if ($itemProps['insert_user_id'] == $itemProps['lastedit_user_id']) {
3469
                $updatedBy = $createdBy;
3470
            } else {
3471
                $updatedBy = $this->__getConfiguredUsernameById($itemProps['lastedit_user_id']);
3472
            }
3473
            $list[$id]['created_by'] = $createdBy;
3474
            $list[$id]['updated_by'] = $updatedBy;
3475
        }
3476
3477
        return $list;
3478
    }
3479
3480
    /**
3481
     * @throws Exception
3482
     */
3483
    public function putCourseWorkVisibility(int $workId, int $status): bool
3484
    {
3485
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3486
3487
        $courseInfo = api_get_course_info_by_id($this->course->getId());
3488
3489
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3490
3491
        switch ($status) {
3492
            case 1:
3493
                return makeVisible($workId, $courseInfo);
3494
            case 0:
3495
                return makeInvisible($workId, $courseInfo);
3496
            default:
3497
                throw new Exception(get_lang('ActionNotAllowed'));
3498
        }
3499
    }
3500
3501
    public function deleteWorkStudentItem(int $workId): string
3502
    {
3503
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3504
3505
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3506
3507
        $courseInfo = api_get_course_info_by_id($this->course->getId());
3508
3509
        $fileDeleted = deleteWorkItem($workId, $courseInfo);
3510
3511
        if ($fileDeleted) {
3512
            return get_lang('TheDocumentHasBeenDeleted');
3513
        }
3514
3515
        return get_lang('YouAreNotAllowedToDeleteThisDocument');
3516
    }
3517
3518
    public function deleteWorkCorrections(int $workId): string
3519
    {
3520
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3521
3522
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3523
3524
        $courseInfo = api_get_course_info_by_id($this->course->getId());
3525
3526
        $result = get_work_user_list(null, null, null, null, $workId);
3527
3528
        if ($result) {
3529
            foreach ($result as $item) {
3530
                $workInfo = get_work_data_by_id($item['id']);
3531
3532
                deleteCorrection($courseInfo, $workInfo);
3533
            }
3534
        }
3535
3536
        return get_lang('Deleted');
3537
    }
3538
3539
    public function getWorkList(int $workId): array
3540
    {
3541
        $isAllowedToEdit = api_is_allowed_to_edit();
3542
3543
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3544
3545
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3546
3547
        $userId = $this->user->getId();
3548
        $courseId = $this->course->getId();
3549
        $sessionId = $this->session ? $this->session->getId() : 0;
3550
3551
        $courseInfo = api_get_course_info_by_id($courseId);
3552
        $webPath = api_get_path(WEB_PATH);
3553
3554
        $whereCondition = !$isAllowedToEdit ? " AND u.id = $userId" : '';
3555
3556
        $works = get_work_user_list(
3557
            0,
3558
            0,
3559
            'title',
3560
            'asc',
3561
            $workId,
3562
            $whereCondition,
3563
            null,
3564
            false,
3565
            $courseId,
3566
            $sessionId
3567
        );
3568
3569
        return array_map(
3570
            function (array $work) use ($courseInfo, $webPath) {
3571
                $itemId = $work['id'];
3572
                $count = getWorkCommentCount($itemId, $courseInfo);
3573
3574
                $work['feedback'] = $count.' '.Display::returnFontAwesomeIcon('comments-o');
3575
                $work['feedback_clean'] = $count;
3576
3577
                $workInfo = get_work_data_by_id($itemId);
3578
                $commentsTmp = getWorkComments($workInfo);
3579
                $comments = [];
3580
3581
                foreach ($commentsTmp as $comment) {
3582
                    $comment['comment'] = str_replace('src="/', 'src="'.$webPath.'app/', $comment['comment']);
3583
                    $comments[] = $comment;
3584
                }
3585
3586
                $work['comments'] = $comments;
3587
3588
                if (empty($workInfo['qualificator_id'])) {
3589
                    $qualificator_id = Display::label(get_lang('NotRevised'), 'warning');
3590
                } else {
3591
                    $qualificator_id = Display::label(get_lang('Revised'), 'success');
3592
                }
3593
3594
                $work['qualificator_id'] = $qualificator_id;
3595
3596
                return $work;
3597
            },
3598
            $works
3599
        );
3600
    }
3601
3602
    public function getWorkStudentsWithoutPublications(int $workId): array
3603
    {
3604
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3605
3606
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3607
3608
        return get_list_users_without_publication($workId);
3609
    }
3610
3611
    public function getWorkUsers(int $workId): array
3612
    {
3613
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3614
3615
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3616
3617
        $courseId = $this->course->getId();
3618
        $sessionId = $this->session ? $this->session->getId() : 0;
3619
        $courseInfo = api_get_course_info_by_id($courseId);
3620
3621
        $items = getAllUserToWork($workId, $courseId);
3622
        $usersAdded = [];
3623
        $result = [
3624
            'users_added' => [],
3625
            'users_to_add' => [],
3626
        ];
3627
3628
        if (!empty($items)) {
3629
            foreach ($items as $data) {
3630
                $usersAdded[] = $data['user_id'];
3631
3632
                $userInfo = api_get_user_info($data['user_id']);
3633
3634
                $result['users_added'][] = [
3635
                    'user_id' => (int) $data['user_id'],
3636
                    'complete_name_with_username' => $userInfo['complete_name_with_username'],
3637
                ];
3638
            }
3639
        }
3640
3641
        if (empty($sessionId)) {
3642
            $status = STUDENT;
3643
        } else {
3644
            $status = 0;
3645
        }
3646
3647
        $userList = CourseManager::get_user_list_from_course_code(
3648
            $courseInfo['code'],
3649
            $sessionId,
3650
            null,
3651
            null,
3652
            $status
3653
        );
3654
3655
        $userToAddList = [];
3656
        foreach ($userList as $user) {
3657
            if (!in_array($user['user_id'], $usersAdded)) {
3658
                $userToAddList[] = $user;
3659
            }
3660
        }
3661
3662
        if (!empty($userToAddList)) {
3663
            foreach ($userToAddList as $user) {
3664
                $userName = api_get_person_name($user['firstname'], $user['lastname']).' ('.$user['username'].') ';
3665
3666
                $result['users_to_add'][] = [
3667
                    'user_id' => (int) $user['user_id'],
3668
                    'complete_name_with_username' => $userName,
3669
                ];
3670
            }
3671
        }
3672
3673
        return $result;
3674
    }
3675
3676
    public function getWorkStudentList(int $workId): array
3677
    {
3678
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3679
3680
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3681
3682
        $courseId = $this->course->getId();
3683
        $courseCode = $this->course->getCode();
3684
        $sessionId = $this->session ? $this->session->getId() : 0;
3685
3686
        $myFolderData = get_work_data_by_id($workId);
3687
3688
        $workParents = [];
3689
3690
        if (empty($myFolderData)) {
3691
            $workParents = getWorkList($workId, $myFolderData);
3692
        }
3693
3694
        $workIdList = [];
3695
3696
        if (!empty($workParents)) {
3697
            foreach ($workParents as $work) {
3698
                $workIdList[] = $work->id;
3699
            }
3700
        }
3701
3702
        $userList = getWorkUserList(
3703
            $courseCode,
3704
            $sessionId,
3705
            0,
3706
            0,
3707
            null,
3708
            null,
3709
            null
3710
        );
3711
3712
        return array_map(
3713
            function ($userId) use ($courseId, $sessionId, $workParents, $workIdList) {
3714
                $user = api_get_user_info($userId);
3715
3716
                $userWorks = 0;
3717
3718
                if (!empty($workIdList)) {
3719
                    $userWorks = getUniqueStudentAttempts(
3720
                        $workIdList,
3721
                        0,
3722
                        $courseId,
3723
                        $sessionId,
3724
                        $user['user_id']
3725
                    );
3726
                }
3727
3728
                $works = $userWorks." / ".count($workParents);
3729
3730
                return [
3731
                    'id' => $userId,
3732
                    'complete_name' => api_get_person_name($user['firstname'], $user['lastname']),
3733
                    'works' => $works,
3734
                ];
3735
            },
3736
            $userList
3737
        );
3738
    }
3739
3740
    public function viewUserProfile(int $userId)
3741
    {
3742
        $url = api_get_path(WEB_CODE_PATH).'social/profile.php';
3743
3744
        if ($userId) {
3745
            $url .= '?'.http_build_query(['u' => $userId]);
3746
        }
3747
3748
        header("Location: $url");
3749
        exit;
3750
    }
3751
3752
    public function viewCourseHome()
3753
    {
3754
        $url = api_get_course_url($this->course->getCode(), $this->session ? $this->session->getId() : 0);
3755
3756
        header("Location: $url");
3757
        exit;
3758
    }
3759
3760
    public function viewDocumentInFrame(int $documentId)
3761
    {
3762
        $courseCode = $this->course->getCode();
3763
        $sessionId = $this->session ? $this->session->getId() : 0;
3764
3765
        $url = api_get_path(WEB_CODE_PATH).'document/showinframes.php?'
3766
            .http_build_query(
3767
                [
3768
                    'cidReq' => $courseCode,
3769
                    'id_session' => $sessionId,
3770
                    'gidReq' => 0,
3771
                    'gradebook' => 0,
3772
                    'origin' => self::SERVICE_NAME,
3773
                    'id' => $documentId,
3774
                ]
3775
            );
3776
3777
        header("Location: $url");
3778
        exit;
3779
    }
3780
3781
    public function viewQuizTool()
3782
    {
3783
        $courseCode = $this->course->getCode();
3784
        $sessionId = $this->session ? $this->session->getId() : 0;
3785
3786
        $url = api_get_path(WEB_CODE_PATH).'exercise/exercise.php?'
3787
            .http_build_query(
3788
                [
3789
                    'cidReq' => $courseCode,
3790
                    'id_session' => $sessionId,
3791
                    'gidReq' => 0,
3792
                    'gradebook' => 0,
3793
                    'origin' => self::SERVICE_NAME,
3794
                ]
3795
            );
3796
3797
        header("Location: $url");
3798
        exit;
3799
    }
3800
3801
    public function viewSurveyTool()
3802
    {
3803
        $courseCode = $this->course->getCode();
3804
        $sessionId = $this->session ? $this->session->getId() : 0;
3805
3806
        $url = api_get_path(WEB_CODE_PATH).'survey/survey_list.php?'
3807
            .http_build_query(
3808
                [
3809
                    'cidReq' => $courseCode,
3810
                    'id_session' => $sessionId,
3811
                    'gidReq' => 0,
3812
                    'gradebook' => 0,
3813
                    'origin' => self::SERVICE_NAME,
3814
                ]
3815
            );
3816
3817
        header("Location: $url");
3818
        exit;
3819
    }
3820
3821
    public function viewMessage(int $messageId)
3822
    {
3823
        $url = api_get_path(WEB_CODE_PATH).'messages/view_message.php?'.http_build_query(['id' => $messageId]);
3824
3825
        header("Location: $url");
3826
        exit;
3827
    }
3828
3829
    public function downloadForumPostAttachment(string $path)
3830
    {
3831
        $courseCode = $this->course->getCode();
3832
        $sessionId = $this->session ? $this->session->getId() : 0;
3833
3834
        $url = api_get_path(WEB_CODE_PATH).'forum/download.php?'
3835
            .http_build_query(
3836
                [
3837
                    'cidReq' => $courseCode,
3838
                    'id_session' => $sessionId,
3839
                    'gidReq' => 0,
3840
                    'gradebook' => 0,
3841
                    'origin' => self::SERVICE_NAME,
3842
                    'file' => Security::remove_XSS($path),
3843
                ]
3844
            );
3845
3846
        header("Location: $url");
3847
        exit;
3848
    }
3849
3850
    public function downloadWorkFolder(int $workId)
3851
    {
3852
        $cidReq = api_get_cidreq();
3853
        $url = api_get_path(WEB_CODE_PATH)."work/downloadfolder.inc.php?id=$workId&$cidReq";
3854
3855
        header("Location: $url");
3856
        exit;
3857
    }
3858
3859
    public function downloadWorkCommentAttachment(int $commentId)
3860
    {
3861
        $cidReq = api_get_cidreq();
3862
        $url = api_get_path(WEB_CODE_PATH)."work/download_comment_file.php?comment_id=$commentId&$cidReq";
3863
3864
        header("Location: $url");
3865
        exit;
3866
    }
3867
3868
    public function downloadWork(int $workId, bool $isCorrection = false)
3869
    {
3870
        $cidReq = api_get_cidreq();
3871
        $url = api_get_path(WEB_CODE_PATH)."work/download.php?$cidReq&"
3872
            .http_build_query(
3873
                [
3874
                    'id' => $workId,
3875
                    'correction' => $isCorrection ? 1 : null,
3876
                ]
3877
            );
3878
3879
        header("Location: $url");
3880
        exit;
3881
    }
3882
3883
    /**
3884
     * @throws Exception
3885
     */
3886
    public function getAllUsersApiKeys(int $page, int $length, bool $force = false, ?int $urlId = null): array
3887
    {
3888
        if (false === api_get_configuration_value('webservice_enable_adminonly_api')
3889
            || !UserManager::is_admin($this->user->getId())
3890
        ) {
3891
            self::throwNotAllowedException();
3892
        }
3893
3894
        $limitOffset = ($page - 1) * $length;
3895
3896
        $currentUrlId = $urlId ?: api_get_current_access_url_id();
3897
3898
        $data = [];
3899
        $data['total'] = UserManager::get_number_of_users(0, $currentUrlId);
3900
        $data['list'] = array_map(
3901
            function (array $user) use ($force) {
3902
                $apiKeys = UserManager::get_api_keys($user['id'], self::SERVICE_NAME);
3903
                $apiKey = $apiKeys ? current($apiKeys) : null;
3904
3905
                if ($force && empty($apiKey)) {
3906
                    $apiKey = self::generateApiKeyForUser((int) $user['id']);
3907
                }
3908
3909
                return [
3910
                    'id' => (int) $user['id'],
3911
                    'username' => $user['username'],
3912
                    'api_key' => $apiKey,
3913
                ];
3914
            },
3915
            $users = UserManager::get_user_list([], [], $limitOffset, $length, $currentUrlId)
3916
        );
3917
        $data['length'] = count($users);
3918
3919
        if ($page * $length < $data['total']) {
3920
            $nextPageQueryParams = [
3921
                'page' => $page + 1,
3922
                'per_page' => $length,
3923
                'url_id' => $urlId,
3924
            ];
3925
3926
            $data['next'] = $this->generateUrl($nextPageQueryParams);
3927
        }
3928
3929
        return $data;
3930
    }
3931
3932
    /**
3933
     * @throws Exception
3934
     */
3935
    public function getUserApiKey(string $username, bool $force = false): array
3936
    {
3937
        if (false === api_get_configuration_value('webservice_enable_adminonly_api')
3938
            || !UserManager::is_admin($this->user->getId())
3939
        ) {
3940
            self::throwNotAllowedException();
3941
        }
3942
3943
        $userInfo = api_get_user_info_from_username($username);
3944
3945
        if (empty($userInfo)) {
3946
            throw new Exception(get_lang('UserNotFound'));
3947
        }
3948
3949
        $apiKeys = UserManager::get_api_keys($userInfo['id'], self::SERVICE_NAME);
3950
        $apiKey = $apiKeys ? current($apiKeys) : null;
3951
3952
        if ($force && empty($apiKey)) {
3953
            $apiKey = self::generateApiKeyForUser((int) $userInfo['id']);
3954
        }
3955
3956
        return [
3957
            'id' => $userInfo['id'],
3958
            'username' => $userInfo['username'],
3959
            'api_key' => $apiKey,
3960
        ];
3961
    }
3962
3963
    /**
3964
     * @throws Exception
3965
     */
3966
    public function getUserLastConnexion(string $username): array
3967
    {
3968
        $userInfo = api_get_user_info_from_username($username);
3969
3970
        if (empty($userInfo)) {
3971
            throw new Exception(get_lang('UserNotFound'));
3972
        }
3973
3974
        $lastConnexionDate = Tracking::get_last_connection_date($userInfo['id']);
3975
3976
        return [
3977
            'id' => $userInfo['id'],
3978
            'username' => $userInfo['username'],
3979
            'last_connexion_date' => $lastConnexionDate,
3980
        ];
3981
    }
3982
3983
    /**
3984
     * @throws Exception
3985
     */
3986
    public function getUserTotalConnexionTime(string $username): array
3987
    {
3988
        $userInfo = api_get_user_info_from_username($username);
3989
3990
        if (empty($userInfo)) {
3991
            throw new Exception(get_lang('UserNotFound'));
3992
        }
3993
3994
        $totalConnexionTimeInSecond = Tracking::get_time_spent_on_the_platform($userInfo['id'], 'ever');
3995
        $totalConnexionTime = api_time_to_hms($totalConnexionTimeInSecond);
3996
3997
        return [
3998
            'id' => $userInfo['id'],
3999
            'username' => $userInfo['username'],
4000
            'total_connexion_time' => $totalConnexionTime,
4001
        ];
4002
    }
4003
4004
    public static function isAllowedByRequest(bool $inpersonate = false): bool
4005
    {
4006
        $username = $_GET['username'] ?? null;
4007
        $apiKey = $_GET['api_key'] ?? null;
4008
4009
        if (empty($username) || empty($apiKey)) {
4010
            return false;
4011
        }
4012
4013
        try {
4014
            $restApi = self::validate($username, $apiKey);
4015
        } catch (Exception $e) {
4016
            return false;
4017
        }
4018
4019
        if ($inpersonate) {
4020
            Login::init_user($restApi->getUser()->getId(), true);
4021
        }
4022
4023
        return (bool) $restApi;
4024
    }
4025
4026
    public function viewMyCourses()
4027
    {
4028
        $url = api_get_path(WEB_PATH).'user_portal.php?'
4029
            .http_build_query(['nosession' => 'true']);
4030
4031
        header("Location: $url");
4032
        exit;
4033
    }
4034
4035
    /**
4036
     * Create a group/class.
4037
     *
4038
     * @param $params
4039
     *
4040
     * @throws Exception
4041
     */
4042
    public function addGroup($params): array
4043
    {
4044
        self::protectAdminEndpoint();
4045
4046
        if (!empty($params['type'])) {
4047
            $params['group_type'] = $params['type'];
4048
        }
4049
4050
        // First check wether the login already exists.
4051
        $userGroup = new UserGroup();
4052
        if ($userGroup->usergroup_exists($params['name'])) {
4053
            throw new Exception($params['name'].' '.get_lang('AlreadyExists'));
4054
        }
4055
4056
        $groupId = $userGroup->save($params);
4057
4058
        if (empty($groupId)) {
4059
            throw new Exception(get_lang('NotRegistered'));
4060
        }
4061
4062
        return [$groupId];
4063
    }
4064
4065
    /**
4066
     * Delete a group/class.
4067
     *
4068
     * @throws Exception
4069
     *
4070
     * @return bool
4071
     */
4072
    public function deleteGroup(int $id): array
4073
    {
4074
        self::protectAdminEndpoint();
4075
4076
        if (empty($id)) {
4077
            return false;
4078
        }
4079
4080
        // First check wether the login already exists.
4081
        $userGroup = new UserGroup();
4082
        if (!$userGroup->delete($id)) {
4083
            throw new Exception(get_lang('NotDeleted'));
4084
        }
4085
4086
        return [$id];
4087
    }
4088
4089
    /**
4090
     * Get the list of users subscribed to the given group/class.
4091
     *
4092
     * @return array The list of users (userID => [firstname, lastname, relation_type]
4093
     */
4094
    public function getGroupSubscribedUsers(int $groupId): array
4095
    {
4096
        $userGroup = new UserGroup();
4097
4098
        return $userGroup->get_all_users_by_group($groupId);
4099
    }
4100
4101
    /**
4102
     * Get the list of courses to which the given group/class is subscribed.
4103
     *
4104
     * @return array The list of courses (ID => [title]
4105
     */
4106
    public function getGroupSubscribedCourses(int $groupId): array
4107
    {
4108
        $userGroup = new UserGroup();
4109
4110
        return $userGroup->get_courses_by_usergroup($groupId, true);
4111
    }
4112
4113
    /**
4114
     * Get the list of sessions to which the given group/class is subscribed.
4115
     *
4116
     * @return array The list of courses (ID => [title]
4117
     */
4118
    public function getGroupSubscribedSessions(int $groupId): array
4119
    {
4120
        $userGroup = new UserGroup();
4121
4122
        return $userGroup->get_sessions_by_usergroup($groupId, true);
4123
    }
4124
4125
    /**
4126
     * Add a new user to the given group/class.
4127
     *
4128
     * @param int $relationType (1:admin, 2:reader, etc. See GROUP_USER_PERMISSION_ constants in api.lib.php)
4129
     *
4130
     * @return array One item array containing true on success, false otherwise
4131
     */
4132
    public function addGroupSubscribedUser(int $groupId, int $userId, int $relationType = 2): array
4133
    {
4134
        $userGroup = new UserGroup();
4135
4136
        if (!$userGroup->groupExists($groupId) or !$userGroup->userExists($userId)) {
4137
            throw new Exception('user_id or group_id does not exist');
4138
        }
4139
4140
        return [$userGroup->add_user_to_group($userId, $groupId, $relationType)];
4141
    }
4142
4143
    /**
4144
     * Get the list of group/class IDs to which the user belongs.
4145
     *
4146
     * @return array Array containing the group IDs like ['groups' => [1, 2, 3]]
4147
     */
4148
    public function getUserSubGroup(int $userId): array
4149
    {
4150
        $userGroup = new UserGroup();
4151
4152
        $res = $userGroup->get_usergroup_by_user($userId);
4153
4154
        return ['groups' => $res];
4155
    }
4156
4157
    /**
4158
     * Add a new course to which the given group/class is subscribed.
4159
     *
4160
     * @return array One item array containing the ID of the course on success, nothing on failure
4161
     */
4162
    public function addGroupSubscribedCourse(int $groupId, int $courseId): array
4163
    {
4164
        $userGroup = new UserGroup();
4165
4166
        return [$userGroup->subscribe_courses_to_usergroup($groupId, [$courseId], false)];
4167
    }
4168
4169
    /**
4170
     * Add a new session to which the given group/class is subscribed.
4171
     *
4172
     * @return array One item array containing the ID of the session on success, nothing on failure
4173
     */
4174
    public function addGroupSubscribedSession(int $groupId, int $sessionId): array
4175
    {
4176
        $userGroup = new UserGroup();
4177
4178
        return [$userGroup->subscribe_sessions_to_usergroup($groupId, [$sessionId], false)];
4179
    }
4180
4181
    /**
4182
     * Remove a user from the given group/class.
4183
     *
4184
     * @return array One item array containing true on success, false otherwise
4185
     */
4186
    public function deleteGroupSubscribedUser(int $groupId, int $userId): array
4187
    {
4188
        $userGroup = new UserGroup();
4189
4190
        return [$userGroup->delete_user_rel_group($userId, $groupId)];
4191
    }
4192
4193
    /**
4194
     * Remove a course to which the given group/class is subscribed.
4195
     *
4196
     * @return array One item array containing true on success, false otherwise
4197
     */
4198
    public function deleteGroupSubscribedCourse(int $groupId, int $courseId): array
4199
    {
4200
        $userGroup = new UserGroup();
4201
4202
        return [$userGroup->unsubscribe_courses_from_usergroup($groupId, [$courseId])];
4203
    }
4204
4205
    /**
4206
     * Remove a session to which the given group/class is subscribed.
4207
     *
4208
     * @return array One item array containing true on success, false otherwise
4209
     */
4210
    public function deleteGroupSubscribedSession(int $groupId, int $sessionId): array
4211
    {
4212
        $userGroup = new UserGroup();
4213
4214
        return [$userGroup->unsubscribeSessionsFromUserGroup($groupId, [$sessionId], false)];
4215
    }
4216
4217
    /**
4218
     * Encode the given parameters (structured array) in JSON format
4219
     * @param array $additionalParams Optional
4220
     *
4221
     * @return string
4222
     */
4223
    private function encodeParams(array $additionalParams = [])
4224
    {
4225
        $params = array_merge(
4226
            $additionalParams,
4227
            [
4228
                'api_key' => $this->apiKey,
4229
                'username' => $this->user->getUsername(),
4230
            ]
4231
        );
4232
4233
        return json_encode($params);
4234
    }
4235
4236
    /**
4237
     * Get audit items from track_e_default.
4238
     * @throws Exception
4239
     */
4240
    public function getAuditItems(
4241
        string $defaultEventType,
4242
        ?int $cId = null,
4243
        ?int $sessionId = null,
4244
        ?string $afterDate = null,
4245
        ?string $beforeDate = null,
4246
        ?int $userId = null,
4247
        int $offset = 0,
4248
        int $limit = 100
4249
    ): array {
4250
        self::protectAdminEndpoint();
4251
4252
        return Event::getAuditItems(
4253
            $defaultEventType,
4254
            $cId,
4255
            $sessionId,
4256
            $afterDate,
4257
            $beforeDate,
4258
            $userId,
4259
            $offset,
4260
            $limit
4261
        );
4262
    }
4263
4264
    /**
4265
     * Generate an API key for webservices access for the given user ID
4266
     */
4267
    protected static function generateApiKeyForUser(int $userId): string
4268
    {
4269
        UserManager::add_api_key($userId, self::SERVICE_NAME);
4270
4271
        $apiKeys = UserManager::get_api_keys($userId, self::SERVICE_NAME);
4272
4273
        return current($apiKeys);
4274
    }
4275
4276
    /**
4277
     * Helper generating a query URL (to the current script) from an array of parameters
4278
     * (course, session, api_key and username) commonly used in webservice calls
4279
     */
4280
    private function generateUrl(array $additionalParams = []): string
4281
    {
4282
        $queryParams = [
4283
            'course' => $this->course ? $this->course->getId() : null,
4284
            'session' => $this->session ? $this->session->getId() : null,
4285
            'api_key' => $this->apiKey,
4286
            'username' => $this->user->getUsername(),
4287
        ];
4288
4289
        return api_get_self().'?'
4290
            .http_build_query(array_merge($queryParams, $additionalParams));
4291
    }
4292
}
4293