Passed
Push — 1.11.x ( b7ceb1...b3c700 )
by Yannick
12:37 queued 15s
created

Rest::getAuditItems()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 21
rs 9.9332
c 0
b 0
f 0
cc 1
nc 1
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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