Passed
Push — 1.11.x ( 872bc4...3ecb3e )
by Yannick
09:37
created

Rest::deleteCampusURL()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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