Passed
Pull Request — master (#6042)
by Yannick
08:29
created

countUsersWhoFinishedCoursesInSessions()   B

Complexity

Conditions 6
Paths 2

Size

Total Lines 43
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 25
nc 2
nop 0
dl 0
loc 43
rs 8.8977
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Component\Utils\NameConvention;
5
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
6
use Chamilo\CoreBundle\Entity\ExtraFieldSavedSearch;
7
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
8
use Chamilo\CoreBundle\Entity\SessionRelCourse;
9
use Chamilo\CoreBundle\Entity\SkillRelUser;
10
use Chamilo\CoreBundle\Entity\SkillRelUserComment;
11
use Chamilo\CoreBundle\Entity\User;
12
use Chamilo\CoreBundle\Entity\UserRelUser;
13
use Chamilo\CoreBundle\Framework\Container;
14
use Chamilo\CoreBundle\Repository\GroupRepository;
15
use Chamilo\CoreBundle\Repository\Node\UserRepository;
16
use ChamiloSession as Session;
17
use Symfony\Component\HttpFoundation\File\UploadedFile;
18
use Chamilo\CoreBundle\Entity\ExtraFieldValues as EntityExtraFieldValues;
19
20
/**
21
 * This library provides functions for user management.
22
 * Include/require it in your code to use its functionality.
23
 *
24
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
25
 */
26
class UserManager
27
{
28
    // This constants are deprecated use the constants located in ExtraField
29
    public const USER_FIELD_TYPE_TEXT = 1;
30
    public const USER_FIELD_TYPE_TEXTAREA = 2;
31
    public const USER_FIELD_TYPE_RADIO = 3;
32
    public const USER_FIELD_TYPE_SELECT = 4;
33
    public const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
34
    public const USER_FIELD_TYPE_DATE = 6;
35
    public const USER_FIELD_TYPE_DATETIME = 7;
36
    public const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
37
    public const USER_FIELD_TYPE_DIVIDER = 9;
38
    public const USER_FIELD_TYPE_TAG = 10;
39
    public const USER_FIELD_TYPE_TIMEZONE = 11;
40
    public const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
41
    public const USER_FIELD_TYPE_FILE = 13;
42
    public const USER_FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
43
44
    public function __construct()
45
    {
46
    }
47
48
    /**
49
     * Repository is use to query the DB, selects, etc.
50
     *
51
     * @return UserRepository
52
     */
53
    public static function getRepository()
54
    {
55
        return Container::getUserRepository();
56
    }
57
58
    /**
59
     * Validates the password.
60
     */
61
    public static function isPasswordValid(User $user, string $plainPassword): bool
62
    {
63
        /**
64
         * @psalm-suppress PrivateService
65
         */
66
        $hasher = Container::$container->get('security.user_password_hasher');
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

66
        /** @scrutinizer ignore-call */ 
67
        $hasher = Container::$container->get('security.user_password_hasher');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
67
68
        return $hasher->isPasswordValid($user, $plainPassword);
69
    }
70
71
    /**
72
     * @param int    $userId
73
     * @param string $password
74
     */
75
    public static function updatePassword($userId, $password)
76
    {
77
        $user = api_get_user_entity($userId);
78
        $userManager = self::getRepository();
79
        $user->setPlainPassword($password);
80
        $userManager->updateUser($user, true);
81
    }
82
83
    /**
84
     * Creates a new user for the platform.
85
     *
86
     * @author Hugues Peeters <[email protected]>,
87
     * @author Roan Embrechts <[email protected]>
88
     *
89
     * @param string        $firstName
90
     * @param string        $lastName
91
     * @param int           $status                  (1 for course tutor, 5 for student, 6 for anonymous)
92
     * @param string        $email
93
     * @param string        $loginName
94
     * @param string        $password
95
     * @param string        $officialCode           Any official code (optional)
96
     * @param string        $language                User language    (optional)
97
     * @param string        $phone                   Phone number    (optional)
98
     * @param string        $pictureUri             Picture URI        (optional)
99
     * @param string        $authSource              Authentication source (defaults to 'platform', dependind on constant)
100
     * @param string        $expirationDate          Account expiration date (optional, defaults to null)
101
     * @param int           $active                  Whether the account is enabled or disabled by default
102
     * @param int           $hrDeptId              The department of HR in which the user is registered (defaults to 0)
103
     * @param array         $extra                   Extra fields (labels must be prefixed by "extra_")
104
     * @param string        $encryptMethod          Used if password is given encrypted. Set to an empty string by default
105
     * @param bool          $sendMail
106
     * @param bool          $isAdmin
107
     * @param string        $address
108
     * @param bool          $sendEmailToAllAdmins
109
     * @param FormValidator $form
110
     * @param int           $creatorId
111
     * @param array         $emailTemplate
112
     * @param string        $redirectToURLAfterLogin
113
     *
114
     * @return mixed new user id - if the new user creation succeeds, false otherwise
115
     * @desc The function tries to retrieve user id from the session.
116
     * If it exists, the current user id is the creator id. If a problem arises,
117
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
118
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
119
     */
120
    public static function create_user(
121
        $firstName,
122
        $lastName,
123
        $status,
124
        $email,
125
        $loginName,
126
        $password,
127
        $officialCode = '',
128
        $language = '',
129
        $phone = '',
130
        $pictureUri = '',
131
        $authSource = null,
132
        $expirationDate = null,
133
        $active = 1,
134
        $hrDeptId = 0,
135
        $extra = [],
136
        $encryptMethod = '',
137
        $sendMail = false,
138
        $isAdmin = false,
139
        $address = '',
140
        $sendEmailToAllAdmins = false,
141
        $form = null,
142
        $creatorId = 0,
143
        $emailTemplate = [],
144
        $redirectToURLAfterLogin = ''
145
    ) {
146
        $authSource = !empty($authSource) ? $authSource : PLATFORM_AUTH_SOURCE;
147
        $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId;
148
149
        if (0 === $creatorId) {
150
            Display::addFlash(
151
                Display::return_message(get_lang('A user creator is needed'))
152
            );
153
154
            return false;
155
        }
156
157
        $creatorInfo = api_get_user_info($creatorId);
158
        $creatorEmail = $creatorInfo['email'] ?? '';
159
160
        // First check if the login exists.
161
        if (!self::is_username_available($loginName)) {
162
            Display::addFlash(
163
                Display::return_message(get_lang('This login is already taken !'))
164
            );
165
166
            return false;
167
        }
168
169
        global $_configuration;
170
        $original_password = $password;
171
172
        $access_url_id = 1;
173
        if (api_get_multiple_access_url()) {
174
            $access_url_id = api_get_current_access_url_id();
175
        } else {
176
            // In some cases, the first access_url ID might be different from 1
177
            // for example when using a DB cluster or hacking the DB manually.
178
            // In this case, we want the first row, not necessarily "1".
179
            $accessUrlRepository = Container::getAccessUrlRepository();
180
            $accessUrl = $accessUrlRepository->getFirstId();
181
            if (!empty($accessUrl[0]) && !empty($accessUrl[0][1])) {
182
                $access_url_id = $accessUrl[0][1];
183
            }
184
        }
185
186
        $hostingLimitUsers = get_hosting_limit($access_url_id, 'hosting_limit_users');
187
188
        if ($hostingLimitUsers !== null && $hostingLimitUsers > 0) {
189
            $num = self::get_number_of_users();
190
            if ($num >= $hostingLimitUsers) {
191
                api_warn_hosting_contact('hosting_limit_users');
192
                Display::addFlash(
193
                    Display::return_message(
194
                        get_lang('Sorry, this installation has a users limit, which has now been reached. To increase the number of users allowed on this Chamilo installation, please contact your hosting provider or, if available, upgrade to a superior hosting plan.'),
195
                        'warning'
196
                    )
197
                );
198
199
                return false;
200
            }
201
        }
202
203
        if (1 === $status) {
204
            $hostingLimitTeachers = get_hosting_limit($access_url_id, 'hosting_limit_teachers');
205
206
            if ($hostingLimitTeachers !== null && $hostingLimitTeachers > 0) {
207
                $num = self::get_number_of_users(1);
208
                if ($num >= $hostingLimitTeachers) {
209
                    Display::addFlash(
210
                        Display::return_message(
211
                            get_lang('Sorry, this installation has a teachers limit, which has now been reached. To increase the number of teachers allowed on this Chamilo installation, please contact your hosting provider or, if available, upgrade to a superior hosting plan.'),
212
                            'warning'
213
                        )
214
                    );
215
                    api_warn_hosting_contact('hosting_limit_teachers');
216
217
                    return false;
218
                }
219
            }
220
        }
221
222
        if (empty($password)) {
223
            if (PLATFORM_AUTH_SOURCE === $authSource) {
224
                Display::addFlash(
225
                    Display::return_message(
226
                        get_lang('Required field').': '.get_lang(
227
                            'Password'
228
                        ),
229
                        'warning'
230
                    )
231
                );
232
233
                return false;
234
            }
235
236
            // We use the authSource as password.
237
            // The real validation will be by processed by the auth
238
            // source not Chamilo
239
            $password = $authSource;
240
        }
241
242
        // Checking the user language
243
        $languages = api_get_languages();
244
245
        // Default to english
246
        if (!in_array($language, array_keys($languages), true)) {
247
            $language = 'en_US';
248
        }
249
250
        $now = new DateTime();
251
        if (empty($expirationDate) || '0000-00-00 00:00:00' === $expirationDate) {
252
            $expirationDate = null;
253
        // Default expiration date
254
            // if there is a default duration of a valid account then
255
            // we have to change the expiration_date accordingly
256
            // Accept 0000-00-00 00:00:00 as a null value to avoid issues with
257
            // third party code using this method with the previous (pre-1.10)
258
            // value of 0000...
259
            /*if ('' != api_get_setting('account_valid_duration')) {
260
                $expirationDate = new DateTime($currentDate);
261
                $days = (int) api_get_setting('account_valid_duration');
262
                $expirationDate->modify('+'.$days.' day');
263
            }*/
264
        } else {
265
            $expirationDate = api_get_utc_datetime($expirationDate, true, true);
266
        }
267
268
        $repo = Container::getUserRepository();
269
        $user = $repo->createUser()
270
            ->setLastname($lastName)
271
            ->setFirstname($firstName)
272
            ->setUsername($loginName)
273
            ->setStatus($status)
274
            ->setPlainPassword($password)
275
            ->setEmail($email)
276
            ->setOfficialCode($officialCode)
277
            ->setCreatorId($creatorId)
278
            ->setAuthSource($authSource)
279
            ->setPhone($phone)
280
            ->setAddress($address)
281
            ->setLocale($language)
282
            ->setRegistrationDate($now)
283
            ->setHrDeptId($hrDeptId)
284
            ->setActive($active)
285
            ->setTimezone(api_get_timezone())
286
        ;
287
288
        if (null !== $expirationDate) {
289
            $user->setExpirationDate($expirationDate);
290
        }
291
292
        $user->setRoleFromStatus($status);
293
294
        // Add user to a group
295
        /*$statusToGroup = [
296
            COURSEMANAGER => 'TEACHER',
297
            STUDENT => 'STUDENT',
298
            DRH => 'RRHH',
299
            SESSIONADMIN => 'SESSION_ADMIN',
300
            STUDENT_BOSS => 'STUDENT_BOSS',
301
            INVITEE => 'INVITEE',
302
        ];
303
304
        if (isset($statusToGroup[$status])) {
305
            $group = Container::$container->get(GroupRepository::class)->findOneBy(['code' => $statusToGroup[$status]]);
306
            if ($group) {
307
                $user->addGroup($group);
308
            }
309
        }*/
310
311
        $repo->updateUser($user, true);
312
313
        $userId = $user->getId();
314
315
        if (!empty($userId)) {
316
            $userLocale = $user->getLocale();
317
            if ($isAdmin) {
318
                self::addUserAsAdmin($user);
319
            }
320
321
            if (api_get_multiple_access_url()) {
322
                UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
323
            } else {
324
                //we are adding by default the access_url_user table with access_url_id = 1
325
                UrlManager::add_user_to_url($userId, 1);
326
            }
327
328
            if (is_array($extra) && count($extra) > 0) {
329
                $extra['item_id'] = $userId;
330
                $userFieldValue = new ExtraFieldValue('user');
331
                /* Force saving of extra fields (otherwise, if the current
332
                user is not admin, fields not visible to the user - most
333
                of them - are just ignored) */
334
                $userFieldValue->saveFieldValues(
335
                    $extra,
336
                    true,
337
                    false,
338
                    [],
339
                    [],
340
                    true
341
                );
342
            } else {
343
                // Create notify settings by default
344
                self::update_extra_field_value(
345
                    $userId,
346
                    'mail_notify_invitation',
347
                    '1'
348
                );
349
                self::update_extra_field_value(
350
                    $userId,
351
                    'mail_notify_message',
352
                    '1'
353
                );
354
                self::update_extra_field_value(
355
                    $userId,
356
                    'mail_notify_group_message',
357
                    '1'
358
                );
359
            }
360
361
            self::update_extra_field_value(
362
                $userId,
363
                'already_logged_in',
364
                'false'
365
            );
366
367
            if (!empty($redirectToURLAfterLogin) && ('true' === api_get_setting('admin.plugin_redirection_enabled'))) {
368
                RedirectionPlugin::insert($userId, $redirectToURLAfterLogin);
369
            }
370
371
            if (!empty($email) && $sendMail) {
372
                $recipient_name = api_get_person_name(
373
                    $firstName,
374
                    $lastName,
375
                    null,
376
                    PERSON_NAME_EMAIL_ADDRESS
377
                );
378
                $tpl = Container::getTwig();
379
                $emailSubject = $tpl->render('@ChamiloCore/Mailer/Legacy/subject_registration_platform.html.twig', ['locale' => $userLocale]);
380
                $sender_name = api_get_person_name(
381
                    api_get_setting('administratorName'),
382
                    api_get_setting('administratorSurname'),
383
                    null,
384
                    PERSON_NAME_EMAIL_ADDRESS
385
                );
386
                $email_admin = api_get_setting('emailAdministrator');
387
388
                $url = api_get_path(WEB_PATH);
389
                if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

389
                if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
390
                    $access_url_id = api_get_current_access_url_id();
391
                    if (-1 != $access_url_id) {
392
                        $urlInfo = api_get_access_url($access_url_id);
393
                        if ($urlInfo) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $urlInfo of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
394
                            $url = $urlInfo['url'];
395
                        }
396
                    }
397
                }
398
399
                // variables for the default template
400
                $params = [
401
                    'complete_name' => stripslashes(api_get_person_name($firstName, $lastName)),
402
                    'login_name' => $loginName,
403
                    'original_password' => stripslashes($original_password),
404
                    'mailWebPath' => $url,
405
                    'new_user' => $user,
406
                    'search_link' => $url,
407
                    'locale' => $userLocale,
408
                ];
409
410
                // ofaj
411
                if ('true' === api_get_setting('session.allow_search_diagnostic')) {
412
                    $urlSearch = api_get_path(WEB_CODE_PATH).'search/search.php';
413
                    $linkSearch = Display::url($urlSearch, $urlSearch);
414
                    $params['search_link'] = $linkSearch;
415
                }
416
417
                $emailBody = $tpl->render(
418
                    '@ChamiloCore/Mailer/Legacy/content_registration_platform.html.twig',
419
                    $params
420
                );
421
422
                $userInfo = api_get_user_info($userId);
423
                $mailTemplateManager = new MailTemplateManager();
424
                $phoneNumber = $extra['mobile_phone_number'] ?? null;
425
426
                $emailBodyTemplate = '';
427
                if (!empty($emailTemplate)) {
428
                    if (isset($emailTemplate['content_registration_platform.tpl']) &&
429
                        !empty($emailTemplate['content_registration_platform.tpl'])
430
                    ) {
431
                        $emailBodyTemplate = $mailTemplateManager->parseTemplate(
432
                            $emailTemplate['content_registration_platform.tpl'],
433
                            $userInfo
434
                        );
435
                    }
436
                }
437
438
                $twoEmail = ('true' === api_get_setting('mail.send_two_inscription_confirmation_mail'));
439
                if (true === $twoEmail) {
440
                    $emailBody = $tpl->render(
441
                        '@ChamiloCore/Mailer/Legacy/new_user_first_email_confirmation.html.twig'
442
                    );
443
444
                    if (!empty($emailBodyTemplate) &&
445
                        isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
446
                        !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
447
                    ) {
448
                        $emailBody = $mailTemplateManager->parseTemplate(
449
                            $emailTemplate['new_user_first_email_confirmation.tpl'],
450
                            $userInfo
451
                        );
452
                    }
453
454
                    api_mail_html(
455
                        $recipient_name,
456
                        $email,
457
                        $emailSubject,
458
                        $emailBody,
459
                        $sender_name,
460
                        $email_admin,
461
                        [],
462
                        [],
463
                        false,
464
                        [],
465
                        $creatorEmail
466
                    );
467
468
                    $emailBody = $tpl->render('@ChamiloCore/Mailer/Legacy/new_user_second_email_confirmation.html.twig');
469
470
                    if (!empty($emailBodyTemplate) &&
471
                        isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
472
                        !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
473
                    ) {
474
                        $emailBody = $mailTemplateManager->parseTemplate(
475
                            $emailTemplate['new_user_second_email_confirmation.tpl'],
476
                            $userInfo
477
                        );
478
                    }
479
480
                    api_mail_html(
481
                        $recipient_name,
482
                        $email,
483
                        $emailSubject,
484
                        $emailBody,
485
                        $sender_name,
486
                        $email_admin,
487
                        null,
488
                        null,
489
                        null,
490
                        [],
491
                        $creatorEmail
492
                    );
493
                } else {
494
                    if (!empty($emailBodyTemplate)) {
495
                        $emailBody = $emailBodyTemplate;
496
                    }
497
                    $sendToInbox = ('true' === api_get_setting('registration.send_inscription_msg_to_inbox'));
498
                    if ($sendToInbox) {
499
                        $adminList = self::get_all_administrators();
500
                        $senderId = 1;
501
                        if (!empty($adminList)) {
502
                            $adminInfo = current($adminList);
503
                            $senderId = $adminInfo['user_id'];
504
                        }
505
506
                        MessageManager::send_message_simple(
507
                            $userId,
508
                            $emailSubject,
509
                            $emailBody,
510
                            $senderId
511
                        );
512
                    } else {
513
                        api_mail_html(
514
                            $recipient_name,
515
                            $email,
516
                            $emailSubject,
517
                            $emailBody,
518
                            $sender_name,
519
                            $email_admin,
520
                            [],
521
                            [],
522
                            false,
523
                            [],
524
                            $creatorEmail
525
                        );
526
                    }
527
                }
528
529
                $notification = api_get_setting('profile.send_notification_when_user_added', true);
530
                if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
531
                    foreach ($notification['admins'] as $adminId) {
532
                        $emailSubjectToAdmin = get_lang('The user has been added').': '.
533
                            api_get_person_name($firstName, $lastName);
534
                        MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
535
                    }
536
                }
537
538
                if ($sendEmailToAllAdmins) {
539
                    $adminList = self::get_all_administrators();
540
                    // variables for the default template
541
                    $renderer = FormValidator::getDefaultRenderer();
542
                    // Form template
543
                    $elementTemplate = ' {label}: {element} <br />';
544
                    $renderer->setElementTemplate($elementTemplate);
545
                    /** @var FormValidator $form */
546
                    $form->freeze(null, $elementTemplate);
547
                    $form->removeElement('submit');
548
                    $formData = $form->returnForm();
549
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
550
                    $params = [
551
                        'complete_name' => stripslashes(api_get_person_name($firstName, $lastName)),
552
                        'user_added' => $user,
553
                        'link' => Display::url($url, $url),
554
                        'form' => $formData,
555
                        'user_language' => $user->getLocale(),
556
                    ];
557
                    $emailBody = $tpl->render(
558
                        '@ChamiloCore/Mailer/Legacy/content_registration_platform_to_admin.html.twig',
559
                        $params
560
                    );
561
562
                    if (!empty($emailBodyTemplate) &&
563
                        isset($emailTemplate['content_registration_platform_to_admin.tpl']) &&
564
                        !empty($emailTemplate['content_registration_platform_to_admin.tpl'])
565
                    ) {
566
                        $emailBody = $mailTemplateManager->parseTemplate(
567
                            $emailTemplate['content_registration_platform_to_admin.tpl'],
568
                            $userInfo
569
                        );
570
                    }
571
572
                    $subject = get_lang('The user has been added');
573
                    foreach ($adminList as $adminId => $data) {
574
                        MessageManager::send_message_simple(
575
                            $adminId,
576
                            $subject,
577
                            $emailBody,
578
                            $userId
579
                        );
580
                    }
581
                }
582
            }
583
            Event::addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId, null, $creatorId);
0 ignored issues
show
Bug introduced by
The method addEvent() does not exist on Event. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

583
            Event::/** @scrutinizer ignore-call */ 
584
                   addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId, null, $creatorId);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
584
        } else {
585
            Display::addFlash(
586
                Display::return_message(
587
                    get_lang('There happened an unknown error. Please contact the platform administrator.')
588
                )
589
            );
590
591
            return false;
592
        }
593
594
        return $userId;
595
    }
596
597
    /**
598
     * Can user be deleted? This function checks whether there's a course
599
     * in which the given user is the
600
     * only course administrator. If that is the case, the user can't be
601
     * deleted because the course would remain without a course admin.
602
     *
603
     * @param int $user_id The user id
604
     *
605
     * @return bool true if user can be deleted
606
     *
607
     * @assert (null) === false
608
     * @assert (-1) === false
609
     * @assert ('abc') === false
610
     */
611
    public static function canDeleteUser($user_id)
612
    {
613
        $deny = api_get_env_variable('DENY_DELETE_USERS', false);
614
615
        if ($deny) {
616
            return false;
617
        }
618
619
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
620
        $user_id = (int) $user_id;
621
622
        if (empty($user_id)) {
623
            return false;
624
        }
625
626
        $sql = "SELECT * FROM $table_course_user
627
                WHERE status = 1 AND user_id = ".$user_id;
628
        $res = Database::query($sql);
629
        while ($course = Database::fetch_object($res)) {
630
            $sql = "SELECT id FROM $table_course_user
631
                    WHERE status=1 AND c_id = ".intval($course->c_id);
632
            $res2 = Database::query($sql);
633
            if (1 == Database::num_rows($res2)) {
634
                return false;
635
            }
636
        }
637
638
        return true;
639
    }
640
641
    /**
642
     * Deletes a user from the system or marks the user as deleted based on the $destroy flag.
643
     * If $destroy is false, the user is only marked as deleted (e.g., active = -1) but not actually removed from the database.
644
     * This allows for the possibility of restoring the user at a later time. If $destroy is true, the user and all their relations
645
     * are permanently removed from the database.
646
     *
647
     * Note: When $destroy is false, the user's relations are not removed, allowing for potential restoration. When $destroy is true,
648
     * the function proceeds to remove all the user's relations, effectively cleaning up all references to the user in the system.
649
     */
650
    public static function delete_user(int $user_id, bool $destroy = false): bool
651
    {
652
        $user_id = (int) $user_id;
653
654
        if (empty($user_id)) {
655
            return false;
656
        }
657
658
        if (!self::canDeleteUser($user_id)) {
659
            return false;
660
        }
661
662
        $repository = Container::getUserRepository();
663
664
        /** @var User $user */
665
        $user = $repository->find($user_id);
666
667
        if (!$user) {
0 ignored issues
show
introduced by
$user is of type Chamilo\CoreBundle\Entity\User, thus it always evaluated to true.
Loading history...
668
            return false;
669
        }
670
671
        $repository->deleteUser($user, $destroy);
672
673
        if ($destroy) {
674
            Event::addEvent(
675
                LOG_USER_DELETE,
676
                LOG_USER_ID,
677
                $user_id,
678
                api_get_utc_datetime(),
679
                api_get_user_id()
680
            );
681
682
            Event::addEvent(
683
                LOG_USER_DELETE,
684
                LOG_USER_OBJECT,
685
                api_get_user_info($user_id),
686
                api_get_utc_datetime(),
687
                api_get_user_id()
688
            );
689
        }
690
691
        return true;
692
    }
693
694
    /**
695
     * Deletes users completely. Can be called either as:
696
     * - UserManager::delete_users(1, 2, 3); or
697
     * - UserManager::delete_users(array(1, 2, 3));.
698
     *
699
     * @param array|int $ids
700
     *
701
     * @return bool True if at least one user was successfuly deleted. False otherwise.
702
     *
703
     * @author Laurent Opprecht
704
     *
705
     * @uses \UserManager::delete_user() to actually delete each user
706
     * @assert (null) === false
707
     * @assert (-1) === false
708
     * @assert (array(-1)) === false
709
     */
710
    public static function delete_users($ids = [])
711
    {
712
        $result = false;
713
        $ids = is_array($ids) ? $ids : func_get_args();
714
        if (!is_array($ids) || 0 == count($ids)) {
715
            return false;
716
        }
717
        $ids = array_map('intval', $ids);
718
        foreach ($ids as $id) {
719
            if (empty($id) || $id < 1) {
720
                continue;
721
            }
722
            $deleted = self::delete_user($id);
723
            $result = $deleted || $result;
724
        }
725
726
        return $result;
727
    }
728
729
    /**
730
     * Disable users. Can be called either as:
731
     * - UserManager::deactivate_users(1, 2, 3);
732
     * - UserManager::deactivate_users(array(1, 2, 3));.
733
     *
734
     * @param array|int $ids
735
     *
736
     * @return bool
737
     *
738
     * @author Laurent Opprecht
739
     * @assert (null) === false
740
     * @assert (array(-1)) === false
741
     */
742
    public static function deactivate_users($ids = [])
743
    {
744
        if (empty($ids)) {
745
            return false;
746
        }
747
748
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
749
750
        $ids = is_array($ids) ? $ids : func_get_args();
751
        $ids = array_map('intval', $ids);
752
        $ids = implode(',', $ids);
753
754
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
755
        $r = Database::query($sql);
756
        if (false !== $r) {
757
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
758
759
            return true;
760
        }
761
762
        return false;
763
    }
764
765
    /**
766
     * Enable users. Can be called either as:
767
     * - UserManager::activate_users(1, 2, 3);
768
     * - UserManager::activate_users(array(1, 2, 3));.
769
     *
770
     * @param array|int IDs of the users to enable
771
     *
772
     * @return bool
773
     *
774
     * @author Laurent Opprecht
775
     * @assert (null) === false
776
     * @assert (array(-1)) === false
777
     */
778
    public static function activate_users($ids = [])
779
    {
780
        if (empty($ids)) {
781
            return false;
782
        }
783
784
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
785
786
        $ids = is_array($ids) ? $ids : func_get_args();
787
        $ids = array_map('intval', $ids);
788
        $ids = implode(',', $ids);
789
790
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
791
        $r = Database::query($sql);
792
        if (false !== $r) {
793
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
794
795
            return true;
796
        }
797
798
        return false;
799
    }
800
801
    /**
802
     * Update user information with all the parameters passed to this function.
803
     *
804
     * @param int    $user_id         The ID of the user to be updated
805
     * @param string $firstname       The user's firstname
806
     * @param string $lastname        The user's lastname
807
     * @param string $username        The user's username (login)
808
     * @param string $password        The user's password
809
     * @param string $auth_source     The authentication source (default: "platform")
810
     * @param string $email           The user's e-mail address
811
     * @param int    $status          The user's status
812
     * @param string $official_code   The user's official code (usually just an internal institutional code)
813
     * @param string $phone           The user's phone number
814
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
815
     * @param string $expiration_date The date at which this user will be automatically disabled
816
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
817
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
818
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
819
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
820
     * @param string $language        The language to which the user account will be set
821
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
822
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
823
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
824
     * @param string $address
825
     * @param array  $emailTemplate
826
     *
827
     * @return bool|int False on error, or the user ID if the user information was updated
828
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
829
     */
830
    public static function update_user(
831
        $user_id,
832
        $firstname,
833
        $lastname,
834
        $username,
835
        $password,
836
        $auth_source,
837
        $email,
838
        $status,
839
        $official_code,
840
        $phone,
841
        $picture_uri,
842
        $expiration_date,
843
        $active,
844
        $creator_id = null,
845
        $hr_dept_id = 0,
846
        $extra = null,
847
        $language = 'en_US',
848
        $encrypt_method = '',
849
        $send_email = false,
850
        $reset_password = 0,
851
        $address = null,
852
        $emailTemplate = []
853
    ) {
854
        $original_password = $password;
855
        $user_id = (int) $user_id;
856
        $creator_id = (int) $creator_id;
857
858
        if (empty($user_id)) {
859
            return false;
860
        }
861
862
        $userManager = self::getRepository();
863
        $user = api_get_user_entity($user_id);
864
865
        if (null === $user) {
866
            return false;
867
        }
868
869
        if (0 == $reset_password) {
870
            $password = null;
871
            $auth_source = $user->getAuthSource();
872
        } elseif (1 == $reset_password) {
873
            $original_password = $password = api_generate_password();
874
            $auth_source = PLATFORM_AUTH_SOURCE;
875
        } elseif (2 == $reset_password) {
876
            $password = $password;
877
            $auth_source = PLATFORM_AUTH_SOURCE;
878
        } elseif (3 == $reset_password) {
879
            $password = $password;
880
            $auth_source = $auth_source;
881
        }
882
883
        // Checking the user language
884
        $languages = array_keys(api_get_languages());
885
        if (!in_array($language, $languages)) {
886
            $language = api_get_setting('platformLanguage');
887
        }
888
889
        $change_active = 0;
890
        $isUserActive = $user->isActive();
891
        if ($active != USER_SOFT_DELETED) {
892
            if ($isUserActive != $active) {
893
                $change_active = 1;
894
            }
895
        }
896
897
        $originalUsername = $user->getUsername();
898
899
        // If username is different from original then check if it exists.
900
        if ($originalUsername !== $username) {
901
            $available = self::is_username_available($username);
902
            if (false === $available) {
903
                return false;
904
            }
905
        }
906
907
        if (!empty($expiration_date)) {
908
            $expiration_date = api_get_utc_datetime($expiration_date);
909
            $expiration_date = new \DateTime($expiration_date, new DateTimeZone('UTC'));
910
        }
911
912
        $previousStatus = $user->getStatus();
913
        $previousRole = $user->getRoleFromStatus($previousStatus);
914
915
        $user
916
            ->removeRole($previousRole)
917
            ->setLastname($lastname)
918
            ->setFirstname($firstname)
919
            ->setUsername($username)
920
            ->setStatus($status)
921
            ->setAuthSource($auth_source)
922
            ->setLocale($language)
923
            ->setEmail($email)
924
            ->setOfficialCode($official_code)
925
            ->setPhone($phone)
926
            ->setAddress($address)
927
            ->setExpirationDate($expiration_date)
928
            ->setActive($active)
929
            ->setHrDeptId((int) $hr_dept_id)
930
        ;
931
932
        if (!is_null($password)) {
933
            $user->setPlainPassword($password);
934
        }
935
936
        $user->setRoleFromStatus($status);
937
        $userManager->updateUser($user, true);
938
        Event::addEvent(LOG_USER_UPDATE, LOG_USER_ID, $user_id);
939
940
        if (1 == $change_active) {
941
            $event_title = LOG_USER_DISABLE;
942
            if (1 == $active) {
943
                $event_title = LOG_USER_ENABLE;
944
            }
945
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
946
        }
947
948
        if (is_array($extra) && count($extra) > 0) {
949
            $res = true;
950
            foreach ($extra as $fname => $fvalue) {
951
                $res = $res && self::update_extra_field_value(
952
                    $user_id,
953
                    $fname,
954
                    $fvalue
955
                );
956
            }
957
        }
958
959
        if (!empty($email) && $send_email) {
960
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
961
            $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
0 ignored issues
show
Bug introduced by
Are you sure api_get_setting('siteName') of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

961
            $emailsubject = '['./** @scrutinizer ignore-type */ api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
Loading history...
962
            $sender_name = api_get_person_name(
963
                api_get_setting('administratorName'),
964
                api_get_setting('administratorSurname'),
965
                null,
966
                PERSON_NAME_EMAIL_ADDRESS
967
            );
968
            $email_admin = api_get_setting('emailAdministrator');
969
            $url = api_get_path(WEB_PATH);
970
            if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

970
            if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
971
                $access_url_id = api_get_current_access_url_id();
972
                if (-1 != $access_url_id) {
973
                    $url = api_get_access_url($access_url_id);
974
                    $url = $url['url'];
975
                }
976
            }
977
978
            $tplContent = new Template(
979
                null,
980
                false,
981
                false,
982
                false,
983
                false,
984
                false
985
            );
986
            // variables for the default template
987
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
988
            $tplContent->assign('login_name', $username);
989
990
            $originalPassword = '';
991
            if ($reset_password > 0) {
992
                $originalPassword = stripslashes($original_password);
993
            }
994
            $tplContent->assign('original_password', $originalPassword);
995
            $tplContent->assign('portal_url', $url);
996
997
            $emailBody = $tplContent->fetch('@ChamiloCore/Mailer/Legacy/user_edit_content.html.twig');
998
999
            $mailTemplateManager = new MailTemplateManager();
1000
1001
            if (!empty($emailTemplate) &&
1002
                isset($emailTemplate['user_edit_content.tpl']) &&
1003
                !empty($emailTemplate['user_edit_content.tpl'])
1004
            ) {
1005
                $userInfo = api_get_user_info($user_id);
1006
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], $userInfo);
1007
            }
1008
1009
            $creatorInfo = api_get_user_info($creator_id);
1010
            $creatorEmail = $creatorInfo['email'] ?? '';
1011
1012
            api_mail_html(
1013
                $recipient_name,
1014
                $email,
1015
                $emailsubject,
1016
                $emailBody,
1017
                $sender_name,
1018
                $email_admin,
1019
                null,
1020
                null,
1021
                null,
1022
                null,
1023
                $creatorEmail
1024
            );
1025
        }
1026
1027
        return $user->getId();
1028
    }
1029
1030
    /**
1031
     * Disables a user.
1032
     *
1033
     * @param int User id
1034
     *
1035
     * @return bool
1036
     *
1037
     * @uses \UserManager::change_active_state() to actually disable the user
1038
     * @assert (0) === false
1039
     */
1040
    public static function disable($user_id)
1041
    {
1042
        if (empty($user_id)) {
1043
            return false;
1044
        }
1045
        self::change_active_state($user_id, 0);
1046
1047
        return true;
1048
    }
1049
1050
    /**
1051
     * Enable a user.
1052
     *
1053
     * @param int User id
1054
     *
1055
     * @return bool
1056
     *
1057
     * @uses \UserManager::change_active_state() to actually disable the user
1058
     * @assert (0) === false
1059
     */
1060
    public static function enable($user_id)
1061
    {
1062
        if (empty($user_id)) {
1063
            return false;
1064
        }
1065
        self::change_active_state($user_id, 1);
1066
1067
        return true;
1068
    }
1069
1070
    /**
1071
     * Returns the user's id based on the original id and field name in
1072
     * the extra fields. Returns 0 if no user was found. This function is
1073
     * mostly useful in the context of a web services-based sinchronization.
1074
     *
1075
     * @param string Original user id
1076
     * @param string Original field name
1077
     *
1078
     * @return int User id
1079
     * @assert ('0','---') === 0
1080
     */
1081
    public static function get_user_id_from_original_id(
1082
        $original_user_id_value,
1083
        $original_user_id_name
1084
    ) {
1085
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1086
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1087
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1088
1089
        $original_user_id_name = Database::escape_string($original_user_id_name);
1090
        $original_user_id_value = Database::escape_string($original_user_id_value);
1091
1092
        $sql = "SELECT item_id as user_id
1093
                FROM $t_uf uf
1094
                INNER JOIN $t_ufv ufv
1095
                ON ufv.field_id = uf.id
1096
                WHERE
1097
                    variable = '$original_user_id_name' AND
1098
                    field_value = '$original_user_id_value' AND
1099
                    item_type = $extraFieldType
1100
                ";
1101
        $res = Database::query($sql);
1102
        $row = Database::fetch_object($res);
1103
        if ($row) {
1104
            return $row->user_id;
1105
        }
1106
1107
        return 0;
1108
    }
1109
1110
    /**
1111
     * Check if a username is available.
1112
     *
1113
     * @param string $username the wanted username
1114
     *
1115
     * @return bool true if the wanted username is available
1116
     * @assert ('') === false
1117
     * @assert ('xyzxyzxyz') === true
1118
     */
1119
    public static function is_username_available($username)
1120
    {
1121
        if (empty($username)) {
1122
            return false;
1123
        }
1124
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1125
        $sql = "SELECT username FROM $table_user
1126
                WHERE username = '".Database::escape_string($username)."'";
1127
        $res = Database::query($sql);
1128
1129
        return 0 == Database::num_rows($res);
1130
    }
1131
1132
    /**
1133
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1134
     *
1135
     * @param string $firstname the first name of the user
1136
     * @param string $lastname  the last name of the user
1137
     *
1138
     * @return string suggests a username that contains only ASCII-letters and digits,
1139
     *                without check for uniqueness within the system
1140
     *
1141
     * @author Julio Montoya Armas
1142
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1143
     * @assert ('','') === false
1144
     * @assert ('a','b') === 'ab'
1145
     */
1146
    public static function create_username($firstname, $lastname)
1147
    {
1148
        if (empty($firstname) && empty($lastname)) {
1149
            return false;
1150
        }
1151
1152
        // The first letter only.
1153
        $firstname = api_substr(
1154
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1155
            0,
1156
            1
1157
        );
1158
        //Looking for a space in the lastname
1159
        $pos = api_strpos($lastname, ' ');
1160
        if (false !== $pos) {
1161
            $lastname = api_substr($lastname, 0, $pos);
1162
        }
1163
1164
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1165
        $username = $firstname.$lastname;
1166
        if (empty($username)) {
1167
            $username = 'user';
1168
        }
1169
1170
        $username = URLify::transliterate($username);
1171
1172
        return strtolower(substr($username, 0, User::USERNAME_MAX_LENGTH - 3));
1173
    }
1174
1175
    /**
1176
     * Creates a unique username, using:
1177
     * 1. the first name and the last name of a user;
1178
     * 2. an already created username but not checked for uniqueness yet.
1179
     *
1180
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1181
     *                          parameter is treated as username which is to be checked f
1182
     *                          or uniqueness and to be modified when it is necessary.
1183
     * @param string $lastname  the last name of the user
1184
     *
1185
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1186
     *                Note: When the method is called several times with same parameters,
1187
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1188
     *
1189
     * @author Ivan Tcholakov, 2009
1190
     */
1191
    public static function create_unique_username($firstname, $lastname = null)
1192
    {
1193
        if (is_null($lastname)) {
1194
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1195
            // For making this method tolerant of mistakes,
1196
            // let us transliterate and purify the suggested input username anyway.
1197
            // So, instead of the sentence $username = $firstname; we place the following:
1198
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1199
        } else {
1200
            $username = self::create_username($firstname, $lastname);
1201
        }
1202
        if (!self::is_username_available($username)) {
1203
            $i = 2;
1204
            $temp_username = substr($username, 0, User::USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1205
            while (!self::is_username_available($temp_username)) {
1206
                $i++;
1207
                $temp_username = substr($username, 0, User::USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1208
            }
1209
            $username = $temp_username;
1210
        }
1211
1212
        $username = URLify::transliterate($username);
1213
1214
        return $username;
1215
    }
1216
1217
    /**
1218
     * Modifies a given username accordingly to the specification for valid characters and length.
1219
     *
1220
     * @param $username string          The input username
1221
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1222
     *                     otherwise compliance may be partial. The default value is FALSE.
1223
     *
1224
     * @return string the resulting purified username
1225
     */
1226
    public static function purify_username($username, $strict = false)
1227
    {
1228
        if ($strict) {
1229
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1230
            // into ASCII letters in order they not to be totally removed.
1231
            // 2. Applying the strict purifier.
1232
            // 3. Length limitation.
1233
            $return = 'true' === api_get_setting('login_is_email') ? substr(preg_replace(USERNAME_PURIFIER_MAIL, '', $username), 0, User::USERNAME_MAX_LENGTH) : substr(preg_replace(USERNAME_PURIFIER, '', $username), 0, User::USERNAME_MAX_LENGTH);
1234
            $return = URLify::transliterate($return);
1235
1236
            // We want everything transliterate() does except converting @ to '(at)'. This is a hack to avoid this.
1237
            $return = str_replace(' (at) ', '@', $return);
1238
1239
            return $return;
1240
        }
1241
1242
        // 1. Applying the shallow purifier.
1243
        // 2. Length limitation.
1244
        return substr(
1245
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1246
            0,
1247
            User::USERNAME_MAX_LENGTH
1248
        );
1249
    }
1250
1251
    /**
1252
     * Checks whether the user id exists in the database.
1253
     *
1254
     * @param int $userId User id
1255
     *
1256
     * @return bool True if user id was found, false otherwise
1257
     */
1258
    public static function is_user_id_valid($userId)
1259
    {
1260
        $resultData = Database::select(
1261
            'COUNT(1) AS count',
1262
            Database::get_main_table(TABLE_MAIN_USER),
1263
            [
1264
                'where' => ['id = ?' => (int) $userId],
1265
            ],
1266
            'first'
1267
        );
1268
1269
        if (false === $resultData) {
1270
            return false;
1271
        }
1272
1273
        return $resultData['count'] > 0;
1274
    }
1275
1276
    /**
1277
     * Checks whether a given username matches to the specification strictly.
1278
     * The empty username is assumed here as invalid.
1279
     * Mostly this function is to be used in the user interface built-in validation routines
1280
     * for providing feedback while usernames are enterd manually.
1281
     *
1282
     * @param string $username the input username
1283
     *
1284
     * @return bool returns TRUE if the username is valid, FALSE otherwise
1285
     */
1286
    public static function is_username_valid($username)
1287
    {
1288
        return !empty($username) && $username == self::purify_username($username, true);
1289
    }
1290
1291
    /**
1292
     * Checks whether a username is empty. If the username contains whitespace characters,
1293
     * such as spaces, tabulators, newlines, etc.,
1294
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
1295
     *
1296
     * @param string $username the given username
1297
     *
1298
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1299
     */
1300
    public static function is_username_empty($username)
1301
    {
1302
        return 0 == strlen(self::purify_username($username, false));
1303
    }
1304
1305
    /**
1306
     * Checks whether a username is too long or not.
1307
     *
1308
     * @param string $username the given username, it should contain only ASCII-letters and digits
1309
     *
1310
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1311
     */
1312
    public static function is_username_too_long($username)
1313
    {
1314
        return strlen($username) > User::USERNAME_MAX_LENGTH;
1315
    }
1316
1317
    /**
1318
     * Get the users by ID.
1319
     *
1320
     * @param array  $ids    student ids
1321
     * @param bool   $active
1322
     * @param string $order
1323
     * @param string $limit
1324
     *
1325
     * @return array $result student information
1326
     */
1327
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
1328
    {
1329
        if (empty($ids)) {
1330
            return [];
1331
        }
1332
1333
        $ids = is_array($ids) ? $ids : [$ids];
1334
        $ids = array_map('intval', $ids);
1335
        $ids = implode(',', $ids);
1336
1337
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
1338
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
1339
        if (!is_null($active)) {
1340
            $sql .= ' AND active='.($active ? '1' : '0');
1341
        }
1342
1343
        if (!is_null($order)) {
1344
            $order = Database::escape_string($order);
1345
            $sql .= ' ORDER BY '.$order;
1346
        }
1347
1348
        if (!is_null($limit)) {
1349
            $limit = Database::escape_string($limit);
1350
            $sql .= ' LIMIT '.$limit;
1351
        }
1352
1353
        $rs = Database::query($sql);
1354
        $result = [];
1355
        while ($row = Database::fetch_array($rs)) {
1356
            $result[] = $row;
1357
        }
1358
1359
        return $result;
1360
    }
1361
1362
    /**
1363
     * Get a list of users of which the given conditions match with an = 'cond'.
1364
     *
1365
     * @param array $conditions a list of condition (example : status=>STUDENT)
1366
     * @param array $order_by   a list of fields on which sort
1367
     *
1368
     * @return array an array with all users of the platform
1369
     *
1370
     * @todo security filter order by
1371
     */
1372
    public static function get_user_list(
1373
        $conditions = [],
1374
        $order_by = [],
1375
        $limit_from = false,
1376
        $limit_to = false,
1377
        $idCampus = null
1378
    ) {
1379
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1380
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1381
        $return_array = [];
1382
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1383
1384
        if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1384
        if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1385
            if ($idCampus) {
1386
                $urlId = $idCampus;
1387
            } else {
1388
                $urlId = api_get_current_access_url_id();
1389
            }
1390
            $sql .= " INNER JOIN $userUrlTable url_user
1391
                      ON (user.id = url_user.user_id)
1392
                      WHERE url_user.access_url_id = $urlId";
1393
        } else {
1394
            $sql .= " WHERE 1=1 ";
1395
        }
1396
1397
        if (count($conditions) > 0) {
1398
            foreach ($conditions as $field => $value) {
1399
                $field = Database::escape_string($field);
1400
                $value = Database::escape_string($value);
1401
                $sql .= " AND $field = '$value'";
1402
            }
1403
        }
1404
1405
        if (count($order_by) > 0) {
1406
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1407
        }
1408
1409
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1410
            $limit_from = (int) $limit_from;
1411
            $limit_to = (int) $limit_to;
1412
            $sql .= " LIMIT $limit_from, $limit_to";
1413
        }
1414
        $sql_result = Database::query($sql);
1415
        while ($result = Database::fetch_array($sql_result)) {
1416
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1417
            $return_array[] = $result;
1418
        }
1419
1420
        return $return_array;
1421
    }
1422
1423
    public static function getUserListExtraConditions(
1424
        $conditions = [],
1425
        $order_by = [],
1426
        $limit_from = false,
1427
        $limit_to = false,
1428
        $idCampus = null,
1429
        $extraConditions = '',
1430
        $getCount = false
1431
    ) {
1432
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1433
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1434
        $return_array = [];
1435
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1436
1437
        if ($getCount) {
1438
            $sql = "SELECT count(user.id) count FROM $user_table user ";
1439
        }
1440
1441
        if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1441
        if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1442
            if ($idCampus) {
1443
                $urlId = $idCampus;
1444
            } else {
1445
                $urlId = api_get_current_access_url_id();
1446
            }
1447
            $sql .= " INNER JOIN $userUrlTable url_user
1448
                      ON (user.id = url_user.user_id)
1449
                      WHERE url_user.access_url_id = $urlId";
1450
        } else {
1451
            $sql .= " WHERE 1=1 ";
1452
        }
1453
1454
        $sql .= " AND status <> ".ANONYMOUS." ";
1455
1456
        if (count($conditions) > 0) {
1457
            foreach ($conditions as $field => $value) {
1458
                $field = Database::escape_string($field);
1459
                $value = Database::escape_string($value);
1460
                $sql .= " AND $field = '$value'";
1461
            }
1462
        }
1463
1464
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
1465
1466
        if (!empty($order_by) && count($order_by) > 0) {
1467
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1468
        }
1469
1470
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1471
            $limit_from = (int) $limit_from;
1472
            $limit_to = (int) $limit_to;
1473
            $sql .= " LIMIT $limit_from, $limit_to";
1474
        }
1475
1476
        $sql_result = Database::query($sql);
1477
1478
        if ($getCount) {
1479
            $result = Database::fetch_array($sql_result);
1480
1481
            return $result['count'];
1482
        }
1483
1484
        while ($result = Database::fetch_array($sql_result)) {
1485
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1486
            $return_array[] = $result;
1487
        }
1488
1489
        return $return_array;
1490
    }
1491
1492
    /**
1493
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
1494
     *
1495
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
1496
     * @param array  $order_by         a list of fields on which sort
1497
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
1498
     * @param string $condition        Whether we want the filters to be combined by AND or OR
1499
     * @param array  $onlyThisUserList
1500
     *
1501
     * @return array an array with all users of the platform
1502
     *
1503
     * @todo optional course code parameter, optional sorting parameters...
1504
     * @todo security filter order_by
1505
     */
1506
    public static function getUserListLike(
1507
        $conditions = [],
1508
        $order_by = [],
1509
        $simple_like = false,
1510
        $condition = 'AND',
1511
        $onlyThisUserList = []
1512
    ) {
1513
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1514
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1515
        $return_array = [];
1516
        $sql_query = "SELECT user.id FROM $user_table user ";
1517
1518
        if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1518
        if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1519
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
1520
        }
1521
1522
        $sql_query .= ' WHERE 1 = 1 ';
1523
        if (count($conditions) > 0) {
1524
            $temp_conditions = [];
1525
            foreach ($conditions as $field => $value) {
1526
                $field = Database::escape_string($field);
1527
                $value = Database::escape_string($value);
1528
                if ($simple_like) {
1529
                    $temp_conditions[] = $field." LIKE '$value%'";
1530
                } else {
1531
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
1532
                }
1533
            }
1534
            if (!empty($temp_conditions)) {
1535
                $sql_query .= ' AND '.implode(' '.$condition.' ', $temp_conditions);
1536
            }
1537
1538
            if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1538
            if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1539
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1540
            }
1541
        } else {
1542
            if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1542
            if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1543
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1544
            }
1545
        }
1546
1547
        if (!empty($onlyThisUserList)) {
1548
            $onlyThisUserListToString = implode("','", $onlyThisUserList);
1549
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
1550
        }
1551
1552
        if (count($order_by) > 0) {
1553
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1554
        }
1555
1556
        $sql_result = Database::query($sql_query);
1557
        while ($result = Database::fetch_array($sql_result)) {
1558
            $userInfo = api_get_user_info($result['id']);
1559
            $return_array[] = $userInfo;
1560
        }
1561
1562
        return $return_array;
1563
    }
1564
1565
    /**
1566
     * Get user path from user ID (returns an array).
1567
     * The return format is a complete path to a folder ending with "/"
1568
     * In case the first level of subdirectory of users/ does not exist, the
1569
     * function will attempt to create it. Probably not the right place to do it
1570
     * but at least it avoids headaches in many other places.
1571
     *
1572
     * @param int    $id   User ID
1573
     * @param string $type Type of path to return (can be 'system', 'web', 'last')
1574
     *
1575
     * @return string User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
1576
     */
1577
    public static function getUserPathById($id, $type)
1578
    {
1579
        $id = (int) $id;
1580
        if (!$id) {
1581
            return null;
1582
        }
1583
1584
        $userPath = "users/$id/";
1585
        if (api_get_setting('split_users_upload_directory') === 'true') {
1586
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
1587
            // In exceptional cases, on some portals, the intermediate base user
1588
            // directory might not have been created. Make sure it is before
1589
            // going further.
1590
1591
            $rootPath = api_get_path(SYS_PATH).'../app/upload/users/'.substr((string) $id, 0, 1);
1592
            if (!is_dir($rootPath)) {
1593
                $perm = api_get_permissions_for_new_directories();
1594
                try {
1595
                    mkdir($rootPath, $perm);
1596
                } catch (Exception $e) {
1597
                    error_log($e->getMessage());
1598
                }
1599
            }
1600
        }
1601
        switch ($type) {
1602
            case 'system': // Base: absolute system path.
1603
                $userPath = api_get_path(SYS_PATH).'../app/upload/'.$userPath;
1604
                break;
1605
            case 'web': // Base: absolute web path.
1606
                $userPath = api_get_path(WEB_PATH).'../app/upload/'.$userPath;
1607
                break;
1608
            case 'last': // Only the last part starting with users/
1609
                break;
1610
        }
1611
1612
        return $userPath;
1613
    }
1614
1615
    /**
1616
     * Gets the current user image.
1617
     *
1618
     * @param string $userId
1619
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
1620
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
1621
     * @param bool   $addRandomId
1622
     * @param array  $userInfo    to avoid query the DB
1623
     *
1624
     * @todo add gravatar support
1625
     * @todo replace $userId with User entity
1626
     *
1627
     * @return string
1628
     */
1629
    public static function getUserPicture(
1630
        $userId,
1631
        int $size = USER_IMAGE_SIZE_MEDIUM,
1632
        $addRandomId = true,
1633
        $userInfo = []
1634
    ) {
1635
        $user = api_get_user_entity($userId);
1636
        $illustrationRepo = Container::getIllustrationRepository();
1637
1638
        switch ($size) {
1639
            case USER_IMAGE_SIZE_SMALL:
1640
                $width = 32;
1641
                break;
1642
            case USER_IMAGE_SIZE_MEDIUM:
1643
                $width = 64;
1644
                break;
1645
            case USER_IMAGE_SIZE_BIG:
1646
                $width = 128;
1647
                break;
1648
            case USER_IMAGE_SIZE_ORIGINAL:
1649
            default:
1650
                $width = 0;
1651
                break;
1652
        }
1653
1654
        $url = $illustrationRepo->getIllustrationUrl($user);
1655
        $params = [];
1656
        if (!empty($width)) {
1657
            $params['w'] = $width;
1658
        }
1659
1660
        if ($addRandomId) {
1661
            $params['rand'] = uniqid('u_', true);
1662
        }
1663
1664
        $paramsToString = '';
1665
        if (!empty($params)) {
1666
            $paramsToString = '?'.http_build_query($params);
1667
        }
1668
1669
        return $url.$paramsToString;
1670
1671
        /*
1672
        // Make sure userInfo is defined. Otherwise, define it!
1673
        if (empty($userInfo) || !is_array($userInfo) || 0 == count($userInfo)) {
1674
            if (empty($user_id)) {
1675
                return '';
1676
            } else {
1677
                $userInfo = api_get_user_info($user_id);
1678
            }
1679
        }
1680
1681
        $imageWebPath = self::get_user_picture_path_by_id(
1682
            $user_id,
1683
            'web',
1684
            $userInfo
1685
        );
1686
        $pictureWebFile = $imageWebPath['file'];
1687
        $pictureWebDir = $imageWebPath['dir'];
1688
1689
        $pictureAnonymousSize = '128';
1690
        $gravatarSize = 22;
1691
        $realSizeName = 'small_';
1692
1693
        switch ($size) {
1694
            case USER_IMAGE_SIZE_SMALL:
1695
                $pictureAnonymousSize = '32';
1696
                $realSizeName = 'small_';
1697
                $gravatarSize = 32;
1698
                break;
1699
            case USER_IMAGE_SIZE_MEDIUM:
1700
                $pictureAnonymousSize = '64';
1701
                $realSizeName = 'medium_';
1702
                $gravatarSize = 64;
1703
                break;
1704
            case USER_IMAGE_SIZE_ORIGINAL:
1705
                $pictureAnonymousSize = '128';
1706
                $realSizeName = '';
1707
                $gravatarSize = 128;
1708
                break;
1709
            case USER_IMAGE_SIZE_BIG:
1710
                $pictureAnonymousSize = '128';
1711
                $realSizeName = 'big_';
1712
                $gravatarSize = 128;
1713
                break;
1714
        }
1715
1716
        $gravatarEnabled = api_get_setting('gravatar_enabled');
1717
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
1718
        if ('unknown.jpg' == $pictureWebFile || empty($pictureWebFile)) {
1719
            if ('true' === $gravatarEnabled) {
1720
                $file = self::getGravatar(
1721
                    $imageWebPath['email'],
1722
                    $gravatarSize,
1723
                    api_get_setting('gravatar_type')
1724
                );
1725
1726
                if ($addRandomId) {
1727
                    $file .= '&rand='.uniqid();
1728
                }
1729
1730
                return $file;
1731
            }
1732
1733
            return $anonymousPath;
1734
        }
1735
1736
        if ($addRandomId) {
1737
            $picture .= '?rand='.uniqid();
1738
        }
1739
1740
        return $picture;*/
1741
    }
1742
1743
    /**
1744
     * Creates new user photos in various sizes of a user, or deletes user photos.
1745
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
1746
     *
1747
     * @param int    $user_id the user internal identification number
1748
     * @param string $file    The common file name for the newly created photos.
1749
     *                        It will be checked and modified for compatibility with the file system.
1750
     *                        If full name is provided, path component is ignored.
1751
     *                        If an empty name is provided, then old user photos are deleted only,
1752
     *
1753
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
1754
     *
1755
     * @param string $source_file    the full system name of the image from which user photos will be created
1756
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
1757
     *
1758
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
1759
     *              When deletion is requested returns empty string.
1760
     *              In case of internal error or negative validation returns FALSE.
1761
     */
1762
    public static function update_user_picture($userId, UploadedFile $file, string $crop = '')
1763
    {
1764
        if (empty($userId) || empty($file)) {
1765
            return false;
1766
        }
1767
1768
        $repo = Container::getUserRepository();
1769
        $user = $repo->find($userId);
1770
        if ($user) {
1771
            $repoIllustration = Container::getIllustrationRepository();
1772
            $repoIllustration->addIllustration($user, $user, $file, $crop);
1773
        }
1774
    }
1775
1776
    /**
1777
     * Deletes user photos.
1778
     *
1779
     * @param int $userId the user internal identification number
1780
     *
1781
     * @return mixed returns empty string on success, FALSE on error
1782
     */
1783
    public static function deleteUserPicture($userId)
1784
    {
1785
        $repo = Container::getUserRepository();
1786
        $user = $repo->find($userId);
1787
        if ($user) {
1788
            $illustrationRepo = Container::getIllustrationRepository();
1789
            $illustrationRepo->deleteIllustration($user);
1790
        }
1791
    }
1792
1793
    /**
1794
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
1795
     * doesn't have any.
1796
     *
1797
     * If there has been a request to remove a production, the function will return
1798
     * without building the list unless forced to do so by the optional second
1799
     * parameter. This increases performance by avoiding to read through the
1800
     * productions on the filesystem before the removal request has been carried
1801
     * out because they'll have to be re-read afterwards anyway.
1802
     *
1803
     * @deprecated This method is being removed from chamilo 2.0
1804
     * @param int  $user_id    User id
1805
     * @param bool $force      Optional parameter to force building after a removal request
1806
     * @param bool $showDelete
1807
     *
1808
     * @return string A string containing the XHTML code to display the production list, or FALSE
1809
     */
1810
    public static function build_production_list($user_id, $force = false, $showDelete = false)
1811
    {
1812
        if (!$force && !empty($_POST['remove_production'])) {
1813
            return true; // postpone reading from the filesystem
1814
        }
1815
1816
        $productions = self::get_user_productions($user_id);
1817
1818
        if (empty($productions)) {
1819
            return false;
1820
        }
1821
1822
        return false;
1823
1824
        $production_dir = self::getUserPathById($user_id, 'web');
0 ignored issues
show
Unused Code introduced by
$production_dir = self::...thById($user_id, 'web') is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1825
        $del_image = Display::returnIconPath('delete.png');
1826
        $add_image = Display::returnIconPath('archive.png');
1827
        $del_text = get_lang('Delete');
1828
        $production_list = '';
1829
        if (count($productions) > 0) {
1830
            $production_list = '<div class="files-production"><ul id="productions">';
1831
            foreach ($productions as $file) {
1832
                $production_list .= '<li>
1833
                    <img src="'.$add_image.'" />
1834
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
1835
                        '.htmlentities($file).'
1836
                    </a>';
1837
                if ($showDelete) {
1838
                    $production_list .= '&nbsp;&nbsp;
1839
                        <input
1840
                            style="width:16px;"
1841
                            type="image"
1842
                            name="remove_production['.urlencode($file).']"
1843
                            src="'.$del_image.'"
1844
                            alt="'.$del_text.'"
1845
                            title="'.$del_text.' '.htmlentities($file).'"
1846
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
1847
                }
1848
            }
1849
            $production_list .= '</ul></div>';
1850
        }
1851
1852
        return $production_list;
1853
    }
1854
1855
    /**
1856
     * Returns an array with the user's productions.
1857
     *
1858
     * @param int $user_id User id
1859
     *
1860
     * @return array An array containing the user's productions
1861
     */
1862
    public static function get_user_productions($user_id)
1863
    {
1864
        return [];
1865
1866
        $production_repository = self::getUserPathById($user_id, 'system');
0 ignored issues
show
Unused Code introduced by
$production_repository =...yId($user_id, 'system') is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1867
        $productions = [];
1868
1869
        if (is_dir($production_repository)) {
1870
            $handle = opendir($production_repository);
1871
            while ($file = readdir($handle)) {
1872
                if ('.' == $file ||
1873
                    '..' == $file ||
1874
                    '.htaccess' == $file ||
1875
                    is_dir($production_repository.$file)
1876
                ) {
1877
                    // skip current/parent directory and .htaccess
1878
                    continue;
1879
                }
1880
1881
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
1882
                    // User's photos should not be listed as productions.
1883
                    continue;
1884
                }
1885
                $productions[] = $file;
1886
            }
1887
        }
1888
1889
        return $productions;
1890
    }
1891
1892
    /**
1893
     * Remove a user production.
1894
     *
1895
     * @param int    $user_id    User id
1896
     * @param string $production The production to remove
1897
     *
1898
     * @return bool
1899
     */
1900
    public static function remove_user_production($user_id, $production)
1901
    {
1902
        throw new Exception('remove_user_production');
1903
        /*$production_path = self::get_user_picture_path_by_id($user_id, 'system');
1904
        $production_file = $production_path['dir'].$production;
1905
        if (is_file($production_file)) {
1906
            unlink($production_file);
1907
1908
            return true;
1909
        }
1910
1911
        return false;*/
1912
    }
1913
1914
    /**
1915
     * Update an extra field value for a given user.
1916
     *
1917
     * @param int    $userId   User ID
1918
     * @param string $variable Field variable name
1919
     * @param string $value    Field value
1920
     *
1921
     * @return bool true if field updated, false otherwise
1922
     */
1923
    public static function update_extra_field_value($userId, $variable, $value = '')
1924
    {
1925
        $extraFieldValue = new ExtraFieldValue('user');
1926
        $params = [
1927
            'item_id' => $userId,
1928
            'variable' => $variable,
1929
            'field_value' => $value,
1930
        ];
1931
1932
        return $extraFieldValue->save($params);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraFieldValue->save($params) also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
1933
    }
1934
1935
    /**
1936
     * Get an array of extra fields with field details (type, default value and options).
1937
     *
1938
     * @param    int    Offset (from which row)
1939
     * @param    int    Number of items
1940
     * @param    int    Column on which sorting is made
1941
     * @param    string    Sorting direction
1942
     * @param    bool    Optional. Whether we get all the fields or just the visible ones
0 ignored issues
show
Documentation Bug introduced by
The doc comment Optional. at position 0 could not be parsed: Unknown type name 'Optional.' at position 0 in Optional..
Loading history...
1943
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
1944
     *
1945
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
1946
     */
1947
    public static function get_extra_fields(
1948
        $from = 0,
1949
        $number_of_items = 0,
1950
        $column = 5,
1951
        $direction = 'ASC',
1952
        $all_visibility = true,
1953
        $field_filter = null
1954
    ) {
1955
        $fields = [];
1956
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1957
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
1958
        $columns = [
1959
            'id',
1960
            'variable',
1961
            'value_type',
1962
            'display_text',
1963
            'default_value',
1964
            'field_order',
1965
            'filter',
1966
        ];
1967
        $column = (int) $column;
1968
        $sort_direction = '';
1969
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
1970
            $sort_direction = strtoupper($direction);
1971
        }
1972
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1973
        $sqlf = "SELECT * FROM $t_uf WHERE item_type = $extraFieldType ";
1974
        if (!$all_visibility) {
1975
            $sqlf .= " AND visible_to_self = 1 ";
1976
        }
1977
        if (!is_null($field_filter)) {
1978
            $field_filter = (int) $field_filter;
1979
            $sqlf .= " AND filter = $field_filter ";
1980
        }
1981
        $sqlf .= " ORDER BY `".$columns[$column]."` $sort_direction ";
1982
        if (0 != $number_of_items) {
1983
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
1984
        }
1985
        $resf = Database::query($sqlf);
1986
        if (Database::num_rows($resf) > 0) {
1987
            while ($rowf = Database::fetch_array($resf)) {
1988
                $fields[$rowf['id']] = [
1989
                    0 => $rowf['id'],
1990
                    1 => $rowf['variable'],
1991
                    2 => $rowf['value_type'],
1992
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
1993
                    4 => $rowf['default_value'],
1994
                    5 => $rowf['field_order'],
1995
                    6 => $rowf['visible_to_self'],
1996
                    7 => $rowf['changeable'],
1997
                    8 => $rowf['filter'],
1998
                    9 => [],
1999
                    10 => '<a name="'.$rowf['id'].'"></a>',
2000
                ];
2001
2002
                $sqlo = "SELECT * FROM $t_ufo
2003
                         WHERE field_id = ".$rowf['id']."
2004
                         ORDER BY option_order ASC";
2005
                $reso = Database::query($sqlo);
2006
                if (Database::num_rows($reso) > 0) {
2007
                    while ($rowo = Database::fetch_array($reso)) {
2008
                        $fields[$rowf['id']][9][$rowo['id']] = [
2009
                            0 => $rowo['id'],
2010
                            1 => $rowo['option_value'],
2011
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2012
                            3 => $rowo['option_order'],
2013
                        ];
2014
                    }
2015
                }
2016
            }
2017
        }
2018
2019
        return $fields;
2020
    }
2021
2022
    /**
2023
     * Creates a new extra field.
2024
     *
2025
     * @param string $variable    Field's internal variable name
2026
     * @param int    $fieldType   Field's type
2027
     * @param string $displayText Field's language var name
2028
     * @param string $default     Field's default value
2029
     *
2030
     * @return int
2031
     */
2032
    public static function create_extra_field(
2033
        $variable,
2034
        $valueType,
2035
        $displayText,
2036
        $default
2037
    ) {
2038
        $extraField = new ExtraField('user');
2039
        $params = [
2040
            'variable' => $variable,
2041
            'value_type' => $valueType,
2042
            'display_text' => $displayText,
2043
            'default_value' => $default,
2044
        ];
2045
2046
        return $extraField->save($params);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraField->save($params) also could return the type boolean which is incompatible with the documented return type integer.
Loading history...
2047
    }
2048
2049
    /**
2050
     * Check if a field is available.
2051
     *
2052
     * @param string $variable
2053
     *
2054
     * @return bool
2055
     */
2056
    public static function is_extra_field_available($variable)
2057
    {
2058
        $extraField = new ExtraField('user');
2059
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2060
2061
        return !empty($data) ? true : false;
2062
    }
2063
2064
    /**
2065
     * Gets user extra fields data.
2066
     *
2067
     * @param    int    User ID
2068
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2069
     * @param    bool    Whether to return invisible fields as well
2070
     * @param    bool    Whether to split multiple-selection fields or not
2071
     *
2072
     * @return array Array of fields => value for the given user
2073
     */
2074
    public static function get_extra_user_data(
2075
        $user_id,
2076
        $prefix = false,
2077
        $allVisibility = true,
2078
        $splitMultiple = false,
2079
        $fieldFilter = null
2080
    ) {
2081
        $user_id = (int) $user_id;
2082
2083
        if (empty($user_id)) {
2084
            return [];
2085
        }
2086
2087
        $extra_data = [];
2088
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2089
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2090
        $sql = "SELECT f.id as id, f.variable as fvar, f.value_type as type
2091
                FROM $t_uf f
2092
                WHERE
2093
                    item_type = ".EntityExtraField::USER_FIELD_TYPE."
2094
                ";
2095
        $filter_cond = '';
2096
2097
        if (!$allVisibility) {
2098
            if (isset($fieldFilter)) {
2099
                $fieldFilter = (int) $fieldFilter;
2100
                $filter_cond .= " AND filter = $fieldFilter ";
2101
            }
2102
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2103
        } else {
2104
            if (isset($fieldFilter)) {
2105
                $fieldFilter = (int) $fieldFilter;
2106
                $sql .= " AND filter = $fieldFilter ";
2107
            }
2108
        }
2109
2110
        $sql .= ' ORDER BY f.field_order';
2111
2112
        $res = Database::query($sql);
2113
        if (Database::num_rows($res) > 0) {
2114
            while ($row = Database::fetch_array($res)) {
2115
                if (self::USER_FIELD_TYPE_TAG == $row['type']) {
2116
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2117
                    $extra_data['extra_'.$row['fvar']] = $tags;
2118
                } else {
2119
                    $sqlu = "SELECT field_value as fval
2120
                            FROM $t_ufv
2121
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2122
                    $resu = Database::query($sqlu);
2123
                    // get default value
2124
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2125
                               WHERE id=".$row['id'];
2126
                    $res_df = Database::query($sql_df);
2127
2128
                    if (Database::num_rows($resu) > 0) {
2129
                        $rowu = Database::fetch_array($resu);
2130
                        $fval = $rowu['fval'];
2131
                        if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2132
                            $fval = explode(';', $rowu['fval']);
2133
                        }
2134
                    } else {
2135
                        $row_df = Database::fetch_array($res_df);
2136
                        $fval = $row_df['fval_df'];
2137
                    }
2138
                    // We get here (and fill the $extra_data array) even if there
2139
                    // is no user with data (we fill it with default values)
2140
                    if ($prefix) {
2141
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2142
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2143
                        } else {
2144
                            $extra_data['extra_'.$row['fvar']] = $fval;
2145
                        }
2146
                    } else {
2147
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2148
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2149
                        } else {
2150
                            $extra_data[$row['fvar']] = $fval;
2151
                        }
2152
                    }
2153
                }
2154
            }
2155
        }
2156
2157
        return $extra_data;
2158
    }
2159
2160
    /** Get extra user data by field.
2161
     * @param int    user ID
2162
     * @param string the internal variable name of the field
2163
     *
2164
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2165
     */
2166
    public static function get_extra_user_data_by_field(
2167
        $user_id,
2168
        $field_variable,
2169
        $prefix = false,
2170
        $all_visibility = true,
2171
        $splitmultiple = false
2172
    ) {
2173
        $user_id = (int) $user_id;
2174
2175
        if (empty($user_id)) {
2176
            return [];
2177
        }
2178
2179
        $extra_data = [];
2180
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2181
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2182
2183
        $sql = "SELECT f.id as id, f.variable as fvar, f.value_type as type
2184
                FROM $t_uf f
2185
                WHERE f.variable = '$field_variable' ";
2186
2187
        if (!$all_visibility) {
2188
            $sql .= " AND f.visible_to_self = 1 ";
2189
        }
2190
2191
        $sql .= " AND item_type = ".EntityExtraField::USER_FIELD_TYPE;
2192
        $sql .= " ORDER BY f.field_order ";
2193
2194
        $res = Database::query($sql);
2195
        if (Database::num_rows($res) > 0) {
2196
            while ($row = Database::fetch_array($res)) {
2197
                $sqlu = "SELECT field_value as fval FROM $t_ufv v
2198
                         INNER JOIN $t_uf f
2199
                         ON (v.field_id = f.id)
2200
                         WHERE
2201
                            item_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2202
                            field_id = ".$row['id']." AND
2203
                            item_id = ".$user_id;
2204
                $resu = Database::query($sqlu);
2205
                $fval = '';
2206
                if (Database::num_rows($resu) > 0) {
2207
                    $rowu = Database::fetch_array($resu);
2208
                    $fval = $rowu['fval'];
2209
                    if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2210
                        $fval = explode(';', $rowu['fval']);
2211
                    }
2212
                }
2213
                if ($prefix) {
2214
                    $extra_data['extra_'.$row['fvar']] = $fval;
2215
                } else {
2216
                    $extra_data[$row['fvar']] = $fval;
2217
                }
2218
            }
2219
        }
2220
2221
        return $extra_data;
2222
    }
2223
2224
    /**
2225
     * Get the extra field information for a certain field (the options as well).
2226
     *
2227
     * @param int $variable The name of the field we want to know everything about
2228
     *
2229
     * @return array Array containing all the information about the extra profile field
2230
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2231
     *               as returned by the database)
2232
     *
2233
     * @author Julio Montoya
2234
     *
2235
     * @since v1.8.6
2236
     */
2237
    public static function get_extra_field_information_by_name($variable)
2238
    {
2239
        $extraField = new ExtraField('user');
2240
2241
        return $extraField->get_handler_field_info_by_field_variable($variable);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraField->get_...eld_variable($variable) also could return the type boolean which is incompatible with the documented return type array.
Loading history...
2242
    }
2243
2244
    /**
2245
     * Get the extra field information for user tag (the options as well).
2246
     *
2247
     * @param int $variable The name of the field we want to know everything about
2248
     *
2249
     * @return array Array containing all the information about the extra profile field
2250
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2251
     *               as returned by the database)
2252
     *
2253
     * @author José Loguercio
2254
     *
2255
     * @since v1.11.0
2256
     */
2257
    public static function get_extra_field_tags_information_by_name($variable)
2258
    {
2259
        $extraField = new ExtraField('user');
2260
2261
        return $extraField->get_handler_field_info_by_tags($variable);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraField->get_...info_by_tags($variable) also could return the type boolean which is incompatible with the documented return type array.
Loading history...
2262
    }
2263
2264
    /**
2265
     * Get all the extra field information of a certain field (also the options).
2266
     *
2267
     * @param int $fieldId the ID of the field we want to know everything of
2268
     *
2269
     * @return array $return containing all th information about the extra profile field
2270
     *
2271
     * @author Julio Montoya
2272
     *
2273
     * @deprecated
2274
     * @since v1.8.6
2275
     */
2276
    public static function get_extra_field_information($fieldId)
2277
    {
2278
        $extraField = new ExtraField('user');
2279
2280
        return $extraField->getFieldInfoByFieldId($fieldId);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraField->getF...InfoByFieldId($fieldId) also could return the type boolean which is incompatible with the documented return type array.
Loading history...
2281
    }
2282
2283
    /**
2284
     * Get extra user data by value.
2285
     *
2286
     * @param string $variable       the internal variable name of the field
2287
     * @param string $value          the internal value of the field
2288
     * @param bool   $all_visibility
2289
     *
2290
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2291
     */
2292
    public static function get_extra_user_data_by_value($variable, $value, $all_visibility = true)
2293
    {
2294
        $extraFieldValue = new ExtraFieldValue('user');
2295
        $extraField = new ExtraField('user');
2296
2297
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2298
2299
        if (false === $info) {
2300
            return [];
2301
        }
2302
2303
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2304
            $variable,
2305
            $value,
2306
            false,
2307
            false,
2308
            true
2309
        );
2310
2311
        $result = [];
2312
        if (!empty($data)) {
2313
            foreach ($data as $item) {
2314
                $result[] = $item['item_id'];
2315
            }
2316
        }
2317
2318
        return $result;
2319
    }
2320
2321
    /**
2322
     * Get extra user data by tags value.
2323
     *
2324
     * @param int    $fieldId the ID of the field we want to know everything of
2325
     * @param string $tag     the tag name for search
2326
     *
2327
     * @return array with extra data info of a user
2328
     *
2329
     * @author José Loguercio
2330
     *
2331
     * @since v1.11.0
2332
     */
2333
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2334
    {
2335
        $extraField = new ExtraField('user');
2336
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2337
        $array = [];
2338
        foreach ($result as $index => $user) {
2339
            $array[] = $user['user_id'];
2340
        }
2341
2342
        return $array;
2343
    }
2344
2345
    /**
2346
     * Get extra user data by field variable.
2347
     *
2348
     * @param string $variable field variable
2349
     *
2350
     * @return array data
2351
     */
2352
    public static function get_extra_user_data_by_field_variable($variable)
2353
    {
2354
        $extraInfo = self::get_extra_field_information_by_name($variable);
2355
        $field_id = (int) $extraInfo['id'];
2356
2357
        $extraField = new ExtraFieldValue('user');
2358
        $data = $extraField->getValuesByFieldId($field_id);
2359
2360
        if (!empty($data)) {
2361
            foreach ($data as $row) {
2362
                $user_id = $row['item_id'];
2363
                $data[$user_id] = $row;
2364
            }
2365
        }
2366
2367
        return $data;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $data could also return false which is incompatible with the documented return type array. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
2368
    }
2369
2370
    /**
2371
     * Get extra user data tags by field variable.
2372
     *
2373
     * @param string $variable field variable
2374
     *
2375
     * @return array
2376
     */
2377
    public static function get_extra_user_data_for_tags($variable)
2378
    {
2379
        $data = self::get_extra_field_tags_information_by_name($variable);
2380
2381
        return $data;
2382
    }
2383
2384
    /**
2385
     * Gives a list of [session_category][session_id] for the current user.
2386
     *
2387
     * @param int  $user_id
2388
     * @param bool $is_time_over                 whether to fill the first element or not
2389
     *                                           (to give space for courses out of categories)
2390
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2391
     * @param bool $ignoreTimeLimit              ignore time start/end
2392
     * @param bool $getCount
2393
     *
2394
     * @return array list of statuses [session_category][session_id]
2395
     *
2396
     * @todo ensure multiple access urls are managed correctly
2397
     */
2398
    public static function get_sessions_by_category(
2399
        $user_id,
2400
        $is_time_over = true,
2401
        $ignore_visibility_for_admins = false,
2402
        $ignoreTimeLimit = false,
2403
        $getCount = false
2404
    ) {
2405
        $user_id = (int) $user_id;
2406
2407
        if (empty($user_id)) {
2408
            return [];
2409
        }
2410
2411
        // Get the list of sessions per user
2412
        $now = new DateTime('now', new DateTimeZone('UTC'));
2413
2414
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2415
        // join would not catch session-courses where the user is general
2416
        // session coach but which do not have students nor coaches registered
2417
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
2418
2419
        if (!$getCount) {
2420
            $dqlSelect = " DISTINCT
2421
                s.id,
2422
                s.title,
2423
                s.accessStartDate AS access_start_date,
2424
                s.accessEndDate AS access_end_date,
2425
                s.duration,
2426
                sc.id AS session_category_id,
2427
                sc.title AS session_category_title,
2428
                sc.dateStart AS session_category_date_start,
2429
                sc.dateEnd AS session_category_date_end,
2430
                s.coachAccessStartDate AS coach_access_start_date,
2431
                s.coachAccessEndDate AS coach_access_end_date,
2432
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
2433
                , s.position AS position
2434
            ";
2435
        }
2436
2437
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
2438
        // is awfully inefficient for large sets of data (1m25s for 58K
2439
        // sessions, BT#14115) but executing a similar query twice and grouping
2440
        // the results afterwards in PHP takes about 1/1000th of the time
2441
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
2442
        $dqlStudent = "SELECT $dqlSelect
2443
            FROM ChamiloCoreBundle:Session AS s
2444
            LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2445
            INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2446
            LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2447
            WHERE scu.user = :user AND url.url = :url ";
2448
        $dqlCoach = "SELECT $dqlSelect
2449
            FROM ChamiloCoreBundle:Session AS s
2450
            INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2451
            LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2452
            INNER JOIN ChamiloCoreBundle:SessionRelUser AS su WITH su.session = s
2453
            WHERE (su.user = :user AND su.relationType = ".SessionEntity::GENERAL_COACH.") AND url.url = :url ";
2454
2455
        // Default order
2456
        $order = 'ORDER BY sc.title, s.title';
2457
2458
        // Order by date if showing all sessions
2459
        $showAllSessions = ('true' === api_get_setting('course.show_all_sessions_on_my_course_page'));
2460
        if ($showAllSessions) {
2461
            $order = 'ORDER BY s.accessStartDate';
2462
        }
2463
2464
        // Order by position
2465
        if ('true' === api_get_setting('session.session_list_order')) {
2466
            $order = 'ORDER BY s.position';
2467
        }
2468
2469
        // Order by dates according to settings
2470
        $orderBySettings = api_get_setting('session.my_courses_session_order', true);
2471
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
2472
            $field = $orderBySettings['field'];
2473
            $orderSetting = $orderBySettings['order'];
2474
            switch ($field) {
2475
                case 'start_date':
2476
                    $order = " ORDER BY s.accessStartDate $orderSetting";
2477
                    break;
2478
                case 'end_date':
2479
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
2480
                    if ('asc' == $orderSetting) {
2481
                        // Put null values at the end
2482
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
2483
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
2484
                    }
2485
                    break;
2486
                case 'name':
2487
                case 'title':
2488
                    $order = " ORDER BY s.title $orderSetting ";
2489
                    break;
2490
            }
2491
        }
2492
2493
        $dqlStudent .= $order;
2494
        $dqlCoach .= $order;
2495
2496
        $accessUrlId = api_get_current_access_url_id();
2497
        $dqlStudent = Database::getManager()
2498
            ->createQuery($dqlStudent)
2499
            ->setParameters(
2500
                ['user' => $user_id, 'url' => $accessUrlId]
2501
            )
2502
        ;
2503
        $dqlCoach = Database::getManager()
2504
            ->createQuery($dqlCoach)
2505
            ->setParameters(
2506
                ['user' => $user_id, 'url' => $accessUrlId]
2507
            )
2508
        ;
2509
2510
        if ($getCount) {
2511
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
2512
        }
2513
2514
        $sessionDataStudent = $dqlStudent->getResult();
2515
        $sessionDataCoach = $dqlCoach->getResult();
2516
2517
        $sessionData = [];
2518
        // First fill $sessionData with student sessions
2519
        if (!empty($sessionDataStudent)) {
2520
            foreach ($sessionDataStudent as $row) {
2521
                $sessionData[$row['id']] = $row;
2522
            }
2523
        }
2524
        // Overwrite session data of the user as a student with session data
2525
        // of the user as a coach.
2526
        // There shouldn't be such duplicate rows, but just in case...
2527
        if (!empty($sessionDataCoach)) {
2528
            foreach ($sessionDataCoach as $row) {
2529
                $sessionData[$row['id']] = $row;
2530
            }
2531
        }
2532
2533
        $collapsable = ('true' === api_get_setting('session.allow_user_session_collapsable'));
2534
2535
2536
2537
        $extraField = new ExtraFieldValue('session');
2538
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
2539
2540
        if (empty($sessionData)) {
2541
            return [];
2542
        }
2543
        $categories = [];
2544
        foreach ($sessionData as $row) {
2545
            $session_id = $row['id'];
2546
            $coachList = SessionManager::getCoachesBySession($session_id);
2547
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
2548
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
2549
            $courseList = self::get_courses_list_by_session($user_id, $session_id);
2550
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
2551
2552
            // User portal filters:
2553
            if (false === $ignoreTimeLimit) {
2554
                if ($is_time_over) {
2555
                    // History
2556
                    if ($row['duration']) {
2557
                        if ($daysLeft >= 0) {
2558
                            continue;
2559
                        }
2560
                    } else {
2561
                        if (empty($row['access_end_date'])) {
2562
                            continue;
2563
                        } else {
2564
                            if ($row['access_end_date'] > $now) {
2565
                                continue;
2566
                            }
2567
                        }
2568
                    }
2569
                } else {
2570
                    // Current user portal
2571
                    $isGeneralCoach = api_get_session_entity($row['id'])->hasUserAsGeneralCoach(api_get_user_entity($user_id));
2572
                    $isCoachOfCourse = in_array($user_id, $coachList);
2573
2574
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
2575
                        // Teachers can access the session depending in the access_coach date
2576
                    } else {
2577
                        if ($row['duration']) {
2578
                            if ($daysLeft <= 0) {
2579
                                continue;
2580
                            }
2581
                        } else {
2582
                            if (isset($row['access_end_date']) &&
2583
                                !empty($row['access_end_date'])
2584
                            ) {
2585
                                if ($row['access_end_date'] <= $now) {
2586
                                    continue;
2587
                                }
2588
                            }
2589
                        }
2590
                    }
2591
                }
2592
            }
2593
2594
            $categories[$row['session_category_id']]['session_category'] = [
2595
                'id' => $row['session_category_id'],
2596
                'name' => $row['session_category_title'],
2597
                'date_start' => $categoryStart,
2598
                'date_end' => $categoryEnd,
2599
            ];
2600
2601
            $visibility = api_get_session_visibility(
2602
                $session_id,
2603
                null,
2604
                $ignore_visibility_for_admins
2605
            );
2606
2607
            if (SESSION_VISIBLE != $visibility) {
2608
                // Course Coach session visibility.
2609
                $blockedCourseCount = 0;
2610
                $closedVisibilityList = [
2611
                    COURSE_VISIBILITY_CLOSED,
2612
                    COURSE_VISIBILITY_HIDDEN,
2613
                ];
2614
2615
                foreach ($courseList as $course) {
2616
                    // Checking session visibility
2617
                    $sessionCourseVisibility = api_get_session_visibility(
2618
                        $session_id,
2619
                        $course['real_id'],
2620
                        $ignore_visibility_for_admins
2621
                    );
2622
2623
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
2624
                    if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
2625
                        $blockedCourseCount++;
2626
                    }
2627
                }
2628
2629
                // If all courses are blocked then no show in the list.
2630
                if ($blockedCourseCount === count($courseList)) {
2631
                    $visibility = SESSION_INVISIBLE;
2632
                } else {
2633
                    $visibility = $sessionCourseVisibility;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sessionCourseVisibility does not seem to be defined for all execution paths leading up to this point.
Loading history...
2634
                }
2635
            }
2636
2637
            switch ($visibility) {
2638
                case SESSION_VISIBLE_READ_ONLY:
2639
                case SESSION_VISIBLE:
2640
                case SESSION_AVAILABLE:
2641
                    break;
2642
                case SESSION_INVISIBLE:
2643
                    if (false === $ignore_visibility_for_admins) {
2644
                        continue 2;
2645
                    }
2646
            }
2647
2648
            $collapsed = '';
2649
            $collapsedAction = '';
2650
            if ($collapsable) {
2651
                $collapsableData = SessionManager::getCollapsableData(
2652
                    $user_id,
2653
                    $session_id,
2654
                    $extraField,
2655
                    $collapsableLink
2656
                );
2657
                $collapsed = $collapsableData['collapsed'];
2658
                $collapsedAction = $collapsableData['collapsable_link'];
2659
            }
2660
2661
            $categories[$row['session_category_id']]['sessions'][] = [
2662
                'session_name' => $row['title'],
2663
                'session_id' => $row['id'],
2664
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
2665
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
2666
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
2667
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
2668
                'courses' => $courseList,
2669
                'collapsed' => $collapsed,
2670
                'collapsable_link' => $collapsedAction,
2671
                'duration' => $row['duration'],
2672
            ];
2673
        }
2674
2675
        return $categories;
2676
    }
2677
2678
    /**
2679
     * Gives a list of [session_id-course_code] => [status] for the current user.
2680
     *
2681
     * @param int $user_id
2682
     * @param int $sessionLimit
2683
     *
2684
     * @return array list of statuses (session_id-course_code => status)
2685
     */
2686
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
2687
    {
2688
        // Database Table Definitions
2689
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2690
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2691
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2692
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
2693
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2694
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2695
2696
        $user_id = (int) $user_id;
2697
2698
        if (empty($user_id)) {
2699
            return [];
2700
        }
2701
2702
        $sessionRepo = Container::getSessionRepository();
2703
2704
        $user = api_get_user_entity($user_id);
2705
        $url = null;
2706
        $formattedUserName = Container::$container->get(NameConvention::class)->getPersonName($user);
2707
2708
        // We filter the courses from the URL
2709
        $join_access_url = $where_access_url = '';
2710
        if (api_get_multiple_access_url()) {
2711
            $access_url_id = api_get_current_access_url_id();
2712
            if (-1 != $access_url_id) {
2713
                $url = api_get_url_entity($access_url_id);
2714
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2715
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
2716
                $where_access_url = " AND access_url_id = $access_url_id ";
2717
            }
2718
        }
2719
2720
        // Courses in which we subscribed out of any session
2721
2722
        $sql = "SELECT
2723
                    course.code,
2724
                    course_rel_user.status course_rel_status,
2725
                    course_rel_user.sort sort,
2726
                    course_rel_user.user_course_cat user_course_cat
2727
                 FROM $tbl_course_user course_rel_user
2728
                 LEFT JOIN $tbl_course course
2729
                 ON course.id = course_rel_user.c_id
2730
                 $join_access_url
2731
                 WHERE
2732
                    course_rel_user.user_id = '".$user_id."' AND
2733
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
2734
                    $where_access_url
2735
                 ORDER BY course_rel_user.sort, course.title ASC";
2736
2737
        $course_list_sql_result = Database::query($sql);
2738
        $personal_course_list = [];
2739
        if (Database::num_rows($course_list_sql_result) > 0) {
2740
            while ($result_row = Database::fetch_assoc($course_list_sql_result)) {
2741
                $course_info = api_get_course_info($result_row['code']);
2742
                $result_row['course_info'] = $course_info;
2743
                $personal_course_list[] = $result_row;
2744
            }
2745
        }
2746
2747
        $coachCourseConditions = '';
2748
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
2749
        if (api_is_allowed_to_create_course()) {
2750
            $sessionListFromCourseCoach = [];
2751
            $sql = " SELECT DISTINCT session_id
2752
                    FROM $tbl_session_course_user
2753
                    WHERE user_id = $user_id AND status = ".SessionEntity::COURSE_COACH;
2754
2755
            $result = Database::query($sql);
2756
            if (Database::num_rows($result)) {
2757
                $result = Database::store_result($result);
2758
                foreach ($result as $session) {
2759
                    $sessionListFromCourseCoach[] = $session['session_id'];
2760
                }
2761
            }
2762
            if (!empty($sessionListFromCourseCoach)) {
2763
                $condition = implode("','", $sessionListFromCourseCoach);
2764
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
2765
            }
2766
        }
2767
2768
        // Get the list of sessions where the user is subscribed
2769
        // This is divided into two different queries
2770
        $sessions = [];
2771
        $sessionLimitRestriction = '';
2772
        if (!empty($sessionLimit)) {
2773
            $sessionLimit = (int) $sessionLimit;
2774
            $sessionLimitRestriction = "LIMIT $sessionLimit";
2775
        }
2776
2777
        $sql = "SELECT DISTINCT s.id, s.title, access_start_date, access_end_date
2778
                FROM $tbl_session_user su INNER JOIN $tbl_session s
2779
                ON (s.id = su.session_id)
2780
                WHERE (
2781
                    su.user_id = $user_id AND
2782
                    su.relation_type = ".SessionEntity::STUDENT."
2783
                )
2784
                $coachCourseConditions
2785
                ORDER BY access_start_date, access_end_date, s.title
2786
                $sessionLimitRestriction
2787
        ";
2788
2789
        $result = Database::query($sql);
2790
        if (Database::num_rows($result) > 0) {
2791
            while ($row = Database::fetch_assoc($result)) {
2792
                $sessions[$row['id']] = $row;
2793
            }
2794
        }
2795
2796
        $sql = "SELECT DISTINCT
2797
                s.id, s.title, s.access_start_date, s.access_end_date
2798
                FROM $tbl_session s
2799
                INNER JOIN $tbl_session_user sru ON sru.session_id = s.id
2800
                WHERE (
2801
                    sru.user_id = $user_id AND sru.relation_type = ".SessionEntity::GENERAL_COACH."
2802
                )
2803
                $coachCourseConditions
2804
                ORDER BY s.access_start_date, s.access_end_date, s.title";
2805
2806
        $result = Database::query($sql);
2807
        if (Database::num_rows($result) > 0) {
2808
            while ($row = Database::fetch_assoc($result)) {
2809
                if (empty($sessions[$row['id']])) {
2810
                    $sessions[$row['id']] = $row;
2811
                }
2812
            }
2813
        }
2814
2815
        if (api_is_allowed_to_create_course()) {
2816
            foreach ($sessions as $enreg) {
2817
                $session_id = $enreg['id'];
2818
                $session_visibility = api_get_session_visibility($session_id);
2819
                $session = api_get_session_entity($session_id);
2820
2821
                if (SESSION_INVISIBLE == $session_visibility) {
2822
                    continue;
2823
                }
2824
2825
                $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
2826
                    $user,
2827
                    $session,
2828
                    SessionEntity::GENERAL_COACH,
2829
                    $url
2830
                );
2831
                $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
2832
                    $user,
2833
                    $session,
2834
                    SessionEntity::COURSE_COACH,
2835
                    $url
2836
                );
2837
2838
                // This query is horribly slow when more than a few thousand
2839
                // users and just a few sessions to which they are subscribed
2840
                $coursesInSession = array_map(
2841
                    function (SessionRelCourse $courseInSession) {
2842
                        $course = $courseInSession->getCourse();
2843
2844
                        return [
2845
                            'code' => $course->getCode(),
2846
                            'i' => $course->getTitle(),
2847
                            'l' => $course->getCourseLanguage(),
2848
                            'sort' => 1,
2849
                        ];
2850
                    },
2851
                    array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
2852
                );
2853
2854
                foreach ($coursesInSession as $result_row) {
2855
                    $result_row['t'] = $formattedUserName;
2856
                    $result_row['email'] = $user->getEmail();
2857
                    $result_row['access_start_date'] = $session->getAccessStartDate()?->format('Y-m-d H:i:s');
2858
                    $result_row['access_end_date'] = $session->getAccessEndDate()?->format('Y-m-d H:i:s');
2859
                    $result_row['session_id'] = $session->getId();
2860
                    $result_row['session_name'] = $session->getTitle();
2861
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
2862
                    $key = $result_row['session_id'].' - '.$result_row['code'];
2863
                    $personal_course_list[$key] = $result_row;
2864
                }
2865
            }
2866
        }
2867
2868
        foreach ($sessions as $enreg) {
2869
            $session_id = $enreg['id'];
2870
            $session_visibility = api_get_session_visibility($session_id);
2871
            if (SESSION_INVISIBLE == $session_visibility) {
2872
                continue;
2873
            }
2874
2875
            /* This query is very similar to the above query,
2876
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
2877
            $sql = "SELECT DISTINCT
2878
                course.code code,
2879
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
2880
                email,
2881
                course.course_language l,
2882
                1 sort,
2883
                access_start_date,
2884
                access_end_date,
2885
                session.id as session_id,
2886
                session.title as session_name,
2887
                IF((session_course_user.user_id = 3 AND session_course_user.status = ".SessionEntity::COURSE_COACH."),'2', '5')
2888
            FROM $tbl_session_course_user as session_course_user
2889
            INNER JOIN $tbl_course AS course
2890
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
2891
            INNER JOIN $tbl_session as session
2892
            ON session_course_user.session_id = session.id
2893
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
2894
            WHERE session_course_user.user_id = $user_id
2895
            ORDER BY i";
2896
2897
            $course_list_sql_result = Database::query($sql);
2898
            while ($result_row = Database::fetch_assoc($course_list_sql_result)) {
2899
                $result_row['course_info'] = api_get_course_info($result_row['code']);
2900
                $key = $result_row['session_id'].' - '.$result_row['code'];
2901
                if (!isset($personal_course_list[$key])) {
2902
                    $personal_course_list[$key] = $result_row;
2903
                }
2904
            }
2905
        }
2906
2907
        return $personal_course_list;
2908
    }
2909
2910
    /**
2911
     * Gives a list of courses for the given user in the given session.
2912
     *
2913
     * @param int $user_id
2914
     * @param int $session_id
2915
     *
2916
     * @return array list of statuses (session_id-course_code => status)
2917
     */
2918
    public static function get_courses_list_by_session($user_id, $session_id)
2919
    {
2920
        // Database Table Definitions
2921
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
2922
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2923
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
2924
2925
        $user_id = (int) $user_id;
2926
        $session_id = (int) $session_id;
2927
2928
        $sessionRepo = Container::getSessionRepository();
2929
2930
        $user = api_get_user_entity($user_id);
2931
        $session = api_get_session_entity($session_id);
2932
        $url = null;
2933
2934
        // We filter the courses from the URL
2935
        $join_access_url = $where_access_url = '';
2936
        if (api_get_multiple_access_url()) {
2937
            $urlId = api_get_current_access_url_id();
2938
            if (-1 != $urlId) {
2939
                $url = api_get_url_entity($urlId);
2940
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
2941
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
2942
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
2943
            }
2944
        }
2945
2946
        /* This query is very similar to the query below, but it will check the
2947
        session_rel_course_user table if there are courses registered
2948
        to our user or not */
2949
        $sql = "SELECT DISTINCT
2950
                    c.title,
2951
                    c.visibility,
2952
                    c.id as real_id,
2953
                    c.code as course_code,
2954
                    sc.position,
2955
                    c.unsubscribe
2956
                FROM $tbl_session_course_user as scu
2957
                INNER JOIN $tbl_session_course sc
2958
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
2959
                INNER JOIN $tableCourse as c
2960
                ON (scu.c_id = c.id)
2961
                $join_access_url
2962
                WHERE
2963
                    scu.user_id = $user_id AND
2964
                    scu.session_id = $session_id
2965
                    $where_access_url
2966
                ORDER BY sc.position ASC";
2967
2968
        $myCourseList = [];
2969
        $courses = [];
2970
        $result = Database::query($sql);
2971
        if (Database::num_rows($result) > 0) {
2972
            while ($result_row = Database::fetch_assoc($result)) {
2973
                $result_row['status'] = 5;
2974
                if (!in_array($result_row['real_id'], $courses)) {
2975
                    $position = $result_row['position'];
2976
                    if (!isset($myCourseList[$position])) {
2977
                        $myCourseList[$position] = $result_row;
2978
                    } else {
2979
                        $myCourseList[] = $result_row;
2980
                    }
2981
                    $courses[] = $result_row['real_id'];
2982
                }
2983
            }
2984
        }
2985
2986
        if (api_is_allowed_to_create_course()) {
2987
            $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
2988
                $user,
2989
                $session,
2990
                SessionEntity::GENERAL_COACH,
2991
                $url
2992
            );
2993
            $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
2994
                $user,
2995
                $session,
2996
                SessionEntity::COURSE_COACH,
2997
                $url
2998
            );
2999
3000
            $coursesInSession = array_map(
3001
                function (SessionRelCourse $courseInSession) {
3002
                    $course = $courseInSession->getCourse();
3003
3004
                    return [
3005
                        'title' => $course->getTitle(),
3006
                        'visibility' => $course->getVisibility(),
3007
                        'real_id' => $course->getId(),
3008
                        'course_code' => $course->getCode(),
3009
                        'position' => $courseInSession->getPosition(),
3010
                        'unsubscribe' => $course->getUnsubscribe(),
3011
                    ];
3012
                },
3013
                array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
3014
            );
3015
3016
            foreach ($coursesInSession as $result_row) {
3017
                $result_row['status'] = 2;
3018
                if (!in_array($result_row['real_id'], $courses)) {
3019
                    $position = $result_row['position'];
3020
                    if (!isset($myCourseList[$position])) {
3021
                        $myCourseList[$position] = $result_row;
3022
                    } else {
3023
                        $myCourseList[] = $result_row;
3024
                    }
3025
                    $courses[] = $result_row['real_id'];
3026
                }
3027
            }
3028
        }
3029
3030
        if (api_is_drh()) {
3031
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3032
            $sessionList = array_keys($sessionList);
3033
            if (in_array($session_id, $sessionList)) {
3034
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3035
                if (!empty($courseList)) {
3036
                    foreach ($courseList as $course) {
3037
                        if (!in_array($course['id'], $courses)) {
3038
                            $position = $course['position'];
3039
                            if (!isset($myCourseList[$position])) {
3040
                                $myCourseList[$position] = $course;
3041
                            } else {
3042
                                $myCourseList[] = $course;
3043
                            }
3044
                        }
3045
                    }
3046
                }
3047
            }
3048
        } else {
3049
            //check if user is general coach for this session
3050
            if ($session && $session->hasUserAsGeneralCoach($user)) {
3051
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3052
                if (!empty($courseList)) {
3053
                    foreach ($courseList as $course) {
3054
                        if (!in_array($course['id'], $courses)) {
3055
                            $position = $course['position'];
3056
                            if (!isset($myCourseList[$position])) {
3057
                                $myCourseList[$position] = $course;
3058
                            } else {
3059
                                $myCourseList[] = $course;
3060
                            }
3061
                        }
3062
                    }
3063
                }
3064
            }
3065
        }
3066
3067
        if (!empty($myCourseList)) {
3068
            ksort($myCourseList);
3069
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
3070
            if (empty($checkPosition)) {
3071
                // The session course list doesn't have any position,
3072
                // then order the course list by course code
3073
                $list = array_column($myCourseList, 'course_code');
3074
                array_multisort($myCourseList, SORT_ASC, $list);
0 ignored issues
show
Bug introduced by
SORT_ASC cannot be passed to array_multisort() as the parameter $rest expects a reference. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3074
                array_multisort($myCourseList, /** @scrutinizer ignore-type */ SORT_ASC, $list);
Loading history...
3075
            }
3076
        }
3077
3078
        return $myCourseList;
3079
    }
3080
3081
    /**
3082
     * Get user id from a username.
3083
     *
3084
     * @param string $username
3085
     *
3086
     * @return int User ID (or false if not found)
3087
     */
3088
    public static function get_user_id_from_username($username)
3089
    {
3090
        if (empty($username)) {
3091
            return false;
3092
        }
3093
        $username = trim($username);
3094
        $username = Database::escape_string($username);
3095
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3096
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3097
        $res = Database::query($sql);
3098
3099
        if (false === $res) {
3100
            return false;
3101
        }
3102
        if (1 !== Database::num_rows($res)) {
3103
            return false;
3104
        }
3105
        $row = Database::fetch_array($res);
3106
3107
        return $row['id'];
3108
    }
3109
3110
    /**
3111
     * Get the users files upload from his share_folder.
3112
     *
3113
     * @param string $user_id      User ID
3114
     * @param string $course       course directory
3115
     * @param string $resourceType resource type: images, all
3116
     *
3117
     * @return string
3118
     */
3119
    /*public static function get_user_upload_files_by_course(
3120
        $user_id,
3121
        $course,
3122
        $resourceType = 'all'
3123
    ) {
3124
        $return = '';
3125
        $user_id = (int) $user_id;
3126
3127
        if (!empty($user_id) && !empty($course)) {
3128
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3129
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3130
            $file_list = [];
3131
3132
            if (is_dir($path)) {
3133
                $handle = opendir($path);
3134
                while ($file = readdir($handle)) {
3135
                    if ('.' == $file || '..' == $file || '.htaccess' == $file || is_dir($path.$file)) {
3136
                        continue; // skip current/parent directory and .htaccess
3137
                    }
3138
                    $file_list[] = $file;
3139
                }
3140
                if (count($file_list) > 0) {
3141
                    $return = "<h4>$course</h4>";
3142
                    $return .= '<ul class="thumbnails">';
3143
                }
3144
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
3145
                foreach ($file_list as $file) {
3146
                    if ('all' == $resourceType) {
3147
                        $return .= '<li>
3148
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
3149
                    } elseif ('images' == $resourceType) {
3150
                        //get extension
3151
                        $ext = explode('.', $file);
3152
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
3153
                            $return .= '<li class="span2">
3154
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
3155
                                                <img src="'.$web_path.urlencode($file).'" >
3156
                                            </a>
3157
                                        </li>';
3158
                        }
3159
                    }
3160
                }
3161
                if (count($file_list) > 0) {
3162
                    $return .= '</ul>';
3163
                }
3164
            }
3165
        }
3166
3167
        return $return;
3168
    }*/
3169
3170
    /**
3171
     * Gets the API key (or keys) and return them into an array.
3172
     *
3173
     * @param int     Optional user id (defaults to the result of api_get_user_id())
3174
     * @param string $api_service
3175
     *
3176
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
3177
     */
3178
    public static function get_api_keys($user_id = null, $api_service = 'default')
3179
    {
3180
        if ($user_id != strval(intval($user_id))) {
3181
            return false;
3182
        }
3183
        if (empty($user_id)) {
3184
            $user_id = api_get_user_id();
3185
        }
3186
        if (false === $user_id) {
3187
            return false;
3188
        }
3189
        $service_name = Database::escape_string($api_service);
3190
        if (false === is_string($service_name)) {
3191
            return false;
3192
        }
3193
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3194
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
3195
        $res = Database::query($sql);
3196
        if (false === $res) {
3197
            return false;
3198
        } //error during query
3199
        $num = Database::num_rows($res);
3200
        if (0 == $num) {
3201
            return false;
3202
        }
3203
        $list = [];
3204
        while ($row = Database::fetch_array($res)) {
3205
            $list[$row['id']] = $row['api_key'];
3206
        }
3207
3208
        return $list;
3209
    }
3210
3211
    /**
3212
     * Adds a new API key to the users' account.
3213
     *
3214
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
3215
     * @param string $api_service
3216
     *
3217
     * @return bool True on success, false on failure
3218
     */
3219
    public static function add_api_key($user_id = null, $api_service = 'default')
3220
    {
3221
        if ($user_id != strval(intval($user_id))) {
3222
            return false;
3223
        }
3224
        if (empty($user_id)) {
3225
            $user_id = api_get_user_id();
3226
        }
3227
        if (false === $user_id) {
3228
            return false;
3229
        }
3230
        $service_name = Database::escape_string($api_service);
3231
        if (false === is_string($service_name)) {
3232
            return false;
3233
        }
3234
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3235
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
3236
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
3237
        $res = Database::query($sql);
3238
        if (false === $res) {
3239
            return false;
3240
        } //error during query
3241
        $num = Database::insert_id();
3242
3243
        return 0 == $num ? false : $num;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 0 == $num ? false : $num also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
3244
    }
3245
3246
    /**
3247
     * Deletes an API key from the user's account.
3248
     *
3249
     * @param   int     API key's internal ID
3250
     *
3251
     * @return bool True on success, false on failure
3252
     */
3253
    public static function delete_api_key($key_id)
3254
    {
3255
        if ($key_id != strval(intval($key_id))) {
3256
            return false;
3257
        }
3258
        if (false === $key_id) {
3259
            return false;
3260
        }
3261
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3262
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
3263
        $res = Database::query($sql);
3264
        if (false === $res) {
3265
            return false;
3266
        } //error during query
3267
        $num = Database::num_rows($res);
3268
        if (1 !== $num) {
3269
            return false;
3270
        }
3271
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
3272
        $res = Database::query($sql);
3273
        if (false === $res) {
3274
            return false;
3275
        } //error during query
3276
3277
        return true;
3278
    }
3279
3280
    /**
3281
     * Regenerate an API key from the user's account.
3282
     *
3283
     * @param   int     user ID (defaults to the results of api_get_user_id())
3284
     * @param   string  API key's internal ID
3285
     *
3286
     * @return int num
3287
     */
3288
    public static function update_api_key($user_id, $api_service)
3289
    {
3290
        if ($user_id != strval(intval($user_id))) {
3291
            return false;
3292
        }
3293
        if (false === $user_id) {
3294
            return false;
3295
        }
3296
        $service_name = Database::escape_string($api_service);
3297
        if (false === is_string($service_name)) {
3298
            return false;
3299
        }
3300
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3301
        $sql = "SELECT id FROM $t_api
3302
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3303
        $res = Database::query($sql);
3304
        $num = Database::num_rows($res);
3305
        if (1 == $num) {
3306
            $id_key = Database::fetch_assoc($res);
3307
            self::delete_api_key($id_key['id']);
3308
            $num = self::add_api_key($user_id, $api_service);
3309
        } elseif (0 == $num) {
3310
            $num = self::add_api_key($user_id, $api_service);
3311
        }
3312
3313
        return $num;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $num also could return the type boolean which is incompatible with the documented return type integer.
Loading history...
3314
    }
3315
3316
    /**
3317
     * @param   int     user ID (defaults to the results of api_get_user_id())
3318
     * @param   string    API key's internal ID
3319
     *
3320
     * @return int row ID, or return false if not found
3321
     */
3322
    public static function get_api_key_id($user_id, $api_service)
3323
    {
3324
        if ($user_id != strval(intval($user_id))) {
3325
            return false;
3326
        }
3327
        if (false === $user_id) {
3328
            return false;
3329
        }
3330
        if (empty($api_service)) {
3331
            return false;
3332
        }
3333
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3334
        $api_service = Database::escape_string($api_service);
3335
        $sql = "SELECT id FROM $t_api
3336
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3337
        $res = Database::query($sql);
3338
        if (Database::num_rows($res) < 1) {
3339
            return false;
3340
        }
3341
        $row = Database::fetch_assoc($res);
3342
3343
        return $row['id'];
3344
    }
3345
3346
    /**
3347
     * Checks if a user_id is platform admin.
3348
     *
3349
     * @param   int user ID
3350
     *
3351
     * @return bool True if is admin, false otherwise
3352
     *
3353
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
3354
     */
3355
    public static function is_admin($user_id)
3356
    {
3357
        $user_id = (int) $user_id;
3358
        if (empty($user_id)) {
3359
            return false;
3360
        }
3361
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3362
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3363
        $res = Database::query($sql);
3364
3365
        return 1 === Database::num_rows($res);
3366
    }
3367
3368
    /**
3369
     * Get the total count of users.
3370
     *
3371
     * @param ?int $status Status of users to be counted
3372
     * @param ?int $access_url_id Access URL ID (optional)
3373
     * @param ?int $active
3374
     *
3375
     * @return mixed Number of users or false on error
3376
     * @throws \Doctrine\DBAL\Exception
3377
     */
3378
    public static function get_number_of_users(
3379
        ?int $status = 0,
3380
        ?int $access_url_id = 1,
3381
        ?int $active = null,
3382
        ?string $dateFrom = null,
3383
        ?string $dateUntil = null
3384
    ): mixed {
3385
        $tableUser = Database::get_main_table(TABLE_MAIN_USER);
3386
        $tableAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3387
3388
        if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

3388
        if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
3389
            $sql = "SELECT count(u.id)
3390
                    FROM $tableUser u
3391
                    INNER JOIN $tableAccessUrlRelUser url_user
3392
                    ON (u.id = url_user.user_id)
3393
                    WHERE url_user.access_url_id = $access_url_id
3394
            ";
3395
        } else {
3396
            $sql = "SELECT count(u.id)
3397
                    FROM $tableUser u
3398
                    WHERE 1 = 1 ";
3399
        }
3400
3401
        $status = (int) $status;
3402
        if (!empty($status) && $status > 0) {
3403
            $sql .= " AND u.status = $status ";
3404
        }
3405
3406
        if (isset($active)) {
3407
            $active = (int) $active;
3408
            $sql .= " AND u.active = $active ";
3409
        }
3410
3411
        if (!empty($dateFrom)) {
3412
            $dateFrom = api_get_utc_datetime("$dateFrom 00:00:00");
3413
            $sql .= " AND u.registration_date >= '$dateFrom' ";
3414
        }
3415
        if (!empty($dateUntil)) {
3416
            $dateUntil = api_get_utc_datetime("$dateUntil 23:59:59");
3417
            $sql .= " AND u.registration_date <= '$dateUntil' ";
3418
        }
3419
3420
        $res = Database::query($sql);
3421
        if (1 === Database::num_rows($res)) {
3422
            return (int) Database::result($res, 0, 0);
3423
        }
3424
3425
        return false;
3426
    }
3427
3428
    /**
3429
     * Gets the tags of a specific field_id
3430
     * USER TAGS.
3431
     *
3432
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
3433
     *
3434
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
3435
     *    Called it "books" for example.
3436
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
3437
     * 3. All the tags are registered in the user_tag table and the relationship between user and tags is in the user_rel_tag table
3438
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
3439
     * 5. Test and enjoy.
3440
     *
3441
     * @param string $tag
3442
     * @param int    $field_id      field_id
3443
     * @param string $return_format how we are going to result value in array or in a string (json)
3444
     * @param $limit
3445
     *
3446
     * @return mixed
3447
     */
3448
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
3449
    {
3450
        // database table definition
3451
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3452
        $field_id = (int) $field_id;
3453
        $limit = (int) $limit;
3454
        $tag = trim(Database::escape_string($tag));
3455
3456
        // all the information of the field
3457
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
3458
                WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag LIMIT $limit";
3459
        $result = Database::query($sql);
3460
        $return = [];
3461
        if (Database::num_rows($result) > 0) {
3462
            while ($row = Database::fetch_assoc($result)) {
3463
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
3464
            }
3465
        }
3466
        if ('json' === $return_format) {
3467
            $return = json_encode($return);
3468
        }
3469
3470
        return $return;
3471
    }
3472
3473
    /**
3474
     * @param int $field_id
3475
     * @param int $limit
3476
     *
3477
     * @return array
3478
     */
3479
    public static function get_top_tags($field_id, $limit = 100)
3480
    {
3481
        // database table definition
3482
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3483
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3484
        $field_id = (int) $field_id;
3485
        $limit = (int) $limit;
3486
        // all the information of the field
3487
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
3488
                INNER JOIN $table_user_tag ut
3489
                ON (ut.id = uv.tag_id)
3490
                WHERE field_id = $field_id
3491
                GROUP BY tag_id
3492
                ORDER BY count DESC
3493
                LIMIT $limit";
3494
        $result = Database::query($sql);
3495
        $return = [];
3496
        if (Database::num_rows($result) > 0) {
3497
            while ($row = Database::fetch_assoc($result)) {
3498
                $return[] = $row;
3499
            }
3500
        }
3501
3502
        return $return;
3503
    }
3504
3505
    /**
3506
     * Get user's tags.
3507
     *
3508
     * @param int $user_id
3509
     * @param int $field_id
3510
     *
3511
     * @return array
3512
     */
3513
    public static function get_user_tags($user_id, $field_id)
3514
    {
3515
        // database table definition
3516
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3517
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3518
        $field_id = (int) $field_id;
3519
        $user_id = (int) $user_id;
3520
3521
        // all the information of the field
3522
        $sql = "SELECT ut.id, tag, count
3523
                FROM $table_user_tag ut
3524
                INNER JOIN $table_user_tag_values uv
3525
                ON (uv.tag_id=ut.ID)
3526
                WHERE field_id = $field_id AND user_id = $user_id
3527
                ORDER BY tag";
3528
        $result = Database::query($sql);
3529
        $return = [];
3530
        if (Database::num_rows($result) > 0) {
3531
            while ($row = Database::fetch_assoc($result)) {
3532
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3533
            }
3534
        }
3535
3536
        return $return;
3537
    }
3538
3539
    /**
3540
     * Get user's tags.
3541
     *
3542
     * @param int  $user_id
3543
     * @param int  $field_id
3544
     * @param bool $show_links show links or not
3545
     *
3546
     * @return string
3547
     */
3548
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
3549
    {
3550
        // database table definition
3551
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3552
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3553
        $field_id = (int) $field_id;
3554
        $user_id = (int) $user_id;
3555
3556
        // all the information of the field
3557
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
3558
                INNER JOIN $table_user_tag_values uv
3559
                ON (uv.tag_id = ut.id)
3560
                WHERE field_id = $field_id AND user_id = $user_id
3561
                ORDER BY tag";
3562
3563
        $result = Database::query($sql);
3564
        $return = [];
3565
        if (Database::num_rows($result) > 0) {
3566
            while ($row = Database::fetch_assoc($result)) {
3567
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3568
            }
3569
        }
3570
        $user_tags = $return;
3571
        $tag_tmp = [];
3572
        foreach ($user_tags as $tag) {
3573
            if ($show_links) {
3574
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
3575
                    $tag['tag'].
3576
                '</a>';
3577
            } else {
3578
                $tag_tmp[] = $tag['tag'];
3579
            }
3580
        }
3581
3582
        if (is_array($user_tags) && count($user_tags) > 0) {
3583
            return implode(', ', $tag_tmp);
3584
        } else {
3585
            return '';
3586
        }
3587
    }
3588
3589
    /**
3590
     * Get the tag id.
3591
     *
3592
     * @param int $tag
3593
     * @param int $field_id
3594
     *
3595
     * @return int returns 0 if fails otherwise the tag id
3596
     */
3597
    public static function get_tag_id($tag, $field_id)
3598
    {
3599
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3600
        $tag = Database::escape_string($tag);
3601
        $field_id = (int) $field_id;
3602
        //with COLLATE latin1_bin to select query in a case sensitive mode
3603
        $sql = "SELECT id FROM $table_user_tag
3604
                WHERE tag LIKE '$tag' AND field_id = $field_id";
3605
        $result = Database::query($sql);
3606
        if (Database::num_rows($result) > 0) {
3607
            $row = Database::fetch_assoc($result);
3608
3609
            return $row['id'];
3610
        } else {
3611
            return 0;
3612
        }
3613
    }
3614
3615
    /**
3616
     * Get the tag id.
3617
     *
3618
     * @param int $tag_id
3619
     * @param int $field_id
3620
     *
3621
     * @return int 0 if fails otherwise the tag id
3622
     */
3623
    public static function get_tag_id_from_id($tag_id, $field_id)
3624
    {
3625
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3626
        $tag_id = (int) $tag_id;
3627
        $field_id = (int) $field_id;
3628
        $sql = "SELECT id FROM $table_user_tag
3629
                WHERE id = '$tag_id' AND field_id = $field_id";
3630
        $result = Database::query($sql);
3631
        if (Database::num_rows($result) > 0) {
3632
            $row = Database::fetch_assoc($result);
3633
3634
            return $row['id'];
3635
        } else {
3636
            return false;
3637
        }
3638
    }
3639
3640
    /**
3641
     * Adds a user-tag value.
3642
     *
3643
     * @param mixed $tag
3644
     * @param int   $user_id
3645
     * @param int   $field_id field id of the tag
3646
     *
3647
     * @return bool True if the tag was inserted or updated. False otherwise.
3648
     *              The return value doesn't take into account *values* added to the tag.
3649
     *              Only the creation/update of the tag field itself.
3650
     */
3651
    public static function add_tag($tag, $user_id, $field_id)
3652
    {
3653
        // database table definition
3654
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3655
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3656
        $tag = trim(Database::escape_string($tag));
3657
        $user_id = (int) $user_id;
3658
        $field_id = (int) $field_id;
3659
3660
        $tag_id = self::get_tag_id($tag, $field_id);
3661
3662
        /* IMPORTANT
3663
         *  @todo we don't create tags with numbers
3664
         *
3665
         */
3666
3667
        //this is a new tag
3668
        if (0 == $tag_id) {
3669
            //the tag doesn't exist
3670
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
3671
            Database::query($sql);
3672
            $last_insert_id = Database::insert_id();
3673
        } else {
3674
            //the tag exists we update it
3675
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
3676
            Database::query($sql);
3677
            $last_insert_id = $tag_id;
3678
        }
3679
3680
        if (!empty($last_insert_id) && (0 != $last_insert_id)) {
3681
            //we insert the relationship user-tag
3682
            $sql = "SELECT tag_id FROM $table_user_tag_values
3683
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
3684
            $result = Database::query($sql);
3685
            //if the relationship does not exist we create it
3686
            if (0 == Database::num_rows($result)) {
3687
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
3688
                Database::query($sql);
3689
            }
3690
3691
            return true;
3692
        }
3693
3694
        return false;
3695
    }
3696
3697
    /**
3698
     * Deletes an user tag.
3699
     *
3700
     * @param int $user_id
3701
     * @param int $field_id
3702
     */
3703
    public static function delete_user_tags($user_id, $field_id)
3704
    {
3705
        // database table definition
3706
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3707
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3708
        $user_id = (int) $user_id;
3709
3710
        $tags = self::get_user_tags($user_id, $field_id);
3711
        if (is_array($tags) && count($tags) > 0) {
3712
            foreach ($tags as $key => $tag) {
3713
                if ($tag['count'] > '0') {
3714
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
3715
                    Database::query($sql);
3716
                }
3717
                $sql = "DELETE FROM $table_user_tag_values
3718
                        WHERE user_id = $user_id AND tag_id = $key";
3719
                Database::query($sql);
3720
            }
3721
        }
3722
    }
3723
3724
    /**
3725
     * Process the tag list comes from the UserManager::update_extra_field_value() function.
3726
     *
3727
     * @param array $tags     the tag list that will be added
3728
     * @param int   $user_id
3729
     * @param int   $field_id
3730
     *
3731
     * @return bool
3732
     */
3733
    public static function process_tags($tags, $user_id, $field_id)
3734
    {
3735
        // We loop the tags and add it to the DB
3736
        if (is_array($tags)) {
3737
            foreach ($tags as $tag) {
3738
                self::add_tag($tag, $user_id, $field_id);
3739
            }
3740
        } else {
3741
            self::add_tag($tags, $user_id, $field_id);
3742
        }
3743
3744
        return true;
3745
    }
3746
3747
    /**
3748
     * Returns a list of all administrators.
3749
     *
3750
     * @return array
3751
     */
3752
    public static function get_all_administrators()
3753
    {
3754
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3755
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
3756
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3757
        $access_url_id = api_get_current_access_url_id();
3758
        if (api_get_multiple_access_url()) {
3759
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3760
                    FROM $tbl_url_rel_user as url
3761
                    INNER JOIN $table_admin as admin
3762
                    ON (admin.user_id=url.user_id)
3763
                    INNER JOIN $table_user u
3764
                    ON (u.id=admin.user_id)
3765
                    WHERE access_url_id ='".$access_url_id."'";
3766
        } else {
3767
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3768
                    FROM $table_admin as admin
3769
                    INNER JOIN $table_user u
3770
                    ON (u.id=admin.user_id)";
3771
        }
3772
        $sql .= !str_contains($sql, 'WHERE') ? ' WHERE u.active <> '.USER_SOFT_DELETED : ' AND u.active <> '.USER_SOFT_DELETED;
3773
        $result = Database::query($sql);
3774
        $return = [];
3775
        if (Database::num_rows($result) > 0) {
3776
            while ($row = Database::fetch_assoc($result)) {
3777
                $return[$row['user_id']] = $row;
3778
            }
3779
        }
3780
3781
        return $return;
3782
    }
3783
3784
    /**
3785
     * Search an user (tags, first name, last name and email ).
3786
     *
3787
     * @param string $tag
3788
     * @param int    $field_id        field id of the tag
3789
     * @param int    $from            where to start in the query
3790
     * @param int    $number_of_items
3791
     * @param bool   $getCount        get count or not
3792
     *
3793
     * @return array
3794
     */
3795
    public static function get_all_user_tags(
3796
        $tag,
3797
        $field_id = 0,
3798
        $from = 0,
3799
        $number_of_items = 10,
3800
        $getCount = false
3801
    ) {
3802
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
3803
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3804
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3805
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3806
3807
        $field_id = intval($field_id);
3808
        $from = intval($from);
3809
        $number_of_items = intval($number_of_items);
3810
3811
        $where_field = "";
3812
        $where_extra_fields = self::get_search_form_where_extra_fields();
3813
        if (0 != $field_id) {
3814
            $where_field = " field_id = $field_id AND ";
3815
        }
3816
3817
        // all the information of the field
3818
        if ($getCount) {
3819
            $select = "SELECT count(DISTINCT u.id) count";
3820
        } else {
3821
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, tag, picture_uri";
3822
        }
3823
3824
        $sql = " $select
3825
                FROM $user_table u
3826
                INNER JOIN $access_url_rel_user_table url_rel_user
3827
                ON (u.id = url_rel_user.user_id)
3828
                LEFT JOIN $table_user_tag_values uv
3829
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
3830
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
3831
                WHERE
3832
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
3833
                    (
3834
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
3835
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
3836
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
3837
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
3838
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
3839
                     )
3840
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
3841
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
3842
3843
        $keyword_active = true;
3844
        // only active users
3845
        if ($keyword_active) {
3846
            $sql .= " AND u.active='1'";
3847
        }
3848
        // avoid anonymous
3849
        $sql .= " AND u.status <> 6 ";
3850
        $sql .= " ORDER BY username";
3851
        $sql .= " LIMIT $from , $number_of_items";
3852
3853
        $result = Database::query($sql);
3854
        $return = [];
3855
3856
        if (Database::num_rows($result) > 0) {
3857
            if ($getCount) {
3858
                $row = Database::fetch_assoc($result);
3859
3860
                return $row['count'];
3861
            }
3862
            while ($row = Database::fetch_assoc($result)) {
3863
                $return[$row['id']] = $row;
3864
            }
3865
        }
3866
3867
        return $return;
3868
    }
3869
3870
    /**
3871
     * Get extra filterable user fields (only type select).
3872
     *
3873
     * @return array Array of extra fields as [int => ['name' => ..., 'variable' => ..., 'data' => ...]] (
3874
     *               or empty array if no extra field)
3875
     */
3876
    public static function getExtraFilterableFields()
3877
    {
3878
        $extraFieldList = self::get_extra_fields();
3879
        $fields = [];
3880
        if (is_array($extraFieldList)) {
3881
            foreach ($extraFieldList as $extraField) {
3882
                // If is enabled to filter and is a "<select>" field type
3883
                if (1 == $extraField[8] && 4 == $extraField[2]) {
3884
                    $fields[] = [
3885
                        'name' => $extraField[3],
3886
                        'variable' => $extraField[1],
3887
                        'data' => $extraField[9],
3888
                    ];
3889
                }
3890
            }
3891
        }
3892
3893
        return $fields;
3894
    }
3895
3896
    /**
3897
     * Get extra where clauses for finding users based on extra filterable user fields (type select).
3898
     *
3899
     * @return string With AND clauses based on user's ID which have the values to search in extra user fields
3900
     *                (or empty if no extra field exists)
3901
     */
3902
    public static function get_search_form_where_extra_fields()
3903
    {
3904
        $useExtraFields = false;
3905
        $extraFields = self::getExtraFilterableFields();
3906
        $extraFieldResult = [];
3907
        if (is_array($extraFields) && count($extraFields) > 0) {
3908
            foreach ($extraFields as $extraField) {
3909
                $varName = 'field_'.$extraField['variable'];
3910
                if (self::is_extra_field_available($extraField['variable'])) {
3911
                    if (isset($_GET[$varName]) && '0' != $_GET[$varName]) {
3912
                        $useExtraFields = true;
3913
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
3914
                            $extraField['variable'],
3915
                            $_GET[$varName]
3916
                        );
3917
                    }
3918
                }
3919
            }
3920
        }
3921
3922
        if ($useExtraFields) {
3923
            $finalResult = [];
3924
            if (count($extraFieldResult) > 1) {
3925
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
3926
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
3927
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
3928
                    }
3929
                }
3930
            } else {
3931
                $finalResult = $extraFieldResult[0];
3932
            }
3933
3934
            if (is_array($finalResult) && count($finalResult) > 0) {
3935
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
3936
            } else {
3937
                //no results
3938
                $whereFilter = " AND u.id  = -1 ";
3939
            }
3940
3941
            return $whereFilter;
3942
        }
3943
3944
        return '';
3945
    }
3946
3947
    /**
3948
     * Show the search form.
3949
     *
3950
     * @param string $query the value of the search box
3951
     *
3952
     * @throws Exception
3953
     *
3954
     * @return string HTML form
3955
     */
3956
    public static function get_search_form($query, $defaultParams = [])
3957
    {
3958
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
3959
        $form = new FormValidator(
3960
            'search_user',
3961
            'get',
3962
            api_get_path(WEB_PATH).'main/social/search.php',
3963
            '',
3964
            [],
3965
            FormValidator::LAYOUT_HORIZONTAL
3966
        );
3967
3968
        $query = Security::remove_XSS($query);
3969
3970
        if (!empty($query)) {
3971
            $form->addHeader(get_lang('Results and feedback').' "'.$query.'"');
0 ignored issues
show
Bug introduced by
Are you sure $query of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3971
            $form->addHeader(get_lang('Results and feedback').' "'./** @scrutinizer ignore-type */ $query.'"');
Loading history...
3972
        }
3973
3974
        $form->addText(
3975
            'q',
3976
            get_lang('Users, Groups'),
3977
            false,
3978
            [
3979
                'id' => 'q',
3980
            ]
3981
        );
3982
        $options = [
3983
            0 => get_lang('Select'),
3984
            1 => get_lang('User'),
3985
            2 => get_lang('Group'),
3986
        ];
3987
        $form->addSelect(
3988
            'search_type',
3989
            get_lang('Type'),
3990
            $options,
3991
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
3992
        );
3993
3994
        // Extra fields
3995
        $extraFields = self::getExtraFilterableFields();
3996
        $defaults = [];
3997
        if (is_array($extraFields) && count($extraFields) > 0) {
3998
            foreach ($extraFields as $extraField) {
3999
                $varName = 'field_'.$extraField['variable'];
4000
                $options = [
4001
                    0 => get_lang('Select'),
4002
                ];
4003
                foreach ($extraField['data'] as $option) {
4004
                    if (isset($_GET[$varName])) {
4005
                        if ($_GET[$varName] == $option[1]) {
4006
                            $defaults[$option[1]] = true;
4007
                        }
4008
                    }
4009
4010
                    $options[$option[1]] = $option[1];
4011
                }
4012
                $form->addSelect($varName, $extraField['name'], $options);
4013
            }
4014
        }
4015
4016
        $defaults['search_type'] = (int) $searchType;
4017
        $defaults['q'] = $query;
4018
4019
        if (!empty($defaultParams)) {
4020
            $defaults = array_merge($defaults, $defaultParams);
4021
        }
4022
        $form->setDefaults($defaults);
4023
        $form->addButtonSearch(get_lang('Search'));
4024
4025
        $js = '<script>
4026
        extra_field_toogle();
4027
        function extra_field_toogle() {
4028
            if (jQuery("select[name=search_type]").val() != "1") {
4029
                jQuery(".extra_field").hide();
4030
            } else {
4031
                jQuery(".extra_field").show();
4032
            }
4033
        }
4034
        </script>';
4035
4036
        return $js.$form->returnForm();
4037
    }
4038
4039
    /**
4040
     * @param int $userId
4041
     *
4042
     * @return array
4043
     */
4044
    public static function getDrhListFromUser($userId)
4045
    {
4046
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4047
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4048
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4049
        $userId = (int) $userId;
4050
4051
        $orderBy = null;
4052
        if (api_is_western_name_order()) {
4053
            $orderBy .= ' ORDER BY firstname, lastname ';
4054
        } else {
4055
            $orderBy .= ' ORDER BY lastname, firstname ';
4056
        }
4057
4058
        $sql = "SELECT u.id, username, u.firstname, u.lastname
4059
                FROM $tblUser u
4060
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
4061
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
4062
                WHERE
4063
                    access_url_id = ".api_get_current_access_url_id()." AND
4064
                    uru.user_id = '$userId' AND
4065
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4066
                    $orderBy
4067
                ";
4068
        $result = Database::query($sql);
4069
4070
        return Database::store_result($result);
4071
    }
4072
4073
    /**
4074
     * get users followed by human resource manager.
4075
     *
4076
     * @param int    $userId
4077
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
4078
     * @param bool   $getOnlyUserId
4079
     * @param bool   $getSql
4080
     * @param bool   $getCount
4081
     * @param int    $from
4082
     * @param int    $numberItems
4083
     * @param int    $column
4084
     * @param string $direction
4085
     * @param int    $active
4086
     * @param string $lastConnectionDate
4087
     *
4088
     * @return array users
4089
     */
4090
    public static function get_users_followed_by_drh(
4091
        $userId,
4092
        $userStatus = 0,
4093
        $getOnlyUserId = false,
4094
        $getSql = false,
4095
        $getCount = false,
4096
        $from = null,
4097
        $numberItems = null,
4098
        $column = null,
4099
        $direction = null,
4100
        $active = null,
4101
        $lastConnectionDate = null
4102
    ) {
4103
        return self::getUsersFollowedByUser(
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::getUsersFol...astConnectionDate, DRH) also could return the type string which is incompatible with the documented return type array.
Loading history...
4104
            $userId,
4105
            $userStatus,
4106
            $getOnlyUserId,
4107
            $getSql,
4108
            $getCount,
4109
            $from,
4110
            $numberItems,
4111
            $column,
4112
            $direction,
4113
            $active,
4114
            $lastConnectionDate,
4115
            DRH
4116
        );
4117
    }
4118
4119
    /**
4120
     * Get users followed by human resource manager.
4121
     *
4122
     * @param int    $userId
4123
     * @param int    $userStatus             Filter users by status (STUDENT, COURSEMANAGER, etc)
4124
     * @param bool   $getOnlyUserId
4125
     * @param bool   $getSql
4126
     * @param bool   $getCount
4127
     * @param int    $from
4128
     * @param int    $numberItems
4129
     * @param int    $column
4130
     * @param string $direction
4131
     * @param int    $active
4132
     * @param string $lastConnectionDate
4133
     * @param int    $status                 the function is called by who? COURSEMANAGER, DRH?
4134
     * @param string $keyword
4135
     * @param bool   $checkSessionVisibility
4136
     *
4137
     * @return mixed Users list (array) or the SQL query if $getSQL was set to true
4138
     */
4139
    public static function getUsersFollowedByUser(
4140
        $userId,
4141
        $userStatus = null,
4142
        $getOnlyUserId = false,
4143
        $getSql = false,
4144
        $getCount = false,
4145
        $from = null,
4146
        $numberItems = null,
4147
        $column = null,
4148
        $direction = null,
4149
        $active = null,
4150
        $lastConnectionDate = null,
4151
        $status = null,
4152
        $keyword = null,
4153
        $checkSessionVisibility = false
4154
    ) {
4155
        // Database Table Definitions
4156
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4157
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4158
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4159
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4160
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4161
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4162
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4163
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4164
4165
        $userId = (int) $userId;
4166
        $limitCondition = '';
4167
4168
        if (isset($from) && isset($numberItems)) {
4169
            $from = (int) $from;
4170
            $numberItems = (int) $numberItems;
4171
            $limitCondition = "LIMIT $from, $numberItems";
4172
        }
4173
4174
        $column = Database::escape_string($column);
4175
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
4176
4177
        $userConditions = '';
4178
        if (!empty($userStatus)) {
4179
            $userConditions .= ' AND u.status = '.intval($userStatus);
4180
        }
4181
4182
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
4183
        if ($getOnlyUserId) {
4184
            $select = " SELECT DISTINCT u.id user_id";
4185
        }
4186
4187
        $masterSelect = "SELECT DISTINCT * FROM ";
4188
4189
        if ($getCount) {
4190
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
4191
            $select = " SELECT DISTINCT(u.id) user_id";
4192
        }
4193
4194
        if (!is_null($active)) {
4195
            $active = intval($active);
4196
            $userConditions .= " AND u.active = $active ";
4197
        }
4198
4199
        if (!empty($keyword)) {
4200
            $keyword = trim(Database::escape_string($keyword));
4201
            $keywordParts = array_filter(explode(' ', $keyword));
4202
            $extraKeyword = '';
4203
            if (!empty($keywordParts)) {
4204
                $keywordPartsFixed = Database::escape_string(implode('%', $keywordParts));
4205
                if (!empty($keywordPartsFixed)) {
4206
                    $extraKeyword .= " OR
4207
                        CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keywordPartsFixed%' OR
4208
                        CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keywordPartsFixed%' ";
4209
                }
4210
            }
4211
            $userConditions .= " AND (
4212
                u.username LIKE '%$keyword%' OR
4213
                u.firstname LIKE '%$keyword%' OR
4214
                u.lastname LIKE '%$keyword%' OR
4215
                u.official_code LIKE '%$keyword%' OR
4216
                u.email LIKE '%$keyword%' OR
4217
                CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keyword%' OR
4218
                CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keyword%'
4219
                $extraKeyword
4220
            )";
4221
        }
4222
4223
        if (!empty($lastConnectionDate)) {
4224
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
4225
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
4226
        }
4227
4228
        $sessionConditionsCoach = null;
4229
        $dateCondition = '';
4230
        $drhConditions = null;
4231
        $teacherSelect = null;
4232
        $urlId = api_get_current_access_url_id();
4233
4234
        switch ($status) {
4235
            case DRH:
4236
                $drhConditions .= " AND
4237
                    friend_user_id = '$userId' AND
4238
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4239
                ";
4240
                break;
4241
            case COURSEMANAGER:
4242
                $drhConditions .= " AND
4243
                    friend_user_id = '$userId' AND
4244
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4245
                ";
4246
4247
                $sessionConditionsTeacher = " AND
4248
                    (scu.status = ".SessionEntity::COURSE_COACH." AND scu.user_id = '$userId')
4249
                ";
4250
4251
                if ($checkSessionVisibility) {
4252
                    $today = api_strtotime('now', 'UTC');
4253
                    $today = date('Y-m-d', $today);
4254
                    $dateCondition = "
4255
                        AND
4256
                        (
4257
                            (s.access_start_date <= '$today' AND '$today' <= s.access_end_date) OR
4258
                            (s.access_start_date IS NULL AND s.access_end_date IS NULL) OR
4259
                            (s.access_start_date <= '$today' AND s.access_end_date IS NULL) OR
4260
                            ('$today' <= s.access_end_date AND s.access_start_date IS NULL)
4261
                        )
4262
					";
4263
                }
4264
4265
                // Use $tbl_session_rel_course_rel_user instead of $tbl_session_rel_user
4266
                /*
4267
                INNER JOIN $tbl_session_rel_user sru
4268
                ON (sru.user_id = u.id)
4269
                INNER JOIN $tbl_session_rel_course_rel_user scu
4270
                ON (scu.user_id = u.id AND scu.c_id IS NOT NULL AND visibility = 1)*/
4271
                $teacherSelect =
4272
                "UNION ALL (
4273
                        $select
4274
                        FROM $tbl_user u
4275
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
4276
                        WHERE
4277
                            (
4278
                                sru.session_id IN (
4279
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
4280
                                    $tbl_session_rel_access_url session_rel_access_rel_user
4281
                                    ON session_rel_access_rel_user.session_id = s.id
4282
                                    INNER JOIN $tbl_session_rel_user sru ON s.id = sru.session_id
4283
                                    WHERE access_url_id = ".$urlId."
4284
                                        AND (sru.relation_type = ".SessionEntity::GENERAL_COACH."
4285
                                        AND sru.user_id = $userId)
4286
                                ) OR sru.session_id IN (
4287
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
4288
                                    INNER JOIN $tbl_session_rel_access_url url
4289
                                    ON (url.session_id = s.id)
4290
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
4291
                                    ON (scu.session_id = s.id)
4292
                                    WHERE access_url_id = ".$urlId."
4293
                                    $sessionConditionsTeacher
4294
                                    $dateCondition
4295
                                )
4296
                            )
4297
                            $userConditions
4298
                    )
4299
                    UNION ALL(
4300
                        $select
4301
                        FROM $tbl_user u
4302
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
4303
                        WHERE cu.c_id IN (
4304
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
4305
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
4306
                        )
4307
                        $userConditions
4308
                    )"
4309
                ;
4310
                break;
4311
            case STUDENT_BOSS:
4312
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".UserRelUser::USER_RELATION_TYPE_BOSS;
4313
                break;
4314
            case HRM_REQUEST:
4315
                $drhConditions .= " AND
4316
                    friend_user_id = '$userId' AND
4317
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_HRM_REQUEST."'
4318
                ";
4319
                break;
4320
        }
4321
4322
        $join = null;
4323
        $sql = " $masterSelect
4324
                (
4325
                    (
4326
                        $select
4327
                        FROM $tbl_user u
4328
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
4329
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
4330
                        $join
4331
                        WHERE
4332
                            access_url_id = ".$urlId."
4333
                            $drhConditions
4334
                            $userConditions
4335
                    )
4336
                    $teacherSelect
4337
4338
                ) as t1";
4339
4340
        if ($getSql) {
4341
            return $sql;
4342
        }
4343
        if ($getCount) {
4344
            $result = Database::query($sql);
4345
            $row = Database::fetch_array($result);
4346
4347
            return $row['count'];
4348
        }
4349
4350
        $orderBy = null;
4351
        if (false == $getOnlyUserId) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
4352
            if (api_is_western_name_order()) {
4353
                $orderBy .= " ORDER BY firstname, lastname ";
4354
            } else {
4355
                $orderBy .= " ORDER BY lastname, firstname ";
4356
            }
4357
4358
            if (!empty($column) && !empty($direction)) {
4359
                // Fixing order due the UNIONs
4360
                $column = str_replace('u.', '', $column);
4361
                $orderBy = " ORDER BY `$column` $direction ";
4362
            }
4363
        }
4364
4365
        $sql .= $orderBy;
4366
        $sql .= $limitCondition;
4367
4368
        $result = Database::query($sql);
4369
        $users = [];
4370
        if (Database::num_rows($result) > 0) {
4371
            while ($row = Database::fetch_array($result)) {
4372
                $users[$row['user_id']] = $row;
4373
            }
4374
        }
4375
4376
        return $users;
4377
    }
4378
4379
    /**
4380
     * Subscribes users to human resource manager (Dashboard feature).
4381
     *
4382
     * @param int   $hr_dept_id
4383
     * @param array $users_id
4384
     * @param bool  $deleteOtherAssignedUsers
4385
     */
4386
    public static function subscribeUsersToHRManager(
4387
        $hr_dept_id,
4388
        $users_id,
4389
        $deleteOtherAssignedUsers = true
4390
    ): void {
4391
        self::subscribeUsersToUser(
4392
            $hr_dept_id,
4393
            $users_id,
4394
            UserRelUser::USER_RELATION_TYPE_RRHH,
4395
            false,
4396
            $deleteOtherAssignedUsers
4397
        );
4398
    }
4399
4400
    /**
4401
     * Register request to assign users to HRM.
4402
     *
4403
     * @param int   $hrmId   The HRM ID
4404
     * @param array $usersId The users IDs
4405
     */
4406
    public static function requestUsersToHRManager($hrmId, $usersId): void
4407
    {
4408
        self::subscribeUsersToUser(
4409
            $hrmId,
4410
            $usersId,
4411
            UserRelUser::USER_RELATION_TYPE_HRM_REQUEST,
4412
            false,
4413
            false
4414
        );
4415
    }
4416
4417
    /**
4418
     * Remove the requests for assign a user to a HRM.
4419
     *
4420
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
4421
     */
4422
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
4423
    {
4424
        $users = implode(', ', $usersId);
4425
        Database::getManager()
4426
            ->createQuery('
4427
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
4428
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
4429
            ')
4430
            ->execute(['hrm_id' => $hrmId, 'relation_type' => UserRelUser::USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
4431
    }
4432
4433
    /**
4434
     * Add subscribed users to a user by relation type.
4435
     *
4436
     * @param int   $userId                   The user id
4437
     * @param array $subscribedUsersId        The id of subscribed users
4438
     * @param int   $relationType             The relation type
4439
     * @param bool  $deleteUsersBeforeInsert
4440
     * @param bool  $deleteOtherAssignedUsers
4441
     */
4442
    public static function subscribeUsersToUser(
4443
        $userId,
4444
        $subscribedUsersId,
4445
        $relationType,
4446
        $deleteUsersBeforeInsert = false,
4447
        $deleteOtherAssignedUsers = true
4448
    ): void {
4449
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4450
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4451
4452
        $userId = (int) $userId;
4453
        $relationType = (int) $relationType;
4454
4455
        if ($deleteOtherAssignedUsers) {
4456
            if (api_get_multiple_access_url()) {
4457
                // Deleting assigned users to hrm_id
4458
                $sql = "SELECT s.user_id
4459
                        FROM $userRelUserTable s
4460
                        INNER JOIN $userRelAccessUrlTable a
4461
                        ON (a.user_id = s.user_id)
4462
                        WHERE
4463
                            friend_user_id = $userId AND
4464
                            relation_type = $relationType AND
4465
                            access_url_id = ".api_get_current_access_url_id();
4466
            } else {
4467
                $sql = "SELECT user_id
4468
                        FROM $userRelUserTable
4469
                        WHERE
4470
                            friend_user_id = $userId AND
4471
                            relation_type = $relationType";
4472
            }
4473
            $result = Database::query($sql);
4474
4475
            if (Database::num_rows($result) > 0) {
4476
                while ($row = Database::fetch_array($result)) {
4477
                    $sql = "DELETE FROM $userRelUserTable
4478
                            WHERE
4479
                                user_id = {$row['user_id']} AND
4480
                                friend_user_id = $userId AND
4481
                                relation_type = $relationType";
4482
                    Database::query($sql);
4483
                }
4484
            }
4485
        }
4486
4487
        if ($deleteUsersBeforeInsert) {
4488
            $sql = "DELETE FROM $userRelUserTable
4489
                    WHERE
4490
                        user_id = $userId AND
4491
                        relation_type = $relationType";
4492
            Database::query($sql);
4493
        }
4494
4495
        // Inserting new user list.
4496
        if (is_array($subscribedUsersId)) {
4497
            foreach ($subscribedUsersId as $subscribedUserId) {
4498
                $subscribedUserId = (int) $subscribedUserId;
4499
                $sql = "SELECT id
4500
                        FROM $userRelUserTable
4501
                        WHERE
4502
                            user_id = $subscribedUserId AND
4503
                            friend_user_id = $userId AND
4504
                            relation_type = $relationType";
4505
4506
                $result = Database::query($sql);
4507
                $num = Database::num_rows($result);
4508
                if (0 === $num) {
4509
                    $userRelUser = (new UserRelUser())
4510
                        ->setUser(api_get_user_entity($subscribedUserId))
4511
                        ->setFriend(api_get_user_entity($userId))
4512
                        ->setRelationType($relationType)
4513
                    ;
4514
                    $em = Database::getManager();
4515
                    $em->persist($userRelUser);
4516
                    $em->flush();
4517
                }
4518
            }
4519
        }
4520
    }
4521
4522
    /**
4523
     * This function checks if a user is followed by provided human resources managers.
4524
     *
4525
     * @param int $user_id
4526
     * @param int $hr_dept_id Human resources manager
4527
     *
4528
     * @return bool
4529
     * @throws Exception
4530
     * @throws \Doctrine\DBAL\Exception
4531
     */
4532
    public static function is_user_followed_by_drh(int $user_id, int $hr_dept_id): bool
4533
    {
4534
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4535
        $result = false;
4536
4537
        $sql = "SELECT user_id FROM $tbl_user_rel_user
4538
                WHERE
4539
                    user_id = $user_id AND
4540
                    friend_user_id = $hr_dept_id AND
4541
                    relation_type = ".UserRelUser::USER_RELATION_TYPE_RRHH;
4542
        $rs = Database::query($sql);
4543
        if (Database::num_rows($rs) > 0) {
4544
            $result = true;
4545
        }
4546
4547
        return $result;
4548
    }
4549
4550
    /**
4551
     * Return the user id of teacher or session administrator.
4552
     *
4553
     * @param array $courseInfo
4554
     *
4555
     * @return int The user id, or 0 if the session ID was negative
4556
     * @throws Exception
4557
     * @throws \Doctrine\DBAL\Exception
4558
     */
4559
    public static function get_user_id_of_course_admin_or_session_admin(array $courseInfo): int
4560
    {
4561
        $session = api_get_session_id();
4562
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4563
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4564
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4565
4566
        if (empty($courseInfo)) {
4567
            return 0;
4568
        }
4569
4570
        $courseId = $courseInfo['real_id'];
4571
4572
        if (0 == $session) {
4573
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
4574
                    INNER JOIN '.$table_course_user.' ru
4575
                    ON ru.user_id = u.id
4576
                    WHERE
4577
                        ru.status = 1 AND
4578
                        ru.c_id = "'.$courseId.'" ';
4579
            $rs = Database::query($sql);
4580
            $num_rows = Database::num_rows($rs);
4581
            if (1 == $num_rows) {
4582
                $row = Database::fetch_array($rs);
4583
4584
                return (int) $row['uid'];
4585
            } else {
4586
                $my_num_rows = $num_rows;
4587
4588
                return (int) Database::result($rs, $my_num_rows - 1, 'uid');
4589
            }
4590
        } elseif ($session > 0) {
4591
            $sql = 'SELECT u.id as uid FROM '.$table_user.' u
4592
                    INNER JOIN '.$table_session_course_user.' sru
4593
                    ON sru.user_id = u.id
4594
                    WHERE
4595
                        sru.c_id = '.$courseId.' AND
4596
                        sru.status = '.SessionEntity::COURSE_COACH;
4597
            $rs = Database::query($sql);
4598
            if (Database::num_rows($rs) > 0) {
4599
                $row = Database::fetch_assoc($rs);
4600
4601
                return (int) $row['uid'];
4602
            }
4603
        }
4604
4605
        return 0;
4606
    }
4607
4608
    /**
4609
     * Determines if a user is a gradebook certified.
4610
     *
4611
     * @param int $cat_id  The category id of gradebook
4612
     * @param int $user_id The user id
4613
     *
4614
     * @return bool
4615
     */
4616
    public static function is_user_certified($cat_id, $user_id)
4617
    {
4618
        $cat_id = (int) $cat_id;
4619
        $user_id = (int) $user_id;
4620
4621
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4622
        $sql = 'SELECT path_certificate
4623
                FROM '.$table.'
4624
                WHERE
4625
                    cat_id = "'.$cat_id.'" AND
4626
                    user_id = "'.$user_id.'"';
4627
        $rs = Database::query($sql);
4628
        $row = Database::fetch_array($rs);
4629
4630
        if (!isset($row['path_certificate']) || '' == $row['path_certificate'] || is_null($row['path_certificate'])) {
4631
            return false;
4632
        }
4633
4634
        return true;
4635
    }
4636
4637
    /**
4638
     * Gets the info about a gradebook certificate for a user by course.
4639
     *
4640
     * @param array $course_info The course code
4641
     * @param int   $session_id
4642
     * @param int   $user_id     The user id
4643
     *
4644
     * @return array if there is not information return false
4645
     */
4646
    public static function get_info_gradebook_certificate($course_info, $session_id, $user_id)
4647
    {
4648
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4649
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
4650
        $session_id = (int) $session_id;
4651
        $user_id = (int) $user_id;
4652
        $courseId = $course_info['real_id'];
4653
4654
        if (empty($session_id)) {
4655
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
4656
        } else {
4657
            $session_condition = " AND session_id = $session_id";
4658
        }
4659
4660
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.'
4661
                WHERE cat_id = (
4662
                    SELECT id FROM '.$tbl_grade_category.'
4663
                    WHERE
4664
                        c_id = "'.$courseId.'" '.$session_condition.'
4665
                    LIMIT 1
4666
                ) AND user_id='.$user_id;
4667
4668
        $rs = Database::query($sql);
4669
        if (Database::num_rows($rs) > 0) {
4670
            $row = Database::fetch_assoc($rs);
4671
            $score = $row['score_certificate'];
4672
            $category_id = $row['cat_id'];
4673
            $cat = Category::load($category_id);
4674
            $displayscore = ScoreDisplay::instance();
4675
            if (isset($cat) && $displayscore->is_custom()) {
4676
                $grade = $displayscore->display_score(
4677
                    [$score, $cat[0]->get_weight()],
4678
                    SCORE_DIV_PERCENT_WITH_CUSTOM
4679
                );
4680
            } else {
4681
                $grade = $displayscore->display_score(
4682
                    [$score, $cat[0]->get_weight()]
4683
                );
4684
            }
4685
            $row['grade'] = $grade;
4686
4687
            return $row;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $row also could return the type boolean which is incompatible with the documented return type array.
Loading history...
4688
        }
4689
4690
        return false;
4691
    }
4692
4693
    /**
4694
     * This function check if the user is a coach inside session course.
4695
     *
4696
     * @param int $user_id    User id
4697
     * @param int $courseId
4698
     * @param int $session_id
4699
     *
4700
     * @return bool True if the user is a coach
4701
     */
4702
    public static function is_session_course_coach($user_id, $courseId, $session_id)
4703
    {
4704
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4705
        // Protect data
4706
        $user_id = intval($user_id);
4707
        $courseId = intval($courseId);
4708
        $session_id = intval($session_id);
4709
        $result = false;
4710
4711
        $sql = "SELECT session_id FROM $table
4712
                WHERE
4713
                  session_id = $session_id AND
4714
                  c_id = $courseId AND
4715
                  user_id = $user_id AND
4716
                  status = ".SessionEntity::COURSE_COACH;
4717
        $res = Database::query($sql);
4718
4719
        if (Database::num_rows($res) > 0) {
4720
            $result = true;
4721
        }
4722
4723
        return $result;
4724
    }
4725
4726
    /**
4727
     * This function returns an icon path that represents the favicon of the website of which the url given.
4728
     * Defaults to the current Chamilo favicon.
4729
     *
4730
     * @param string $url1 URL of website where to look for favicon.ico
4731
     * @param string $url2 Optional second URL of website where to look for favicon.ico
4732
     *
4733
     * @return string Path of icon to load
4734
     */
4735
    public static function get_favicon_from_url($url1, $url2 = null)
4736
    {
4737
        $icon_link = '';
4738
        $url = $url1;
4739
        if (empty($url1)) {
4740
            $url = $url2;
4741
            if (empty($url)) {
4742
                $url = api_get_access_url(api_get_current_access_url_id());
4743
                $url = $url[0];
4744
            }
4745
        }
4746
        if (!empty($url)) {
4747
            $pieces = parse_url($url);
4748
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
4749
        }
4750
4751
        return $icon_link;
4752
    }
4753
4754
    public static function addUserAsAdmin(User $user)
4755
    {
4756
        $userId = $user->getId();
4757
4758
        if (!self::is_admin($userId)) {
4759
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
4760
            $sql = "INSERT INTO $table SET user_id = $userId";
4761
            Database::query($sql);
4762
        }
4763
4764
        $user->addRole('ROLE_ADMIN');
4765
        self::getRepository()->updateUser($user, true);
4766
    }
4767
4768
    public static function removeUserAdmin(User $user)
4769
    {
4770
        $userId = (int) $user->getId();
4771
        if (self::is_admin($userId)) {
4772
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
4773
            $sql = "DELETE FROM $table WHERE user_id = $userId";
4774
            Database::query($sql);
4775
            $user->removeRole('ROLE_ADMIN');
4776
            self::getRepository()->updateUser($user, true);
4777
        }
4778
    }
4779
4780
    /**
4781
     * @param string $from
4782
     * @param string $to
4783
     */
4784
    public static function update_all_user_languages($from, $to)
4785
    {
4786
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4787
        $from = Database::escape_string($from);
4788
        $to = Database::escape_string($to);
4789
4790
        if (!empty($to) && !empty($from)) {
4791
            $sql = "UPDATE $table_user SET language = '$to'
4792
                    WHERE language = '$from'";
4793
            Database::query($sql);
4794
        }
4795
    }
4796
4797
    /**
4798
     * Subscribe boss to students.
4799
     *
4800
     * @param int   $bossId                   The boss id
4801
     * @param array $usersId                  The users array
4802
     * @param bool  $deleteOtherAssignedUsers
4803
     */
4804
    public static function subscribeBossToUsers($bossId, $usersId, $deleteOtherAssignedUsers = true): void
4805
    {
4806
        self::subscribeUsersToUser(
4807
            $bossId,
4808
            $usersId,
4809
            UserRelUser::USER_RELATION_TYPE_BOSS,
4810
            false,
4811
            $deleteOtherAssignedUsers
4812
        );
4813
    }
4814
4815
    /**
4816
     * @param int $userId
4817
     *
4818
     * @return bool
4819
     */
4820
    public static function removeAllBossFromStudent($userId)
4821
    {
4822
        $userId = (int) $userId;
4823
4824
        if (empty($userId)) {
4825
            return false;
4826
        }
4827
4828
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4829
        $sql = "DELETE FROM $userRelUserTable
4830
                WHERE user_id = $userId AND relation_type = ".UserRelUser::USER_RELATION_TYPE_BOSS;
4831
        Database::query($sql);
4832
4833
        return true;
4834
    }
4835
4836
    /**
4837
     * Subscribe boss to students, if $bossList is empty then the boss list will be empty too.
4838
     *
4839
     * @param int   $studentId
4840
     * @param array $bossList
4841
     * @param bool  $sendNotification
4842
     *
4843
     * @return mixed Affected rows or false on failure
4844
     */
4845
    public static function subscribeUserToBossList(
4846
        $studentId,
4847
        $bossList,
4848
        $sendNotification = false
4849
    ) {
4850
        $inserted = 0;
4851
        if (!empty($bossList)) {
4852
            sort($bossList);
4853
            $studentId = (int) $studentId;
4854
            $studentInfo = api_get_user_info($studentId);
4855
4856
            if (empty($studentInfo)) {
4857
                return false;
4858
            }
4859
4860
            $previousBossList = self::getStudentBossList($studentId);
4861
            $previousBossList = !empty($previousBossList) ? array_column($previousBossList, 'boss_id') : [];
4862
            sort($previousBossList);
4863
4864
            // Boss list is the same, nothing changed.
4865
            if ($bossList == $previousBossList) {
4866
                return false;
4867
            }
4868
4869
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4870
            self::removeAllBossFromStudent($studentId);
4871
4872
            foreach ($bossList as $bossId) {
4873
                $bossId = (int) $bossId;
4874
                $bossInfo = api_get_user_info($bossId);
4875
4876
                if (empty($bossInfo)) {
4877
                    continue;
4878
                }
4879
4880
                $bossLanguage = $bossInfo['locale'];
4881
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
4882
                        VALUES ($studentId, $bossId, ".UserRelUser::USER_RELATION_TYPE_BOSS.")";
4883
                $insertId = Database::query($sql);
4884
4885
                if ($insertId) {
4886
                    if ($sendNotification) {
4887
                        $name = $studentInfo['complete_name'];
4888
                        $url = api_get_path(WEB_CODE_PATH).'my_space/myStudents.php?student='.$studentId;
4889
                        $url = Display::url($url, $url);
4890
                        $subject = sprintf(get_lang('You have been assigned the learner %s', $bossLanguage), $name);
4891
                        $message = sprintf(get_lang('You have been assigned the learner %s with url %s', $bossLanguage), $name, $url);
4892
                        MessageManager::send_message_simple(
4893
                            $bossId,
4894
                            $subject,
4895
                            $message
4896
                        );
4897
                    }
4898
                    $inserted++;
4899
                }
4900
            }
4901
        } else {
4902
            self::removeAllBossFromStudent($studentId);
4903
        }
4904
4905
        return $inserted;
4906
    }
4907
4908
    /**
4909
     * Get users followed by student boss.
4910
     *
4911
     * @param int    $userId
4912
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
4913
     * @param bool   $getOnlyUserId
4914
     * @param bool   $getSql
4915
     * @param bool   $getCount
4916
     * @param int    $from
4917
     * @param int    $numberItems
4918
     * @param int    $column
4919
     * @param string $direction
4920
     * @param int    $active
4921
     * @param string $lastConnectionDate
4922
     *
4923
     * @return array users
4924
     */
4925
    public static function getUsersFollowedByStudentBoss(
4926
        $userId,
4927
        $userStatus = 0,
4928
        $getOnlyUserId = false,
4929
        $getSql = false,
4930
        $getCount = false,
4931
        $from = null,
4932
        $numberItems = null,
4933
        $column = null,
4934
        $direction = null,
4935
        $active = null,
4936
        $lastConnectionDate = null
4937
    ) {
4938
        return self::getUsersFollowedByUser(
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::getUsersFol...tionDate, STUDENT_BOSS) also could return the type string which is incompatible with the documented return type array.
Loading history...
4939
            $userId,
4940
            $userStatus,
4941
            $getOnlyUserId,
4942
            $getSql,
4943
            $getCount,
4944
            $from,
4945
            $numberItems,
4946
            $column,
4947
            $direction,
4948
            $active,
4949
            $lastConnectionDate,
4950
            STUDENT_BOSS
4951
        );
4952
    }
4953
4954
    /**
4955
     * @return array
4956
     */
4957
    public static function getOfficialCodeGrouped()
4958
    {
4959
        $user = Database::get_main_table(TABLE_MAIN_USER);
4960
        $sql = "SELECT DISTINCT official_code
4961
                FROM $user
4962
                GROUP BY official_code";
4963
        $result = Database::query($sql);
4964
        $values = Database::store_result($result, 'ASSOC');
4965
        $result = [];
4966
        foreach ($values as $value) {
4967
            $result[$value['official_code']] = $value['official_code'];
4968
        }
4969
4970
        return $result;
4971
    }
4972
4973
    /**
4974
     * @param string $officialCode
4975
     *
4976
     * @return array
4977
     */
4978
    public static function getUsersByOfficialCode($officialCode)
4979
    {
4980
        $user = Database::get_main_table(TABLE_MAIN_USER);
4981
        $officialCode = Database::escape_string($officialCode);
4982
4983
        $sql = "SELECT DISTINCT id
4984
                FROM $user
4985
                WHERE official_code = '$officialCode'
4986
                ";
4987
        $result = Database::query($sql);
4988
4989
        $users = [];
4990
        while ($row = Database::fetch_array($result)) {
4991
            $users[] = $row['id'];
4992
        }
4993
4994
        return $users;
4995
    }
4996
4997
    /**
4998
     * Calc the expended time (in seconds) by a user in a course.
4999
     *
5000
     * @param int    $userId    The user id
5001
     * @param int    $courseId  The course id
5002
     * @param int    $sessionId Optional. The session id
5003
     * @param string $from      Optional. From date
5004
     * @param string $until     Optional. Until date
5005
     *
5006
     * @return int The time
5007
     */
5008
    public static function getTimeSpentInCourses(
5009
        $userId,
5010
        $courseId,
5011
        $sessionId = 0,
5012
        $from = '',
5013
        $until = ''
5014
    ) {
5015
        $userId = (int) $userId;
5016
        $sessionId = (int) $sessionId;
5017
5018
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5019
        $whereConditions = [
5020
            'user_id = ? ' => $userId,
5021
            'AND c_id = ? ' => $courseId,
5022
            'AND session_id = ? ' => $sessionId,
5023
        ];
5024
5025
        if (!empty($from) && !empty($until)) {
5026
            $whereConditions["AND (login_course_date >= '?' "] = $from;
5027
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
5028
        }
5029
5030
        $trackResult = Database::select(
5031
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
5032
            $trackCourseAccessTable,
5033
            [
5034
                'where' => $whereConditions,
5035
            ],
5036
            'first'
5037
        );
5038
5039
        if (false != $trackResult) {
5040
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
5041
        }
5042
5043
        return 0;
5044
    }
5045
5046
    /**
5047
     * Get the boss user ID from a followed user id.
5048
     *
5049
     * @param $userId
5050
     *
5051
     * @return bool
5052
     */
5053
    public static function getFirstStudentBoss($userId)
5054
    {
5055
        $userId = (int) $userId;
5056
        if ($userId > 0) {
5057
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5058
            $row = Database::select(
5059
                'DISTINCT friend_user_id AS boss_id',
5060
                $userRelTable,
5061
                [
5062
                    'where' => [
5063
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
5064
                            $userId,
5065
                            UserRelUser::USER_RELATION_TYPE_BOSS,
5066
                        ],
5067
                    ],
5068
                ]
5069
            );
5070
            if (!empty($row)) {
5071
                return $row[0]['boss_id'];
5072
            }
5073
        }
5074
5075
        return false;
5076
    }
5077
5078
    /**
5079
     * Get the boss user ID from a followed user id.
5080
     *
5081
     * @param int $userId student id
5082
     *
5083
     * @return array
5084
     */
5085
    public static function getStudentBossList($userId)
5086
    {
5087
        $userId = (int) $userId;
5088
5089
        if ($userId > 0) {
5090
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5091
5092
            return Database::select(
5093
                'DISTINCT friend_user_id AS boss_id',
5094
                $userRelTable,
5095
                [
5096
                    'where' => [
5097
                        'user_id = ? AND relation_type = ? ' => [
5098
                            $userId,
5099
                            UserRelUser::USER_RELATION_TYPE_BOSS,
5100
                        ],
5101
                    ],
5102
                ]
5103
            );
5104
        }
5105
5106
        return [];
5107
    }
5108
5109
    /**
5110
     * @param int $bossId
5111
     * @param int $studentId
5112
     *
5113
     * @return bool
5114
     */
5115
    public static function userIsBossOfStudent($bossId, $studentId)
5116
    {
5117
        $result = false;
5118
        $bossList = self::getStudentBossList($studentId);
5119
        if (!empty($bossList)) {
5120
            $bossList = array_column($bossList, 'boss_id');
5121
            if (in_array($bossId, $bossList)) {
5122
                $result = true;
5123
            }
5124
        }
5125
5126
        return $result;
5127
    }
5128
5129
    /**
5130
     * Displays the name of the user and makes the link to the user profile.
5131
     *
5132
     * @param array $userInfo
5133
     *
5134
     * @return string
5135
     */
5136
    public static function getUserProfileLink($userInfo)
5137
    {
5138
        if (isset($userInfo) && isset($userInfo['user_id'])) {
5139
            return Display::url(
5140
                $userInfo['complete_name_with_username'],
5141
                $userInfo['profile_url']
5142
            );
5143
        }
5144
5145
        return get_lang('Anonymous');
5146
    }
5147
5148
    /**
5149
     * Get users whose name matches $firstname and $lastname.
5150
     *
5151
     * @param string $firstname Firstname to search
5152
     * @param string $lastname  Lastname to search
5153
     *
5154
     * @return array The user list
5155
     */
5156
    public static function getUsersByName($firstname, $lastname)
5157
    {
5158
        $firstname = Database::escape_string($firstname);
5159
        $lastname = Database::escape_string($lastname);
5160
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
5161
5162
        $sql = <<<SQL
5163
            SELECT id, username, lastname, firstname
5164
            FROM $userTable
5165
            WHERE
5166
                firstname LIKE '$firstname%' AND
5167
                lastname LIKE '$lastname%'
5168
SQL;
5169
        $result = Database::query($sql);
5170
        $users = [];
5171
        while ($resultData = Database::fetch_object($result)) {
5172
            $users[] = $resultData;
5173
        }
5174
5175
        return $users;
5176
    }
5177
5178
    /**
5179
     * @param int $optionSelected
5180
     *
5181
     * @return string
5182
     */
5183
    public static function getUserSubscriptionTab($optionSelected = 1)
5184
    {
5185
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
5186
        if (('true' === $allowAdmin && api_is_allowed_to_edit()) ||
5187
            api_is_platform_admin()
5188
        ) {
5189
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
5190
5191
            $headers = [
5192
                [
5193
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
5194
                    'content' => get_lang('Learners'),
5195
                ],
5196
                [
5197
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
5198
                    'content' => get_lang('Trainers'),
5199
                ],
5200
                /*[
5201
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
5202
                    'content' => get_lang('Learners'),
5203
                ],
5204
                [
5205
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
5206
                    'content' => get_lang('Trainers'),
5207
                ],*/
5208
                [
5209
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
5210
                    'content' => get_lang('Groups'),
5211
                ],
5212
                [
5213
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
5214
                    'content' => get_lang('Classes'),
5215
                ],
5216
            ];
5217
5218
            return Display::tabsOnlyLink($headers, $optionSelected);
5219
        }
5220
5221
        return '';
5222
    }
5223
5224
    /**
5225
     * Make sure this function is protected because it does NOT check password!
5226
     *
5227
     * This function defines globals.
5228
     *
5229
     * @param int  $userId
5230
     * @param bool $checkIfUserCanLoginAs
5231
     *
5232
     * @return bool
5233
     *
5234
     * @author Evie Embrechts
5235
     * @author Yannick Warnier <[email protected]>
5236
     */
5237
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
5238
    {
5239
        $userId = (int) $userId;
5240
        $userInfo = api_get_user_info($userId);
5241
5242
        // Check if the user is allowed to 'login_as'
5243
        $canLoginAs = true;
5244
        if ($checkIfUserCanLoginAs) {
5245
            $canLoginAs = api_can_login_as($userId);
5246
        }
5247
5248
        if (!$canLoginAs || empty($userInfo)) {
5249
            return false;
5250
        }
5251
5252
        if ($userId) {
5253
            $logInfo = [
5254
                'tool' => 'logout',
5255
                'tool_id' => 0,
5256
                'tool_id_detail' => 0,
5257
                'action' => '',
5258
                'info' => 'Change user (login as)',
5259
            ];
5260
            Event::registerLog($logInfo);
0 ignored issues
show
Bug introduced by
The method registerLog() does not exist on Event. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

5260
            Event::/** @scrutinizer ignore-call */ 
5261
                   registerLog($logInfo);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
5261
5262
            // Logout the current user
5263
            self::loginDelete(api_get_user_id());
5264
5265
            return true;
5266
5267
            Session::erase('_user');
0 ignored issues
show
Unused Code introduced by
ChamiloSession::erase('_user') is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
5268
            Session::erase('is_platformAdmin');
5269
            Session::erase('is_allowedCreateCourse');
5270
            Session::erase('_uid');
5271
5272
            // Cleaning session variables
5273
            $_user['firstName'] = $userInfo['firstname'];
5274
            $_user['lastName'] = $userInfo['lastname'];
5275
            $_user['mail'] = $userInfo['email'];
5276
            $_user['official_code'] = $userInfo['official_code'];
5277
            $_user['picture_uri'] = $userInfo['picture_uri'];
5278
            $_user['user_id'] = $userId;
5279
            $_user['id'] = $userId;
5280
            $_user['status'] = $userInfo['status'];
5281
5282
            // Filling session variables with new data
5283
            Session::write('_uid', $userId);
5284
            Session::write('_user', $userInfo);
5285
            Session::write('is_platformAdmin', (bool) self::is_admin($userId));
5286
            Session::write('is_allowedCreateCourse', 1 == $userInfo['status']);
5287
            // will be useful later to know if the user is actually an admin or not (example reporting)
5288
            Session::write('login_as', true);
5289
            $logInfo = [
5290
                'tool' => 'login',
5291
                'tool_id' => 0,
5292
                'tool_id_detail' => 0,
5293
                'info' => $userId,
5294
            ];
5295
            Event::registerLog($logInfo);
5296
5297
            return true;
5298
        }
5299
5300
        return false;
5301
    }
5302
5303
    /**
5304
     * Remove all login records from the track_e_online stats table,
5305
     * for the given user ID.
5306
     *
5307
     * @param int $userId User ID
5308
     */
5309
    public static function loginDelete($userId)
5310
    {
5311
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5312
        $userId = (int) $userId;
5313
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
5314
        Database::query($query);
5315
    }
5316
5317
    /**
5318
     * Login as first admin user registered in the platform.
5319
     *
5320
     * @return array
5321
     */
5322
    public static function logInAsFirstAdmin()
5323
    {
5324
        $adminList = self::get_all_administrators();
5325
5326
        if (!empty($adminList)) {
5327
            $userInfo = current($adminList);
5328
            if (!empty($userInfo)) {
5329
                $result = self::loginAsUser($userInfo['user_id'], false);
5330
                if ($result && api_is_platform_admin()) {
5331
                    return api_get_user_info();
0 ignored issues
show
Bug Best Practice introduced by
The expression return api_get_user_info() could also return false which is incompatible with the documented return type array. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
5332
                }
5333
            }
5334
        }
5335
5336
        return [];
5337
    }
5338
5339
    /**
5340
     * Check if user is teacher of a student based in their courses.
5341
     *
5342
     * @param $teacherId
5343
     * @param $studentId
5344
     *
5345
     * @return array
5346
     */
5347
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
5348
    {
5349
        $courses = CourseManager::getCoursesFollowedByUser(
5350
            $teacherId,
5351
            COURSEMANAGER
5352
        );
5353
        if (empty($courses)) {
5354
            return false;
5355
        }
5356
5357
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
5358
        if (empty($coursesFromUser)) {
5359
            return false;
5360
        }
5361
5362
        $coursesCodeList = array_column($courses, 'code');
5363
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
5364
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
5365
        $commonCourses = array_filter($commonCourses);
5366
5367
        if (!empty($commonCourses)) {
5368
            return $commonCourses;
5369
        }
5370
5371
        return [];
5372
    }
5373
5374
    /**
5375
     * @param int $teacherId
5376
     * @param int $studentId
5377
     *
5378
     * @return bool
5379
     */
5380
    public static function isTeacherOfStudent($teacherId, $studentId)
5381
    {
5382
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
5383
            $teacherId,
5384
            $studentId
5385
        );
5386
5387
        if (!empty($courses)) {
5388
            return true;
5389
        }
5390
5391
        return false;
5392
    }
5393
5394
    /**
5395
     * Send user confirmation mail.
5396
     *
5397
     * @throws Exception
5398
     */
5399
    public static function sendUserConfirmationMail(User $user)
5400
    {
5401
        $uniqueId = api_get_unique_id();
5402
        $user->setConfirmationToken($uniqueId);
5403
5404
        Database::getManager()->persist($user);
5405
        Database::getManager()->flush();
5406
5407
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
5408
5409
        // Check if the user was originally set for an automated subscription to a course or session
5410
        $courseCodeToRedirect = Session::read('course_redirect');
5411
        $sessionToRedirect = Session::read('session_redirect');
5412
        if (!empty($courseCodeToRedirect)) {
5413
            $url .= '&c='.$courseCodeToRedirect;
5414
        }
5415
        if (!empty($sessionToRedirect)) {
5416
            $url .= '&s='.$sessionToRedirect;
5417
        }
5418
        $mailSubject = get_lang('Registration confirmation');
5419
        $mailBody = get_lang('Registration confirmationEmailMessage')
5420
            .PHP_EOL
5421
            .Display::url($url, $url);
5422
5423
        api_mail_html(
5424
            self::formatUserFullName($user),
5425
            $user->getEmail(),
5426
            $mailSubject,
5427
            $mailBody
5428
        );
5429
        Display::addFlash(Display::return_message(get_lang('Check your e-mail and follow the instructions.')));
5430
    }
5431
5432
    /**
5433
     * Anonymize a user. Replace personal info by anonymous info.
5434
     *
5435
     * @param int  $userId   User id
5436
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
5437
     *
5438
     * @throws \Exception
5439
     *
5440
     * @return bool
5441
     * @assert (0) === false
5442
     */
5443
    public static function anonymize($userId, $deleteIP = true)
5444
    {
5445
        global $debug;
5446
5447
        $userId = (int) $userId;
5448
5449
        if (empty($userId)) {
5450
            return false;
5451
        }
5452
5453
        $em = Database::getManager();
5454
        $user = api_get_user_entity($userId);
5455
        $uniqueId = uniqid('anon', true);
5456
        $user
5457
            ->setFirstname($uniqueId)
5458
            ->setLastname($uniqueId)
5459
            ->setBiography('')
5460
            ->setAddress('')
5461
            //->setCurriculumItems(null)
5462
            ->setDateOfBirth(null)
5463
            ->setCompetences('')
5464
            ->setDiplomas('')
5465
            ->setOpenarea('')
5466
            ->setTeach('')
5467
            ->setProductions(null)
5468
            ->setOpenid('')
5469
            ->setEmailCanonical($uniqueId.'@example.com')
5470
            ->setEmail($uniqueId.'@example.com')
5471
            ->setUsername($uniqueId)
5472
            ->setUsernameCanonical($uniqueId)
5473
            ->setPhone('')
5474
            ->setOfficialCode('')
5475
        ;
5476
5477
        self::deleteUserPicture($userId);
5478
        self::cleanUserRequestsOfRemoval($userId);
5479
5480
        // The IP address is a border-case personal data, as it does
5481
        // not directly allow for personal identification (it is not
5482
        // a completely safe value in most countries - the IP could
5483
        // be used by neighbours and crackers)
5484
        if ($deleteIP) {
5485
            $substitute = '127.0.0.1';
5486
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
5487
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
5488
            $res = Database::query($sql);
5489
            if (false === $res && $debug > 0) {
5490
                error_log("Could not anonymize IP address for user $userId ($sql)");
5491
            }
5492
5493
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5494
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5495
            $res = Database::query($sql);
5496
            if (false === $res && $debug > 0) {
5497
                error_log("Could not anonymize IP address for user $userId ($sql)");
5498
            }
5499
5500
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
5501
            $sql = "UPDATE $table SET user_ip = '$substitute' WHERE exe_user_id = $userId";
5502
            $res = Database::query($sql);
5503
            if (false === $res && $debug > 0) {
5504
                error_log("Could not anonymize IP address for user $userId ($sql)");
5505
            }
5506
5507
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
5508
            $sql = "UPDATE $table SET user_ip = '$substitute' WHERE login_user_id = $userId";
5509
            $res = Database::query($sql);
5510
            if (false === $res && $debug > 0) {
5511
                error_log("Could not anonymize IP address for user $userId ($sql)");
5512
            }
5513
5514
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5515
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
5516
            $res = Database::query($sql);
5517
            if (false === $res && $debug > 0) {
5518
                error_log("Could not anonymize IP address for user $userId ($sql)");
5519
            }
5520
5521
            $table = Database::get_course_table(TABLE_WIKI);
5522
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5523
            $res = Database::query($sql);
5524
            if (false === $res && $debug > 0) {
5525
                error_log("Could not anonymize IP address for user $userId ($sql)");
5526
            }
5527
5528
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
5529
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
5530
            $res = Database::query($sql);
5531
            if (false === $res && $debug > 0) {
5532
                error_log("Could not anonymize IP address for user $userId ($sql)");
5533
            }
5534
5535
            $table = Database::get_course_table(TABLE_WIKI);
5536
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5537
            $res = Database::query($sql);
5538
            if (false === $res && $debug > 0) {
5539
                error_log("Could not anonymize IP address for user $userId ($sql)");
5540
            }
5541
        }
5542
5543
        $extraFieldRepository = $em->getRepository(EntityExtraField::class);
5544
        $autoRemoveFields = $extraFieldRepository->findBy([
5545
            'autoRemove' => 1,
5546
            'itemType' => EntityExtraField::USER_FIELD_TYPE
5547
        ]);
5548
5549
        foreach ($autoRemoveFields as $field) {
5550
            $extraFieldValueRepository = $em->getRepository(EntityExtraFieldValues::class);
5551
            $extraFieldValue = $extraFieldValueRepository->findOneBy([
5552
                'field' => $field,
5553
                'itemId' => $userId
5554
            ]);
5555
5556
            if ($extraFieldValue) {
5557
                $em->remove($extraFieldValue);
5558
            }
5559
        }
5560
5561
        $em->persist($user);
5562
        $em->flush();
5563
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
5564
5565
        return true;
5566
    }
5567
5568
    /**
5569
     * @param int $userId
5570
     *
5571
     * @throws Exception
5572
     *
5573
     * @return string
5574
     */
5575
    public static function anonymizeUserWithVerification($userId)
5576
    {
5577
        $allowDelete = ('true' === api_get_setting('session.allow_delete_user_for_session_admin'));
5578
5579
        $message = '';
5580
        if (api_is_platform_admin() ||
5581
            ($allowDelete && api_is_session_admin())
5582
        ) {
5583
            $userToUpdateInfo = api_get_user_info($userId);
5584
            $currentUserId = api_get_user_id();
5585
5586
            if ($userToUpdateInfo &&
5587
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
5588
            ) {
5589
                if ($userId != $currentUserId &&
5590
                    self::anonymize($userId)
5591
                ) {
5592
                    $message = Display::return_message(
5593
                        sprintf(get_lang('User %s information anonymized.'), $userToUpdateInfo['complete_name_with_username']),
5594
                        'confirmation'
5595
                    );
5596
                } else {
5597
                    $message = Display::return_message(
5598
                        sprintf(get_lang('We could not anonymize user %s information. Please try again or check the logs.'), $userToUpdateInfo['complete_name_with_username']),
5599
                        'error'
5600
                    );
5601
                }
5602
            } else {
5603
                $message = Display::return_message(
5604
                    sprintf(get_lang('You don\'t have permissions to anonymize user %s. You need the same permissions as to delete users.'), $userToUpdateInfo['complete_name_with_username']),
5605
                    'error'
5606
                );
5607
            }
5608
        }
5609
5610
        return $message;
5611
    }
5612
5613
    /**
5614
     * @param int $userId
5615
     *
5616
     * @throws Exception
5617
     *
5618
     * @return string
5619
     */
5620
    public static function deleteUserWithVerification($userId, bool $destroy = false)
5621
    {
5622
        $allowDelete = ('true' === api_get_setting('session.allow_delete_user_for_session_admin'));
5623
        $message = Display::return_message(get_lang('You cannot delete this user'), 'error');
5624
        $userToUpdateInfo = api_get_user_info($userId);
5625
5626
        // User must exist.
5627
        if (empty($userToUpdateInfo)) {
5628
            return $message;
5629
        }
5630
5631
        $currentUserId = api_get_user_id();
5632
5633
        // Cannot delete myself.
5634
        if ($userId == $currentUserId) {
5635
            return $message;
5636
        }
5637
5638
        if (api_is_platform_admin() ||
5639
            ($allowDelete && api_is_session_admin())
5640
        ) {
5641
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
5642
                if (self::delete_user($userId, $destroy)) {
5643
                    $message = Display::return_message(
5644
                        get_lang('The user has been deleted').': '.$userToUpdateInfo['complete_name_with_username'],
5645
                        'confirmation'
5646
                    );
5647
                } else {
5648
                    $message = Display::return_message(get_lang('You cannot delete this userBecauseOwnsCourse'), 'error');
5649
                }
5650
            }
5651
        }
5652
5653
        return $message;
5654
    }
5655
5656
    /**
5657
     * @return array
5658
     */
5659
    public static function createDataPrivacyExtraFields()
5660
    {
5661
        self::create_extra_field(
5662
            'request_for_legal_agreement_consent_removal_justification',
5663
            1, //text
5664
            'Request for legal agreement consent removal justification	',
5665
            ''
5666
        );
5667
5668
        self::create_extra_field(
5669
            'request_for_delete_account_justification',
5670
            1, //text
5671
            'Request for delete account justification',
5672
            ''
5673
        );
5674
5675
        $extraFieldId = self::create_extra_field(
5676
            'request_for_legal_agreement_consent_removal',
5677
            1, //text
5678
            'Request for legal agreement consent removal',
5679
            ''
5680
        );
5681
5682
        $extraFieldIdDeleteAccount = self::create_extra_field(
5683
            'request_for_delete_account',
5684
            1, //text
5685
            'Request for delete user account',
5686
            ''
5687
        );
5688
5689
        return [
5690
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
5691
            'delete_legal' => $extraFieldId,
5692
        ];
5693
    }
5694
5695
    /**
5696
     * @param int $userId
5697
     */
5698
    public static function cleanUserRequestsOfRemoval($userId)
5699
    {
5700
        $userId = (int) $userId;
5701
5702
        $extraFieldValue = new ExtraFieldValue('user');
5703
        $extraFieldsToDelete = [
5704
            'legal_accept',
5705
            'request_for_legal_agreement_consent_removal',
5706
            'request_for_legal_agreement_consent_removal_justification',
5707
            'request_for_delete_account_justification', // just in case delete also this
5708
            'request_for_delete_account',
5709
        ];
5710
5711
        foreach ($extraFieldsToDelete as $variable) {
5712
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
5713
                $userId,
5714
                $variable
5715
            );
5716
            if ($value && isset($value['id'])) {
5717
                $extraFieldValue->delete($value['id']);
5718
            }
5719
        }
5720
    }
5721
5722
    /**
5723
     * @param int $searchYear
5724
     *
5725
     * @throws Exception
5726
     *
5727
     * @return array
5728
     */
5729
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
5730
    {
5731
        $timezone = new DateTimeZone(api_get_timezone());
5732
5733
        $sessions = [];
5734
        if (DRH == $userInfo['status']) {
5735
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
5736
        } elseif (api_is_platform_admin(true)) {
5737
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
5738
        } else {
5739
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
5740
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
5741
5742
            foreach ($sessionsByCategory as $sessionsInCategory) {
5743
                $sessions = array_merge($sessions, $sessionsInCategory);
5744
            }
5745
        }
5746
5747
        $sessions = array_map(
5748
            function ($sessionInfo) {
5749
                if (!isset($sessionInfo['session_id'])) {
5750
                    $sessionInfo['session_id'] = $sessionInfo['id'];
5751
                }
5752
                if (!isset($sessionInfo['session_name'])) {
5753
                    $sessionInfo['session_name'] = $sessionInfo['name'];
5754
                }
5755
5756
                return $sessionInfo;
5757
            },
5758
            $sessions
5759
        );
5760
5761
        $calendarSessions = [];
5762
5763
        foreach ($sessions as $sessionInfo) {
5764
            if (!empty($sessionInfo['duration'])) {
5765
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
5766
                    $sessionInfo['session_id'],
5767
                    $userInfo['id']
5768
                );
5769
5770
                if (empty($courseAccess)) {
5771
                    continue;
5772
                }
5773
5774
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
5775
                $lastAccessDate = clone $firstAcessDate;
5776
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
5777
5778
                $firstAccessYear = (int) $firstAcessDate->format('Y');
5779
                $lastAccessYear = (int) $lastAccessDate->format('Y');
5780
5781
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
5782
                    $calendarSessions[$sessionInfo['session_id']] = [
5783
                        'name' => $sessionInfo['session_name'],
5784
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
5785
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
5786
                    ];
5787
                }
5788
5789
                continue;
5790
            }
5791
5792
            $accessStartDate = !empty($sessionInfo['access_start_date'])
5793
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
5794
                : null;
5795
            $accessEndDate = !empty($sessionInfo['access_end_date'])
5796
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
5797
                : null;
5798
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
5799
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
5800
5801
            $isValid = false;
5802
5803
            if ($accessStartYear && $accessEndYear) {
5804
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
5805
                    $isValid = true;
5806
                }
5807
            }
5808
5809
            if ($accessStartYear && !$accessEndYear) {
5810
                if ($accessStartYear == $searchYear) {
5811
                    $isValid = true;
5812
                }
5813
            }
5814
5815
            if (!$accessStartYear && $accessEndYear) {
5816
                if ($accessEndYear == $searchYear) {
5817
                    $isValid = true;
5818
                }
5819
            }
5820
5821
            if ($isValid) {
5822
                $calendarSessions[$sessionInfo['session_id']] = [
5823
                    'name' => $sessionInfo['session_name'],
5824
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
5825
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
5826
                ];
5827
            }
5828
        }
5829
5830
        return $calendarSessions;
5831
    }
5832
5833
    /**
5834
     * Get sessions info for planification calendar.
5835
     *
5836
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
5837
     * @param int   $searchYear
5838
     *
5839
     * @throws Exception
5840
     *
5841
     * @return array
5842
     */
5843
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
5844
    {
5845
        $timezone = new DateTimeZone(api_get_timezone());
5846
        $calendar = [];
5847
5848
        foreach ($sessionsList as $sessionId => $sessionInfo) {
5849
            $startDate = $sessionInfo['access_start_date']
5850
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
5851
                : null;
5852
            $endDate = $sessionInfo['access_end_date']
5853
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
5854
                : null;
5855
5856
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
5857
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
5858
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
5859
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
5860
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
5861
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
5862
5863
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
5864
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
5865
5866
            $calendar[] = [
5867
                'id' => $sessionId,
5868
                'name' => $sessionInfo['name'],
5869
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
5870
                'start_in_last_year' => $startYear < $searchYear,
5871
                'end_in_next_year' => $endYear > $searchYear,
5872
                'no_start' => !$startWeek,
5873
                'no_end' => !$endWeek,
5874
                'start' => $start,
5875
                'duration' => $duration > 0 ? $duration : 1,
5876
            ];
5877
        }
5878
5879
        usort(
5880
            $calendar,
5881
            function ($sA, $sB) {
5882
                if ($sA['start'] == $sB['start']) {
5883
                    return 0;
5884
                }
5885
5886
                if ($sA['start'] < $sB['start']) {
5887
                    return -1;
5888
                }
5889
5890
                return 1;
5891
            }
5892
        );
5893
5894
        return $calendar;
5895
    }
5896
5897
    /**
5898
     * Return the user's full name. Optionally with the username.
5899
     */
5900
    public static function formatUserFullName(User $user, bool $includeUsername = false): string
5901
    {
5902
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
5903
5904
        if ($includeUsername && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
5905
            $username = $user->getUsername();
5906
5907
            return "$fullName ($username)";
5908
        }
5909
5910
        return $fullName;
5911
    }
5912
5913
    /**
5914
     * @param int $userId
5915
     *
5916
     * @return array
5917
     */
5918
    public static function getUserCareers($userId)
5919
    {
5920
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
5921
        $tableCareer = Database::get_main_table(TABLE_CAREER);
5922
        $userId = (int) $userId;
5923
5924
        $sql = "SELECT c.id, c.title
5925
                FROM $table uc
5926
                INNER JOIN $tableCareer c
5927
                ON uc.career_id = c.id
5928
                WHERE user_id = $userId
5929
                ORDER BY uc.created_at
5930
                ";
5931
        $result = Database::query($sql);
5932
5933
        return Database::store_result($result, 'ASSOC');
5934
    }
5935
5936
    /**
5937
     * @param int $userId
5938
     * @param int $careerId
5939
     */
5940
    public static function addUserCareer($userId, $careerId)
5941
    {
5942
        if ('true' !== api_get_setting('profile.allow_career_users')) {
5943
            return false;
5944
        }
5945
5946
        if (false === self::userHasCareer($userId, $careerId)) {
5947
            $params = ['user_id' => $userId, 'career_id' => $careerId, 'created_at' => api_get_utc_datetime(), 'updated_at' => api_get_utc_datetime()];
5948
            $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
5949
            Database::insert($table, $params);
5950
        }
5951
5952
        return true;
5953
    }
5954
5955
    /**
5956
     * @param int   $userCareerId
5957
     * @param array $data
5958
     *
5959
     * @return bool
5960
     */
5961
    public static function updateUserCareer($userCareerId, $data)
5962
    {
5963
        if ('true' !== api_get_setting('profile.allow_career_users')) {
5964
            return false;
5965
        }
5966
5967
        $params = ['extra_data' => $data, 'updated_at' => api_get_utc_datetime()];
5968
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
5969
        Database::update(
5970
            $table,
5971
            $params,
5972
            ['id = ?' => (int) $userCareerId]
5973
        );
5974
5975
        return true;
5976
    }
5977
5978
    /**
5979
     * @param int $userId
5980
     * @param int $careerId
5981
     *
5982
     * @return array
5983
     */
5984
    public static function getUserCareer($userId, $careerId)
5985
    {
5986
        $userId = (int) $userId;
5987
        $careerId = (int) $careerId;
5988
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
5989
5990
        $sql = "SELECT * FROM $table WHERE user_id = $userId AND career_id = $careerId";
5991
        $result = Database::query($sql);
5992
5993
        return Database::fetch_assoc($result);
0 ignored issues
show
Bug Best Practice introduced by
The expression return Database::fetch_assoc($result) also could return the type boolean which is incompatible with the documented return type array.
Loading history...
5994
    }
5995
5996
    /**
5997
     * @param int $userId
5998
     * @param int $careerId
5999
     *
6000
     * @return bool
6001
     */
6002
    public static function userHasCareer($userId, $careerId)
6003
    {
6004
        $userId = (int) $userId;
6005
        $careerId = (int) $careerId;
6006
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6007
6008
        $sql = "SELECT id FROM $table WHERE user_id = $userId AND career_id = $careerId";
6009
        $result = Database::query($sql);
6010
6011
        return Database::num_rows($result) > 0;
6012
    }
6013
6014
    /**
6015
     * Disables or enables a user.
6016
     *
6017
     * @param int $user_id
6018
     * @param int $active  Enable or disable
6019
     *
6020
     * @return bool True on success, false on failure
6021
     * @assert (-1,0) === false
6022
     * @assert (1,1) === true
6023
     */
6024
    public static function change_active_state($user_id, $active)
6025
    {
6026
        $user_id = (int) $user_id;
6027
        $active = (int) $active;
6028
6029
        if (empty($user_id)) {
6030
            return false;
6031
        }
6032
6033
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6034
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
6035
        $r = Database::query($sql);
6036
        $ev = LOG_USER_DISABLE;
6037
        if (1 == $active) {
6038
            $ev = LOG_USER_ENABLE;
6039
        }
6040
        if (false !== $r) {
6041
            Event::addEvent($ev, LOG_USER_ID, $user_id);
6042
        }
6043
6044
        return $r;
6045
    }
6046
6047
    /**
6048
     * Get either a Gravatar URL or complete image tag for a specified email address.
6049
     *
6050
     * @param string $email The email address
6051
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
6052
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
6053
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
6054
     * @param bool   $img   True to return a complete IMG tag False for just the URL
6055
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
6056
     *
6057
     * @return string containing either just a URL or a complete image tag
6058
     * @source http://gravatar.com/site/implement/images/php/
6059
     */
6060
    private static function getGravatar(
0 ignored issues
show
Unused Code introduced by
The method getGravatar() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
6061
        $email,
6062
        $s = 80,
6063
        $d = 'mm',
6064
        $r = 'g',
6065
        $img = false,
6066
        $atts = []
6067
    ) {
6068
        $url = 'http://www.gravatar.com/avatar/';
6069
        if (!empty($_SERVER['HTTPS'])) {
6070
            $url = 'https://secure.gravatar.com/avatar/';
6071
        }
6072
        $url .= md5(strtolower(trim($email)));
6073
        $url .= "?s=$s&d=$d&r=$r";
6074
        if ($img) {
6075
            $url = '<img src="'.$url.'"';
6076
            foreach ($atts as $key => $val) {
6077
                $url .= ' '.$key.'="'.$val.'"';
6078
            }
6079
            $url .= ' />';
6080
        }
6081
6082
        return $url;
6083
    }
6084
6085
    /**
6086
     * Count users in courses and if they have certificate.
6087
     * This function is resource intensive.
6088
     *
6089
     * @return array
6090
     * @throws Exception
6091
     * @throws \Doctrine\DBAL\Exception
6092
     */
6093
    public static function countUsersWhoFinishedCourses()
6094
    {
6095
        $courses = [];
6096
        $currentAccessUrlId = api_get_current_access_url_id();
6097
        $sql = "SELECT course.code, cru.user_id
6098
                FROM course_rel_user cru
6099
                    JOIN course ON cru.c_id = course.id
6100
                    JOIN access_url_rel_user auru on cru.user_id = auru.user_id
6101
                    JOIN access_url_rel_course ON course.id = access_url_rel_course.c_id
6102
                WHERE access_url_rel_course.access_url_id = $currentAccessUrlId
6103
                ORDER BY course.code
6104
        ";
6105
        $res = Database::query($sql);
6106
        if (Database::num_rows($res) > 0) {
6107
            while ($row = Database::fetch_array($res)) {
6108
                if (!isset($courses[$row['code']])) {
6109
                    $courses[$row['code']] = [
6110
                        'subscribed' => 0,
6111
                        'finished' => 0,
6112
                    ];
6113
                }
6114
                $courses[$row['code']]['subscribed']++;
6115
                $entityManager = Database::getManager();
6116
                $repository = $entityManager->getRepository('ChamiloCoreBundle:GradebookCategory');
6117
                //todo check when have more than 1 gradebook
6118
                /** @var \Chamilo\CoreBundle\Entity\GradebookCategory $gradebook */
6119
                $gradebook = $repository->findOneBy(['courseCode' => $row['code']]);
6120
                if (!empty($gradebook)) {
6121
                    $finished = 0;
6122
                    $gb = Category::createCategoryObjectFromEntity($gradebook);
0 ignored issues
show
Bug introduced by
The method createCategoryObjectFromEntity() does not exist on Category. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

6122
                    /** @scrutinizer ignore-call */ 
6123
                    $gb = Category::createCategoryObjectFromEntity($gradebook);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
6123
                    $finished = $gb->is_certificate_available($row['user_id']);
6124
                    if (!empty($finished)) {
6125
                        $courses[$row['code']]['finished']++;
6126
                    }
6127
                }
6128
            }
6129
        }
6130
        return $courses;
6131
    }
6132
6133
    /**
6134
     * Count users in sessions and if they have certificate.
6135
     * This function is resource intensive.
6136
     *
6137
     * @return array
6138
     * @throws Exception
6139
     * @throws \Doctrine\DBAL\Exception
6140
     */
6141
    public static function countUsersWhoFinishedCoursesInSessions()
6142
    {
6143
        $coursesInSessions = [];
6144
        $currentAccessUrlId = api_get_current_access_url_id();
6145
        $sql = "SELECT course.code, srcru.session_id, srcru.user_id, session.title
6146
                FROM session_rel_course_rel_user srcru
6147
                    JOIN course ON srcru.c_id = course.id
6148
                    JOIN access_url_rel_session aurs on srcru.session_id = aurs.session_id
6149
                    JOIN session ON srcru.session_id = session.id
6150
                WHERE aurs.access_url_id = $currentAccessUrlId
6151
                ORDER BY course.code, session.title
6152
        ";
6153
        $res = Database::query($sql);
6154
        if (Database::num_rows($res) > 0) {
6155
            while ($row = Database::fetch_array($res)) {
6156
                $index = $row['code'].' ('.$row['title'].')';
6157
                if (!isset($coursesInSessions[$index])) {
6158
                    $coursesInSessions[$index] = [
6159
                        'subscribed' => 0,
6160
                        'finished' => 0,
6161
                    ];
6162
                }
6163
                $coursesInSessions[$index]['subscribed']++;
6164
                $entityManager = Database::getManager();
6165
                $repository = $entityManager->getRepository('ChamiloCoreBundle:GradebookCategory');
6166
                /** @var \Chamilo\CoreBundle\Entity\GradebookCategory $gradebook */
6167
                $gradebook = $repository->findOneBy(
6168
                    [
6169
                        'courseCode' => $row['code'],
6170
                        'sessionId' => $row['session_id'],
6171
                    ]
6172
                );
6173
                if (!empty($gradebook)) {
6174
                    $finished = 0;
6175
                    $gb = Category::createCategoryObjectFromEntity($gradebook);
6176
                    $finished = $gb->is_certificate_available($row['user_id']);
6177
                    if (!empty($finished)) {
6178
                        $coursesInSessions[$index]['finished']++;
6179
                    }
6180
                }
6181
            }
6182
        }
6183
        return $coursesInSessions;
6184
    }
6185
6186
}
6187