Passed
Push — 1.11.x ( 67076c...34c335 )
by Yannick
11:06 queued 01:52
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
113
    public const GET_SESSION_FROM_EXTRA_FIELD = 'get_session_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): 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
            $shortList[] = [
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
        }
1705
1706
        return $shortList;
1707
    }
1708
1709
    /**
1710
     * Returns an array of groups with id, group_type, name, description, visibility.
1711
     *
1712
     * @param array $params An array of parameters to filter the results (currently supports 'type')
1713
     *
1714
     * @throws Exception
1715
     */
1716
    public function getGroups(array $params): array
1717
    {
1718
        self::protectAdminEndpoint();
1719
1720
        if ('*' === $params['type']) {
1721
            $conditions = [];
1722
        } else {
1723
            $conditions = ['where' => ['group_type = ?' => $params['type']]];
1724
        }
1725
        $userGroup = new UserGroup();
1726
        $groups = $userGroup->getDataToExport($conditions);
1727
        $list = [];
1728
        /** @var \Chamilo\UserBundle\Entity\Group $item */
1729
        foreach ($groups as $item) {
1730
            $listTemp = [
1731
                'id' => $item['id'],
1732
                'name' => $item['name'],
1733
                'description' => $item['description'],
1734
                'visibility' => $item['visibility'],
1735
                'type' => $item['group_type'],
1736
            ];
1737
            if (in_array($item['group_type'], [0, 1])) {
1738
                $listTemp['type_name'] = ($item['group_type'] == 0) ? 'class' : 'social';
1739
            }
1740
            if (in_array($item['visibility'], [1, 2])) {
1741
                $listTemp['visibility_name'] = ($item['visibility'] == 1) ? 'open' : 'closed';
1742
            }
1743
            $list[] = $listTemp;
1744
        }
1745
1746
        return $list;
1747
    }
1748
1749
    /**
1750
     * @throws Exception
1751
     */
1752
    public function addSession(array $params): array
1753
    {
1754
        self::protectAdminEndpoint();
1755
1756
        $name = $params['name'];
1757
        $coach_username = (int) $params['coach_username'];
1758
        $startDate = $params['access_start_date'];
1759
        $endDate = $params['access_end_date'];
1760
        $displayStartDate = $startDate;
1761
        $displayEndDate = $endDate;
1762
        $description = $params['description'];
1763
        $idUrlCampus = $params['id_campus'];
1764
        $extraFields = isset($params['extra']) ? $params['extra'] : [];
1765
1766
        $return = SessionManager::create_session(
1767
            $name,
1768
            $startDate,
1769
            $endDate,
1770
            $displayStartDate,
1771
            $displayEndDate,
1772
            null,
1773
            null,
1774
            $coach_username,
1775
            null,
1776
            1,
1777
            false,
1778
            null,
1779
            $description,
1780
            1,
1781
            $extraFields,
1782
            null,
1783
            false,
1784
            $idUrlCampus
1785
        );
1786
1787
        if ($return) {
1788
            $out = [
1789
                'status' => true,
1790
                'message' => get_lang('ANewSessionWasCreated'),
1791
                'id_session' => $return,
1792
            ];
1793
        } else {
1794
            $out = [
1795
                'status' => false,
1796
                'message' => get_lang('ErrorOccurred'),
1797
            ];
1798
        }
1799
1800
        return $out;
1801
    }
1802
1803
    public function addCourse(array $courseParam): array
1804
    {
1805
        self::protectAdminEndpoint();
1806
1807
        $idCampus = isset($courseParam['id_campus']) ? $courseParam['id_campus'] : 1;
1808
        $title = isset($courseParam['title']) ? $courseParam['title'] : '';
1809
        $wantedCode = isset($courseParam['wanted_code']) ? $courseParam['wanted_code'] : null;
1810
        $diskQuota = isset($courseParam['disk_quota']) ? $courseParam['disk_quota'] : '100';
1811
        $visibility = isset($courseParam['visibility']) ? (int) $courseParam['visibility'] : null;
1812
        $removeCampusId = $courseParam['remove_campus_id_from_wanted_code'] ?? 0;
1813
        $language = $courseParam['language'] ?? '';
1814
1815
        if (isset($courseParam['visibility'])) {
1816
            if ($courseParam['visibility'] &&
1817
                $courseParam['visibility'] >= 0 &&
1818
                $courseParam['visibility'] <= 3
1819
            ) {
1820
                $visibility = (int) $courseParam['visibility'];
1821
            }
1822
        }
1823
1824
        $params = [];
1825
        $params['title'] = $title;
1826
        $params['wanted_code'] = 'CAMPUS_'.$idCampus.'_'.$wantedCode;
1827
        if (1 === (int) $removeCampusId) {
1828
            $params['wanted_code'] = $wantedCode;
1829
        }
1830
        $params['user_id'] = $this->user->getId();
1831
        $params['visibility'] = $visibility;
1832
        $params['disk_quota'] = $diskQuota;
1833
        $params['course_language'] = $language;
1834
1835
        foreach ($courseParam as $key => $value) {
1836
            if (substr($key, 0, 6) === 'extra_') { //an extra field
1837
                $params[$key] = $value;
1838
            }
1839
        }
1840
1841
        $courseInfo = CourseManager::create_course($params, $params['user_id'], $idCampus);
1842
        $results = [];
1843
        if (!empty($courseInfo)) {
1844
            $results['status'] = true;
1845
            $results['id'] = $courseInfo['real_id'];
1846
            $results['code_course'] = $courseInfo['code'];
1847
            $results['title_course'] = $courseInfo['title'];
1848
            $extraFieldValues = new ExtraFieldValue('course');
1849
            $extraFields = $extraFieldValues->getAllValuesByItem($courseInfo['real_id']);
1850
            $results['extra_fields'] = $extraFields;
1851
            $results['message'] = sprintf(get_lang('CourseXAdded'), $courseInfo['code']);
1852
        } else {
1853
            $results['status'] = false;
1854
            $results['message'] = get_lang('CourseCreationFailed');
1855
        }
1856
1857
        return $results;
1858
    }
1859
1860
    /**
1861
     * @param $userParam
1862
     *
1863
     * @throws Exception
1864
     */
1865
    public function addUser($userParam): array
1866
    {
1867
        self::protectAdminEndpoint();
1868
1869
        $firstName = $userParam['firstname'];
1870
        $lastName = $userParam['lastname'];
1871
        $status = $userParam['status'];
1872
        $email = $userParam['email'];
1873
        $loginName = $userParam['loginname'];
1874
        $password = $userParam['password'];
1875
1876
        $official_code = '';
1877
        $language = '';
1878
        $phone = '';
1879
        $picture_uri = '';
1880
        $auth_source = $userParam['auth_source'] ?? PLATFORM_AUTH_SOURCE;
1881
        $expiration_date = '';
1882
        $active = 1;
1883
        $hr_dept_id = 0;
1884
        $original_user_id_name = $userParam['original_user_id_name'];
1885
        $original_user_id_value = $userParam['original_user_id_value'];
1886
        $sendMail = (empty($userParam['send_mail']) ? false : true);
1887
1888
        $extra_list = isset($userParam['extra']) ? $userParam['extra'] : [];
1889
        if (isset($userParam['language'])) {
1890
            $language = $userParam['language'];
1891
        }
1892
        if (isset($userParam['phone'])) {
1893
            $phone = $userParam['phone'];
1894
        }
1895
        if (isset($userParam['expiration_date'])) {
1896
            $expiration_date = $userParam['expiration_date'];
1897
        }
1898
1899
        // Default language.
1900
        if (empty($language)) {
1901
            $language = api_get_setting('platformLanguage');
1902
        }
1903
1904
        // First check wether the login already exists.
1905
        if (!UserManager::is_username_available($loginName)) {
1906
            throw new Exception(get_lang('UserNameNotAvailable'));
1907
        }
1908
1909
        $userId = UserManager::create_user(
1910
            $firstName,
1911
            $lastName,
1912
            $status,
1913
            $email,
1914
            $loginName,
1915
            $password,
1916
            $official_code,
1917
            $language,
1918
            $phone,
1919
            $picture_uri,
1920
            $auth_source,
1921
            $expiration_date,
1922
            $active,
1923
            $hr_dept_id,
1924
            [],
1925
            '',
1926
            $sendMail
1927
        );
1928
1929
        if (empty($userId)) {
1930
            throw new Exception(get_lang('UserNotRegistered'));
1931
        }
1932
1933
        if (api_is_multiple_url_enabled()) {
1934
            if (api_get_current_access_url_id() != -1) {
1935
                UrlManager::add_user_to_url(
1936
                    $userId,
1937
                    api_get_current_access_url_id()
1938
                );
1939
            } else {
1940
                UrlManager::add_user_to_url($userId, 1);
1941
            }
1942
        } else {
1943
            // We add by default the access_url_user table with access_url_id = 1
1944
            UrlManager::add_user_to_url($userId, 1);
1945
        }
1946
1947
        // Save new field label into user_field table.
1948
        UserManager::create_extra_field(
1949
            $original_user_id_name,
1950
            1,
1951
            $original_user_id_name,
1952
            ''
1953
        );
1954
        // Save the external system's id into user_field_value table.
1955
        UserManager::update_extra_field_value(
1956
            $userId,
1957
            $original_user_id_name,
1958
            $original_user_id_value
1959
        );
1960
1961
        if (is_array($extra_list) && count($extra_list) > 0) {
1962
            foreach ($extra_list as $extra) {
1963
                $extra_field_name = $extra['field_name'];
1964
                $extra_field_value = $extra['field_value'];
1965
                // Save new field label into user_field table.
1966
                UserManager::create_extra_field(
1967
                    $extra_field_name,
1968
                    1,
1969
                    $extra_field_name,
1970
                    ''
1971
                );
1972
                // Save the external system's id into user_field_value table.
1973
                UserManager::update_extra_field_value(
1974
                    $userId,
1975
                    $extra_field_name,
1976
                    $extra_field_value
1977
                );
1978
            }
1979
        }
1980
1981
        return [$userId];
1982
    }
1983
1984
    /**
1985
     * @throws Exception
1986
     */
1987
    public function addUserGetApikey(array $userParams): array
1988
    {
1989
        list($userId) = $this->addUser($userParams);
1990
1991
        UserManager::add_api_key($userId, self::SERVICE_NAME);
1992
1993
        $apiKey = UserManager::get_api_keys($userId, self::SERVICE_NAME);
1994
1995
        return [
1996
            'id' => $userId,
1997
            'api_key' => current($apiKey),
1998
        ];
1999
    }
2000
2001
    /**
2002
     * @throws Exception
2003
     */
2004
    public function updateUserApiKey(int $userId, string $oldApiKey): array
2005
    {
2006
        if (!api_is_platform_admin() && $userId != $this->user->getId()) {
2007
            self::throwNotAllowedException();
2008
        }
2009
2010
        if (false === $currentApiKeys = UserManager::get_api_keys($userId, self::SERVICE_NAME)) {
2011
            self::throwNotAllowedException();
2012
        }
2013
2014
        if (current($currentApiKeys) !== $oldApiKey) {
2015
            self::throwNotAllowedException();
2016
        }
2017
2018
        UserManager::update_api_key($userId, self::SERVICE_NAME);
2019
2020
        $apiKey = UserManager::get_api_keys($userId, self::SERVICE_NAME);
2021
2022
        return [
2023
            'api_key' => current($apiKey),
2024
        ];
2025
    }
2026
2027
    /**
2028
     * Subscribe User to Course.
2029
     *
2030
     * @throws Exception
2031
     */
2032
    public function subscribeUserToCourse(array $params): array
2033
    {
2034
        $course_id = $params['course_id'];
2035
        $course_code = $params['course_code'];
2036
        $user_id = $params['user_id'];
2037
        $status = $params['status'] ?? STUDENT;
2038
2039
        if (!api_is_platform_admin() && $user_id != $this->user->getId()) {
2040
            self::throwNotAllowedException();
2041
        }
2042
2043
        if (!$course_id && !$course_code) {
2044
            return [false];
2045
        }
2046
        if (!$course_code) {
2047
            $course_code = CourseManager::get_course_code_from_course_id($course_id);
2048
        }
2049
2050
        if (CourseManager::subscribeUser($user_id, $course_code, $status, 0, 0, false)) {
2051
            return [true];
2052
        }
2053
2054
        return [false];
2055
    }
2056
2057
    /**
2058
     * @throws Exception
2059
     */
2060
    public function subscribeUserToCoursePassword($courseCode, $password)
2061
    {
2062
        $courseInfo = api_get_course_info($courseCode);
2063
2064
        if (empty($courseInfo)) {
2065
            throw new Exception(get_lang('NoCourse'));
2066
        }
2067
2068
        if (sha1($password) === $courseInfo['registration_code']) {
2069
            CourseManager::processAutoSubscribeToCourse($courseCode);
2070
2071
            return;
2072
        }
2073
2074
        throw new Exception(get_lang('CourseRegistrationCodeIncorrect'));
2075
    }
2076
2077
    /**
2078
     * @throws Exception
2079
     */
2080
    public function unSubscribeUserToCourse(array $params): array
2081
    {
2082
        $courseId = $params['course_id'];
2083
        $courseCode = $params['course_code'];
2084
        $userId = $params['user_id'];
2085
2086
        if (!api_is_platform_admin() && $userId != $this->user->getId()) {
2087
            self::throwNotAllowedException();
2088
        }
2089
2090
        if (!$courseId && !$courseCode) {
2091
            return [false];
2092
        }
2093
2094
        if (!$courseCode) {
2095
            $courseCode = CourseManager::get_course_code_from_course_id($courseId);
2096
        }
2097
2098
        if (CourseManager::unsubscribe_user($userId, $courseCode)) {
2099
            return [true];
2100
        }
2101
2102
        return [false];
2103
    }
2104
2105
    public function deleteUserMessage($messageId, $messageType)
2106
    {
2107
        if ($messageType === 'sent') {
2108
            return MessageManager::delete_message_by_user_sender($this->user->getId(), $messageId);
2109
        } else {
2110
            return MessageManager::delete_message_by_user_receiver($this->user->getId(), $messageId);
2111
        }
2112
    }
2113
2114
    /**
2115
     * Set a given message as already read.
2116
     *
2117
     * @param $messageId
2118
     */
2119
    public function setMessageRead(int $messageId)
2120
    {
2121
        // MESSAGE_STATUS_NEW is also used for messages that have been "read"
2122
        MessageManager::update_message_status($this->user->getId(), $messageId, MESSAGE_STATUS_NEW);
2123
    }
2124
2125
    /**
2126
     * Add a group.
2127
     *
2128
     * @param array Params
2129
     */
2130
    public function createGroup($params)
2131
    {
2132
        self::protectAdminEndpoint();
2133
2134
        $name = $params['name'];
2135
        $description = $params['description'];
2136
    }
2137
2138
    /**
2139
     * Add Campus Virtual.
2140
     *
2141
     * @param array Params Campus
2142
     *
2143
     * @return array
2144
     */
2145
    public function createCampusURL($params)
2146
    {
2147
        $urlCampus = Security::remove_XSS($params['url']);
2148
        $description = Security::remove_XSS($params['description']);
2149
2150
        $active = isset($params['active']) ? intval($params['active']) : 0;
2151
        $num = UrlManager::url_exist($urlCampus);
2152
        if ($num == 0) {
2153
            // checking url
2154
            if (substr($urlCampus, strlen($urlCampus) - 1, strlen($urlCampus)) == '/') {
2155
                $idCampus = UrlManager::add($urlCampus, $description, $active, true);
2156
            } else {
2157
                //create
2158
                $idCampus = UrlManager::add($urlCampus.'/', $description, $active, true);
2159
            }
2160
2161
            return [
2162
                'status' => true,
2163
                'id_campus' => $idCampus,
2164
            ];
2165
        }
2166
2167
        return [
2168
            'status' => false,
2169
            'id_campus' => 0,
2170
        ];
2171
    }
2172
2173
    /**
2174
     * Edit Campus Virtual.
2175
     *
2176
     * @param array Params Campus
2177
     *
2178
     * @return array
2179
     */
2180
    public function editCampusURL($params)
2181
    {
2182
        $urlCampus = Security::remove_XSS($params['url']);
2183
        $description = Security::remove_XSS($params['description']);
2184
2185
        $active = isset($params['active']) ? intval($params['active']) : 0;
2186
        $url_id = isset($params['id']) ? intval($params['id']) : 0;
2187
2188
        if (!empty($url_id)) {
2189
            //we can't change the status of the url with id=1
2190
            if ($url_id == 1) {
2191
                $active = 1;
2192
            }
2193
            //checking url
2194
            if (substr($urlCampus, strlen($urlCampus) - 1, strlen($urlCampus)) == '/') {
2195
                UrlManager::update($url_id, $urlCampus, $description, $active);
2196
            } else {
2197
                UrlManager::update($url_id, $urlCampus.'/', $description, $active);
2198
            }
2199
2200
            return [true];
2201
        }
2202
2203
        return [false];
2204
    }
2205
2206
    /**
2207
     * Delete Campus Virtual.
2208
     *
2209
     * @param array Params Campus
2210
     *
2211
     * @return array
2212
     */
2213
    public function deleteCampusURL($params)
2214
    {
2215
        $url_id = isset($params['id']) ? intval($params['id']) : 0;
2216
2217
        $result = UrlManager::delete($url_id);
2218
        if ($result) {
2219
            return [
2220
                'status' => true,
2221
                'message' => get_lang('URLDeleted'),
2222
            ];
2223
        } else {
2224
            return [
2225
                'status' => false,
2226
                'message' => get_lang('Error'),
2227
            ];
2228
        }
2229
    }
2230
2231
    /**
2232
     * @throws Exception
2233
     */
2234
    public function addCoursesSession(array $params): array
2235
    {
2236
        self::protectAdminEndpoint();
2237
2238
        $sessionId = $params['id_session'];
2239
        $courseList = $params['list_courses'];
2240
        $importAssignments = isset($params['import_assignments']) && 1 === (int) $params['import_assignments'];
2241
2242
        $result = SessionManager::add_courses_to_session(
2243
            $sessionId,
2244
            $courseList,
2245
            true,
2246
            false,
2247
            false,
2248
            $importAssignments
2249
        );
2250
2251
        if ($result) {
2252
            return [
2253
                'status' => $result,
2254
                'message' => get_lang('Updated'),
2255
            ];
2256
        }
2257
2258
        return [
2259
            'status' => $result,
2260
            'message' => get_lang('ErrorOccurred'),
2261
        ];
2262
    }
2263
2264
    /**
2265
     * Simple legacy shortcut to subscribeUsersToSession.
2266
     *
2267
     * @throws Exception
2268
     */
2269
    public function addUsersSession(array $params): array
2270
    {
2271
        return self::subscribeUsersToSession($params);
2272
    }
2273
2274
    /**
2275
     * Subscribe a list of users to the given session.
2276
     *
2277
     * @param array $params Containing 'id_session' and 'list_users' entries
2278
     *
2279
     * @throws Exception
2280
     */
2281
    public function subscribeUsersToSession(array $params): array
2282
    {
2283
        $sessionId = $params['id_session'];
2284
        $userList = $params['list_users'];
2285
2286
        if (!is_array($userList)) {
2287
            $userList = [];
2288
        }
2289
2290
        if (!api_is_platform_admin() && !in_array($this->user->getId(), $userList)) {
2291
            self::throwNotAllowedException();
2292
        }
2293
2294
        SessionManager::subscribeUsersToSession(
2295
            $sessionId,
2296
            $userList,
2297
            null,
2298
            false
2299
        );
2300
2301
        return [
2302
            'status' => true,
2303
            'message' => get_lang('UsersAdded'),
2304
        ];
2305
    }
2306
2307
    /**
2308
     * Unsubscribe a given list of users from the given session.
2309
     *
2310
     * @throws Exception
2311
     */
2312
    public function unsubscribeUsersFromSession(array $params): array
2313
    {
2314
        self::protectAdminEndpoint();
2315
2316
        $sessionId = $params['id_session'];
2317
        $userList = $params['list_users'];
2318
2319
        if (!is_array($userList)) {
2320
            $userList = [];
2321
        }
2322
2323
        if (!api_is_platform_admin() && !in_array($this->user->getId(), $userList)) {
2324
            self::throwNotAllowedException();
2325
        }
2326
2327
        foreach ($userList as $userId) {
2328
            SessionManager::unsubscribe_user_from_session(
2329
                $sessionId,
2330
                $userId
2331
            );
2332
        }
2333
2334
        return [
2335
            'status' => true,
2336
            'message' => get_lang('UserUnsubscribed'),
2337
        ];
2338
    }
2339
2340
    /**
2341
     * Creates a session from a model session.
2342
     *
2343
     * @throws Exception
2344
     */
2345
    public function createSessionFromModel(HttpRequest $request): int
2346
    {
2347
        self::protectAdminEndpoint();
2348
2349
        $modelSessionId = $request->request->getInt('modelSessionId');
2350
        $sessionName = $request->request->get('sessionName');
2351
        $startDate = $request->request->get('startDate');
2352
        $endDate = $request->request->get('endDate');
2353
        $extraFields = $request->request->get('extraFields', []);
2354
        $duplicateAgendaContent = $request->request->getBoolean('duplicateAgendaContent');
2355
2356
        if (empty($modelSessionId) || empty($sessionName) || empty($startDate) || empty($endDate)) {
2357
            throw new Exception(get_lang('NoData'));
2358
        }
2359
2360
        if (!SessionManager::isValidId($modelSessionId)) {
2361
            throw new Exception(get_lang('ModelSessionDoesNotExist'));
2362
        }
2363
2364
        $modelSession = SessionManager::fetch($modelSessionId);
2365
2366
        $modelSession['accessUrlId'] = 1;
2367
        if (api_is_multiple_url_enabled()) {
2368
            if (api_get_current_access_url_id() != -1) {
2369
                $modelSession['accessUrlId'] = api_get_current_access_url_id();
2370
            }
2371
        }
2372
2373
        $newSessionId = SessionManager::create_session(
2374
            $sessionName,
2375
            $startDate,
2376
            $endDate,
2377
            $startDate,
2378
            $endDate,
2379
            $startDate,
2380
            $endDate,
2381
            $modelSession['id_coach'],
2382
            $modelSession['session_category_id'],
2383
            $modelSession['visibility'],
2384
            false,
2385
            $modelSession['duration'],
2386
            $modelSession['description'],
2387
            $modelSession['show_description'],
2388
            $extraFields,
2389
            $modelSession['session_admin_id'],
2390
            $modelSession['send_subscription_notification'],
2391
            $modelSession['accessUrlId']
2392
        );
2393
2394
        if (empty($newSessionId)) {
2395
            throw new Exception(get_lang('SessionNotRegistered'));
2396
        }
2397
2398
        if (is_string($newSessionId)) {
2399
            throw new Exception($newSessionId);
2400
        }
2401
2402
        $promotionId = $modelSession['promotion_id'];
2403
        if ($promotionId) {
2404
            $sessionList = array_keys(SessionManager::get_all_sessions_by_promotion($promotionId));
2405
            $sessionList[] = $newSessionId;
2406
            SessionManager::subscribe_sessions_to_promotion($modelSession['promotion_id'], $sessionList);
2407
        }
2408
2409
        $modelExtraFields = [];
2410
        $fields = SessionManager::getFilteredExtraFields($modelSessionId);
2411
        if (is_array($fields) and !empty($fields)) {
2412
            foreach ($fields as $field) {
2413
                $modelExtraFields[$field['variable']] = $field['value'];
2414
            }
2415
        }
2416
        $allExtraFields = array_merge($modelExtraFields, $extraFields);
2417
        foreach ($allExtraFields as $name => $value) {
2418
            // SessionManager::update_session_extra_field_value returns false when no row is changed,
2419
            // which can happen since extra field values are initialized by SessionManager::create_session
2420
            // therefore we do not throw an exception when false is returned
2421
            SessionManager::update_session_extra_field_value($newSessionId, $name, $value);
2422
        }
2423
2424
        $courseList = array_keys(SessionManager::get_course_list_by_session_id($modelSessionId));
2425
        if (is_array($courseList)
2426
            && !empty($courseList)
2427
            && !SessionManager::add_courses_to_session($newSessionId, $courseList)) {
2428
            throw new Exception(get_lang('CoursesNotAddedToSession'));
2429
        }
2430
2431
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
2432
        $courseListOrdered = SessionManager::get_course_list_by_session_id($modelSessionId, null, 'position');
2433
        $count = 0;
2434
        foreach ($courseListOrdered as $course) {
2435
            if ($course['position'] == '') {
2436
                $course['position'] = $count;
2437
            }
2438
            // Saving order.
2439
            $sql = "UPDATE $table SET position = ".$course['position']."
2440
                    WHERE session_id = $newSessionId AND c_id = '".$course['real_id']."'";
2441
            Database::query($sql);
2442
            $count++;
2443
        }
2444
2445
        if ($duplicateAgendaContent) {
2446
            foreach ($courseList as $courseId) {
2447
                SessionManager::importAgendaFromSessionModel($modelSessionId, $newSessionId, $courseId);
2448
            }
2449
        }
2450
2451
        if (api_is_multiple_url_enabled()) {
2452
            if (api_get_current_access_url_id() != -1) {
2453
                UrlManager::add_session_to_url(
2454
                    $newSessionId,
2455
                    api_get_current_access_url_id()
2456
                );
2457
            } else {
2458
                UrlManager::add_session_to_url($newSessionId, 1);
2459
            }
2460
        } else {
2461
            UrlManager::add_session_to_url($newSessionId, 1);
2462
        }
2463
2464
        return $newSessionId;
2465
    }
2466
2467
    /**
2468
     * subscribes a user to a session.
2469
     *
2470
     * @throws Exception
2471
     */
2472
    public function subscribeUserToSessionFromUsername(int $sessionId, string $loginName): bool
2473
    {
2474
        if (!SessionManager::isValidId($sessionId)) {
2475
            throw new Exception(get_lang('SessionNotFound'));
2476
        }
2477
2478
        $userId = UserManager::get_user_id_from_username($loginName);
2479
        if (false === $userId) {
2480
            throw new Exception(get_lang('UserNotFound'));
2481
        }
2482
2483
        if (!api_is_platform_admin() && $userId != $this->user->getId()) {
2484
            self::throwNotAllowedException();
2485
        }
2486
2487
        $subscribed = SessionManager::subscribeUsersToSession(
2488
            $sessionId,
2489
            [$userId],
2490
            SESSION_VISIBLE_READ_ONLY,
2491
            false
2492
        );
2493
        if (!$subscribed) {
2494
            throw new Exception(get_lang('UserNotSubscribed'));
2495
        }
2496
2497
        return true;
2498
    }
2499
2500
    /**
2501
     * finds the session which has a specific value in a specific extra field.
2502
     *
2503
     * @param $fieldName
2504
     * @param $fieldValue
2505
     *
2506
     * @throws Exception when no session matched or more than one session matched
2507
     *
2508
     * @return int, the matching session id
2509
     */
2510
    public function getSessionFromExtraField($fieldName, $fieldValue)
2511
    {
2512
        // find sessions that that have value in field
2513
        $valueModel = new ExtraFieldValue('session');
2514
        $sessionIdList = $valueModel->get_item_id_from_field_variable_and_field_value(
2515
            $fieldName,
2516
            $fieldValue,
2517
            false,
2518
            false,
2519
            true
2520
        );
2521
2522
        // throw if none found
2523
        if (empty($sessionIdList)) {
2524
            throw new Exception(get_lang('NoSessionMatched'));
2525
        }
2526
2527
        // throw if more than one found
2528
        if (count($sessionIdList) > 1) {
2529
            throw new Exception(get_lang('MoreThanOneSessionMatched'));
2530
        }
2531
2532
        // return sessionId
2533
        return intval($sessionIdList[0]['item_id']);
2534
    }
2535
2536
    /**
2537
     * Get a list of users subscribed to the given session.
2538
     *
2539
     * @params int $sessionId
2540
     * @params int $moveInfo Whether to return the "moved_*" fields or not
2541
     */
2542
    public function getUsersSubscribedToSession(int $sessionId, int $moveInfo = 0): array
2543
    {
2544
        self::protectAdminEndpoint();
2545
2546
        $users = SessionManager::get_users_by_session($sessionId);
2547
2548
        $userList = [];
2549
        foreach ($users as $user) {
2550
            $userInfo = [
2551
                'user_id' => $user['user_id'],
2552
                'username' => $user['username'],
2553
                'firstname' => $user['firstname'],
2554
                'lastname' => $user['lastname'],
2555
                'status' => $user['relation_type'],
2556
            ];
2557
            if (1 === $moveInfo) {
2558
                $userInfo['moved_to'] = $user['moved_to'];
2559
                $userInfo['moved_status'] = $user['moved_status'];
2560
                $userInfo['moved_at'] = $user['moved_at'];
2561
            }
2562
            $userList[] = $userInfo;
2563
        }
2564
2565
        return $userList;
2566
    }
2567
2568
    /**
2569
     * Updates a user identified by its login name.
2570
     *
2571
     * @throws Exception on failure
2572
     */
2573
    public function updateUserFromUserName(array $parameters): bool
2574
    {
2575
        // find user
2576
        $userId = null;
2577
        if (empty($parameters)) {
2578
            throw new Exception('NoData');
2579
        }
2580
        foreach ($parameters as $name => $value) {
2581
            if (strtolower($name) === 'loginname') {
2582
                $userId = UserManager::get_user_id_from_username($value);
2583
                if (false === $userId) {
2584
                    throw new Exception(get_lang('UserNotFound'));
2585
                }
2586
                break;
2587
            }
2588
        }
2589
        if (is_null($userId)) {
2590
            throw new Exception(get_lang('NoData'));
2591
        }
2592
2593
        if (!api_is_platform_admin() && $userId != $this->user->getId()) {
2594
            self::throwNotAllowedException();
2595
        }
2596
2597
        /** @var User $user */
2598
        $user = UserManager::getRepository()->find($userId);
2599
        if (empty($user)) {
2600
            throw new Exception(get_lang('CouldNotLoadUser'));
2601
        }
2602
2603
        // tell the world we are about to update a user
2604
        $hook = HookUpdateUser::create();
2605
        if (!empty($hook)) {
2606
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_PRE);
2607
        }
2608
2609
        // apply submitted modifications
2610
        foreach ($parameters as $name => $value) {
2611
            switch (strtolower($name)) {
2612
                case 'email':
2613
                    $user->setEmail($value);
2614
                    break;
2615
                case 'enabled':
2616
                    $user->setEnabled($value);
2617
                    break;
2618
                case 'lastname':
2619
                    $user->setLastname($value);
2620
                    break;
2621
                case 'firstname':
2622
                    $user->setFirstname($value);
2623
                    break;
2624
                case 'phone':
2625
                    $user->setPhone($value);
2626
                    break;
2627
                case 'address':
2628
                    $user->setAddress($value);
2629
                    break;
2630
                case 'roles':
2631
                    $user->setRoles($value);
2632
                    break;
2633
                case 'profile_completed':
2634
                    $user->setProfileCompleted($value);
2635
                    break;
2636
                case 'auth_source':
2637
                    $user->setAuthSource($value);
2638
                    break;
2639
                case 'status':
2640
                    $user->setStatus($value);
2641
                    break;
2642
                case 'official_code':
2643
                    $user->setOfficialCode($value);
2644
                    break;
2645
                case 'picture_uri':
2646
                    $user->setPictureUri($value);
2647
                    break;
2648
                case 'creator_id':
2649
                    $user->setCreatorId($value);
2650
                    break;
2651
                case 'competences':
2652
                    $user->setCompetences($value);
2653
                    break;
2654
                case 'diplomas':
2655
                    $user->setDiplomas($value);
2656
                    break;
2657
                case 'openarea':
2658
                    $user->setOpenArea($value);
2659
                    break;
2660
                case 'teach':
2661
                    $user->setTeach($value);
2662
                    break;
2663
                case 'productions':
2664
                    $user->setProductions($value);
2665
                    break;
2666
                case 'language':
2667
                    $languages = api_get_languages();
2668
                    if (!in_array($value, $languages['folder'])) {
2669
                        throw new Exception(get_lang('LanguageUnavailable'));
2670
                    }
2671
                    $user->setLanguage($value);
2672
                    break;
2673
                case 'registration_date':
2674
                    $user->setRegistrationDate($value);
2675
                    break;
2676
                case 'expiration_date':
2677
                    $user->setExpirationDate(
2678
                        new DateTime(
2679
                            api_get_utc_datetime($value),
2680
                            new DateTimeZone('UTC')
2681
                        )
2682
                    );
2683
                    break;
2684
                case 'active':
2685
                    // see UserManager::update_user() usermanager.lib.php:1205
2686
                    if ($user->getActive() != $value) {
2687
                        $user->setActive($value);
2688
                        Event::addEvent($value ? LOG_USER_ENABLE : LOG_USER_DISABLE, LOG_USER_ID, $userId);
2689
                    }
2690
                    break;
2691
                case 'openid':
2692
                    $user->setOpenId($value);
2693
                    break;
2694
                case 'theme':
2695
                    $user->setTheme($value);
2696
                    break;
2697
                case 'hr_dept_id':
2698
                    $user->setHrDeptId($value);
2699
                    break;
2700
                case 'extra':
2701
                    if (is_array($value)) {
2702
                        if (count($value) > 0) {
2703
                            if (is_array($value[0])) {
2704
                                foreach ($value as $field) {
2705
                                    $fieldName = $field['field_name'];
2706
                                    $fieldValue = $field['field_value'];
2707
                                    if (!isset($fieldName) || !isset($fieldValue) ||
2708
                                        !UserManager::update_extra_field_value($userId, $fieldName, $fieldValue)) {
2709
                                        throw new Exception(get_lang('CouldNotUpdateExtraFieldValue').': '.print_r($field, true));
2710
                                    }
2711
                                }
2712
                            } else {
2713
                                foreach ($value as $fieldName => $fieldValue) {
2714
                                    if (!UserManager::update_extra_field_value($userId, $fieldName, $fieldValue)) {
2715
                                        throw new Exception(get_lang('CouldNotUpdateExtraFieldValue').': '.$fieldName);
2716
                                    }
2717
                                }
2718
                            }
2719
                        }
2720
                    }
2721
                    break;
2722
                case 'username':
2723
                case 'api_key':
2724
                case 'action':
2725
                case 'loginname':
2726
                    break;
2727
                case 'email_canonical':
2728
                case 'locked':
2729
                case 'expired':
2730
                case 'credentials_expired':
2731
                case 'credentials_expire_at':
2732
                case 'expires_at':
2733
                case 'salt':
2734
                case 'last_login':
2735
                case 'created_at':
2736
                case 'updated_at':
2737
                case 'confirmation_token':
2738
                case 'password_requested_at':
2739
                case 'password': // see UserManager::update_user usermanager.lib.php:1182
2740
                case 'username_canonical':
2741
                default:
2742
                    throw new Exception(get_lang('UnsupportedUpdate')." '$name'");
2743
            }
2744
        }
2745
2746
        // save modifications
2747
        UserManager::getManager()->updateUser($user, true);
2748
2749
        // tell the world we just updated this user
2750
        if (!empty($hook)) {
2751
            $hook->setEventData(['user' => $user]);
2752
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_POST);
2753
        }
2754
2755
        // invalidate cache for this user
2756
        $cacheAvailable = api_get_configuration_value('apc');
2757
        if ($cacheAvailable === true) {
2758
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$userId;
2759
            if (apcu_exists($apcVar)) {
2760
                apcu_delete($apcVar);
2761
            }
2762
        }
2763
2764
        return true;
2765
    }
2766
2767
    /**
2768
     * Returns whether a user login name exists.
2769
     *
2770
     * @param string $loginname the user login name
2771
     *
2772
     * @return bool whether the user login name exists
2773
     */
2774
    public function usernameExist($loginname)
2775
    {
2776
        return false !== api_get_user_info_from_username($loginname);
2777
    }
2778
2779
    /**
2780
     * Returns whether a user group name exists.
2781
     *
2782
     * @param string $name the group name
2783
     *
2784
     * @return bool whether the group name exists
2785
     */
2786
    public function groupExists($name)
2787
    {
2788
        $userGroup = new UserGroup();
2789
2790
        return false !== $userGroup->usergroup_exists($name);
2791
    }
2792
2793
    /**
2794
     * This service roughly matches what the call to MDL's API core_course_get_contents function returns.
2795
     *
2796
     * @return array
2797
     */
2798
    public function getCourseQuizMdlCompat()
2799
    {
2800
        $userId = $this->user->getId();
2801
        $courseId = $this->course->getId();
2802
        $sessionId = $this->session ? $this->session->getId() : 0;
2803
2804
        $toolVisibility = CourseHome::getToolVisibility(TOOL_QUIZ, $courseId, $sessionId);
2805
2806
        $json = [
2807
            "id" => $this->course->getId(),
2808
            "name" => get_lang('Exercises'),
2809
            "visible" => (int) $toolVisibility,
2810
            "summary" => '',
2811
            "summaryformat" => 1,
2812
            "section" => 1,
2813
            "hiddenbynumsections" => 0,
2814
            "uservisible" => $toolVisibility,
2815
            "modules" => [],
2816
        ];
2817
2818
        $quizIcon = Display::return_icon('quiz.png', '', [], ICON_SIZE_SMALL, false, true);
2819
2820
        $json['modules'] = array_map(
2821
            function (array $exercise) use ($quizIcon) {
2822
                return [
2823
                    'id' => (int) $exercise['id'],
2824
                    'url' => $exercise['url'],
2825
                    'name' => $exercise['name'],
2826
                    'instance' => 1,
2827
                    'visible' => 1,
2828
                    'uservisible' => true,
2829
                    'visibleoncoursepage' => 0,
2830
                    'modicon' => $quizIcon,
2831
                    'modname' => 'quiz',
2832
                    'modplural' => get_lang('Exercises'),
2833
                    'availability' => null,
2834
                    'indent' => 0,
2835
                    'onclick' => '',
2836
                    'afterlink' => null,
2837
                    'customdata' => "",
2838
                    'noviewlink' => false,
2839
                    'completion' => (int) ($exercise[1] > 0),
2840
                ];
2841
            },
2842
            Exercise::exerciseGrid(0, '', $userId, $courseId, $sessionId, true)
2843
        );
2844
2845
        return [$json];
2846
    }
2847
2848
    /**
2849
     * @throws Exception
2850
     */
2851
    public function updateSession(array $params): array
2852
    {
2853
        self::protectAdminEndpoint();
2854
2855
        $id = $params['session_id'];
2856
        $reset = $params['reset'] ?? null;
2857
        $name = $params['name'] ?? null;
2858
        $coachId = isset($params['id_coach']) ? (int) $params['id_coach'] : null;
2859
        $sessionCategoryId = isset($params['session_category_id']) ? (int) $params['session_category_id'] : null;
2860
        $description = $params['description'] ?? null;
2861
        $showDescription = $params['show_description'] ?? null;
2862
        $duration = $params['duration'] ?? null;
2863
        $visibility = $params['visibility'] ?? null;
2864
        $promotionId = $params['promotion_id'] ?? null;
2865
        $displayStartDate = $params['display_start_date'] ?? null;
2866
        $displayEndDate = $params['display_end_date'] ?? null;
2867
        $accessStartDate = $params['access_start_date'] ?? null;
2868
        $accessEndDate = $params['access_end_date'] ?? null;
2869
        $coachStartDate = $params['coach_access_start_date'] ?? null;
2870
        $coachEndDate = $params['coach_access_end_date'] ?? null;
2871
        $sendSubscriptionNotification = $params['send_subscription_notification'] ?? null;
2872
        $extraFields = $params['extra'] ?? [];
2873
2874
        $reset = (bool) $reset;
2875
        $visibility = (int) $visibility;
2876
        $tblSession = Database::get_main_table(TABLE_MAIN_SESSION);
2877
2878
        if (!SessionManager::isValidId($id)) {
2879
            throw new Exception(get_lang('NoData'));
2880
        }
2881
2882
        if (!empty($accessStartDate) && !api_is_valid_date($accessStartDate, 'Y-m-d H:i') &&
2883
            !api_is_valid_date($accessStartDate, 'Y-m-d H:i:s')
2884
        ) {
2885
            throw new Exception(get_lang('InvalidDate'));
2886
        }
2887
2888
        if (!empty($accessEndDate) && !api_is_valid_date($accessEndDate, 'Y-m-d H:i') &&
2889
            !api_is_valid_date($accessEndDate, 'Y-m-d H:i:s')
2890
        ) {
2891
            throw new Exception(get_lang('InvalidDate'));
2892
        }
2893
2894
        if (!empty($accessStartDate) && !empty($accessEndDate) && $accessStartDate >= $accessEndDate) {
2895
            throw new Exception(get_lang('InvalidDate'));
2896
        }
2897
2898
        $values = [];
2899
2900
        if ($reset) {
2901
            $values['name'] = $name;
2902
            $values['id_coach'] = $coachId;
2903
            $values['session_category_id'] = $sessionCategoryId;
2904
            $values['description'] = $description;
2905
            $values['show_description'] = $showDescription;
2906
            $values['duration'] = $duration;
2907
            $values['visibility'] = $visibility;
2908
            $values['promotion_id'] = $promotionId;
2909
            $values['display_start_date'] = !empty($displayStartDate) ? api_get_utc_datetime($displayStartDate) : null;
2910
            $values['display_end_date'] = !empty($displayEndDate) ? api_get_utc_datetime($displayEndDate) : null;
2911
            $values['access_start_date'] = !empty($accessStartDate) ? api_get_utc_datetime($accessStartDate) : null;
2912
            $values['access_end_date'] = !empty($accessEndDate) ? api_get_utc_datetime($accessEndDate) : null;
2913
            $values['coach_access_start_date'] = !empty($coachStartDate) ? api_get_utc_datetime($coachStartDate) : null;
2914
            $values['coach_access_end_date'] = !empty($coachEndDate) ? api_get_utc_datetime($coachEndDate) : null;
2915
            $values['send_subscription_notification'] = $sendSubscriptionNotification;
2916
        } else {
2917
            if (!empty($name)) {
2918
                $values['name'] = $name;
2919
            }
2920
2921
            if (!empty($coachId)) {
2922
                $values['id_coach'] = $coachId;
2923
            }
2924
2925
            if (!empty($sessionCategoryId)) {
2926
                $values['session_category_id'] = $sessionCategoryId;
2927
            }
2928
2929
            if (!empty($description)) {
2930
                $values['description'] = $description;
2931
            }
2932
2933
            if (!empty($showDescription)) {
2934
                $values['show_description'] = $showDescription;
2935
            }
2936
2937
            if (!empty($duration)) {
2938
                $values['duration'] = $duration;
2939
            }
2940
2941
            if (!empty($visibility)) {
2942
                $values['visibility'] = $visibility;
2943
            }
2944
2945
            if (!empty($promotionId)) {
2946
                $values['promotion_id'] = $promotionId;
2947
            }
2948
2949
            if (!empty($displayStartDate)) {
2950
                $values['display_start_date'] = api_get_utc_datetime($displayStartDate);
2951
            }
2952
2953
            if (!empty($displayEndDate)) {
2954
                $values['display_end_date'] = api_get_utc_datetime($displayEndDate);
2955
            }
2956
2957
            if (!empty($accessStartDate)) {
2958
                $values['access_start_date'] = api_get_utc_datetime($accessStartDate);
2959
            }
2960
2961
            if (!empty($accessEndDate)) {
2962
                $values['access_end_date'] = api_get_utc_datetime($accessEndDate);
2963
            }
2964
2965
            if (!empty($coachStartDate)) {
2966
                $values['coach_access_start_date'] = api_get_utc_datetime($coachStartDate);
2967
            }
2968
2969
            if (!empty($coachEndDate)) {
2970
                $values['coach_access_end_date'] = api_get_utc_datetime($coachEndDate);
2971
            }
2972
2973
            if (!empty($sendSubscriptionNotification)) {
2974
                $values['send_subscription_notification'] = $sendSubscriptionNotification;
2975
            }
2976
        }
2977
2978
        Database::update(
2979
            $tblSession,
2980
            $values,
2981
            ['id = ?' => $id]
2982
        );
2983
2984
        if (!empty($extraFields)) {
2985
            $extraFields['item_id'] = $id;
2986
            $sessionFieldValue = new ExtraFieldValue('session');
2987
            $sessionFieldValue->saveFieldValues($extraFields);
2988
        }
2989
2990
        return [
2991
            'status' => true,
2992
            'message' => get_lang('Updated'),
2993
            'id_session' => $id,
2994
        ];
2995
    }
2996
2997
    public function checkConditionalLogin(): bool
2998
    {
2999
        $file = api_get_path(SYS_CODE_PATH).'auth/conditional_login/conditional_login.php';
3000
3001
        if (!file_exists($file)) {
3002
            return true;
3003
        }
3004
3005
        include_once $file;
3006
3007
        if (!isset($login_conditions)) {
3008
            return true;
3009
        }
3010
3011
        foreach ($login_conditions as $condition) {
3012
            //If condition fails we redirect to the URL defined by the condition
3013
            if (!isset($condition['conditional_function'])) {
3014
                continue;
3015
            }
3016
3017
            $function = $condition['conditional_function'];
3018
            $result = $function(['user_id' => $this->user->getId()]);
3019
3020
            if ($result == false) {
3021
                return false;
3022
            }
3023
        }
3024
3025
        return true;
3026
    }
3027
3028
    public function getLegalConditions(): array
3029
    {
3030
        $language = api_get_language_id(
3031
            api_get_interface_language()
3032
        );
3033
3034
        $termPreview = LegalManager::get_last_condition($language);
3035
3036
        if ($termPreview) {
3037
            return $termPreview;
3038
        }
3039
3040
        $language = api_get_language_id(
3041
            api_get_setting('platformLanguage')
3042
        );
3043
3044
        $termPreview = LegalManager::get_last_condition($language);
3045
3046
        if ($termPreview) {
3047
            return $termPreview;
3048
        }
3049
3050
        $language = api_get_language_id('english');
3051
3052
        return LegalManager::get_last_condition($language);
3053
    }
3054
3055
    public function updateConditionAccepted()
3056
    {
3057
        $legalAcceptType = $_POST['legal_accept_type'] ?? null;
3058
3059
        $condArray = explode(':', $legalAcceptType);
3060
        $condArray = array_map('intval', $condArray);
3061
3062
        if (empty($condArray[0]) || empty($condArray[1])) {
3063
            return;
3064
        }
3065
3066
        $conditionToSave = intval($condArray[0]).':'.intval($condArray[1]).':'.time();
3067
3068
        LegalManager::sendEmailToUserBoss(
3069
            $this->user->getId(),
3070
            $conditionToSave
3071
        );
3072
    }
3073
3074
    /**
3075
     * Get the list of test with last user attempt and his datetime.
3076
     *
3077
     * @param array $fields A list of extra fields to include in the answer. Searches for the field in exercise, then in course
3078
     *
3079
     * @throws Exception
3080
     */
3081
    public function getTestUpdatesList($fields = []): array
3082
    {
3083
        self::protectAdminEndpoint();
3084
3085
        $tableCQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
3086
        $tableTrackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3087
        $tableUser = Database::get_main_table(TABLE_MAIN_USER);
3088
        $resultArray = [];
3089
3090
        // Check the extra fields criteria (whether to add extra field information or not)
3091
        $fieldSource = [];
3092
        $extraFieldValuesTable = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3093
        $fieldsSearchString = "SELECT field_id, value FROM $extraFieldValuesTable WHERE item_id = %d AND field_id = %d";
3094
        if (count($fields) > 0) {
3095
            // For each field, check where to get it from (quiz or course)
3096
            $quizExtraField = new ExtraField('exercise');
3097
            $courseExtraField = new ExtraField('course');
3098
            foreach ($fields as $fieldName) {
3099
                $fieldExists = $quizExtraField->get_handler_field_info_by_field_variable($fieldName);
3100
                if ($fieldExists === false) {
3101
                    // The field does not exist on the exercise, so use it from the course
3102
                    $courseFieldExists = $courseExtraField->get_handler_field_info_by_field_variable($fieldName);
3103
                    if ($courseFieldExists === false) {
3104
                        continue;
3105
                    }
3106
                    $fieldSource[$fieldName] = ['item_type' => 'course', 'id' => $courseFieldExists['id']];
3107
                } else {
3108
                    $fieldSource[$fieldName] = ['item_type' => 'exercise', 'id' => $fieldExists['id']];
3109
                }
3110
            }
3111
        }
3112
3113
        $sql = "
3114
            SELECT q.iid AS id,
3115
                q.title,
3116
                MAX(a.start_date) AS last_attempt_time,
3117
                u.username AS last_attempt_username,
3118
                q.c_id
3119
            FROM $tableCQuiz q
3120
            JOIN $tableTrackExercises a ON q.iid = a.exe_exo_id
3121
            JOIN $tableUser u ON a.exe_user_id = u.id
3122
            GROUP BY q.iid
3123
        ";
3124
3125
        $result = Database::query($sql);
3126
        if (Database::num_rows($result) > 0) {
3127
            while ($row = Database::fetch_assoc($result)) {
3128
                // Check the whole extra fields thing
3129
                if (count($fieldSource) > 0) {
3130
                    foreach ($fieldSource as $fieldName => $fieldDetails) {
3131
                        if ($fieldDetails['item_type'] == 'course') {
3132
                            $itemId = $row['c_id'];
3133
                        } else {
3134
                            $itemId = $row['id'];
3135
                        }
3136
                        $fieldResult = Database::query(sprintf($fieldsSearchString, $itemId, $fieldDetails['id']));
3137
                        if (Database::num_rows($fieldResult) > 0) {
3138
                            $fieldRow = Database::fetch_assoc($fieldResult);
3139
                            $row['extra_'.$fieldName] = $fieldRow['value'];
3140
                        } else {
3141
                            $row['extra_'.$fieldName] = '';
3142
                        }
3143
                    }
3144
                }
3145
                // Get item authoring data
3146
                $itemProps = api_get_last_item_property_info($row['c_id'], 'quiz', $row['id']);
3147
                $row['created_by'] = $this->__getConfiguredUsernameById($itemProps['insert_user_id']);
3148
                if ($itemProps['insert_user_id'] == $itemProps['lastedit_user_id']) {
3149
                    $row['updated_by'] = $row['created_by'];
3150
                } else {
3151
                    $row['updated_by'] = $this->__getConfiguredUsernameById($itemProps['lastedit_user_id']);
3152
                }
3153
                $resultArray[] = $row;
3154
            }
3155
        }
3156
3157
        return $resultArray;
3158
    }
3159
3160
    /**
3161
     * Get tests results data
3162
     * Not support sessions
3163
     * By default, is successful if score greater than 50%.
3164
     *
3165
     * @throws Exception
3166
     *
3167
     * @return array e.g: [ { "id": 4, "title": "aiken", "updated_by": "-", "type": "1", "completion": 0 } ]
3168
     */
3169
    public function getTestAverageResultsList(array $ids = [], ?array $fields = []): array
3170
    {
3171
        self::protectAdminEndpoint();
3172
        $tableTrackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3173
        $tableCQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
3174
        $tableCourseRelUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3175
3176
        $resultArray = [];
3177
        $countUsersInCourses = [];
3178
        $extraArray = [];
3179
3180
        if (!empty($ids)) {
3181
            if (!is_array($ids)) {
3182
                $ids = [$ids];
3183
            }
3184
            if (!is_array($fields)) {
3185
                $fields = [$fields];
3186
            }
3187
            if (!empty($fields)) {
3188
                foreach ($fields as $field) {
3189
                    $extraArray['extra_'.$field] = '';
3190
                }
3191
            }
3192
3193
            $queryUsersInCourses = "
3194
                SELECT c_id, count(*)
3195
                FROM $tableCourseRelUser
3196
                GROUP BY c_id
3197
                ORDER BY c_id;
3198
            ";
3199
3200
            $resultUsersInCourses = Database::query($queryUsersInCourses);
3201
            while ($row = Database::fetch_array($resultUsersInCourses)) {
3202
                $countUsersInCourses[$row[0]] = $row[1];
3203
            }
3204
3205
            foreach ($ids as $item) {
3206
                $item = (int) $item;
3207
3208
                $queryCQuiz = "
3209
                    SELECT c_id,
3210
                        title,
3211
                        feedback_type,
3212
                        pass_percentage
3213
                    FROM $tableCQuiz
3214
                    WHERE iid = $item";
3215
3216
                $resultCQuiz = Database::query($queryCQuiz);
3217
                if (Database::num_rows($resultCQuiz) <= 0) {
3218
                    continue;
3219
                }
3220
                $row = Database::fetch_assoc($resultCQuiz);
3221
3222
                $cId = $row['c_id'];
3223
                $title = $row['title'];
3224
                $type = Exercise::getFeedbackTypeLiteral($row['feedback_type']);
3225
                $passPercentage = empty($row['pass_percentage']) ? 0.5 : $row['pass_percentage'];
3226
3227
                // Get item authoring data
3228
                $itemProps = api_get_last_item_property_info($row['c_id'], 'quiz', $item);
3229
                $createdBy = $this->__getConfiguredUsernameById($itemProps['insert_user_id']);
3230
                if ($itemProps['insert_user_id'] == $itemProps['lastedit_user_id']) {
3231
                    $updatedBy = $createdBy;
3232
                } else {
3233
                    $updatedBy = $this->__getConfiguredUsernameById($itemProps['lastedit_user_id']);
3234
                }
3235
3236
                $sql = "
3237
                    SELECT a.exe_exo_id AS id,
3238
                           a.exe_user_id,
3239
                           MAX(a.start_date),
3240
                           a.exe_result,
3241
                           a.exe_weighting
3242
                    FROM $tableTrackExercises a
3243
                    WHERE a.exe_exo_id = $item
3244
                    GROUP BY a.exe_exo_id, a.exe_user_id
3245
                ";
3246
3247
                $result = Database::query($sql);
3248
                if (Database::num_rows($result) > 0) {
3249
                    $countAttempts = 0;
3250
                    $countSuccess = 0;
3251
                    $scoreSum = 0;
3252
3253
                    while ($row = Database::fetch_assoc($result)) {
3254
3255
                        // If test is badly configured, with all questions at score 0
3256
                        if ($row['exe_weighting'] == 0) {
3257
                            continue;
3258
                        }
3259
                        $score = $row['exe_result'] / $row['exe_weighting'];
3260
                        if ($score >= $passPercentage) {
3261
                            $countSuccess++;
3262
                        }
3263
                        $scoreSum += $score;
3264
                        $countAttempts++;
3265
                    }
3266
                    $completionMethod = 'Success on users count';
3267
                    if ($countAttempts === 0) {
3268
                        // In some cases, there are no attempts at all. Return 0 completion & score.
3269
                        $averageScore = 0;
3270
                        $completion = 0;
3271
                    } else {
3272
                        $averageScore = round(($scoreSum / $countAttempts) * 100, 2);
3273
                        if (empty($countUsersInCourses[$cId])) {
3274
                            // Users might have all been unsubscribed from the course since taking the test
3275
                            $completion = $countSuccess / $countAttempts;
3276
                            $completionMethod = 'Success on attempts count';
3277
                        } else {
3278
                            $completion = $countSuccess / $countUsersInCourses[$cId];
3279
                        }
3280
                    }
3281
                    $params = [
3282
                        'id' => $item,
3283
                        'title' => $title,
3284
                        'created_by' => $createdBy,
3285
                        'updated_by' => $updatedBy,
3286
                        'type' => $type,
3287
                        'completion' => $completion,
3288
                        'completion_method' => $completionMethod,
3289
                        'number_of_last_attempts' => $countAttempts,
3290
                        'average_score_in_percent' => $averageScore,
3291
                    ];
3292
                    foreach ($extraArray as $name => $value) {
3293
                        $params[$name] = $value;
3294
                    }
3295
                    $resultArray[] = $params;
3296
                }
3297
            }
3298
        }
3299
3300
        return $resultArray;
3301
    }
3302
3303
    public function logout()
3304
    {
3305
        online_logout($this->user->getId());
3306
3307
        Event::courseLogout(
3308
            [
3309
                'uid' => $this->user->getId(),
3310
                'cid' => $this->course ? $this->course->getId() : 0,
3311
                'sid' => $this->session ? $this->session->getId() : 0,
3312
            ]
3313
        );
3314
    }
3315
3316
    /**
3317
     * @throws Exception
3318
     */
3319
    public function setThreadNotify(int $threadId): string
3320
    {
3321
        require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
3322
3323
        $result = set_notification(
3324
            'thread',
3325
            $threadId,
3326
            false,
3327
            api_get_user_info($this->user->getId()),
3328
            api_get_course_info($this->course->getCode())
3329
        );
3330
3331
        if (false === $result) {
3332
            self::throwNotAllowedException();
3333
        }
3334
3335
        return $result;
3336
    }
3337
3338
    public function getCourseWorks(): array
3339
    {
3340
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3341
3342
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3343
3344
        $isAllowedToEdit = $this->user->getStatus() !== STUDENT;
3345
3346
        $courseId = $this->course->getId();
3347
        $sessionId = $this->session ? $this->session->getId() : 0;
3348
3349
        $courseInfo = api_get_course_info_by_id($this->course->getId());
3350
3351
        $works = array_filter(
3352
            getWorkListTeacherData($courseId, $sessionId, 0, 0, 0, 'title', 'ASC', ''),
3353
            function (array $work) use ($isAllowedToEdit, $courseInfo, $courseId, $sessionId) {
3354
                if (!$isAllowedToEdit
3355
                    && !userIsSubscribedToWork($this->user->getId(), $work['id'], $courseId)
3356
                ) {
3357
                    return false;
3358
                }
3359
3360
                $visibility = api_get_item_visibility($courseInfo, 'work', $work['id'], $sessionId);
3361
3362
                if (!$isAllowedToEdit && $visibility != 1) {
3363
                    return false;
3364
                }
3365
3366
                return true;
3367
            }
3368
        );
3369
3370
        return array_map(
3371
            function (array $work) use ($isAllowedToEdit, $courseInfo) {
3372
                $work['type'] = 'work.png';
3373
3374
                if (!$isAllowedToEdit) {
3375
                    $workList = get_work_user_list(
3376
                        0,
3377
                        1000,
3378
                        null,
3379
                        null,
3380
                        $work['id'],
3381
                        ' AND u.id = '.$this->user->getId()
3382
                    );
3383
3384
                    $count = getTotalWorkComment($workList, $courseInfo);
3385
                    $lastWork = getLastWorkStudentFromParentByUser($this->user->getId(), $work, $courseInfo);
3386
3387
                    $work['feedback'] = ' '.Display::label('0 '.get_lang('Feedback'), 'warning');
3388
3389
                    if (!empty($count)) {
3390
                        $work['feedback'] = ' '.Display::label($count.' '.get_lang('Feedback'), 'info');
3391
                    }
3392
3393
                    $work['last_upload'] = '';
3394
3395
                    if (!empty($lastWork)) {
3396
                        $work['last_upload'] = !empty($lastWork['qualification'])
3397
                            ? $lastWork['qualification_rounded'].' - '
3398
                            : '';
3399
                        $work['last_upload'] .= api_get_local_time($lastWork['sent_date']);
3400
                    }
3401
                }
3402
3403
                return $work;
3404
            },
3405
            $works
3406
        );
3407
    }
3408
3409
    /**
3410
     * Returns a list of exercises in the given course. The given course is received through generic param at instanciation.
3411
     *
3412
     * @param array $fields A list of extra fields to include in the answer. Searches for the field in exercise, then in course
3413
     */
3414
    public function getCourseExercises($fields = []): array
3415
    {
3416
        Event::event_access_tool(TOOL_QUIZ);
3417
3418
        $sessionId = $this->session ? $this->session->getId() : 0;
3419
        $courseInfo = api_get_course_info_by_id($this->course->getId());
3420
3421
        // Check the extra fields criteria (whether to add extra field information or not)
3422
        $fieldSource = [];
3423
        if (count($fields) > 0) {
3424
            // For each field, check where to get it from (quiz or course)
3425
            $quizExtraField = new ExtraField('exercise');
3426
            $courseExtraField = new ExtraField('course');
3427
            foreach ($fields as $fieldName) {
3428
                $fieldExists = $quizExtraField->get_handler_field_info_by_field_variable($fieldName);
3429
                if ($fieldExists === false) {
3430
                    // The field does not exist on the exercise, so use it from the course
3431
                    $courseFieldExists = $courseExtraField->get_handler_field_info_by_field_variable($fieldName);
3432
                    if ($courseFieldExists === false) {
3433
                        continue;
3434
                    }
3435
                    $fieldSource[$fieldName] = ['item_type' => 'course', 'id' => $courseFieldExists['id']];
3436
                } else {
3437
                    $fieldSource[$fieldName] = ['item_type' => 'exercise', 'id' => $fieldExists['id']];
3438
                }
3439
            }
3440
        }
3441
        $list = ExerciseLib::get_all_exercises($courseInfo, $sessionId);
3442
3443
        // Now check the whole extra fields thing
3444
        if (count($fieldSource) > 0) {
3445
            $extraFieldValuesTable = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3446
            $fieldsSearchString = "SELECT field_id, value FROM $extraFieldValuesTable WHERE item_id = %d AND field_id = %d";
3447
            foreach ($list as $id => $exercise) {
3448
                foreach ($fieldSource as $fieldName => $fieldDetails) {
3449
                    if ($fieldDetails['item_type'] == 'course') {
3450
                        $itemId = $exercise['c_id'];
3451
                    } else {
3452
                        $itemId = $exercise['iid'];
3453
                    }
3454
                    $result = Database::query(sprintf($fieldsSearchString, $itemId, $fieldDetails['id']));
3455
                    if (Database::num_rows($result) > 0) {
3456
                        $row = Database::fetch_assoc($result);
3457
                        $list[$id]['extra_'.$fieldName] = $row['value'];
3458
                    } else {
3459
                        $list[$id]['extra_'.$fieldName] = '';
3460
                    }
3461
                }
3462
            }
3463
        }
3464
        foreach ($list as $id => $row) {
3465
            // Get item authoring data
3466
            $itemProps = api_get_last_item_property_info($row['c_id'], 'quiz', $row['iid']);
3467
            $createdBy = $this->__getConfiguredUsernameById($itemProps['insert_user_id']);
3468
            if ($itemProps['insert_user_id'] == $itemProps['lastedit_user_id']) {
3469
                $updatedBy = $createdBy;
3470
            } else {
3471
                $updatedBy = $this->__getConfiguredUsernameById($itemProps['lastedit_user_id']);
3472
            }
3473
            $list[$id]['created_by'] = $createdBy;
3474
            $list[$id]['updated_by'] = $updatedBy;
3475
        }
3476
3477
        return $list;
3478
    }
3479
3480
    /**
3481
     * @throws Exception
3482
     */
3483
    public function putCourseWorkVisibility(int $workId, int $status): bool
3484
    {
3485
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3486
3487
        $courseInfo = api_get_course_info_by_id($this->course->getId());
3488
3489
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3490
3491
        switch ($status) {
3492
            case 1:
3493
                return makeVisible($workId, $courseInfo);
3494
            case 0:
3495
                return makeInvisible($workId, $courseInfo);
3496
            default:
3497
                throw new Exception(get_lang('ActionNotAllowed'));
3498
        }
3499
    }
3500
3501
    public function deleteWorkStudentItem(int $workId): string
3502
    {
3503
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3504
3505
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3506
3507
        $courseInfo = api_get_course_info_by_id($this->course->getId());
3508
3509
        $fileDeleted = deleteWorkItem($workId, $courseInfo);
3510
3511
        if ($fileDeleted) {
3512
            return get_lang('TheDocumentHasBeenDeleted');
3513
        }
3514
3515
        return get_lang('YouAreNotAllowedToDeleteThisDocument');
3516
    }
3517
3518
    public function deleteWorkCorrections(int $workId): string
3519
    {
3520
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3521
3522
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3523
3524
        $courseInfo = api_get_course_info_by_id($this->course->getId());
3525
3526
        $result = get_work_user_list(null, null, null, null, $workId);
3527
3528
        if ($result) {
3529
            foreach ($result as $item) {
3530
                $workInfo = get_work_data_by_id($item['id']);
3531
3532
                deleteCorrection($courseInfo, $workInfo);
3533
            }
3534
        }
3535
3536
        return get_lang('Deleted');
3537
    }
3538
3539
    public function getWorkList(int $workId): array
3540
    {
3541
        $isAllowedToEdit = api_is_allowed_to_edit();
3542
3543
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3544
3545
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3546
3547
        $userId = $this->user->getId();
3548
        $courseId = $this->course->getId();
3549
        $sessionId = $this->session ? $this->session->getId() : 0;
3550
3551
        $courseInfo = api_get_course_info_by_id($courseId);
3552
        $webPath = api_get_path(WEB_PATH);
3553
3554
        $whereCondition = !$isAllowedToEdit ? " AND u.id = $userId" : '';
3555
3556
        $works = get_work_user_list(
3557
            0,
3558
            0,
3559
            'title',
3560
            'asc',
3561
            $workId,
3562
            $whereCondition,
3563
            null,
3564
            false,
3565
            $courseId,
3566
            $sessionId
3567
        );
3568
3569
        return array_map(
3570
            function (array $work) use ($courseInfo, $webPath) {
3571
                $itemId = $work['id'];
3572
                $count = getWorkCommentCount($itemId, $courseInfo);
3573
3574
                $work['feedback'] = $count.' '.Display::returnFontAwesomeIcon('comments-o');
3575
                $work['feedback_clean'] = $count;
3576
3577
                $workInfo = get_work_data_by_id($itemId);
3578
                $commentsTmp = getWorkComments($workInfo);
3579
                $comments = [];
3580
3581
                foreach ($commentsTmp as $comment) {
3582
                    $comment['comment'] = str_replace('src="/', 'src="'.$webPath.'app/', $comment['comment']);
3583
                    $comments[] = $comment;
3584
                }
3585
3586
                $work['comments'] = $comments;
3587
3588
                if (empty($workInfo['qualificator_id'])) {
3589
                    $qualificator_id = Display::label(get_lang('NotRevised'), 'warning');
3590
                } else {
3591
                    $qualificator_id = Display::label(get_lang('Revised'), 'success');
3592
                }
3593
3594
                $work['qualificator_id'] = $qualificator_id;
3595
3596
                return $work;
3597
            },
3598
            $works
3599
        );
3600
    }
3601
3602
    public function getWorkStudentsWithoutPublications(int $workId): array
3603
    {
3604
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3605
3606
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3607
3608
        return get_list_users_without_publication($workId);
3609
    }
3610
3611
    public function getWorkUsers(int $workId): array
3612
    {
3613
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3614
3615
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3616
3617
        $courseId = $this->course->getId();
3618
        $sessionId = $this->session ? $this->session->getId() : 0;
3619
        $courseInfo = api_get_course_info_by_id($courseId);
3620
3621
        $items = getAllUserToWork($workId, $courseId);
3622
        $usersAdded = [];
3623
        $result = [
3624
            'users_added' => [],
3625
            'users_to_add' => [],
3626
        ];
3627
3628
        if (!empty($items)) {
3629
            foreach ($items as $data) {
3630
                $usersAdded[] = $data['user_id'];
3631
3632
                $userInfo = api_get_user_info($data['user_id']);
3633
3634
                $result['users_added'][] = [
3635
                    'user_id' => (int) $data['user_id'],
3636
                    'complete_name_with_username' => $userInfo['complete_name_with_username'],
3637
                ];
3638
            }
3639
        }
3640
3641
        if (empty($sessionId)) {
3642
            $status = STUDENT;
3643
        } else {
3644
            $status = 0;
3645
        }
3646
3647
        $userList = CourseManager::get_user_list_from_course_code(
3648
            $courseInfo['code'],
3649
            $sessionId,
3650
            null,
3651
            null,
3652
            $status
3653
        );
3654
3655
        $userToAddList = [];
3656
        foreach ($userList as $user) {
3657
            if (!in_array($user['user_id'], $usersAdded)) {
3658
                $userToAddList[] = $user;
3659
            }
3660
        }
3661
3662
        if (!empty($userToAddList)) {
3663
            foreach ($userToAddList as $user) {
3664
                $userName = api_get_person_name($user['firstname'], $user['lastname']).' ('.$user['username'].') ';
3665
3666
                $result['users_to_add'][] = [
3667
                    'user_id' => (int) $user['user_id'],
3668
                    'complete_name_with_username' => $userName,
3669
                ];
3670
            }
3671
        }
3672
3673
        return $result;
3674
    }
3675
3676
    public function getWorkStudentList(int $workId): array
3677
    {
3678
        Event::event_access_tool(TOOL_STUDENTPUBLICATION);
3679
3680
        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
3681
3682
        $courseId = $this->course->getId();
3683
        $courseCode = $this->course->getCode();
3684
        $sessionId = $this->session ? $this->session->getId() : 0;
3685
3686
        $myFolderData = get_work_data_by_id($workId);
3687
3688
        $workParents = [];
3689
3690
        if (empty($myFolderData)) {
3691
            $workParents = getWorkList($workId, $myFolderData);
3692
        }
3693
3694
        $workIdList = [];
3695
3696
        if (!empty($workParents)) {
3697
            foreach ($workParents as $work) {
3698
                $workIdList[] = $work->id;
3699
            }
3700
        }
3701
3702
        $userList = getWorkUserList(
3703
            $courseCode,
3704
            $sessionId,
3705
            0,
3706
            0,
3707
            null,
3708
            null,
3709
            null
3710
        );
3711
3712
        return array_map(
3713
            function ($userId) use ($courseId, $sessionId, $workParents, $workIdList) {
3714
                $user = api_get_user_info($userId);
3715
3716
                $userWorks = 0;
3717
3718
                if (!empty($workIdList)) {
3719
                    $userWorks = getUniqueStudentAttempts(
3720
                        $workIdList,
3721
                        0,
3722
                        $courseId,
3723
                        $sessionId,
3724
                        $user['user_id']
3725
                    );
3726
                }
3727
3728
                $works = $userWorks." / ".count($workParents);
3729
3730
                return [
3731
                    'id' => $userId,
3732
                    'complete_name' => api_get_person_name($user['firstname'], $user['lastname']),
3733
                    'works' => $works,
3734
                ];
3735
            },
3736
            $userList
3737
        );
3738
    }
3739
3740
    public function viewUserProfile(int $userId)
3741
    {
3742
        $url = api_get_path(WEB_CODE_PATH).'social/profile.php';
3743
3744
        if ($userId) {
3745
            $url .= '?'.http_build_query(['u' => $userId]);
3746
        }
3747
3748
        header("Location: $url");
3749
        exit;
3750
    }
3751
3752
    public function viewCourseHome()
3753
    {
3754
        $url = api_get_course_url($this->course->getCode(), $this->session ? $this->session->getId() : 0);
3755
3756
        header("Location: $url");
3757
        exit;
3758
    }
3759
3760
    public function viewDocumentInFrame(int $documentId)
3761
    {
3762
        $courseCode = $this->course->getCode();
3763
        $sessionId = $this->session ? $this->session->getId() : 0;
3764
3765
        $url = api_get_path(WEB_CODE_PATH).'document/showinframes.php?'
3766
            .http_build_query(
3767
                [
3768
                    'cidReq' => $courseCode,
3769
                    'id_session' => $sessionId,
3770
                    'gidReq' => 0,
3771
                    'gradebook' => 0,
3772
                    'origin' => self::SERVICE_NAME,
3773
                    'id' => $documentId,
3774
                ]
3775
            );
3776
3777
        header("Location: $url");
3778
        exit;
3779
    }
3780
3781
    public function viewQuizTool()
3782
    {
3783
        $courseCode = $this->course->getCode();
3784
        $sessionId = $this->session ? $this->session->getId() : 0;
3785
3786
        $url = api_get_path(WEB_CODE_PATH).'exercise/exercise.php?'
3787
            .http_build_query(
3788
                [
3789
                    'cidReq' => $courseCode,
3790
                    'id_session' => $sessionId,
3791
                    'gidReq' => 0,
3792
                    'gradebook' => 0,
3793
                    'origin' => self::SERVICE_NAME,
3794
                ]
3795
            );
3796
3797
        header("Location: $url");
3798
        exit;
3799
    }
3800
3801
    public function viewSurveyTool()
3802
    {
3803
        $courseCode = $this->course->getCode();
3804
        $sessionId = $this->session ? $this->session->getId() : 0;
3805
3806
        $url = api_get_path(WEB_CODE_PATH).'survey/survey_list.php?'
3807
            .http_build_query(
3808
                [
3809
                    'cidReq' => $courseCode,
3810
                    'id_session' => $sessionId,
3811
                    'gidReq' => 0,
3812
                    'gradebook' => 0,
3813
                    'origin' => self::SERVICE_NAME,
3814
                ]
3815
            );
3816
3817
        header("Location: $url");
3818
        exit;
3819
    }
3820
3821
    public function viewMessage(int $messageId)
3822
    {
3823
        $url = api_get_path(WEB_CODE_PATH).'messages/view_message.php?'.http_build_query(['id' => $messageId]);
3824
3825
        header("Location: $url");
3826
        exit;
3827
    }
3828
3829
    public function downloadForumPostAttachment(string $path)
3830
    {
3831
        $courseCode = $this->course->getCode();
3832
        $sessionId = $this->session ? $this->session->getId() : 0;
3833
3834
        $url = api_get_path(WEB_CODE_PATH).'forum/download.php?'
3835
            .http_build_query(
3836
                [
3837
                    'cidReq' => $courseCode,
3838
                    'id_session' => $sessionId,
3839
                    'gidReq' => 0,
3840
                    'gradebook' => 0,
3841
                    'origin' => self::SERVICE_NAME,
3842
                    'file' => Security::remove_XSS($path),
3843
                ]
3844
            );
3845
3846
        header("Location: $url");
3847
        exit;
3848
    }
3849
3850
    public function downloadWorkFolder(int $workId)
3851
    {
3852
        $cidReq = api_get_cidreq();
3853
        $url = api_get_path(WEB_CODE_PATH)."work/downloadfolder.inc.php?id=$workId&$cidReq";
3854
3855
        header("Location: $url");
3856
        exit;
3857
    }
3858
3859
    public function downloadWorkCommentAttachment(int $commentId)
3860
    {
3861
        $cidReq = api_get_cidreq();
3862
        $url = api_get_path(WEB_CODE_PATH)."work/download_comment_file.php?comment_id=$commentId&$cidReq";
3863
3864
        header("Location: $url");
3865
        exit;
3866
    }
3867
3868
    public function downloadWork(int $workId, bool $isCorrection = false)
3869
    {
3870
        $cidReq = api_get_cidreq();
3871
        $url = api_get_path(WEB_CODE_PATH)."work/download.php?$cidReq&"
3872
            .http_build_query(
3873
                [
3874
                    'id' => $workId,
3875
                    'correction' => $isCorrection ? 1 : null,
3876
                ]
3877
            );
3878
3879
        header("Location: $url");
3880
        exit;
3881
    }
3882
3883
    /**
3884
     * @throws Exception
3885
     */
3886
    public function getAllUsersApiKeys(int $page, int $length, bool $force = false, ?int $urlId = null): array
3887
    {
3888
        if (false === api_get_configuration_value('webservice_enable_adminonly_api')
3889
            || !UserManager::is_admin($this->user->getId())
3890
        ) {
3891
            self::throwNotAllowedException();
3892
        }
3893
3894
        $limitOffset = ($page - 1) * $length;
3895
3896
        $currentUrlId = $urlId ?: api_get_current_access_url_id();
3897
3898
        $data = [];
3899
        $data['total'] = UserManager::get_number_of_users(0, $currentUrlId);
3900
        $data['list'] = array_map(
3901
            function (array $user) use ($force) {
3902
                $apiKeys = UserManager::get_api_keys($user['id'], self::SERVICE_NAME);
3903
                $apiKey = $apiKeys ? current($apiKeys) : null;
3904
3905
                if ($force && empty($apiKey)) {
3906
                    $apiKey = self::generateApiKeyForUser((int) $user['id']);
3907
                }
3908
3909
                return [
3910
                    'id' => (int) $user['id'],
3911
                    'username' => $user['username'],
3912
                    'api_key' => $apiKey,
3913
                ];
3914
            },
3915
            $users = UserManager::get_user_list([], [], $limitOffset, $length, $currentUrlId)
3916
        );
3917
        $data['length'] = count($users);
3918
3919
        if ($page * $length < $data['total']) {
3920
            $nextPageQueryParams = [
3921
                'page' => $page + 1,
3922
                'per_page' => $length,
3923
                'url_id' => $urlId,
3924
            ];
3925
3926
            $data['next'] = $this->generateUrl($nextPageQueryParams);
3927
        }
3928
3929
        return $data;
3930
    }
3931
3932
    /**
3933
     * @throws Exception
3934
     */
3935
    public function getUserApiKey(string $username, bool $force = false): array
3936
    {
3937
        if (false === api_get_configuration_value('webservice_enable_adminonly_api')
3938
            || !UserManager::is_admin($this->user->getId())
3939
        ) {
3940
            self::throwNotAllowedException();
3941
        }
3942
3943
        $userInfo = api_get_user_info_from_username($username);
3944
3945
        if (empty($userInfo)) {
3946
            throw new Exception(get_lang('UserNotFound'));
3947
        }
3948
3949
        $apiKeys = UserManager::get_api_keys($userInfo['id'], self::SERVICE_NAME);
3950
        $apiKey = $apiKeys ? current($apiKeys) : null;
3951
3952
        if ($force && empty($apiKey)) {
3953
            $apiKey = self::generateApiKeyForUser((int) $userInfo['id']);
3954
        }
3955
3956
        return [
3957
            'id' => $userInfo['id'],
3958
            'username' => $userInfo['username'],
3959
            'api_key' => $apiKey,
3960
        ];
3961
    }
3962
3963
    /**
3964
     * @throws Exception
3965
     */
3966
    public function getUserLastConnexion(string $username): array
3967
    {
3968
        $userInfo = api_get_user_info_from_username($username);
3969
3970
        if (empty($userInfo)) {
3971
            throw new Exception(get_lang('UserNotFound'));
3972
        }
3973
3974
        $lastConnexionDate = Tracking::get_last_connection_date($userInfo['id']);
3975
3976
        return [
3977
            'id' => $userInfo['id'],
3978
            'username' => $userInfo['username'],
3979
            'last_connexion_date' => $lastConnexionDate,
3980
        ];
3981
    }
3982
3983
    /**
3984
     * @throws Exception
3985
     */
3986
    public function getUserTotalConnexionTime(string $username): array
3987
    {
3988
        $userInfo = api_get_user_info_from_username($username);
3989
3990
        if (empty($userInfo)) {
3991
            throw new Exception(get_lang('UserNotFound'));
3992
        }
3993
3994
        $totalConnexionTimeInSecond = Tracking::get_time_spent_on_the_platform($userInfo['id'], 'ever');
3995
        $totalConnexionTime = api_time_to_hms($totalConnexionTimeInSecond);
3996
3997
        return [
3998
            'id' => $userInfo['id'],
3999
            'username' => $userInfo['username'],
4000
            'total_connexion_time' => $totalConnexionTime,
4001
        ];
4002
    }
4003
4004
    public static function isAllowedByRequest(bool $inpersonate = false): bool
4005
    {
4006
        $username = $_GET['username'] ?? null;
4007
        $apiKey = $_GET['api_key'] ?? null;
4008
4009
        if (empty($username) || empty($apiKey)) {
4010
            return false;
4011
        }
4012
4013
        try {
4014
            $restApi = self::validate($username, $apiKey);
4015
        } catch (Exception $e) {
4016
            return false;
4017
        }
4018
4019
        if ($inpersonate) {
4020
            Login::init_user($restApi->getUser()->getId(), true);
4021
        }
4022
4023
        return (bool) $restApi;
4024
    }
4025
4026
    public function viewMyCourses()
4027
    {
4028
        $url = api_get_path(WEB_PATH).'user_portal.php?'
4029
            .http_build_query(['nosession' => 'true']);
4030
4031
        header("Location: $url");
4032
        exit;
4033
    }
4034
4035
    /**
4036
     * Create a group/class.
4037
     *
4038
     * @param $params
4039
     *
4040
     * @throws Exception
4041
     */
4042
    public function addGroup($params): array
4043
    {
4044
        self::protectAdminEndpoint();
4045
4046
        if (!empty($params['type'])) {
4047
            $params['group_type'] = $params['type'];
4048
        }
4049
4050
        // First check wether the login already exists.
4051
        $userGroup = new UserGroup();
4052
        if ($userGroup->usergroup_exists($params['name'])) {
4053
            throw new Exception($params['name'].' '.get_lang('AlreadyExists'));
4054
        }
4055
4056
        $groupId = $userGroup->save($params);
4057
4058
        if (empty($groupId)) {
4059
            throw new Exception(get_lang('NotRegistered'));
4060
        }
4061
4062
        return [$groupId];
4063
    }
4064
4065
    /**
4066
     * Delete a group/class.
4067
     *
4068
     * @throws Exception
4069
     *
4070
     * @return bool
4071
     */
4072
    public function deleteGroup(int $id): array
4073
    {
4074
        self::protectAdminEndpoint();
4075
4076
        if (empty($id)) {
4077
            return false;
4078
        }
4079
4080
        // First check wether the login already exists.
4081
        $userGroup = new UserGroup();
4082
        if (!$userGroup->delete($id)) {
4083
            throw new Exception(get_lang('NotDeleted'));
4084
        }
4085
4086
        return [$id];
4087
    }
4088
4089
    /**
4090
     * Get the list of users subscribed to the given group/class.
4091
     *
4092
     * @return array The list of users (userID => [firstname, lastname, relation_type]
4093
     */
4094
    public function getGroupSubscribedUsers(int $groupId): array
4095
    {
4096
        $userGroup = new UserGroup();
4097
4098
        return $userGroup->get_all_users_by_group($groupId);
4099
    }
4100
4101
    /**
4102
     * Get the list of courses to which the given group/class is subscribed.
4103
     *
4104
     * @return array The list of courses (ID => [title]
4105
     */
4106
    public function getGroupSubscribedCourses(int $groupId): array
4107
    {
4108
        $userGroup = new UserGroup();
4109
4110
        return $userGroup->get_courses_by_usergroup($groupId, true);
4111
    }
4112
4113
    /**
4114
     * Get the list of sessions to which the given group/class is subscribed.
4115
     *
4116
     * @return array The list of courses (ID => [title]
4117
     */
4118
    public function getGroupSubscribedSessions(int $groupId): array
4119
    {
4120
        $userGroup = new UserGroup();
4121
4122
        return $userGroup->get_sessions_by_usergroup($groupId, true);
4123
    }
4124
4125
    /**
4126
     * Add a new user to the given group/class.
4127
     *
4128
     * @param int $relationType (1:admin, 2:reader, etc. See GROUP_USER_PERMISSION_ constants in api.lib.php)
4129
     *
4130
     * @return array One item array containing true on success, false otherwise
4131
     */
4132
    public function addGroupSubscribedUser(int $groupId, int $userId, int $relationType = 2): array
4133
    {
4134
        $userGroup = new UserGroup();
4135
4136
        if (!$userGroup->groupExists($groupId) or !$userGroup->userExists($userId)) {
4137
            throw new Exception('user_id or group_id does not exist');
4138
        }
4139
4140
        return [$userGroup->add_user_to_group($userId, $groupId, $relationType)];
4141
    }
4142
4143
    /**
4144
     * Get the list of group/class IDs to which the user belongs.
4145
     *
4146
     * @return array Array containing the group IDs like ['groups' => [1, 2, 3]]
4147
     */
4148
    public function getUserSubGroup(int $userId): array
4149
    {
4150
        $userGroup = new UserGroup();
4151
4152
        $res = $userGroup->get_usergroup_by_user($userId);
4153
4154
        return ['groups' => $res];
4155
    }
4156
4157
    /**
4158
     * Add a new course to which the given group/class is subscribed.
4159
     *
4160
     * @return array One item array containing the ID of the course on success, nothing on failure
4161
     */
4162
    public function addGroupSubscribedCourse(int $groupId, int $courseId): array
4163
    {
4164
        $userGroup = new UserGroup();
4165
4166
        return [$userGroup->subscribe_courses_to_usergroup($groupId, [$courseId], false)];
4167
    }
4168
4169
    /**
4170
     * Add a new session to which the given group/class is subscribed.
4171
     *
4172
     * @return array One item array containing the ID of the session on success, nothing on failure
4173
     */
4174
    public function addGroupSubscribedSession(int $groupId, int $sessionId): array
4175
    {
4176
        $userGroup = new UserGroup();
4177
4178
        return [$userGroup->subscribe_sessions_to_usergroup($groupId, [$sessionId], false)];
4179
    }
4180
4181
    /**
4182
     * Remove a user from the given group/class.
4183
     *
4184
     * @return array One item array containing true on success, false otherwise
4185
     */
4186
    public function deleteGroupSubscribedUser(int $groupId, int $userId): array
4187
    {
4188
        $userGroup = new UserGroup();
4189
4190
        return [$userGroup->delete_user_rel_group($userId, $groupId)];
4191
    }
4192
4193
    /**
4194
     * Remove a course to which the given group/class is subscribed.
4195
     *
4196
     * @return array One item array containing true on success, false otherwise
4197
     */
4198
    public function deleteGroupSubscribedCourse(int $groupId, int $courseId): array
4199
    {
4200
        $userGroup = new UserGroup();
4201
4202
        return [$userGroup->unsubscribe_courses_from_usergroup($groupId, [$courseId])];
4203
    }
4204
4205
    /**
4206
     * Remove a session to which the given group/class is subscribed.
4207
     *
4208
     * @return array One item array containing true on success, false otherwise
4209
     */
4210
    public function deleteGroupSubscribedSession(int $groupId, int $sessionId): array
4211
    {
4212
        $userGroup = new UserGroup();
4213
4214
        return [$userGroup->unsubscribeSessionsFromUserGroup($groupId, [$sessionId], false)];
4215
    }
4216
4217
    /**
4218
     * Encode the given parameters (structured array) in JSON format
4219
     * @param array $additionalParams Optional
4220
     *
4221
     * @return string
4222
     */
4223
    private function encodeParams(array $additionalParams = [])
4224
    {
4225
        $params = array_merge(
4226
            $additionalParams,
4227
            [
4228
                'api_key' => $this->apiKey,
4229
                'username' => $this->user->getUsername(),
4230
            ]
4231
        );
4232
4233
        return json_encode($params);
4234
    }
4235
4236
    /**
4237
     * Get audit items from track_e_default.
4238
     * @throws Exception
4239
     */
4240
    public function getAuditItems(
4241
        string $defaultEventType,
4242
        ?int $cId = null,
4243
        ?int $sessionId = null,
4244
        ?string $afterDate = null,
4245
        ?string $beforeDate = null,
4246
        ?int $userId = null,
4247
        int $offset = 0,
4248
        int $limit = 100
4249
    ): array {
4250
        self::protectAdminEndpoint();
4251
4252
        return Event::getAuditItems(
4253
            $defaultEventType,
4254
            $cId,
4255
            $sessionId,
4256
            $afterDate,
4257
            $beforeDate,
4258
            $userId,
4259
            $offset,
4260
            $limit
4261
        );
4262
    }
4263
4264
    /**
4265
     * Generate an API key for webservices access for the given user ID
4266
     */
4267
    protected static function generateApiKeyForUser(int $userId): string
4268
    {
4269
        UserManager::add_api_key($userId, self::SERVICE_NAME);
4270
4271
        $apiKeys = UserManager::get_api_keys($userId, self::SERVICE_NAME);
4272
4273
        return current($apiKeys);
4274
    }
4275
4276
    /**
4277
     * Helper generating a query URL (to the current script) from an array of parameters
4278
     * (course, session, api_key and username) commonly used in webservice calls
4279
     */
4280
    private function generateUrl(array $additionalParams = []): string
4281
    {
4282
        $queryParams = [
4283
            'course' => $this->course ? $this->course->getId() : null,
4284
            'session' => $this->session ? $this->session->getId() : null,
4285
            'api_key' => $this->apiKey,
4286
            'username' => $this->user->getUsername(),
4287
        ];
4288
4289
        return api_get_self().'?'
4290
            .http_build_query(array_merge($queryParams, $additionalParams));
4291
    }
4292
}
4293