Passed
Push — 1.11.x ( 889011...806f34 )
by Yannick
09:33
created

Rest::addGroupSubscribedSession()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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