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