Passed
Push — 1.11.x ( 03e8c2...2a4008 )
by Yannick
13:02 queued 15s
created

Rest::deleteGroupSubscribedCourse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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