Passed
Push — 1.11.x ( 01f8a3...8c0173 )
by Yannick
09:16
created

Rest::unsubscribeUsersFromSession()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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