Passed
Push — 1.11.x ( 5ef542...649c39 )
by Yannick
10:43
created

Rest::getUsersCampus()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 39
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

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