Passed
Push — 1.11.x ( e0bd09...e4d865 )
by Yannick
09:15
created

Rest::getUserSubGroup()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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