Passed
Push — master ( a20ee1...cb41e8 )
by
unknown
16:41 queued 08:14
created

UserManager::delete_user()   C

Complexity

Conditions 12
Paths 12

Size

Total Lines 70
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 12
eloc 41
c 1
b 0
f 0
nc 12
nop 2
dl 0
loc 70
rs 6.9666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
5
use Chamilo\CoreBundle\Entity\ExtraFieldValues as EntityExtraFieldValues;
6
use Chamilo\CoreBundle\Entity\GradebookCategory;
7
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
8
use Chamilo\CoreBundle\Entity\SessionRelCourse;
9
use Chamilo\CoreBundle\Entity\User;
10
use Chamilo\CoreBundle\Entity\UserAuthSource;
11
use Chamilo\CoreBundle\Entity\UserRelUser;
12
use Chamilo\CoreBundle\Event\AbstractEvent;
13
use Chamilo\CoreBundle\Event\Events;
14
use Chamilo\CoreBundle\Event\UserCreatedEvent;
15
use Chamilo\CoreBundle\Event\UserUpdatedEvent;
16
use Chamilo\CoreBundle\Framework\Container;
17
use Chamilo\CoreBundle\Repository\LanguageRepository;
18
use Chamilo\CoreBundle\Repository\Node\UserRepository;
19
use Chamilo\CoreBundle\Helpers\NameConventionHelper;
20
use ChamiloSession as Session;
21
use Symfony\Component\HttpFoundation\File\UploadedFile;
22
use Symfony\Contracts\Translation\TranslatorInterface;
23
24
/**
25
 * This library provides functions for user management.
26
 * Include/require it in your code to use its functionality.
27
 *
28
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
29
 */
30
class UserManager
31
{
32
    // This constants are deprecated use the constants located in ExtraField
33
    public const USER_FIELD_TYPE_TEXT = 1;
34
    public const USER_FIELD_TYPE_TEXTAREA = 2;
35
    public const USER_FIELD_TYPE_RADIO = 3;
36
    public const USER_FIELD_TYPE_SELECT = 4;
37
    public const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
38
    public const USER_FIELD_TYPE_DATE = 6;
39
    public const USER_FIELD_TYPE_DATETIME = 7;
40
    public const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
41
    public const USER_FIELD_TYPE_DIVIDER = 9;
42
    public const USER_FIELD_TYPE_TAG = 10;
43
    public const USER_FIELD_TYPE_TIMEZONE = 11;
44
    public const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
45
    public const USER_FIELD_TYPE_FILE = 13;
46
    public const USER_FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
47
48
    public function __construct()
49
    {
50
    }
51
52
    /**
53
     * Repository is use to query the DB, selects, etc.
54
     *
55
     * @return UserRepository
56
     */
57
    public static function getRepository()
58
    {
59
        return Container::getUserRepository();
60
    }
61
62
    /**
63
     * Validates the password.
64
     */
65
    public static function isPasswordValid(User $user, string $plainPassword): bool
66
    {
67
        /**
68
         * @psalm-suppress PrivateService
69
         */
70
        $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

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

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

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

591
            Event::/** @scrutinizer ignore-call */ 
592
                   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...
592
        } else {
593
            Display::addFlash(
594
                Display::return_message(
595
                    get_lang('There happened an unknown error. Please contact the platform administrator.')
596
                )
597
            );
598
599
            return false;
600
        }
601
602
        return $userId;
603
    }
604
605
    /**
606
     * Returns the human-readable language name for a given ISO code.
607
     *
608
     * Accepts ISO 639-1/2 codes (e.g. "en", "es", "eng"). If $iso is null or the
609
     * code is unknown, an empty string is returned.
610
     *
611
     * @param string|null $iso Two- or three-letter ISO 639 language code.
612
     * @return string          Language name in English (e.g. "English", "Spanish").
613
     */
614
    private static function resolveLanguageName(?string $iso): string
615
    {
616
        if (empty($iso)) {
617
            return '';
618
        }
619
620
        /** @var LanguageRepository $langRepo */
621
        $langRepo = Container::$container->get(LanguageRepository::class);
622
        $entity   = $langRepo->findOneBy(['isocode' => $iso]);
623
624
        return $entity ? $entity->getOriginalName() : $iso;
625
    }
626
627
    /**
628
     * Build the “profile” HTML (core + dynamic extra fields) for the admin email, localized in $adminLocale.
629
     *
630
     * @param User $user
631
     * @param array  $extraParams  Raw POST values from registration (keys: "extra_*").
632
     * @param string $adminLocale  e.g. "es_ES", "fr_FR".
633
     */
634
    private static function renderRegistrationProfileHtml(User $user, array $extraParams, string $adminLocale): string
635
    {
636
        $statusLabel = ((int) $user->getStatus() === COURSEMANAGER)
637
            ? get_lang('Teach courses', $adminLocale)
638
            : get_lang('Follow courses', $adminLocale);
639
640
        $languageName = $user->getLocale();
641
        $langRepo = Container::$container->get(LanguageRepository::class);
642
        if ($langRepo && ($lang = $langRepo->findOneBy(['isocode' => $user->getLocale()]))) {
643
            $languageName = $lang->getOriginalName();
644
        }
645
646
        $corePairs = [
647
            get_lang('E-mail',        $adminLocale) => (string) $user->getEmail(),
648
            get_lang('First name',    $adminLocale) => (string) $user->getFirstname(),
649
            get_lang('Last name',     $adminLocale) => (string) $user->getLastname(),
650
            get_lang('Username',      $adminLocale) => (string) $user->getUsername(),
651
            get_lang('Official code', $adminLocale) => (string) ($user->getOfficialCode() ?? ''),
652
            get_lang('Phone',         $adminLocale) => (string) ($user->getPhone() ?? ''),
653
            get_lang('Address',       $adminLocale) => (string) ($user->getAddress() ?? ''),
654
            get_lang('Language',      $adminLocale) => (string) $languageName,
655
            get_lang('What do you want to do?', $adminLocale) => $statusLabel,
656
        ];
657
658
        if ($user->getDateOfBirth() instanceof \DateTimeInterface) {
659
            $corePairs[get_lang('Date of birth', $adminLocale)] = $user->getDateOfBirth()->format('Y-m-d');
660
        }
661
662
        $efv = new \ExtraFieldValue('user');
663
        $ef  = new \ExtraField('user');
664
665
        $extraPairs = [];
666
        $presentVars = [];
667
        $rows = $efv->getAllValuesByItem((int) $user->getId());
668
669
        if (is_array($rows)) {
670
            foreach ($rows as $row) {
671
                $fieldId   = (int)$row['id'];
672
                $variable  = (string)$row['variable'];
673
                $presentVars[$variable] = true;
674
675
                $tr = $efv->get_values_by_handler_and_field_id((int) $user->getId(), $fieldId, true);
676
                $val = null;
677
678
                if ($tr && array_key_exists('value', $tr)) {
679
                    $val = $tr['value'];
680
                } else {
681
                    $val = $row['value'];
682
                }
683
684
                $type = (int)$row['value_type'];
685
                if ($type === \ExtraField::FIELD_TYPE_CHECKBOX) {
686
                    $val = ((string)$val === '1') ? get_lang('Yes', $adminLocale) : get_lang('No', $adminLocale);
687
                }
688
                if ($type === \ExtraField::FIELD_TYPE_TAG && is_array($val)) {
689
                    $val = implode(', ', array_map(fn($t) => is_array($t) ? (string)($t['value'] ?? '') : (string)$t, $val));
690
                }
691
                if (is_string($val)) {
692
                    $val = str_replace(['<br />','<br>','<br/>'], ', ', $val);
693
                }
694
695
                $label = !empty($row['display_text'])
696
                    ? get_lang($row['display_text'], $adminLocale)
697
                    : get_lang(ucwords(str_replace('_',' ',$variable)), $adminLocale);
698
699
                if ($val !== '' && $val !== null) {
700
                    $extraPairs[$label] = (string)$val;
701
                }
702
            }
703
        }
704
705
        foreach ($extraParams as $k => $v) {
706
            if (strpos($k, 'extra_') !== 0) continue;
707
            $variable = substr($k, 6);
708
            if (isset($presentVars[$variable])) continue;
709
710
            $def = $ef->get_handler_field_info_by_field_variable($variable);
711
            $label = $def && !empty($def['display_text'])
712
                ? get_lang($def['display_text'], $adminLocale)
713
                : get_lang(ucwords(str_replace('_',' ',$variable)), $adminLocale);
714
715
            $val = $v;
716
            if (is_array($val)) {
717
                $val = implode(', ', array_map('strval', $val));
718
            } elseif ($val === '1') {
719
                $val = get_lang('Yes', $adminLocale);
720
            } elseif ($val === '0') {
721
                $val = get_lang('No', $adminLocale);
722
            }
723
724
            if ($val !== '' && $val !== null) {
725
                $extraPairs[$label] = (string)$val;
726
            }
727
        }
728
729
        $html = '<div class="form-horizontal">';
730
        foreach ($corePairs as $k => $v) {
731
            if ($v === '' || $v === null) continue;
732
            $html .= '<div>'.$k.': '.\Security::remove_XSS((string)$v).'</div>';
733
        }
734
        foreach ($extraPairs as $k => $v) {
735
            $html .= '<div>'.$k.': '.\Security::remove_XSS((string)$v).'</div>';
736
        }
737
        $html .= '</div>';
738
739
        return $html;
740
    }
741
742
    /**
743
     * Can user be deleted? This function checks whether there's a course
744
     * in which the given user is the
745
     * only course administrator. If that is the case, the user can't be
746
     * deleted because the course would remain without a course admin.
747
     *
748
     * @param int $user_id The user id
749
     *
750
     * @return bool true if user can be deleted
751
     *
752
     * @assert (null) === false
753
     * @assert (-1) === false
754
     * @assert ('abc') === false
755
     */
756
    public static function canDeleteUser($user_id)
757
    {
758
        $deny = api_get_env_variable('DENY_DELETE_USERS', false);
759
760
        if ($deny) {
761
            return false;
762
        }
763
764
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
765
        $user_id = (int) $user_id;
766
767
        if (empty($user_id)) {
768
            return false;
769
        }
770
771
        $sql = "SELECT * FROM $table_course_user
772
                WHERE status = 1 AND user_id = ".$user_id;
773
        $res = Database::query($sql);
774
        while ($course = Database::fetch_object($res)) {
775
            $sql = "SELECT id FROM $table_course_user
776
                    WHERE status=1 AND c_id = ".intval($course->c_id);
777
            $res2 = Database::query($sql);
778
            if (1 == Database::num_rows($res2)) {
779
                return false;
780
            }
781
        }
782
783
        return true;
784
    }
785
786
    /**
787
     * Deletes a user from the system or marks the user as deleted based on the $destroy flag.
788
     * If $destroy is false, the user is only marked as deleted (e.g., active = -1) but not actually removed from the database.
789
     * This allows for the possibility of restoring the user at a later time. If $destroy is true, the user and all their relations
790
     * are permanently removed from the database.
791
     *
792
     * Note: When $destroy is false, the user's relations are not removed, allowing for potential restoration. When $destroy is true,
793
     * the function proceeds to remove all the user's relations, effectively cleaning up all references to the user in the system.
794
     */
795
    public static function delete_user(int $user_id, bool $destroy = false): bool
796
    {
797
        $user_id = (int) $user_id;
798
799
        if (empty($user_id)) {
800
            return false;
801
        }
802
803
        if (!self::canDeleteUser($user_id)) {
804
            return false;
805
        }
806
807
        $repository = Container::getUserRepository();
808
809
        /** @var User $user */
810
        $user = $repository->find($user_id);
811
812
        if (!$user) {
0 ignored issues
show
introduced by
$user is of type Chamilo\CoreBundle\Entity\User, thus it always evaluated to true.
Loading history...
813
            return false;
814
        }
815
816
        $fallbackUser = $repository->getFallbackUser();
817
        $fallbackId = (int) $fallbackUser->getId();
818
        if ($destroy) {
819
            if ($fallbackId && $fallbackId !== $user_id) {
820
                $em = Database::getManager();
821
                $em->createQuery(
822
                    'UPDATE ' . User::class . ' u
823
                     SET u.creatorId = :newCreator
824
                     WHERE u.creatorId = :oldCreator AND u.id <> :fallbackId'
825
                            )
826
                    ->setParameter('newCreator', $fallbackId)
827
                    ->setParameter('oldCreator', $user_id)
828
                    ->setParameter('fallbackId', $fallbackId)
829
                    ->execute();
830
            }
831
        }
832
833
        $repository->deleteUser($user, $destroy);
834
835
        if ($destroy) {
836
            Event::addEvent(
837
                LOG_USER_DELETE,
838
                LOG_USER_ID,
839
                $user_id,
840
                api_get_utc_datetime(),
841
                api_get_user_id()
842
            );
843
844
            // Log one event per affected user AFTER the deletion
845
            if (!empty($affectedIds) && $fallbackId && $fallbackId !== $user_id) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $affectedIds seems to never exist and therefore empty should always be true.
Loading history...
846
                $nowUtc = api_get_utc_datetime();
847
                $actor  = api_get_user_id();
848
                foreach ($affectedIds as $affectedId) {
849
                    Event::addEvent(
850
                        LOG_USER_CREATOR_DELETED,
851
                        LOG_USER_ID,
852
                        [
853
                            'user_id'        => $affectedId,
854
                            'old_creator_id' => $user_id,
855
                            'new_creator_id' => $fallbackId,
856
                        ],
857
                        $nowUtc,
858
                        $actor
859
                    );
860
                }
861
            }
862
        }
863
864
        return true;
865
    }
866
867
    /**
868
     * Deletes users completely. Can be called either as:
869
     * - UserManager::delete_users(1, 2, 3); or
870
     * - UserManager::delete_users(array(1, 2, 3));.
871
     *
872
     * @param array|int $ids
873
     *
874
     * @return bool True if at least one user was successfuly deleted. False otherwise.
875
     *
876
     * @author Laurent Opprecht
877
     *
878
     * @uses \UserManager::delete_user() to actually delete each user
879
     * @assert (null) === false
880
     * @assert (-1) === false
881
     * @assert (array(-1)) === false
882
     */
883
    public static function delete_users($ids = [])
884
    {
885
        $result = false;
886
        $ids = is_array($ids) ? $ids : func_get_args();
887
        if (!is_array($ids) || 0 == count($ids)) {
888
            return false;
889
        }
890
        $ids = array_map('intval', $ids);
891
        foreach ($ids as $id) {
892
            if (empty($id) || $id < 1) {
893
                continue;
894
            }
895
            $deleted = self::delete_user($id);
896
            $result = $deleted || $result;
897
        }
898
899
        return $result;
900
    }
901
902
    /**
903
     * Disable users. Can be called either as:
904
     * - UserManager::deactivate_users(1, 2, 3);
905
     * - UserManager::deactivate_users(array(1, 2, 3));.
906
     *
907
     * @param array|int $ids
908
     *
909
     * @return bool
910
     *
911
     * @author Laurent Opprecht
912
     * @assert (null) === false
913
     * @assert (array(-1)) === false
914
     */
915
    public static function deactivate_users($ids = [])
916
    {
917
        if (empty($ids)) {
918
            return false;
919
        }
920
921
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
922
923
        $ids = is_array($ids) ? $ids : func_get_args();
924
        $ids = array_map('intval', $ids);
925
        $ids = implode(',', $ids);
926
927
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
928
        $r = Database::query($sql);
929
        if (false !== $r) {
930
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
931
932
            return true;
933
        }
934
935
        return false;
936
    }
937
938
    /**
939
     * Enable users. Can be called either as:
940
     * - UserManager::activate_users(1, 2, 3);
941
     * - UserManager::activate_users(array(1, 2, 3));.
942
     *
943
     * @param array|int IDs of the users to enable
944
     *
945
     * @return bool
946
     *
947
     * @author Laurent Opprecht
948
     * @assert (null) === false
949
     * @assert (array(-1)) === false
950
     */
951
    public static function activate_users($ids = [])
952
    {
953
        if (empty($ids)) {
954
            return false;
955
        }
956
957
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
958
959
        $ids = is_array($ids) ? $ids : func_get_args();
960
        $ids = array_map('intval', $ids);
961
        $ids = implode(',', $ids);
962
963
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
964
        $r = Database::query($sql);
965
        if (false !== $r) {
966
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
967
968
            return true;
969
        }
970
971
        return false;
972
    }
973
974
    /**
975
     * Update user information with all the parameters passed to this function.
976
     *
977
     * @param int    $user_id         The ID of the user to be updated
978
     * @param string $firstname       The user's firstname
979
     * @param string $lastname        The user's lastname
980
     * @param string $username        The user's username (login)
981
     * @param string $password        The user's password
982
     * @param string $auth_sources     The authentication source (default: "platform")
983
     * @param string $email           The user's e-mail address
984
     * @param int    $status          The user's status
985
     * @param string $official_code   The user's official code (usually just an internal institutional code)
986
     * @param string $phone           The user's phone number
987
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
988
     * @param string $expiration_date The date at which this user will be automatically disabled
989
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
990
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
991
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
992
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
993
     * @param string $language        The language to which the user account will be set
994
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
995
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
996
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
997
     * @param string $address
998
     * @param array  $emailTemplate
999
     *
1000
     * @return bool|int False on error, or the user ID if the user information was updated
1001
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
1002
     */
1003
    public static function update_user(
1004
        $user_id,
1005
        $firstname,
1006
        $lastname,
1007
        $username,
1008
        $password,
1009
        array $auth_sources,
1010
        $email,
1011
        $status,
1012
        $official_code,
1013
        $phone,
1014
        $picture_uri,
1015
        $expiration_date,
1016
        $active,
1017
        $creator_id = null,
1018
        $hr_dept_id = 0,
1019
        $extra = null,
1020
        $language = 'en_US',
1021
        $encrypt_method = '',
1022
        $send_email = false,
1023
        $reset_password = 0,
1024
        $address = null,
1025
        $emailTemplate = []
1026
    ) {
1027
        $eventDispatcher = Container::getEventDispatcher();
1028
1029
        $eventDispatcher->dispatch(
1030
            new UserUpdatedEvent([], AbstractEvent::TYPE_PRE),
1031
            Events::USER_UPDATED
1032
        );
1033
1034
        $original_password = $password;
1035
        $user_id = (int) $user_id;
1036
        $creator_id = (int) $creator_id;
1037
1038
        if (empty($user_id)) {
1039
            return false;
1040
        }
1041
1042
        $userManager = self::getRepository();
1043
        $user = api_get_user_entity($user_id);
1044
1045
        if (null === $user) {
1046
            return false;
1047
        }
1048
1049
        $accessUrl = Container::getAccessUrlUtil()->getCurrent();
1050
1051
        if (0 == $reset_password) {
1052
            $password = null;
1053
            $auth_sources = $user->getAuthSourcesAuthentications($accessUrl);
1054
        } elseif (1 == $reset_password) {
1055
            $original_password = $password = api_generate_password();
1056
            $auth_sources = [UserAuthSource::PLATFORM];
1057
        } elseif (2 == $reset_password) {
1058
            $auth_sources = [UserAuthSource::PLATFORM];
1059
        }
1060
1061
        // Checking the user language
1062
        $languages = array_keys(api_get_languages());
1063
        if (!in_array($language, $languages)) {
1064
            $language = api_get_setting('platformLanguage');
1065
        }
1066
1067
        $change_active = 0;
1068
        $isUserActive = $user->isActive();
1069
        if ($active != USER_SOFT_DELETED) {
1070
            if ($isUserActive != $active) {
1071
                $change_active = 1;
1072
            }
1073
        }
1074
1075
        $originalUsername = $user->getUsername();
1076
1077
        // If username is different from original then check if it exists.
1078
        if ($originalUsername !== $username) {
1079
            $available = self::is_username_available($username);
1080
            if (false === $available) {
1081
                return false;
1082
            }
1083
        }
1084
1085
        if (!empty($expiration_date)) {
1086
            $expiration_date = api_get_utc_datetime($expiration_date);
1087
            $expiration_date = new \DateTime($expiration_date, new DateTimeZone('UTC'));
1088
        }
1089
1090
        $previousStatus = $user->getStatus();
1091
        $previousRole = $user->getRoleFromStatus($previousStatus);
1092
1093
        $user
1094
            ->removeRole($previousRole)
1095
            ->setLastname($lastname)
1096
            ->setFirstname($firstname)
1097
            ->setUsername($username)
1098
            ->setStatus($status)
1099
            ->setLocale($language)
1100
            ->setEmail($email)
1101
            ->setOfficialCode($official_code)
1102
            ->setPhone($phone)
1103
            ->setAddress($address)
1104
            ->setExpirationDate($expiration_date)
1105
            ->setActive($active)
1106
            ->setHrDeptId((int) $hr_dept_id)
1107
            ->removeAuthSources()
1108
        ;
1109
1110
        foreach ($auth_sources as $authSource) {
1111
            $user->addAuthSourceByAuthentication($authSource, $accessUrl);
1112
        }
1113
1114
        if (!is_null($password)) {
1115
            $user->setPlainPassword($password);
1116
        }
1117
1118
        $user->setRoleFromStatus($status);
1119
        $userManager->updateUser($user, true);
1120
        Event::addEvent(LOG_USER_UPDATE, LOG_USER_ID, $user_id);
1121
1122
        if (1 == $change_active) {
1123
            $event_title = LOG_USER_DISABLE;
1124
            if (1 == $active) {
1125
                $event_title = LOG_USER_ENABLE;
1126
            }
1127
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
1128
        }
1129
1130
        if (is_array($extra) && count($extra) > 0) {
1131
            $res = true;
1132
            foreach ($extra as $fname => $fvalue) {
1133
                $res = $res && self::update_extra_field_value(
1134
                    $user_id,
1135
                    $fname,
1136
                    $fvalue
1137
                );
1138
            }
1139
        }
1140
1141
        if (!empty($email) && $send_email) {
1142
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
1143
            $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

1143
            $emailsubject = '['./** @scrutinizer ignore-type */ api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
Loading history...
1144
            $sender_name = api_get_person_name(
1145
                api_get_setting('administratorName'),
1146
                api_get_setting('administratorSurname'),
1147
                null,
1148
                PERSON_NAME_EMAIL_ADDRESS
1149
            );
1150
            $email_admin = api_get_setting('emailAdministrator');
1151
            $url = api_get_path(WEB_PATH);
1152
            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 AccessUrlUtil::isMultiple ( Ignorable by Annotation )

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

1152
            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...
1153
                $access_url_id = api_get_current_access_url_id();
1154
                if (-1 != $access_url_id) {
1155
                    $url = api_get_access_url($access_url_id);
1156
                    $url = $url['url'];
1157
                }
1158
            }
1159
1160
            $tplContent = new Template(
1161
                null,
1162
                false,
1163
                false,
1164
                false,
1165
                false,
1166
                false
1167
            );
1168
            // variables for the default template
1169
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
1170
            $tplContent->assign('login_name', $username);
1171
1172
            $originalPassword = '';
1173
            if ($reset_password > 0) {
1174
                $originalPassword = stripslashes($original_password);
1175
            }
1176
            $tplContent->assign('original_password', $originalPassword);
1177
            $tplContent->assign('portal_url', $url);
1178
1179
            $emailBody = $tplContent->fetch('@ChamiloCore/Mailer/Legacy/user_edit_content.html.twig');
1180
1181
            $mailTemplateManager = new MailTemplateManager();
1182
1183
            if (!empty($emailTemplate) &&
1184
                isset($emailTemplate['user_edit_content.tpl']) &&
1185
                !empty($emailTemplate['user_edit_content.tpl'])
1186
            ) {
1187
                $userInfo = api_get_user_info($user_id);
1188
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], $userInfo);
1189
            }
1190
1191
            $creatorInfo = api_get_user_info($creator_id);
1192
            $creatorEmail = $creatorInfo['email'] ?? '';
1193
1194
            api_mail_html(
1195
                $recipient_name,
1196
                $email,
1197
                $emailsubject,
1198
                $emailBody,
1199
                $sender_name,
1200
                $email_admin,
1201
                null,
1202
                null,
1203
                null,
1204
                null,
1205
                $creatorEmail
1206
            );
1207
        }
1208
1209
        $eventDispatcher->dispatch(
1210
            new UserUpdatedEvent(['user' => $user], AbstractEvent::TYPE_POST),
1211
            Events::USER_UPDATED
1212
        );
1213
1214
        return $user->getId();
1215
    }
1216
1217
    /**
1218
     * Disables a user.
1219
     *
1220
     * @param int User id
1221
     *
1222
     * @return bool
1223
     *
1224
     * @uses \UserManager::change_active_state() to actually disable the user
1225
     * @assert (0) === false
1226
     */
1227
    public static function disable($user_id)
1228
    {
1229
        if (empty($user_id)) {
1230
            return false;
1231
        }
1232
        self::change_active_state($user_id, 0);
1233
1234
        return true;
1235
    }
1236
1237
    /**
1238
     * Enable a user.
1239
     *
1240
     * @param int User id
1241
     *
1242
     * @return bool
1243
     *
1244
     * @uses \UserManager::change_active_state() to actually disable the user
1245
     * @assert (0) === false
1246
     */
1247
    public static function enable($user_id)
1248
    {
1249
        if (empty($user_id)) {
1250
            return false;
1251
        }
1252
        self::change_active_state($user_id, 1);
1253
1254
        return true;
1255
    }
1256
1257
    /**
1258
     * Returns the user's id based on the original id and field name in
1259
     * the extra fields. Returns 0 if no user was found. This function is
1260
     * mostly useful in the context of a web services-based sinchronization.
1261
     *
1262
     * @param string Original user id
1263
     * @param string Original field name
1264
     *
1265
     * @return int User id
1266
     * @assert ('0','---') === 0
1267
     */
1268
    public static function get_user_id_from_original_id(
1269
        $original_user_id_value,
1270
        $original_user_id_name
1271
    ) {
1272
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1273
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1274
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1275
1276
        $original_user_id_name = Database::escape_string($original_user_id_name);
1277
        $original_user_id_value = Database::escape_string($original_user_id_value);
1278
1279
        $sql = "SELECT item_id as user_id
1280
                FROM $t_uf uf
1281
                INNER JOIN $t_ufv ufv
1282
                ON ufv.field_id = uf.id
1283
                WHERE
1284
                    variable = '$original_user_id_name' AND
1285
                    field_value = '$original_user_id_value' AND
1286
                    item_type = $extraFieldType
1287
                ";
1288
        $res = Database::query($sql);
1289
        $row = Database::fetch_object($res);
1290
        if ($row) {
1291
            return $row->user_id;
1292
        }
1293
1294
        return 0;
1295
    }
1296
1297
    /**
1298
     * Check if a username is available.
1299
     *
1300
     * @param string $username the wanted username
1301
     *
1302
     * @return bool true if the wanted username is available
1303
     * @assert ('') === false
1304
     * @assert ('xyzxyzxyz') === true
1305
     */
1306
    public static function is_username_available($username)
1307
    {
1308
        if (empty($username)) {
1309
            return false;
1310
        }
1311
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1312
        $sql = "SELECT username FROM $table_user
1313
                WHERE username = '".Database::escape_string($username)."'";
1314
        $res = Database::query($sql);
1315
1316
        return 0 == Database::num_rows($res);
1317
    }
1318
1319
    /**
1320
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1321
     *
1322
     * @param string $firstname the first name of the user
1323
     * @param string $lastname  the last name of the user
1324
     *
1325
     * @return string suggests a username that contains only ASCII-letters and digits,
1326
     *                without check for uniqueness within the system
1327
     *
1328
     * @author Julio Montoya Armas
1329
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1330
     * @assert ('','') === false
1331
     * @assert ('a','b') === 'ab'
1332
     */
1333
    public static function create_username($firstname, $lastname)
1334
    {
1335
        if (empty($firstname) && empty($lastname)) {
1336
            return false;
1337
        }
1338
1339
        // The first letter only.
1340
        $firstname = api_substr(
1341
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1342
            0,
1343
            1
1344
        );
1345
        //Looking for a space in the lastname
1346
        $pos = api_strpos($lastname, ' ');
1347
        if (false !== $pos) {
1348
            $lastname = api_substr($lastname, 0, $pos);
1349
        }
1350
1351
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1352
        $username = $firstname.$lastname;
1353
        if (empty($username)) {
1354
            $username = 'user';
1355
        }
1356
1357
        $username = URLify::transliterate($username);
1358
1359
        return strtolower(substr($username, 0, User::USERNAME_MAX_LENGTH - 3));
1360
    }
1361
1362
    /**
1363
     * Creates a unique username, using:
1364
     * 1. the first name and the last name of a user;
1365
     * 2. an already created username but not checked for uniqueness yet.
1366
     *
1367
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1368
     *                          parameter is treated as username which is to be checked f
1369
     *                          or uniqueness and to be modified when it is necessary.
1370
     * @param string $lastname  the last name of the user
1371
     *
1372
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1373
     *                Note: When the method is called several times with same parameters,
1374
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1375
     *
1376
     * @author Ivan Tcholakov, 2009
1377
     */
1378
    public static function create_unique_username($firstname, $lastname = null)
1379
    {
1380
        if (is_null($lastname)) {
1381
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1382
            // For making this method tolerant of mistakes,
1383
            // let us transliterate and purify the suggested input username anyway.
1384
            // So, instead of the sentence $username = $firstname; we place the following:
1385
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1386
        } else {
1387
            $username = self::create_username($firstname, $lastname);
1388
        }
1389
        if (!self::is_username_available($username)) {
1390
            $i = 2;
1391
            $temp_username = substr($username, 0, User::USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1392
            while (!self::is_username_available($temp_username)) {
1393
                $i++;
1394
                $temp_username = substr($username, 0, User::USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1395
            }
1396
            $username = $temp_username;
1397
        }
1398
1399
        $username = URLify::transliterate($username);
1400
1401
        return $username;
1402
    }
1403
1404
    /**
1405
     * Modifies a given username accordingly to the specification for valid characters and length.
1406
     *
1407
     * @param $username string          The input username
1408
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1409
     *                     otherwise compliance may be partial. The default value is FALSE.
1410
     *
1411
     * @return string the resulting purified username
1412
     */
1413
    public static function purify_username($username, $strict = false)
1414
    {
1415
        if ($strict) {
1416
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1417
            // into ASCII letters in order they not to be totally removed.
1418
            // 2. Applying the strict purifier.
1419
            // 3. Length limitation.
1420
            $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);
1421
            $return = URLify::transliterate($return);
1422
1423
            // We want everything transliterate() does except converting @ to '(at)'. This is a hack to avoid this.
1424
            $return = str_replace(' (at) ', '@', $return);
1425
1426
            return $return;
1427
        }
1428
1429
        // 1. Applying the shallow purifier.
1430
        // 2. Length limitation.
1431
        return substr(
1432
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1433
            0,
1434
            User::USERNAME_MAX_LENGTH
1435
        );
1436
    }
1437
1438
    /**
1439
     * Checks whether the user id exists in the database.
1440
     *
1441
     * @param int $userId User id
1442
     *
1443
     * @return bool True if user id was found, false otherwise
1444
     */
1445
    public static function is_user_id_valid($userId)
1446
    {
1447
        $resultData = Database::select(
1448
            'COUNT(1) AS count',
1449
            Database::get_main_table(TABLE_MAIN_USER),
1450
            [
1451
                'where' => ['id = ?' => (int) $userId],
1452
            ],
1453
            'first'
1454
        );
1455
1456
        if (false === $resultData) {
1457
            return false;
1458
        }
1459
1460
        return $resultData['count'] > 0;
1461
    }
1462
1463
    /**
1464
     * Checks whether a given username matches to the specification strictly.
1465
     * The empty username is assumed here as invalid.
1466
     * Mostly this function is to be used in the user interface built-in validation routines
1467
     * for providing feedback while usernames are enterd manually.
1468
     *
1469
     * @param string $username the input username
1470
     *
1471
     * @return bool returns TRUE if the username is valid, FALSE otherwise
1472
     */
1473
    public static function is_username_valid($username)
1474
    {
1475
        return !empty($username) && $username == self::purify_username($username, true);
1476
    }
1477
1478
    /**
1479
     * Checks whether a username is empty. If the username contains whitespace characters,
1480
     * such as spaces, tabulators, newlines, etc.,
1481
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
1482
     *
1483
     * @param string $username the given username
1484
     *
1485
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1486
     */
1487
    public static function is_username_empty($username)
1488
    {
1489
        return 0 == strlen(self::purify_username($username, false));
1490
    }
1491
1492
    /**
1493
     * Checks whether a username is too long or not.
1494
     *
1495
     * @param string $username the given username, it should contain only ASCII-letters and digits
1496
     *
1497
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1498
     */
1499
    public static function is_username_too_long($username)
1500
    {
1501
        return strlen($username) > User::USERNAME_MAX_LENGTH;
1502
    }
1503
1504
    /**
1505
     * Get the users by ID.
1506
     *
1507
     * @param array  $ids    student ids
1508
     * @param bool   $active
1509
     * @param string $order
1510
     * @param string $limit
1511
     *
1512
     * @return array $result student information
1513
     */
1514
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
1515
    {
1516
        if (empty($ids)) {
1517
            return [];
1518
        }
1519
1520
        $ids = is_array($ids) ? $ids : [$ids];
1521
        $ids = array_map('intval', $ids);
1522
        $ids = implode(',', $ids);
1523
1524
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
1525
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
1526
        if (!is_null($active)) {
1527
            $sql .= ' AND active='.($active ? '1' : '0');
1528
        }
1529
1530
        if (!is_null($order)) {
1531
            $order = Database::escape_string($order);
1532
            $sql .= ' ORDER BY '.$order;
1533
        }
1534
1535
        if (!is_null($limit)) {
1536
            $limit = Database::escape_string($limit);
1537
            $sql .= ' LIMIT '.$limit;
1538
        }
1539
1540
        $rs = Database::query($sql);
1541
        $result = [];
1542
        while ($row = Database::fetch_array($rs)) {
1543
            $result[] = $row;
1544
        }
1545
1546
        return $result;
1547
    }
1548
1549
    /**
1550
     * Get a list of users of which the given conditions match with an = 'cond'.
1551
     *
1552
     * @param array $conditions a list of condition (example : status=>STUDENT)
1553
     * @param array $order_by   a list of fields on which sort
1554
     *
1555
     * @return array an array with all users of the platform
1556
     *
1557
     * @todo security filter order by
1558
     */
1559
    public static function get_user_list(
1560
        $conditions = [],
1561
        $order_by = [],
1562
        $limit_from = false,
1563
        $limit_to = false,
1564
        $idCampus = null
1565
    ) {
1566
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1567
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1568
        $return_array = [];
1569
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1570
1571
        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 AccessUrlUtil::isMultiple ( Ignorable by Annotation )

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

1571
        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...
1572
            if ($idCampus) {
1573
                $urlId = $idCampus;
1574
            } else {
1575
                $urlId = api_get_current_access_url_id();
1576
            }
1577
            $sql .= " INNER JOIN $userUrlTable url_user
1578
                      ON (user.id = url_user.user_id)
1579
                      WHERE url_user.access_url_id = $urlId";
1580
        } else {
1581
            $sql .= " WHERE 1=1 ";
1582
        }
1583
1584
        if (count($conditions) > 0) {
1585
            foreach ($conditions as $field => $value) {
1586
                $field = Database::escape_string($field);
1587
                $value = Database::escape_string($value);
1588
                $sql .= " AND $field = '$value'";
1589
            }
1590
        }
1591
1592
        if (count($order_by) > 0) {
1593
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1594
        }
1595
1596
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1597
            $limit_from = (int) $limit_from;
1598
            $limit_to = (int) $limit_to;
1599
            $sql .= " LIMIT $limit_from, $limit_to";
1600
        }
1601
        $sql_result = Database::query($sql);
1602
        while ($result = Database::fetch_array($sql_result)) {
1603
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1604
            $return_array[] = $result;
1605
        }
1606
1607
        return $return_array;
1608
    }
1609
1610
    public static function getUserListExtraConditions(
1611
        $conditions = [],
1612
        $order_by = [],
1613
        $limit_from = false,
1614
        $limit_to = false,
1615
        $idCampus = null,
1616
        $extraConditions = '',
1617
        $getCount = false
1618
    ) {
1619
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1620
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1621
        $return_array = [];
1622
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1623
1624
        if ($getCount) {
1625
            $sql = "SELECT count(user.id) count FROM $user_table user ";
1626
        }
1627
1628
        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 AccessUrlUtil::isMultiple ( Ignorable by Annotation )

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

1628
        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...
1629
            if ($idCampus) {
1630
                $urlId = $idCampus;
1631
            } else {
1632
                $urlId = api_get_current_access_url_id();
1633
            }
1634
            $sql .= " INNER JOIN $userUrlTable url_user
1635
                      ON (user.id = url_user.user_id)
1636
                      WHERE url_user.access_url_id = $urlId";
1637
        } else {
1638
            $sql .= " WHERE 1=1 ";
1639
        }
1640
1641
        $sql .= " AND status <> ".ANONYMOUS." ";
1642
1643
        if (count($conditions) > 0) {
1644
            foreach ($conditions as $field => $value) {
1645
                $field = Database::escape_string($field);
1646
                $value = Database::escape_string($value);
1647
                $sql .= " AND $field = '$value'";
1648
            }
1649
        }
1650
1651
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
1652
1653
        if (!empty($order_by) && count($order_by) > 0) {
1654
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1655
        }
1656
1657
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1658
            $limit_from = (int) $limit_from;
1659
            $limit_to = (int) $limit_to;
1660
            $sql .= " LIMIT $limit_from, $limit_to";
1661
        }
1662
1663
        $sql_result = Database::query($sql);
1664
1665
        if ($getCount) {
1666
            $result = Database::fetch_array($sql_result);
1667
1668
            return $result['count'];
1669
        }
1670
1671
        while ($result = Database::fetch_array($sql_result)) {
1672
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1673
            $return_array[] = $result;
1674
        }
1675
1676
        return $return_array;
1677
    }
1678
1679
    /**
1680
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
1681
     *
1682
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
1683
     * @param array  $order_by         a list of fields on which sort
1684
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
1685
     * @param string $condition        Whether we want the filters to be combined by AND or OR
1686
     * @param array  $onlyThisUserList
1687
     *
1688
     * @return array an array with all users of the platform
1689
     *
1690
     * @todo optional course code parameter, optional sorting parameters...
1691
     * @todo security filter order_by
1692
     */
1693
    public static function getUserListLike(
1694
        $conditions = [],
1695
        $order_by = [],
1696
        $simple_like = false,
1697
        $condition = 'AND',
1698
        $onlyThisUserList = [],
1699
        int $limit = 0,
1700
        int $offset = 0
1701
    ) {
1702
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1703
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1704
        $return_array = [];
1705
        $sql_query = "SELECT user.id, user.username, user.firstname, user.lastname, user.official_code, user.status
1706
                  FROM $user_table user ";
1707
1708
        if (api_get_multiple_access_url()) {
1709
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
1710
        }
1711
1712
        $sql_query .= ' WHERE 1 = 1 ';
1713
        if (count($conditions) > 0) {
1714
            $temp_conditions = [];
1715
            foreach ($conditions as $field => $value) {
1716
                $field = Database::escape_string($field);
1717
                $value = Database::escape_string($value);
1718
                if ($simple_like) {
1719
                    $temp_conditions[] = "$field LIKE '$value%'";
1720
                } else {
1721
                    if (in_array($field, ['user.id', 'user.status'])) {
1722
                        $temp_conditions[] = "$field = '$value'";
1723
                    } else {
1724
                        $temp_conditions[] = "$field LIKE '%$value%'";
1725
                    }
1726
                }
1727
            }
1728
            if (!empty($temp_conditions)) {
1729
                $sql_query .= ' AND '.implode(" $condition ", $temp_conditions);
1730
            }
1731
1732
            if (api_get_multiple_access_url()) {
1733
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1734
            }
1735
        } else {
1736
            if (api_get_multiple_access_url()) {
1737
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1738
            }
1739
        }
1740
1741
        if (!empty($onlyThisUserList)) {
1742
            $onlyThisUserListToString = implode("','", array_map('intval', $onlyThisUserList));
1743
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
1744
        }
1745
1746
        if (!empty($order_by)) {
1747
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1748
        }
1749
1750
        if ($limit > 0) {
1751
            $sql_query .= ' LIMIT '.intval($limit);
1752
            if ($offset > 0) {
1753
                $sql_query .= ' OFFSET '.intval($offset);
1754
            }
1755
        }
1756
1757
        $sql_result = Database::query($sql_query);
1758
        while ($result = Database::fetch_array($sql_result)) {
1759
            $return_array[] = $result;
1760
        }
1761
1762
        return $return_array;
1763
    }
1764
1765
    /**
1766
     * Get user path from user ID (returns an array).
1767
     * The return format is a complete path to a folder ending with "/"
1768
     * In case the first level of subdirectory of users/ does not exist, the
1769
     * function will attempt to create it. Probably not the right place to do it
1770
     * but at least it avoids headaches in many other places.
1771
     *
1772
     * @param int    $id   User ID
1773
     * @param string $type Type of path to return (can be 'system', 'web', 'last')
1774
     *
1775
     * @return string User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
1776
     */
1777
    public static function getUserPathById($id, $type)
1778
    {
1779
        $id = (int) $id;
1780
        if (!$id) {
1781
            return null;
1782
        }
1783
1784
        $userPath = "users/$id/";
1785
        if (api_get_setting('split_users_upload_directory') === 'true') {
1786
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
1787
            // In exceptional cases, on some portals, the intermediate base user
1788
            // directory might not have been created. Make sure it is before
1789
            // going further.
1790
1791
            $rootPath = api_get_path(SYS_PATH).'../app/upload/users/'.substr((string) $id, 0, 1);
1792
            if (!is_dir($rootPath)) {
1793
                $perm = api_get_permissions_for_new_directories();
1794
                try {
1795
                    mkdir($rootPath, $perm);
1796
                } catch (Exception $e) {
1797
                    error_log($e->getMessage());
1798
                }
1799
            }
1800
        }
1801
        switch ($type) {
1802
            case 'system': // Base: absolute system path.
1803
                $userPath = api_get_path(SYS_PATH).'../app/upload/'.$userPath;
1804
                break;
1805
            case 'web': // Base: absolute web path.
1806
                $userPath = api_get_path(WEB_PATH).'../app/upload/'.$userPath;
1807
                break;
1808
            case 'last': // Only the last part starting with users/
1809
                break;
1810
        }
1811
1812
        return $userPath;
1813
    }
1814
1815
    /**
1816
     * Gets the current user image.
1817
     *
1818
     * @param string $userId
1819
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
1820
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
1821
     * @param bool   $addRandomId
1822
     * @param array  $userInfo    to avoid query the DB
1823
     *
1824
     * @todo add gravatar support
1825
     * @todo replace $userId with User entity
1826
     *
1827
     * @return string
1828
     */
1829
    public static function getUserPicture(
1830
        $userId,
1831
        int $size = USER_IMAGE_SIZE_MEDIUM,
1832
        $addRandomId = true,
1833
        $userInfo = []
1834
    ) {
1835
        $user = api_get_user_entity($userId);
1836
        $illustrationRepo = Container::getIllustrationRepository();
1837
1838
        switch ($size) {
1839
            case USER_IMAGE_SIZE_SMALL:
1840
                $width = 32;
1841
                break;
1842
            case USER_IMAGE_SIZE_MEDIUM:
1843
                $width = 64;
1844
                break;
1845
            case USER_IMAGE_SIZE_BIG:
1846
                $width = 128;
1847
                break;
1848
            case USER_IMAGE_SIZE_ORIGINAL:
1849
            default:
1850
                $width = 0;
1851
                break;
1852
        }
1853
1854
        $url = $illustrationRepo->getIllustrationUrl($user);
1855
        $params = [];
1856
        if (!empty($width)) {
1857
            $params['w'] = $width;
1858
        }
1859
1860
        if ($addRandomId) {
1861
            $params['rand'] = uniqid('u_', true);
1862
        }
1863
1864
        $paramsToString = '';
1865
        if (!empty($params)) {
1866
            $paramsToString = '?'.http_build_query($params);
1867
        }
1868
1869
        return $url.$paramsToString;
1870
1871
        /*
1872
        // Make sure userInfo is defined. Otherwise, define it!
1873
        if (empty($userInfo) || !is_array($userInfo) || 0 == count($userInfo)) {
1874
            if (empty($user_id)) {
1875
                return '';
1876
            } else {
1877
                $userInfo = api_get_user_info($user_id);
1878
            }
1879
        }
1880
1881
        $imageWebPath = self::get_user_picture_path_by_id(
1882
            $user_id,
1883
            'web',
1884
            $userInfo
1885
        );
1886
        $pictureWebFile = $imageWebPath['file'];
1887
        $pictureWebDir = $imageWebPath['dir'];
1888
1889
        $pictureAnonymousSize = '128';
1890
        $gravatarSize = 22;
1891
        $realSizeName = 'small_';
1892
1893
        switch ($size) {
1894
            case USER_IMAGE_SIZE_SMALL:
1895
                $pictureAnonymousSize = '32';
1896
                $realSizeName = 'small_';
1897
                $gravatarSize = 32;
1898
                break;
1899
            case USER_IMAGE_SIZE_MEDIUM:
1900
                $pictureAnonymousSize = '64';
1901
                $realSizeName = 'medium_';
1902
                $gravatarSize = 64;
1903
                break;
1904
            case USER_IMAGE_SIZE_ORIGINAL:
1905
                $pictureAnonymousSize = '128';
1906
                $realSizeName = '';
1907
                $gravatarSize = 128;
1908
                break;
1909
            case USER_IMAGE_SIZE_BIG:
1910
                $pictureAnonymousSize = '128';
1911
                $realSizeName = 'big_';
1912
                $gravatarSize = 128;
1913
                break;
1914
        }
1915
1916
        $gravatarEnabled = api_get_setting('gravatar_enabled');
1917
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
1918
        if ('unknown.jpg' == $pictureWebFile || empty($pictureWebFile)) {
1919
            if ('true' === $gravatarEnabled) {
1920
                $file = self::getGravatar(
1921
                    $imageWebPath['email'],
1922
                    $gravatarSize,
1923
                    api_get_setting('gravatar_type')
1924
                );
1925
1926
                if ($addRandomId) {
1927
                    $file .= '&rand='.uniqid();
1928
                }
1929
1930
                return $file;
1931
            }
1932
1933
            return $anonymousPath;
1934
        }
1935
1936
        if ($addRandomId) {
1937
            $picture .= '?rand='.uniqid();
1938
        }
1939
1940
        return $picture;*/
1941
    }
1942
1943
    /**
1944
     * Creates new user photos in various sizes of a user, or deletes user photos.
1945
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
1946
     *
1947
     * @param int    $user_id the user internal identification number
1948
     * @param string $file    The common file name for the newly created photos.
1949
     *                        It will be checked and modified for compatibility with the file system.
1950
     *                        If full name is provided, path component is ignored.
1951
     *                        If an empty name is provided, then old user photos are deleted only,
1952
     *
1953
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
1954
     *
1955
     * @param string $source_file    the full system name of the image from which user photos will be created
1956
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
1957
     *
1958
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
1959
     *              When deletion is requested returns empty string.
1960
     *              In case of internal error or negative validation returns FALSE.
1961
     */
1962
    public static function update_user_picture($userId, UploadedFile $file, string $crop = '')
1963
    {
1964
        if (empty($userId) || empty($file)) {
1965
            return false;
1966
        }
1967
1968
        $repo = Container::getUserRepository();
1969
        $user = $repo->find($userId);
1970
        if ($user) {
1971
            $repoIllustration = Container::getIllustrationRepository();
1972
            $repoIllustration->addIllustration($user, $user, $file, $crop);
1973
        }
1974
    }
1975
1976
    /**
1977
     * Deletes user photos.
1978
     *
1979
     * @param int $userId the user internal identification number
1980
     *
1981
     * @return mixed returns empty string on success, FALSE on error
1982
     */
1983
    public static function deleteUserPicture($userId)
1984
    {
1985
        $repo = Container::getUserRepository();
1986
        $user = $repo->find($userId);
1987
        if ($user) {
1988
            $illustrationRepo = Container::getIllustrationRepository();
1989
            $illustrationRepo->deleteIllustration($user);
1990
        }
1991
    }
1992
1993
    /**
1994
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
1995
     * doesn't have any.
1996
     *
1997
     * If there has been a request to remove a production, the function will return
1998
     * without building the list unless forced to do so by the optional second
1999
     * parameter. This increases performance by avoiding to read through the
2000
     * productions on the filesystem before the removal request has been carried
2001
     * out because they'll have to be re-read afterwards anyway.
2002
     *
2003
     * @deprecated This method is being removed from chamilo 2.0
2004
     * @param int  $user_id    User id
2005
     * @param bool $force      Optional parameter to force building after a removal request
2006
     * @param bool $showDelete
2007
     *
2008
     * @return string A string containing the XHTML code to display the production list, or FALSE
2009
     */
2010
    public static function build_production_list($user_id, $force = false, $showDelete = false)
2011
    {
2012
        if (!$force && !empty($_POST['remove_production'])) {
2013
            return true; // postpone reading from the filesystem
2014
        }
2015
2016
        $productions = self::get_user_productions($user_id);
2017
2018
        if (empty($productions)) {
2019
            return false;
2020
        }
2021
2022
        return false;
2023
2024
        $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...
2025
        $del_image = Display::returnIconPath('delete.png');
2026
        $add_image = Display::returnIconPath('archive.png');
2027
        $del_text = get_lang('Delete');
2028
        $production_list = '';
2029
        if (count($productions) > 0) {
2030
            $production_list = '<div class="files-production"><ul id="productions">';
2031
            foreach ($productions as $file) {
2032
                $production_list .= '<li>
2033
                    <img src="'.$add_image.'" />
2034
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2035
                        '.htmlentities($file).'
2036
                    </a>';
2037
                if ($showDelete) {
2038
                    $production_list .= '&nbsp;&nbsp;
2039
                        <input
2040
                            style="width:16px;"
2041
                            type="image"
2042
                            name="remove_production['.urlencode($file).']"
2043
                            src="'.$del_image.'"
2044
                            alt="'.$del_text.'"
2045
                            title="'.$del_text.' '.htmlentities($file).'"
2046
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2047
                }
2048
            }
2049
            $production_list .= '</ul></div>';
2050
        }
2051
2052
        return $production_list;
2053
    }
2054
2055
    /**
2056
     * Returns an array with the user's productions.
2057
     *
2058
     * @param int $user_id User id
2059
     *
2060
     * @return array An array containing the user's productions
2061
     */
2062
    public static function get_user_productions($user_id)
2063
    {
2064
        return [];
2065
2066
        $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...
2067
        $productions = [];
2068
2069
        if (is_dir($production_repository)) {
2070
            $handle = opendir($production_repository);
2071
            while ($file = readdir($handle)) {
2072
                if ('.' == $file ||
2073
                    '..' == $file ||
2074
                    '.htaccess' == $file ||
2075
                    is_dir($production_repository.$file)
2076
                ) {
2077
                    // skip current/parent directory and .htaccess
2078
                    continue;
2079
                }
2080
2081
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2082
                    // User's photos should not be listed as productions.
2083
                    continue;
2084
                }
2085
                $productions[] = $file;
2086
            }
2087
        }
2088
2089
        return $productions;
2090
    }
2091
2092
    /**
2093
     * Remove a user production.
2094
     *
2095
     * @param int    $user_id    User id
2096
     * @param string $production The production to remove
2097
     *
2098
     * @return bool
2099
     */
2100
    public static function remove_user_production($user_id, $production)
2101
    {
2102
        throw new Exception('remove_user_production');
2103
        /*$production_path = self::get_user_picture_path_by_id($user_id, 'system');
2104
        $production_file = $production_path['dir'].$production;
2105
        if (is_file($production_file)) {
2106
            unlink($production_file);
2107
2108
            return true;
2109
        }
2110
2111
        return false;*/
2112
    }
2113
2114
    /**
2115
     * Update an extra field value for a given user.
2116
     *
2117
     * @param int    $userId   User ID
2118
     * @param string $variable Field variable name
2119
     * @param string $value    Field value
2120
     *
2121
     * @return bool true if field updated, false otherwise
2122
     */
2123
    public static function update_extra_field_value($userId, $variable, $value = '')
2124
    {
2125
        $extraFieldValue = new ExtraFieldValue('user');
2126
        $params = [
2127
            'item_id' => $userId,
2128
            'variable' => $variable,
2129
            'field_value' => $value,
2130
        ];
2131
2132
        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...
2133
    }
2134
2135
    /**
2136
     * Get an array of extra fields with field details (type, default value and options).
2137
     *
2138
     * @param    int    Offset (from which row)
2139
     * @param    int    Number of items
2140
     * @param    int    Column on which sorting is made
2141
     * @param    string    Sorting direction
2142
     * @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...
2143
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2144
     *
2145
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2146
     */
2147
    public static function get_extra_fields(
2148
        $from = 0,
2149
        $number_of_items = 0,
2150
        $column = 5,
2151
        $direction = 'ASC',
2152
        $all_visibility = true,
2153
        $field_filter = null
2154
    ) {
2155
        $fields = [];
2156
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2157
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2158
        $columns = [
2159
            'id',
2160
            'variable',
2161
            'value_type',
2162
            'display_text',
2163
            'default_value',
2164
            'field_order',
2165
            'filter',
2166
        ];
2167
        $column = (int) $column;
2168
        $sort_direction = '';
2169
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2170
            $sort_direction = strtoupper($direction);
2171
        }
2172
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2173
        $sqlf = "SELECT * FROM $t_uf WHERE item_type = $extraFieldType ";
2174
        if (!$all_visibility) {
2175
            $sqlf .= " AND visible_to_self = 1 ";
2176
        }
2177
        if (!is_null($field_filter)) {
2178
            $field_filter = (int) $field_filter;
2179
            $sqlf .= " AND filter = $field_filter ";
2180
        }
2181
        $sqlf .= " ORDER BY `".$columns[$column]."` $sort_direction ";
2182
        if (0 != $number_of_items) {
2183
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2184
        }
2185
        $resf = Database::query($sqlf);
2186
        if (Database::num_rows($resf) > 0) {
2187
            while ($rowf = Database::fetch_array($resf)) {
2188
                $fields[$rowf['id']] = [
2189
                    0 => $rowf['id'],
2190
                    1 => $rowf['variable'],
2191
                    2 => $rowf['value_type'],
2192
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2193
                    4 => $rowf['default_value'],
2194
                    5 => $rowf['field_order'],
2195
                    6 => $rowf['visible_to_self'],
2196
                    7 => $rowf['changeable'],
2197
                    8 => $rowf['filter'],
2198
                    9 => [],
2199
                    10 => '<a name="'.$rowf['id'].'"></a>',
2200
                ];
2201
2202
                $sqlo = "SELECT * FROM $t_ufo
2203
                         WHERE field_id = ".$rowf['id']."
2204
                         ORDER BY option_order ASC";
2205
                $reso = Database::query($sqlo);
2206
                if (Database::num_rows($reso) > 0) {
2207
                    while ($rowo = Database::fetch_array($reso)) {
2208
                        $fields[$rowf['id']][9][$rowo['id']] = [
2209
                            0 => $rowo['id'],
2210
                            1 => $rowo['option_value'],
2211
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2212
                            3 => $rowo['option_order'],
2213
                        ];
2214
                    }
2215
                }
2216
            }
2217
        }
2218
2219
        return $fields;
2220
    }
2221
2222
    /**
2223
     * Creates a new extra field.
2224
     *
2225
     * @param string $variable    Field's internal variable name
2226
     * @param int    $fieldType   Field's type
2227
     * @param string $displayText Field's language var name
2228
     * @param string $default     Field's default value
2229
     *
2230
     * @return int
2231
     */
2232
    public static function create_extra_field(
2233
        $variable,
2234
        $valueType,
2235
        $displayText,
2236
        $default
2237
    ) {
2238
        $extraField = new ExtraField('user');
2239
        $params = [
2240
            'variable' => $variable,
2241
            'value_type' => $valueType,
2242
            'display_text' => $displayText,
2243
            'default_value' => $default,
2244
        ];
2245
2246
        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...
2247
    }
2248
2249
    /**
2250
     * Check if a field is available.
2251
     *
2252
     * @param string $variable
2253
     *
2254
     * @return bool
2255
     */
2256
    public static function is_extra_field_available($variable)
2257
    {
2258
        $extraField = new ExtraField('user');
2259
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2260
2261
        return !empty($data) ? true : false;
2262
    }
2263
2264
    /**
2265
     * Gets user extra fields data.
2266
     *
2267
     * @param    int    User ID
2268
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2269
     * @param    bool    Whether to return invisible fields as well
2270
     * @param    bool    Whether to split multiple-selection fields or not
2271
     *
2272
     * @return array Array of fields => value for the given user
2273
     */
2274
    public static function get_extra_user_data(
2275
        $user_id,
2276
        $prefix = false,
2277
        $allVisibility = true,
2278
        $splitMultiple = false,
2279
        $fieldFilter = null
2280
    ) {
2281
        $user_id = (int) $user_id;
2282
2283
        if (empty($user_id)) {
2284
            return [];
2285
        }
2286
2287
        $extra_data = [];
2288
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2289
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2290
        $sql = "SELECT f.id as id, f.variable as fvar, f.value_type as type
2291
                FROM $t_uf f
2292
                WHERE
2293
                    item_type = ".EntityExtraField::USER_FIELD_TYPE."
2294
                ";
2295
        $filter_cond = '';
2296
2297
        if (!$allVisibility) {
2298
            if (isset($fieldFilter)) {
2299
                $fieldFilter = (int) $fieldFilter;
2300
                $filter_cond .= " AND filter = $fieldFilter ";
2301
            }
2302
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2303
        } else {
2304
            if (isset($fieldFilter)) {
2305
                $fieldFilter = (int) $fieldFilter;
2306
                $sql .= " AND filter = $fieldFilter ";
2307
            }
2308
        }
2309
2310
        $sql .= ' ORDER BY f.field_order';
2311
2312
        $res = Database::query($sql);
2313
        if (Database::num_rows($res) > 0) {
2314
            while ($row = Database::fetch_array($res)) {
2315
                if (self::USER_FIELD_TYPE_TAG == $row['type']) {
2316
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2317
                    $extra_data['extra_'.$row['fvar']] = $tags;
2318
                } else {
2319
                    $sqlu = "SELECT field_value as fval
2320
                            FROM $t_ufv
2321
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2322
                    $resu = Database::query($sqlu);
2323
                    // get default value
2324
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2325
                               WHERE id=".$row['id'];
2326
                    $res_df = Database::query($sql_df);
2327
2328
                    if (Database::num_rows($resu) > 0) {
2329
                        $rowu = Database::fetch_array($resu);
2330
                        $fval = $rowu['fval'];
2331
                        if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2332
                            $fval = explode(';', $rowu['fval']);
2333
                        }
2334
                    } else {
2335
                        $row_df = Database::fetch_array($res_df);
2336
                        $fval = $row_df['fval_df'];
2337
                    }
2338
                    // We get here (and fill the $extra_data array) even if there
2339
                    // is no user with data (we fill it with default values)
2340
                    if ($prefix) {
2341
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2342
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2343
                        } else {
2344
                            $extra_data['extra_'.$row['fvar']] = $fval;
2345
                        }
2346
                    } else {
2347
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2348
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2349
                        } else {
2350
                            $extra_data[$row['fvar']] = $fval;
2351
                        }
2352
                    }
2353
                }
2354
            }
2355
        }
2356
2357
        return $extra_data;
2358
    }
2359
2360
    /** Get extra user data by field.
2361
     * @param int    user ID
2362
     * @param string the internal variable name of the field
2363
     *
2364
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2365
     */
2366
    public static function get_extra_user_data_by_field(
2367
        $user_id,
2368
        $field_variable,
2369
        $prefix = false,
2370
        $all_visibility = true,
2371
        $splitmultiple = false
2372
    ) {
2373
        $user_id = (int) $user_id;
2374
2375
        if (empty($user_id)) {
2376
            return [];
2377
        }
2378
2379
        $extra_data = [];
2380
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2381
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2382
2383
        $sql = "SELECT f.id as id, f.variable as fvar, f.value_type as type
2384
                FROM $t_uf f
2385
                WHERE f.variable = '$field_variable' ";
2386
2387
        if (!$all_visibility) {
2388
            $sql .= " AND f.visible_to_self = 1 ";
2389
        }
2390
2391
        $sql .= " AND item_type = ".EntityExtraField::USER_FIELD_TYPE;
2392
        $sql .= " ORDER BY f.field_order ";
2393
2394
        $res = Database::query($sql);
2395
        if (Database::num_rows($res) > 0) {
2396
            while ($row = Database::fetch_array($res)) {
2397
                $sqlu = "SELECT field_value as fval FROM $t_ufv v
2398
                         INNER JOIN $t_uf f
2399
                         ON (v.field_id = f.id)
2400
                         WHERE
2401
                            item_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2402
                            field_id = ".$row['id']." AND
2403
                            item_id = ".$user_id;
2404
                $resu = Database::query($sqlu);
2405
                $fval = '';
2406
                if (Database::num_rows($resu) > 0) {
2407
                    $rowu = Database::fetch_array($resu);
2408
                    $fval = $rowu['fval'];
2409
                    if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2410
                        $fval = explode(';', $rowu['fval']);
2411
                    }
2412
                }
2413
                if ($prefix) {
2414
                    $extra_data['extra_'.$row['fvar']] = $fval;
2415
                } else {
2416
                    $extra_data[$row['fvar']] = $fval;
2417
                }
2418
            }
2419
        }
2420
2421
        return $extra_data;
2422
    }
2423
2424
    /**
2425
     * Get the extra field information for a certain field (the options as well).
2426
     *
2427
     * @param int $variable The name of the field we want to know everything about
2428
     *
2429
     * @return array Array containing all the information about the extra profile field
2430
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2431
     *               as returned by the database)
2432
     *
2433
     * @author Julio Montoya
2434
     *
2435
     * @since v1.8.6
2436
     */
2437
    public static function get_extra_field_information_by_name($variable)
2438
    {
2439
        $extraField = new ExtraField('user');
2440
2441
        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...
2442
    }
2443
2444
    /**
2445
     * Get the extra field information for user tag (the options as well).
2446
     *
2447
     * @param int $variable The name of the field we want to know everything about
2448
     *
2449
     * @return array Array containing all the information about the extra profile field
2450
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2451
     *               as returned by the database)
2452
     *
2453
     * @author José Loguercio
2454
     *
2455
     * @since v1.11.0
2456
     */
2457
    public static function get_extra_field_tags_information_by_name($variable)
2458
    {
2459
        $extraField = new ExtraField('user');
2460
2461
        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...
2462
    }
2463
2464
    /**
2465
     * Get all the extra field information of a certain field (also the options).
2466
     *
2467
     * @param int $fieldId the ID of the field we want to know everything of
2468
     *
2469
     * @return array $return containing all th information about the extra profile field
2470
     *
2471
     * @author Julio Montoya
2472
     *
2473
     * @deprecated
2474
     * @since v1.8.6
2475
     */
2476
    public static function get_extra_field_information($fieldId)
2477
    {
2478
        $extraField = new ExtraField('user');
2479
2480
        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...
2481
    }
2482
2483
    /**
2484
     * Get extra user data by value.
2485
     *
2486
     * @param string $variable       the internal variable name of the field
2487
     * @param string $value          the internal value of the field
2488
     * @param bool   $all_visibility
2489
     *
2490
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2491
     */
2492
    public static function get_extra_user_data_by_value($variable, $value, $all_visibility = true)
2493
    {
2494
        $extraFieldValue = new ExtraFieldValue('user');
2495
        $extraField = new ExtraField('user');
2496
2497
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2498
2499
        if (false === $info) {
2500
            return [];
2501
        }
2502
2503
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2504
            $variable,
2505
            $value,
2506
            false,
2507
            false,
2508
            true
2509
        );
2510
2511
        $result = [];
2512
        if (!empty($data)) {
2513
            foreach ($data as $item) {
2514
                $result[] = $item['item_id'];
2515
            }
2516
        }
2517
2518
        return $result;
2519
    }
2520
2521
    /**
2522
     * Get extra user data by tags value.
2523
     *
2524
     * @param int    $fieldId the ID of the field we want to know everything of
2525
     * @param string $tag     the tag name for search
2526
     *
2527
     * @return array with extra data info of a user
2528
     *
2529
     * @author José Loguercio
2530
     *
2531
     * @since v1.11.0
2532
     */
2533
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2534
    {
2535
        $extraField = new ExtraField('user');
2536
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2537
        $array = [];
2538
        foreach ($result as $index => $user) {
2539
            $array[] = $user['user_id'];
2540
        }
2541
2542
        return $array;
2543
    }
2544
2545
    /**
2546
     * Get extra user data by field variable.
2547
     *
2548
     * @param string $variable field variable
2549
     *
2550
     * @return array data
2551
     */
2552
    public static function get_extra_user_data_by_field_variable($variable)
2553
    {
2554
        $extraInfo = self::get_extra_field_information_by_name($variable);
2555
        $field_id = (int) $extraInfo['id'];
2556
2557
        $extraField = new ExtraFieldValue('user');
2558
        $data = $extraField->getValuesByFieldId($field_id);
2559
2560
        if (!empty($data)) {
2561
            foreach ($data as $row) {
2562
                $user_id = $row['item_id'];
2563
                $data[$user_id] = $row;
2564
            }
2565
        }
2566
2567
        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...
2568
    }
2569
2570
    /**
2571
     * Get extra user data tags by field variable.
2572
     *
2573
     * @param string $variable field variable
2574
     *
2575
     * @return array
2576
     */
2577
    public static function get_extra_user_data_for_tags($variable)
2578
    {
2579
        $data = self::get_extra_field_tags_information_by_name($variable);
2580
2581
        return $data;
2582
    }
2583
2584
    /**
2585
     * Gives a list of [session_category][session_id] for the current user.
2586
     *
2587
     * @param int  $user_id
2588
     * @param bool $is_time_over                 whether to fill the first element or not
2589
     *                                           (to give space for courses out of categories)
2590
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2591
     * @param bool $ignoreTimeLimit              ignore time start/end
2592
     * @param bool $getCount
2593
     *
2594
     * @return array list of statuses [session_category][session_id]
2595
     *
2596
     * @todo ensure multiple access urls are managed correctly
2597
     */
2598
    public static function get_sessions_by_category(
2599
        $user_id,
2600
        $is_time_over = true,
2601
        $ignore_visibility_for_admins = false,
2602
        $ignoreTimeLimit = false,
2603
        $getCount = false
2604
    ) {
2605
        $user_id = (int) $user_id;
2606
2607
        if (empty($user_id)) {
2608
            return [];
2609
        }
2610
2611
        // Get the list of sessions per user
2612
        $now = new DateTime('now', new DateTimeZone('UTC'));
2613
2614
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2615
        // join would not catch session-courses where the user is general
2616
        // session coach but which do not have students nor coaches registered
2617
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
2618
2619
        if (!$getCount) {
2620
            $dqlSelect = " DISTINCT
2621
                s.id,
2622
                s.title,
2623
                s.accessStartDate AS access_start_date,
2624
                s.accessEndDate AS access_end_date,
2625
                s.duration,
2626
                sc.id AS session_category_id,
2627
                sc.title AS session_category_title,
2628
                sc.dateStart AS session_category_date_start,
2629
                sc.dateEnd AS session_category_date_end,
2630
                s.coachAccessStartDate AS coach_access_start_date,
2631
                s.coachAccessEndDate AS coach_access_end_date,
2632
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
2633
                , s.position AS position
2634
            ";
2635
        }
2636
2637
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
2638
        // is awfully inefficient for large sets of data (1m25s for 58K
2639
        // sessions, BT#14115) but executing a similar query twice and grouping
2640
        // the results afterwards in PHP takes about 1/1000th of the time
2641
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
2642
        $dqlStudent = "SELECT $dqlSelect
2643
            FROM ChamiloCoreBundle:Session AS s
2644
            LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2645
            INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2646
            LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2647
            WHERE scu.user = :user AND url.url = :url ";
2648
        $dqlCoach = "SELECT $dqlSelect
2649
            FROM ChamiloCoreBundle:Session AS s
2650
            INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2651
            LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2652
            INNER JOIN ChamiloCoreBundle:SessionRelUser AS su WITH su.session = s
2653
            WHERE (su.user = :user AND su.relationType = ".SessionEntity::GENERAL_COACH.") AND url.url = :url ";
2654
2655
        // Default order
2656
        $order = 'ORDER BY sc.title, s.title';
2657
2658
        // Order by date if showing all sessions
2659
        $showAllSessions = ('true' === api_get_setting('course.show_all_sessions_on_my_course_page'));
2660
        if ($showAllSessions) {
2661
            $order = 'ORDER BY s.accessStartDate';
2662
        }
2663
2664
        // Order by position
2665
        if ('true' === api_get_setting('session.session_list_order')) {
2666
            $order = 'ORDER BY s.position';
2667
        }
2668
2669
        // Order by dates according to settings
2670
        $orderBySettings = api_get_setting('session.my_courses_session_order', true);
2671
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
2672
            $field = $orderBySettings['field'];
2673
            $orderSetting = $orderBySettings['order'];
2674
            switch ($field) {
2675
                case 'start_date':
2676
                    $order = " ORDER BY s.accessStartDate $orderSetting";
2677
                    break;
2678
                case 'end_date':
2679
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
2680
                    if ('asc' == $orderSetting) {
2681
                        // Put null values at the end
2682
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
2683
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
2684
                    }
2685
                    break;
2686
                case 'name':
2687
                case 'title':
2688
                    $order = " ORDER BY s.title $orderSetting ";
2689
                    break;
2690
            }
2691
        }
2692
2693
        $dqlStudent .= $order;
2694
        $dqlCoach .= $order;
2695
2696
        $accessUrlId = api_get_current_access_url_id();
2697
        $dqlStudent = Database::getManager()
2698
            ->createQuery($dqlStudent)
2699
            ->setParameters(
2700
                ['user' => $user_id, 'url' => $accessUrlId]
2701
            )
2702
        ;
2703
        $dqlCoach = Database::getManager()
2704
            ->createQuery($dqlCoach)
2705
            ->setParameters(
2706
                ['user' => $user_id, 'url' => $accessUrlId]
2707
            )
2708
        ;
2709
2710
        if ($getCount) {
2711
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
2712
        }
2713
2714
        $sessionDataStudent = $dqlStudent->getResult();
2715
        $sessionDataCoach = $dqlCoach->getResult();
2716
2717
        $sessionData = [];
2718
        // First fill $sessionData with student sessions
2719
        if (!empty($sessionDataStudent)) {
2720
            foreach ($sessionDataStudent as $row) {
2721
                $sessionData[$row['id']] = $row;
2722
            }
2723
        }
2724
        // Overwrite session data of the user as a student with session data
2725
        // of the user as a coach.
2726
        // There shouldn't be such duplicate rows, but just in case...
2727
        if (!empty($sessionDataCoach)) {
2728
            foreach ($sessionDataCoach as $row) {
2729
                $sessionData[$row['id']] = $row;
2730
            }
2731
        }
2732
2733
        $collapsable = ('true' === api_get_setting('session.allow_user_session_collapsable'));
2734
2735
2736
2737
        $extraField = new ExtraFieldValue('session');
2738
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
2739
2740
        if (empty($sessionData)) {
2741
            return [];
2742
        }
2743
        $categories = [];
2744
        foreach ($sessionData as $row) {
2745
            $session_id = $row['id'];
2746
            $coachList = SessionManager::getCoachesBySession($session_id);
2747
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
2748
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
2749
            $courseList = self::get_courses_list_by_session($user_id, $session_id);
2750
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
2751
2752
            // User portal filters:
2753
            if (false === $ignoreTimeLimit) {
2754
                if ($is_time_over) {
2755
                    // History
2756
                    if ($row['duration']) {
2757
                        if ($daysLeft >= 0) {
2758
                            continue;
2759
                        }
2760
                    } else {
2761
                        if (empty($row['access_end_date'])) {
2762
                            continue;
2763
                        } else {
2764
                            if ($row['access_end_date'] > $now) {
2765
                                continue;
2766
                            }
2767
                        }
2768
                    }
2769
                } else {
2770
                    // Current user portal
2771
                    $isGeneralCoach = api_get_session_entity($row['id'])->hasUserAsGeneralCoach(api_get_user_entity($user_id));
2772
                    $isCoachOfCourse = in_array($user_id, $coachList);
2773
2774
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
2775
                        // Teachers can access the session depending in the access_coach date
2776
                    } else {
2777
                        if ($row['duration']) {
2778
                            if ($daysLeft <= 0) {
2779
                                continue;
2780
                            }
2781
                        } else {
2782
                            if (isset($row['access_end_date']) &&
2783
                                !empty($row['access_end_date'])
2784
                            ) {
2785
                                if ($row['access_end_date'] <= $now) {
2786
                                    continue;
2787
                                }
2788
                            }
2789
                        }
2790
                    }
2791
                }
2792
            }
2793
2794
            $categories[$row['session_category_id']]['session_category'] = [
2795
                'id' => $row['session_category_id'],
2796
                'name' => $row['session_category_title'],
2797
                'date_start' => $categoryStart,
2798
                'date_end' => $categoryEnd,
2799
            ];
2800
2801
            $visibility = api_get_session_visibility(
0 ignored issues
show
Deprecated Code introduced by
The function api_get_session_visibility() has been deprecated: Use Session::setAccessVisibilityByUser() instead. ( Ignorable by Annotation )

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

2801
            $visibility = /** @scrutinizer ignore-deprecated */ api_get_session_visibility(

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...
2802
                $session_id,
2803
                null,
2804
                $ignore_visibility_for_admins
2805
            );
2806
2807
            if (SESSION_VISIBLE != $visibility) {
2808
                // Course Coach session visibility.
2809
                $blockedCourseCount = 0;
2810
                $closedVisibilityList = [
2811
                    COURSE_VISIBILITY_CLOSED,
2812
                    COURSE_VISIBILITY_HIDDEN,
2813
                ];
2814
2815
                foreach ($courseList as $course) {
2816
                    // Checking session visibility
2817
                    $sessionCourseVisibility = api_get_session_visibility(
0 ignored issues
show
Deprecated Code introduced by
The function api_get_session_visibility() has been deprecated: Use Session::setAccessVisibilityByUser() instead. ( Ignorable by Annotation )

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

2817
                    $sessionCourseVisibility = /** @scrutinizer ignore-deprecated */ api_get_session_visibility(

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...
2818
                        $session_id,
2819
                        $course['real_id'],
2820
                        $ignore_visibility_for_admins
2821
                    );
2822
2823
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
2824
                    if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

2824
                    if (false === $courseIsVisible || /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $sessionCourseVisibility) {
Loading history...
2825
                        $blockedCourseCount++;
2826
                    }
2827
                }
2828
2829
                // If all courses are blocked then no show in the list.
2830
                if ($blockedCourseCount === count($courseList)) {
2831
                    $visibility = SESSION_INVISIBLE;
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

2831
                    $visibility = /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE;
Loading history...
2832
                } else {
2833
                    $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...
2834
                }
2835
            }
2836
2837
            switch ($visibility) {
2838
                case SESSION_VISIBLE_READ_ONLY:
2839
                case SESSION_VISIBLE:
2840
                case SESSION_AVAILABLE:
2841
                    break;
2842
                case SESSION_INVISIBLE:
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

2842
                case /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE:
Loading history...
2843
                    if (false === $ignore_visibility_for_admins) {
2844
                        continue 2;
2845
                    }
2846
            }
2847
2848
            $collapsed = '';
2849
            $collapsedAction = '';
2850
            if ($collapsable) {
2851
                $collapsableData = SessionManager::getCollapsableData(
2852
                    $user_id,
2853
                    $session_id,
2854
                    $extraField,
2855
                    $collapsableLink
2856
                );
2857
                $collapsed = $collapsableData['collapsed'];
2858
                $collapsedAction = $collapsableData['collapsable_link'];
2859
            }
2860
2861
            $categories[$row['session_category_id']]['sessions'][] = [
2862
                'session_name' => $row['title'],
2863
                'session_id' => $row['id'],
2864
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
2865
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
2866
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
2867
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
2868
                'courses' => $courseList,
2869
                'collapsed' => $collapsed,
2870
                'collapsable_link' => $collapsedAction,
2871
                'duration' => $row['duration'],
2872
            ];
2873
        }
2874
2875
        return $categories;
2876
    }
2877
2878
    /**
2879
     * Gives a list of [session_id-course_code] => [status] for the current user.
2880
     *
2881
     * @param  int  $user_id
2882
     * @param  int  $sessionLimit
2883
     *
2884
     * @return array list of statuses (session_id-course_code => status)
2885
     *
2886
     * @throws Exception
2887
     */
2888
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
2889
    {
2890
        // Database Table Definitions
2891
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2892
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2893
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2894
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
2895
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2896
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2897
2898
        $user_id = (int) $user_id;
2899
2900
        if (empty($user_id)) {
2901
            return [];
2902
        }
2903
2904
        $sessionRepo = Container::getSessionRepository();
2905
2906
        $user = api_get_user_entity($user_id);
2907
        $url = null;
2908
        $formattedUserName = Container::$container->get(NameConventionHelper::class)->getPersonName($user);
2909
2910
        // We filter the courses from the URL
2911
        $join_access_url = $where_access_url = '';
2912
        if (api_get_multiple_access_url()) {
2913
            $access_url_id = api_get_current_access_url_id();
2914
            if (-1 != $access_url_id) {
2915
                $url = api_get_url_entity($access_url_id);
2916
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2917
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
2918
                $where_access_url = " AND access_url_id = $access_url_id ";
2919
            }
2920
        }
2921
2922
        // Courses in which we subscribed out of any session
2923
2924
        $sql = "SELECT
2925
                    course.code,
2926
                    course_rel_user.status course_rel_status,
2927
                    course_rel_user.sort sort,
2928
                    course_rel_user.user_course_cat user_course_cat
2929
                 FROM $tbl_course_user course_rel_user
2930
                 LEFT JOIN $tbl_course course
2931
                 ON course.id = course_rel_user.c_id
2932
                 $join_access_url
2933
                 WHERE
2934
                    course_rel_user.user_id = '".$user_id."' AND
2935
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
2936
                    $where_access_url
2937
                 ORDER BY course_rel_user.sort, course.title ASC";
2938
2939
        $course_list_sql_result = Database::query($sql);
2940
        $personal_course_list = [];
2941
        if (Database::num_rows($course_list_sql_result) > 0) {
2942
            while ($result_row = Database::fetch_assoc($course_list_sql_result)) {
2943
                $course_info = api_get_course_info($result_row['code']);
2944
                $result_row['course_info'] = $course_info;
2945
                $personal_course_list[] = $result_row;
2946
            }
2947
        }
2948
2949
        $coachCourseConditions = '';
2950
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
2951
        if (api_is_allowed_to_create_course()) {
2952
            $sessionListFromCourseCoach = [];
2953
            $sql = " SELECT DISTINCT session_id
2954
                    FROM $tbl_session_course_user
2955
                    WHERE user_id = $user_id AND status = ".SessionEntity::COURSE_COACH;
2956
2957
            $result = Database::query($sql);
2958
            if (Database::num_rows($result)) {
2959
                $result = Database::store_result($result);
2960
                foreach ($result as $session) {
2961
                    $sessionListFromCourseCoach[] = $session['session_id'];
2962
                }
2963
            }
2964
            if (!empty($sessionListFromCourseCoach)) {
2965
                $condition = implode("','", $sessionListFromCourseCoach);
2966
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
2967
            }
2968
        }
2969
2970
        // Get the list of sessions where the user is subscribed
2971
        // This is divided into two different queries
2972
        $sessions = [];
2973
        $sessionLimitRestriction = '';
2974
        if (!empty($sessionLimit)) {
2975
            $sessionLimit = (int) $sessionLimit;
2976
            $sessionLimitRestriction = "LIMIT $sessionLimit";
2977
        }
2978
2979
        $sql = "SELECT DISTINCT s.id, s.title, access_start_date, access_end_date
2980
                FROM $tbl_session_user su INNER JOIN $tbl_session s
2981
                ON (s.id = su.session_id)
2982
                WHERE (
2983
                    su.user_id = $user_id AND
2984
                    su.relation_type = ".SessionEntity::STUDENT."
2985
                )
2986
                $coachCourseConditions
2987
                ORDER BY access_start_date, access_end_date, s.title
2988
                $sessionLimitRestriction
2989
        ";
2990
2991
        $result = Database::query($sql);
2992
        if (Database::num_rows($result) > 0) {
2993
            while ($row = Database::fetch_assoc($result)) {
2994
                $sessions[$row['id']] = $row;
2995
            }
2996
        }
2997
2998
        $sql = "SELECT DISTINCT
2999
                s.id, s.title, s.access_start_date, s.access_end_date
3000
                FROM $tbl_session s
3001
                INNER JOIN $tbl_session_user sru ON sru.session_id = s.id
3002
                WHERE (
3003
                    sru.user_id = $user_id AND sru.relation_type = ".SessionEntity::GENERAL_COACH."
3004
                )
3005
                $coachCourseConditions
3006
                ORDER BY s.access_start_date, s.access_end_date, s.title";
3007
3008
        $result = Database::query($sql);
3009
        if (Database::num_rows($result) > 0) {
3010
            while ($row = Database::fetch_assoc($result)) {
3011
                if (empty($sessions[$row['id']])) {
3012
                    $sessions[$row['id']] = $row;
3013
                }
3014
            }
3015
        }
3016
3017
        if (api_is_allowed_to_create_course()) {
3018
            foreach ($sessions as $enreg) {
3019
                $session_id = $enreg['id'];
3020
                $session_visibility = api_get_session_visibility($session_id);
0 ignored issues
show
Deprecated Code introduced by
The function api_get_session_visibility() has been deprecated: Use Session::setAccessVisibilityByUser() instead. ( Ignorable by Annotation )

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

3020
                $session_visibility = /** @scrutinizer ignore-deprecated */ api_get_session_visibility($session_id);

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...
3021
                $session = api_get_session_entity($session_id);
3022
3023
                if (SESSION_INVISIBLE == $session_visibility) {
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

3023
                if (/** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $session_visibility) {
Loading history...
3024
                    continue;
3025
                }
3026
3027
                $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
3028
                    $user,
3029
                    $session,
3030
                    SessionEntity::GENERAL_COACH,
3031
                    $url
3032
                );
3033
                $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
3034
                    $user,
3035
                    $session,
3036
                    SessionEntity::COURSE_COACH,
3037
                    $url
3038
                );
3039
3040
                // This query is horribly slow when more than a few thousand
3041
                // users and just a few sessions to which they are subscribed
3042
                $coursesInSession = array_map(
3043
                    function (SessionRelCourse $courseInSession) {
3044
                        $course = $courseInSession->getCourse();
3045
3046
                        return [
3047
                            'code' => $course->getCode(),
3048
                            'i' => $course->getTitle(),
3049
                            'l' => $course->getCourseLanguage(),
3050
                            'sort' => 1,
3051
                        ];
3052
                    },
3053
                    array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
3054
                );
3055
3056
                foreach ($coursesInSession as $result_row) {
3057
                    $result_row['t'] = $formattedUserName;
3058
                    $result_row['email'] = $user->getEmail();
3059
                    $result_row['access_start_date'] = $session->getAccessStartDate()?->format('Y-m-d H:i:s');
3060
                    $result_row['access_end_date'] = $session->getAccessEndDate()?->format('Y-m-d H:i:s');
3061
                    $result_row['session_id'] = $session->getId();
3062
                    $result_row['session_name'] = $session->getTitle();
3063
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3064
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3065
                    $personal_course_list[$key] = $result_row;
3066
                }
3067
            }
3068
        }
3069
3070
        foreach ($sessions as $enreg) {
3071
            $session_id = $enreg['id'];
3072
            $session_visibility = api_get_session_visibility($session_id);
0 ignored issues
show
Deprecated Code introduced by
The function api_get_session_visibility() has been deprecated: Use Session::setAccessVisibilityByUser() instead. ( Ignorable by Annotation )

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

3072
            $session_visibility = /** @scrutinizer ignore-deprecated */ api_get_session_visibility($session_id);

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...
3073
            if (SESSION_INVISIBLE == $session_visibility) {
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

3073
            if (/** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $session_visibility) {
Loading history...
3074
                continue;
3075
            }
3076
3077
            /* This query is very similar to the above query,
3078
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3079
            $sql = "SELECT DISTINCT
3080
                course.code code,
3081
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
3082
                email,
3083
                course.course_language l,
3084
                1 sort,
3085
                access_start_date,
3086
                access_end_date,
3087
                session.id as session_id,
3088
                session.title as session_name,
3089
                IF((session_course_user.user_id = 3 AND session_course_user.status = ".SessionEntity::COURSE_COACH."),'2', '5')
3090
            FROM $tbl_session_course_user as session_course_user
3091
            INNER JOIN $tbl_course AS course
3092
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
3093
            INNER JOIN $tbl_session as session
3094
            ON session_course_user.session_id = session.id
3095
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
3096
            WHERE session_course_user.user_id = $user_id
3097
            ORDER BY i";
3098
3099
            $course_list_sql_result = Database::query($sql);
3100
            while ($result_row = Database::fetch_assoc($course_list_sql_result)) {
3101
                $result_row['course_info'] = api_get_course_info($result_row['code']);
3102
                $key = $result_row['session_id'].' - '.$result_row['code'];
3103
                if (!isset($personal_course_list[$key])) {
3104
                    $personal_course_list[$key] = $result_row;
3105
                }
3106
            }
3107
        }
3108
3109
        return $personal_course_list;
3110
    }
3111
3112
    /**
3113
     * Gives a list of courses for the given user in the given session.
3114
     *
3115
     * @param int $user_id
3116
     * @param int $session_id
3117
     *
3118
     * @return array list of statuses (session_id-course_code => status)
3119
     */
3120
    public static function get_courses_list_by_session($user_id, $session_id)
3121
    {
3122
        // Database Table Definitions
3123
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
3124
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3125
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3126
3127
        $user_id = (int) $user_id;
3128
        $session_id = (int) $session_id;
3129
3130
        $sessionRepo = Container::getSessionRepository();
3131
3132
        $user = api_get_user_entity($user_id);
3133
        $session = api_get_session_entity($session_id);
3134
        $url = null;
3135
3136
        // We filter the courses from the URL
3137
        $join_access_url = $where_access_url = '';
3138
        if (api_get_multiple_access_url()) {
3139
            $urlId = api_get_current_access_url_id();
3140
            if (-1 != $urlId) {
3141
                $url = api_get_url_entity($urlId);
3142
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3143
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
3144
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
3145
            }
3146
        }
3147
3148
        /* This query is very similar to the query below, but it will check the
3149
        session_rel_course_user table if there are courses registered
3150
        to our user or not */
3151
        $sql = "SELECT DISTINCT
3152
                    c.title,
3153
                    c.visibility,
3154
                    c.id as real_id,
3155
                    c.code as course_code,
3156
                    sc.position,
3157
                    c.unsubscribe
3158
                FROM $tbl_session_course_user as scu
3159
                INNER JOIN $tbl_session_course sc
3160
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3161
                INNER JOIN $tableCourse as c
3162
                ON (scu.c_id = c.id)
3163
                $join_access_url
3164
                WHERE
3165
                    scu.user_id = $user_id AND
3166
                    scu.session_id = $session_id
3167
                    $where_access_url
3168
                ORDER BY sc.position ASC";
3169
3170
        $myCourseList = [];
3171
        $courses = [];
3172
        $result = Database::query($sql);
3173
        if (Database::num_rows($result) > 0) {
3174
            while ($result_row = Database::fetch_assoc($result)) {
3175
                $result_row['status'] = 5;
3176
                if (!in_array($result_row['real_id'], $courses)) {
3177
                    $position = $result_row['position'];
3178
                    if (!isset($myCourseList[$position])) {
3179
                        $myCourseList[$position] = $result_row;
3180
                    } else {
3181
                        $myCourseList[] = $result_row;
3182
                    }
3183
                    $courses[] = $result_row['real_id'];
3184
                }
3185
            }
3186
        }
3187
3188
        if (api_is_allowed_to_create_course()) {
3189
            $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
3190
                $user,
3191
                $session,
3192
                SessionEntity::GENERAL_COACH,
3193
                $url
3194
            );
3195
            $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
3196
                $user,
3197
                $session,
3198
                SessionEntity::COURSE_COACH,
3199
                $url
3200
            );
3201
3202
            $coursesInSession = array_map(
3203
                function (SessionRelCourse $courseInSession) {
3204
                    $course = $courseInSession->getCourse();
3205
3206
                    return [
3207
                        'title' => $course->getTitle(),
3208
                        'visibility' => $course->getVisibility(),
3209
                        'real_id' => $course->getId(),
3210
                        'course_code' => $course->getCode(),
3211
                        'position' => $courseInSession->getPosition(),
3212
                        'unsubscribe' => $course->getUnsubscribe(),
3213
                    ];
3214
                },
3215
                array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
3216
            );
3217
3218
            foreach ($coursesInSession as $result_row) {
3219
                $result_row['status'] = 2;
3220
                if (!in_array($result_row['real_id'], $courses)) {
3221
                    $position = $result_row['position'];
3222
                    if (!isset($myCourseList[$position])) {
3223
                        $myCourseList[$position] = $result_row;
3224
                    } else {
3225
                        $myCourseList[] = $result_row;
3226
                    }
3227
                    $courses[] = $result_row['real_id'];
3228
                }
3229
            }
3230
        }
3231
3232
        if (api_is_drh()) {
3233
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3234
            $sessionList = array_keys($sessionList);
3235
            if (in_array($session_id, $sessionList)) {
3236
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3237
                if (!empty($courseList)) {
3238
                    foreach ($courseList as $course) {
3239
                        if (!in_array($course['id'], $courses)) {
3240
                            $position = $course['position'];
3241
                            if (!isset($myCourseList[$position])) {
3242
                                $myCourseList[$position] = $course;
3243
                            } else {
3244
                                $myCourseList[] = $course;
3245
                            }
3246
                        }
3247
                    }
3248
                }
3249
            }
3250
        } else {
3251
            //check if user is general coach for this session
3252
            if ($session && $session->hasUserAsGeneralCoach($user)) {
3253
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3254
                if (!empty($courseList)) {
3255
                    foreach ($courseList as $course) {
3256
                        if (!in_array($course['id'], $courses)) {
3257
                            $position = $course['position'];
3258
                            if (!isset($myCourseList[$position])) {
3259
                                $myCourseList[$position] = $course;
3260
                            } else {
3261
                                $myCourseList[] = $course;
3262
                            }
3263
                        }
3264
                    }
3265
                }
3266
            }
3267
        }
3268
3269
        if (!empty($myCourseList)) {
3270
            ksort($myCourseList);
3271
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
3272
            if (empty($checkPosition)) {
3273
                // The session course list doesn't have any position,
3274
                // then order the course list by course code
3275
                $list = array_column($myCourseList, 'course_code');
3276
                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

3276
                array_multisort($myCourseList, /** @scrutinizer ignore-type */ SORT_ASC, $list);
Loading history...
3277
            }
3278
        }
3279
3280
        return $myCourseList;
3281
    }
3282
3283
    /**
3284
     * Get user id from a username.
3285
     *
3286
     * @param string $username
3287
     *
3288
     * @return int User ID (or false if not found)
3289
     */
3290
    public static function get_user_id_from_username($username)
3291
    {
3292
        if (empty($username)) {
3293
            return false;
3294
        }
3295
        $username = trim($username);
3296
        $username = Database::escape_string($username);
3297
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3298
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3299
        $res = Database::query($sql);
3300
3301
        if (false === $res) {
3302
            return false;
3303
        }
3304
        if (1 !== Database::num_rows($res)) {
3305
            return false;
3306
        }
3307
        $row = Database::fetch_array($res);
3308
3309
        return $row['id'];
3310
    }
3311
3312
    /**
3313
     * Get the users files upload from his share_folder.
3314
     *
3315
     * @param string $user_id      User ID
3316
     * @param string $course       course directory
3317
     * @param string $resourceType resource type: images, all
3318
     *
3319
     * @return string
3320
     */
3321
    /*public static function get_user_upload_files_by_course(
3322
        $user_id,
3323
        $course,
3324
        $resourceType = 'all'
3325
    ) {
3326
        $return = '';
3327
        $user_id = (int) $user_id;
3328
3329
        if (!empty($user_id) && !empty($course)) {
3330
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3331
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3332
            $file_list = [];
3333
3334
            if (is_dir($path)) {
3335
                $handle = opendir($path);
3336
                while ($file = readdir($handle)) {
3337
                    if ('.' == $file || '..' == $file || '.htaccess' == $file || is_dir($path.$file)) {
3338
                        continue; // skip current/parent directory and .htaccess
3339
                    }
3340
                    $file_list[] = $file;
3341
                }
3342
                if (count($file_list) > 0) {
3343
                    $return = "<h4>$course</h4>";
3344
                    $return .= '<ul class="thumbnails">';
3345
                }
3346
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
3347
                foreach ($file_list as $file) {
3348
                    if ('all' == $resourceType) {
3349
                        $return .= '<li>
3350
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
3351
                    } elseif ('images' == $resourceType) {
3352
                        //get extension
3353
                        $ext = explode('.', $file);
3354
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
3355
                            $return .= '<li class="span2">
3356
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
3357
                                                <img src="'.$web_path.urlencode($file).'" >
3358
                                            </a>
3359
                                        </li>';
3360
                        }
3361
                    }
3362
                }
3363
                if (count($file_list) > 0) {
3364
                    $return .= '</ul>';
3365
                }
3366
            }
3367
        }
3368
3369
        return $return;
3370
    }*/
3371
3372
    /**
3373
     * Gets the API key (or keys) and return them into an array.
3374
     *
3375
     * @param int     Optional user id (defaults to the result of api_get_user_id())
3376
     * @param string $api_service
3377
     *
3378
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
3379
     */
3380
    public static function get_api_keys($user_id = null, $api_service = 'default')
3381
    {
3382
        if ($user_id != strval(intval($user_id))) {
3383
            return false;
3384
        }
3385
        if (empty($user_id)) {
3386
            $user_id = api_get_user_id();
3387
        }
3388
        if (false === $user_id) {
3389
            return false;
3390
        }
3391
        $service_name = Database::escape_string($api_service);
3392
        if (false === is_string($service_name)) {
3393
            return false;
3394
        }
3395
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3396
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
3397
        $res = Database::query($sql);
3398
        if (false === $res) {
3399
            return false;
3400
        } //error during query
3401
        $num = Database::num_rows($res);
3402
        if (0 == $num) {
3403
            return false;
3404
        }
3405
        $list = [];
3406
        while ($row = Database::fetch_array($res)) {
3407
            $list[$row['id']] = $row['api_key'];
3408
        }
3409
3410
        return $list;
3411
    }
3412
3413
    /**
3414
     * Adds a new API key to the users' account.
3415
     *
3416
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
3417
     * @param string $api_service
3418
     *
3419
     * @return bool True on success, false on failure
3420
     */
3421
    public static function add_api_key($user_id = null, $api_service = 'default')
3422
    {
3423
        if ($user_id != strval(intval($user_id))) {
3424
            return false;
3425
        }
3426
        if (empty($user_id)) {
3427
            $user_id = api_get_user_id();
3428
        }
3429
        if (false === $user_id) {
3430
            return false;
3431
        }
3432
        $service_name = Database::escape_string($api_service);
3433
        if (false === is_string($service_name)) {
3434
            return false;
3435
        }
3436
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3437
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
3438
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
3439
        $res = Database::query($sql);
3440
        if (false === $res) {
3441
            return false;
3442
        } //error during query
3443
        $num = Database::insert_id();
3444
3445
        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...
3446
    }
3447
3448
    /**
3449
     * Deletes an API key from the user's account.
3450
     *
3451
     * @param   int     API key's internal ID
3452
     *
3453
     * @return bool True on success, false on failure
3454
     */
3455
    public static function delete_api_key($key_id)
3456
    {
3457
        if ($key_id != strval(intval($key_id))) {
3458
            return false;
3459
        }
3460
        if (false === $key_id) {
3461
            return false;
3462
        }
3463
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3464
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
3465
        $res = Database::query($sql);
3466
        if (false === $res) {
3467
            return false;
3468
        } //error during query
3469
        $num = Database::num_rows($res);
3470
        if (1 !== $num) {
3471
            return false;
3472
        }
3473
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
3474
        $res = Database::query($sql);
3475
        if (false === $res) {
3476
            return false;
3477
        } //error during query
3478
3479
        return true;
3480
    }
3481
3482
    /**
3483
     * Regenerate an API key from the user's account.
3484
     *
3485
     * @param   int     user ID (defaults to the results of api_get_user_id())
3486
     * @param   string  API key's internal ID
3487
     *
3488
     * @return int num
3489
     */
3490
    public static function update_api_key($user_id, $api_service)
3491
    {
3492
        if ($user_id != strval(intval($user_id))) {
3493
            return false;
3494
        }
3495
        if (false === $user_id) {
3496
            return false;
3497
        }
3498
        $service_name = Database::escape_string($api_service);
3499
        if (false === is_string($service_name)) {
3500
            return false;
3501
        }
3502
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3503
        $sql = "SELECT id FROM $t_api
3504
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3505
        $res = Database::query($sql);
3506
        $num = Database::num_rows($res);
3507
        if (1 == $num) {
3508
            $id_key = Database::fetch_assoc($res);
3509
            self::delete_api_key($id_key['id']);
3510
            $num = self::add_api_key($user_id, $api_service);
3511
        } elseif (0 == $num) {
3512
            $num = self::add_api_key($user_id, $api_service);
3513
        }
3514
3515
        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...
3516
    }
3517
3518
    /**
3519
     * @param   int     user ID (defaults to the results of api_get_user_id())
3520
     * @param   string    API key's internal ID
3521
     *
3522
     * @return int row ID, or return false if not found
3523
     */
3524
    public static function get_api_key_id($user_id, $api_service)
3525
    {
3526
        if ($user_id != strval(intval($user_id))) {
3527
            return false;
3528
        }
3529
        if (false === $user_id) {
3530
            return false;
3531
        }
3532
        if (empty($api_service)) {
3533
            return false;
3534
        }
3535
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3536
        $api_service = Database::escape_string($api_service);
3537
        $sql = "SELECT id FROM $t_api
3538
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3539
        $res = Database::query($sql);
3540
        if (Database::num_rows($res) < 1) {
3541
            return false;
3542
        }
3543
        $row = Database::fetch_assoc($res);
3544
3545
        return $row['id'];
3546
    }
3547
3548
    /**
3549
     * Checks if a user_id is platform admin.
3550
     *
3551
     * @param   int user ID
3552
     *
3553
     * @return bool True if is admin, false otherwise
3554
     *
3555
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
3556
     */
3557
    public static function is_admin($user_id)
3558
    {
3559
        $user_id = (int) $user_id;
3560
        if (empty($user_id)) {
3561
            return false;
3562
        }
3563
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3564
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3565
        $res = Database::query($sql);
3566
3567
        return 1 === Database::num_rows($res);
3568
    }
3569
3570
    /**
3571
     * Get the total count of users.
3572
     *
3573
     * @param ?int $status Status of users to be counted
3574
     * @param ?int $access_url_id Access URL ID (optional)
3575
     * @param ?int $active
3576
     *
3577
     * @return mixed Number of users or false on error
3578
     * @throws \Doctrine\DBAL\Exception
3579
     */
3580
    public static function get_number_of_users(
3581
        ?int $status = 0,
3582
        ?int $access_url_id = 1,
3583
        ?int $active = null,
3584
        ?string $dateFrom = null,
3585
        ?string $dateUntil = null
3586
    ): mixed {
3587
        $tableUser = Database::get_main_table(TABLE_MAIN_USER);
3588
        $tableAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3589
3590
        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 AccessUrlUtil::isMultiple ( Ignorable by Annotation )

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

3590
        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...
3591
            $sql = "SELECT count(u.id)
3592
                    FROM $tableUser u
3593
                    INNER JOIN $tableAccessUrlRelUser url_user
3594
                    ON (u.id = url_user.user_id)
3595
                    WHERE url_user.access_url_id = $access_url_id
3596
            ";
3597
        } else {
3598
            $sql = "SELECT count(u.id)
3599
                    FROM $tableUser u
3600
                    WHERE 1 = 1 ";
3601
        }
3602
3603
        $status = (int) $status;
3604
        if (!empty($status) && $status > 0) {
3605
            $sql .= " AND u.status = $status ";
3606
        }
3607
3608
        if (isset($active)) {
3609
            $active = (int) $active;
3610
            $sql .= " AND u.active = $active ";
3611
        }
3612
3613
        if (!empty($dateFrom)) {
3614
            $dateFrom = api_get_utc_datetime("$dateFrom 00:00:00");
3615
            $sql .= " AND u.created_at >= '$dateFrom' ";
3616
        }
3617
        if (!empty($dateUntil)) {
3618
            $dateUntil = api_get_utc_datetime("$dateUntil 23:59:59");
3619
            $sql .= " AND u.created_at <= '$dateUntil' ";
3620
        }
3621
3622
        $res = Database::query($sql);
3623
        if (1 === Database::num_rows($res)) {
3624
            return (int) Database::result($res, 0, 0);
3625
        }
3626
3627
        return false;
3628
    }
3629
3630
    /**
3631
     * Gets the tags of a specific field_id
3632
     * USER TAGS.
3633
     *
3634
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
3635
     *
3636
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
3637
     *    Called it "books" for example.
3638
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
3639
     * 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
3640
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
3641
     * 5. Test and enjoy.
3642
     *
3643
     * @param string $tag
3644
     * @param int    $field_id      field_id
3645
     * @param string $return_format how we are going to result value in array or in a string (json)
3646
     * @param $limit
3647
     *
3648
     * @return mixed
3649
     */
3650
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
3651
    {
3652
        // database table definition
3653
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3654
        $field_id = (int) $field_id;
3655
        $limit = (int) $limit;
3656
        $tag = trim(Database::escape_string($tag));
3657
3658
        // all the information of the field
3659
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
3660
                WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag LIMIT $limit";
3661
        $result = Database::query($sql);
3662
        $return = [];
3663
        if (Database::num_rows($result) > 0) {
3664
            while ($row = Database::fetch_assoc($result)) {
3665
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
3666
            }
3667
        }
3668
        if ('json' === $return_format) {
3669
            $return = json_encode($return);
3670
        }
3671
3672
        return $return;
3673
    }
3674
3675
    /**
3676
     * @param int $field_id
3677
     * @param int $limit
3678
     *
3679
     * @return array
3680
     */
3681
    public static function get_top_tags($field_id, $limit = 100)
3682
    {
3683
        // database table definition
3684
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3685
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3686
        $field_id = (int) $field_id;
3687
        $limit = (int) $limit;
3688
        // all the information of the field
3689
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
3690
                INNER JOIN $table_user_tag ut
3691
                ON (ut.id = uv.tag_id)
3692
                WHERE field_id = $field_id
3693
                GROUP BY tag_id
3694
                ORDER BY count DESC
3695
                LIMIT $limit";
3696
        $result = Database::query($sql);
3697
        $return = [];
3698
        if (Database::num_rows($result) > 0) {
3699
            while ($row = Database::fetch_assoc($result)) {
3700
                $return[] = $row;
3701
            }
3702
        }
3703
3704
        return $return;
3705
    }
3706
3707
    /**
3708
     * Get user's tags.
3709
     *
3710
     * @param int $user_id
3711
     * @param int $field_id
3712
     *
3713
     * @return array
3714
     */
3715
    public static function get_user_tags($user_id, $field_id)
3716
    {
3717
        // database table definition
3718
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3719
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3720
        $field_id = (int) $field_id;
3721
        $user_id = (int) $user_id;
3722
3723
        // all the information of the field
3724
        $sql = "SELECT ut.id, tag, count
3725
                FROM $table_user_tag ut
3726
                INNER JOIN $table_user_tag_values uv
3727
                ON (uv.tag_id=ut.ID)
3728
                WHERE field_id = $field_id AND user_id = $user_id
3729
                ORDER BY tag";
3730
        $result = Database::query($sql);
3731
        $return = [];
3732
        if (Database::num_rows($result) > 0) {
3733
            while ($row = Database::fetch_assoc($result)) {
3734
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3735
            }
3736
        }
3737
3738
        return $return;
3739
    }
3740
3741
    /**
3742
     * Get user's tags.
3743
     *
3744
     * @param int  $user_id
3745
     * @param int  $field_id
3746
     * @param bool $show_links show links or not
3747
     *
3748
     * @return string
3749
     */
3750
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
3751
    {
3752
        // database table definition
3753
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3754
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3755
        $field_id = (int) $field_id;
3756
        $user_id = (int) $user_id;
3757
3758
        // all the information of the field
3759
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
3760
                INNER JOIN $table_user_tag_values uv
3761
                ON (uv.tag_id = ut.id)
3762
                WHERE field_id = $field_id AND user_id = $user_id
3763
                ORDER BY tag";
3764
3765
        $result = Database::query($sql);
3766
        $return = [];
3767
        if (Database::num_rows($result) > 0) {
3768
            while ($row = Database::fetch_assoc($result)) {
3769
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3770
            }
3771
        }
3772
        $user_tags = $return;
3773
        $tag_tmp = [];
3774
        foreach ($user_tags as $tag) {
3775
            if ($show_links) {
3776
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
3777
                    $tag['tag'].
3778
                '</a>';
3779
            } else {
3780
                $tag_tmp[] = $tag['tag'];
3781
            }
3782
        }
3783
3784
        if (is_array($user_tags) && count($user_tags) > 0) {
3785
            return implode(', ', $tag_tmp);
3786
        } else {
3787
            return '';
3788
        }
3789
    }
3790
3791
    /**
3792
     * Get the tag id.
3793
     *
3794
     * @param int $tag
3795
     * @param int $field_id
3796
     *
3797
     * @return int returns 0 if fails otherwise the tag id
3798
     */
3799
    public static function get_tag_id($tag, $field_id)
3800
    {
3801
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3802
        $tag = Database::escape_string($tag);
3803
        $field_id = (int) $field_id;
3804
        //with COLLATE latin1_bin to select query in a case sensitive mode
3805
        $sql = "SELECT id FROM $table_user_tag
3806
                WHERE tag LIKE '$tag' AND field_id = $field_id";
3807
        $result = Database::query($sql);
3808
        if (Database::num_rows($result) > 0) {
3809
            $row = Database::fetch_assoc($result);
3810
3811
            return $row['id'];
3812
        } else {
3813
            return 0;
3814
        }
3815
    }
3816
3817
    /**
3818
     * Get the tag id.
3819
     *
3820
     * @param int $tag_id
3821
     * @param int $field_id
3822
     *
3823
     * @return int 0 if fails otherwise the tag id
3824
     */
3825
    public static function get_tag_id_from_id($tag_id, $field_id)
3826
    {
3827
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3828
        $tag_id = (int) $tag_id;
3829
        $field_id = (int) $field_id;
3830
        $sql = "SELECT id FROM $table_user_tag
3831
                WHERE id = '$tag_id' AND field_id = $field_id";
3832
        $result = Database::query($sql);
3833
        if (Database::num_rows($result) > 0) {
3834
            $row = Database::fetch_assoc($result);
3835
3836
            return $row['id'];
3837
        } else {
3838
            return false;
3839
        }
3840
    }
3841
3842
    /**
3843
     * Adds a user-tag value.
3844
     *
3845
     * @param mixed $tag
3846
     * @param int   $user_id
3847
     * @param int   $field_id field id of the tag
3848
     *
3849
     * @return bool True if the tag was inserted or updated. False otherwise.
3850
     *              The return value doesn't take into account *values* added to the tag.
3851
     *              Only the creation/update of the tag field itself.
3852
     */
3853
    public static function add_tag($tag, $user_id, $field_id)
3854
    {
3855
        // database table definition
3856
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3857
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3858
        $tag = trim(Database::escape_string($tag));
3859
        $user_id = (int) $user_id;
3860
        $field_id = (int) $field_id;
3861
3862
        $tag_id = self::get_tag_id($tag, $field_id);
3863
3864
        /* IMPORTANT
3865
         *  @todo we don't create tags with numbers
3866
         *
3867
         */
3868
3869
        //this is a new tag
3870
        if (0 == $tag_id) {
3871
            //the tag doesn't exist
3872
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
3873
            Database::query($sql);
3874
            $last_insert_id = Database::insert_id();
3875
        } else {
3876
            //the tag exists we update it
3877
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
3878
            Database::query($sql);
3879
            $last_insert_id = $tag_id;
3880
        }
3881
3882
        if (!empty($last_insert_id) && (0 != $last_insert_id)) {
3883
            //we insert the relationship user-tag
3884
            $sql = "SELECT tag_id FROM $table_user_tag_values
3885
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
3886
            $result = Database::query($sql);
3887
            //if the relationship does not exist we create it
3888
            if (0 == Database::num_rows($result)) {
3889
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
3890
                Database::query($sql);
3891
            }
3892
3893
            return true;
3894
        }
3895
3896
        return false;
3897
    }
3898
3899
    /**
3900
     * Deletes an user tag.
3901
     *
3902
     * @param int $user_id
3903
     * @param int $field_id
3904
     */
3905
    public static function delete_user_tags($user_id, $field_id)
3906
    {
3907
        // database table definition
3908
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3909
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3910
        $user_id = (int) $user_id;
3911
3912
        $tags = self::get_user_tags($user_id, $field_id);
3913
        if (is_array($tags) && count($tags) > 0) {
3914
            foreach ($tags as $key => $tag) {
3915
                if ($tag['count'] > '0') {
3916
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
3917
                    Database::query($sql);
3918
                }
3919
                $sql = "DELETE FROM $table_user_tag_values
3920
                        WHERE user_id = $user_id AND tag_id = $key";
3921
                Database::query($sql);
3922
            }
3923
        }
3924
    }
3925
3926
    /**
3927
     * Process the tag list comes from the UserManager::update_extra_field_value() function.
3928
     *
3929
     * @param array $tags     the tag list that will be added
3930
     * @param int   $user_id
3931
     * @param int   $field_id
3932
     *
3933
     * @return bool
3934
     */
3935
    public static function process_tags($tags, $user_id, $field_id)
3936
    {
3937
        // We loop the tags and add it to the DB
3938
        if (is_array($tags)) {
3939
            foreach ($tags as $tag) {
3940
                self::add_tag($tag, $user_id, $field_id);
3941
            }
3942
        } else {
3943
            self::add_tag($tags, $user_id, $field_id);
3944
        }
3945
3946
        return true;
3947
    }
3948
3949
    /**
3950
     * Returns a list of all administrators.
3951
     *
3952
     * @return array
3953
     */
3954
    public static function get_all_administrators()
3955
    {
3956
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3957
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
3958
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3959
        $access_url_id = api_get_current_access_url_id();
3960
        if (api_get_multiple_access_url()) {
3961
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active, locale
3962
                    FROM $tbl_url_rel_user as url
3963
                    INNER JOIN $table_admin as admin
3964
                    ON (admin.user_id=url.user_id)
3965
                    INNER JOIN $table_user u
3966
                    ON (u.id=admin.user_id)
3967
                    WHERE access_url_id ='".$access_url_id."'";
3968
        } else {
3969
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active, locale
3970
                    FROM $table_admin as admin
3971
                    INNER JOIN $table_user u
3972
                    ON (u.id=admin.user_id)";
3973
        }
3974
        $sql .= !str_contains($sql, 'WHERE') ? ' WHERE u.active <> '.USER_SOFT_DELETED : ' AND u.active <> '.USER_SOFT_DELETED;
3975
        $result = Database::query($sql);
3976
        $return = [];
3977
        if (Database::num_rows($result) > 0) {
3978
            while ($row = Database::fetch_assoc($result)) {
3979
                $return[$row['user_id']] = $row;
3980
            }
3981
        }
3982
3983
        return $return;
3984
    }
3985
3986
    /**
3987
     * Search an user (tags, first name, last name and email ).
3988
     *
3989
     * @param string $tag
3990
     * @param int    $field_id        field id of the tag
3991
     * @param int    $from            where to start in the query
3992
     * @param int    $number_of_items
3993
     * @param bool   $getCount        get count or not
3994
     *
3995
     * @return array
3996
     */
3997
    public static function get_all_user_tags(
3998
        $tag,
3999
        $field_id = 0,
4000
        $from = 0,
4001
        $number_of_items = 10,
4002
        $getCount = false
4003
    ) {
4004
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
4005
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4006
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4007
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4008
4009
        $field_id = intval($field_id);
4010
        $from = intval($from);
4011
        $number_of_items = intval($number_of_items);
4012
4013
        $where_field = "";
4014
        $where_extra_fields = self::get_search_form_where_extra_fields();
4015
        if (0 != $field_id) {
4016
            $where_field = " field_id = $field_id AND ";
4017
        }
4018
4019
        // all the information of the field
4020
        if ($getCount) {
4021
            $select = "SELECT count(DISTINCT u.id) count";
4022
        } else {
4023
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, tag, picture_uri";
4024
        }
4025
4026
        $sql = " $select
4027
                FROM $user_table u
4028
                INNER JOIN $access_url_rel_user_table url_rel_user
4029
                ON (u.id = url_rel_user.user_id)
4030
                LEFT JOIN $table_user_tag_values uv
4031
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
4032
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
4033
                WHERE
4034
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
4035
                    (
4036
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
4037
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
4038
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
4039
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
4040
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
4041
                     )
4042
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
4043
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
4044
4045
        $keyword_active = true;
4046
        // only active users
4047
        if ($keyword_active) {
4048
            $sql .= " AND u.active='1'";
4049
        }
4050
        // avoid anonymous
4051
        $sql .= " AND u.status <> 6 ";
4052
        $sql .= " ORDER BY username";
4053
        $sql .= " LIMIT $from , $number_of_items";
4054
4055
        $result = Database::query($sql);
4056
        $return = [];
4057
4058
        if (Database::num_rows($result) > 0) {
4059
            if ($getCount) {
4060
                $row = Database::fetch_assoc($result);
4061
4062
                return $row['count'];
4063
            }
4064
            while ($row = Database::fetch_assoc($result)) {
4065
                $return[$row['id']] = $row;
4066
            }
4067
        }
4068
4069
        return $return;
4070
    }
4071
4072
    /**
4073
     * Get extra filterable user fields (only type select).
4074
     *
4075
     * @return array Array of extra fields as [int => ['name' => ..., 'variable' => ..., 'data' => ...]] (
4076
     *               or empty array if no extra field)
4077
     */
4078
    public static function getExtraFilterableFields()
4079
    {
4080
        $extraFieldList = self::get_extra_fields();
4081
        $fields = [];
4082
        if (is_array($extraFieldList)) {
4083
            foreach ($extraFieldList as $extraField) {
4084
                // If is enabled to filter and is a "<select>" field type
4085
                if (1 == $extraField[8] && 4 == $extraField[2]) {
4086
                    $fields[] = [
4087
                        'name' => $extraField[3],
4088
                        'variable' => $extraField[1],
4089
                        'data' => $extraField[9],
4090
                    ];
4091
                }
4092
            }
4093
        }
4094
4095
        return $fields;
4096
    }
4097
4098
    /**
4099
     * Get extra where clauses for finding users based on extra filterable user fields (type select).
4100
     *
4101
     * @return string With AND clauses based on user's ID which have the values to search in extra user fields
4102
     *                (or empty if no extra field exists)
4103
     */
4104
    public static function get_search_form_where_extra_fields()
4105
    {
4106
        $useExtraFields = false;
4107
        $extraFields = self::getExtraFilterableFields();
4108
        $extraFieldResult = [];
4109
        if (is_array($extraFields) && count($extraFields) > 0) {
4110
            foreach ($extraFields as $extraField) {
4111
                $varName = 'field_'.$extraField['variable'];
4112
                if (self::is_extra_field_available($extraField['variable'])) {
4113
                    if (isset($_GET[$varName]) && '0' != $_GET[$varName]) {
4114
                        $useExtraFields = true;
4115
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
4116
                            $extraField['variable'],
4117
                            $_GET[$varName]
4118
                        );
4119
                    }
4120
                }
4121
            }
4122
        }
4123
4124
        if ($useExtraFields) {
4125
            $finalResult = [];
4126
            if (count($extraFieldResult) > 1) {
4127
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
4128
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
4129
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
4130
                    }
4131
                }
4132
            } else {
4133
                $finalResult = $extraFieldResult[0];
4134
            }
4135
4136
            if (is_array($finalResult) && count($finalResult) > 0) {
4137
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
4138
            } else {
4139
                //no results
4140
                $whereFilter = " AND u.id  = -1 ";
4141
            }
4142
4143
            return $whereFilter;
4144
        }
4145
4146
        return '';
4147
    }
4148
4149
    /**
4150
     * Show the search form.
4151
     *
4152
     * @param string $query the value of the search box
4153
     *
4154
     * @throws Exception
4155
     *
4156
     * @return string HTML form
4157
     */
4158
    public static function get_search_form($query, $defaultParams = [])
4159
    {
4160
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
4161
        $form = new FormValidator(
4162
            'search_user',
4163
            'get',
4164
            api_get_path(WEB_PATH).'main/social/search.php',
4165
            '',
4166
            [],
4167
            FormValidator::LAYOUT_HORIZONTAL
4168
        );
4169
4170
        $query = Security::remove_XSS($query);
4171
4172
        if (!empty($query)) {
4173
            $form->addHeader(get_lang('Results and feedback').' "'.$query.'"');
4174
        }
4175
4176
        $form->addText(
4177
            'q',
4178
            get_lang('Users, Groups'),
4179
            false,
4180
            [
4181
                'id' => 'q',
4182
            ]
4183
        );
4184
        $options = [
4185
            0 => get_lang('Select'),
4186
            1 => get_lang('User'),
4187
            2 => get_lang('Group'),
4188
        ];
4189
        $form->addSelect(
4190
            'search_type',
4191
            get_lang('Type'),
4192
            $options,
4193
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
4194
        );
4195
4196
        // Extra fields
4197
        $extraFields = self::getExtraFilterableFields();
4198
        $defaults = [];
4199
        if (is_array($extraFields) && count($extraFields) > 0) {
4200
            foreach ($extraFields as $extraField) {
4201
                $varName = 'field_'.$extraField['variable'];
4202
                $options = [
4203
                    0 => get_lang('Select'),
4204
                ];
4205
                foreach ($extraField['data'] as $option) {
4206
                    if (isset($_GET[$varName])) {
4207
                        if ($_GET[$varName] == $option[1]) {
4208
                            $defaults[$option[1]] = true;
4209
                        }
4210
                    }
4211
4212
                    $options[$option[1]] = $option[1];
4213
                }
4214
                $form->addSelect($varName, $extraField['name'], $options);
4215
            }
4216
        }
4217
4218
        $defaults['search_type'] = (int) $searchType;
4219
        $defaults['q'] = $query;
4220
4221
        if (!empty($defaultParams)) {
4222
            $defaults = array_merge($defaults, $defaultParams);
4223
        }
4224
        $form->setDefaults($defaults);
4225
        $form->addButtonSearch(get_lang('Search'));
4226
4227
        $js = '<script>
4228
        extra_field_toogle();
4229
        function extra_field_toogle() {
4230
            if (jQuery("select[name=search_type]").val() != "1") {
4231
                jQuery(".extra_field").hide();
4232
            } else {
4233
                jQuery(".extra_field").show();
4234
            }
4235
        }
4236
        </script>';
4237
4238
        return $js.$form->returnForm();
4239
    }
4240
4241
    /**
4242
     * @param int $userId
4243
     *
4244
     * @return array
4245
     */
4246
    public static function getDrhListFromUser($userId)
4247
    {
4248
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4249
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4250
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4251
        $userId = (int) $userId;
4252
4253
        $orderBy = null;
4254
        if (api_is_western_name_order()) {
4255
            $orderBy .= ' ORDER BY firstname, lastname ';
4256
        } else {
4257
            $orderBy .= ' ORDER BY lastname, firstname ';
4258
        }
4259
4260
        $sql = "SELECT u.id, username, u.firstname, u.lastname
4261
                FROM $tblUser u
4262
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
4263
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
4264
                WHERE
4265
                    access_url_id = ".api_get_current_access_url_id()." AND
4266
                    uru.user_id = '$userId' AND
4267
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4268
                    $orderBy
4269
                ";
4270
        $result = Database::query($sql);
4271
4272
        return Database::store_result($result);
4273
    }
4274
4275
    /**
4276
     * get users followed by human resource manager.
4277
     *
4278
     * @param int    $userId
4279
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
4280
     * @param bool   $getOnlyUserId
4281
     * @param bool   $getSql
4282
     * @param bool   $getCount
4283
     * @param int    $from
4284
     * @param int    $numberItems
4285
     * @param int    $column
4286
     * @param string $direction
4287
     * @param int    $active
4288
     * @param string $lastConnectionDate
4289
     *
4290
     * @return array users
4291
     */
4292
    public static function get_users_followed_by_drh(
4293
        $userId,
4294
        $userStatus = 0,
4295
        $getOnlyUserId = false,
4296
        $getSql = false,
4297
        $getCount = false,
4298
        $from = null,
4299
        $numberItems = null,
4300
        $column = null,
4301
        $direction = null,
4302
        $active = null,
4303
        $lastConnectionDate = null
4304
    ) {
4305
        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...
4306
            $userId,
4307
            $userStatus,
4308
            $getOnlyUserId,
4309
            $getSql,
4310
            $getCount,
4311
            $from,
4312
            $numberItems,
4313
            $column,
4314
            $direction,
4315
            $active,
4316
            $lastConnectionDate,
4317
            DRH
4318
        );
4319
    }
4320
4321
    /**
4322
     * Get users followed by human resource manager.
4323
     *
4324
     * @param int    $userId
4325
     * @param int    $userStatus             Filter users by status (STUDENT, COURSEMANAGER, etc)
4326
     * @param bool   $getOnlyUserId
4327
     * @param bool   $getSql
4328
     * @param bool   $getCount
4329
     * @param int    $from
4330
     * @param int    $numberItems
4331
     * @param int    $column
4332
     * @param string $direction
4333
     * @param int    $active
4334
     * @param string $lastConnectionDate
4335
     * @param int    $status                 the function is called by who? COURSEMANAGER, DRH?
4336
     * @param string $keyword
4337
     * @param bool   $checkSessionVisibility
4338
     *
4339
     * @return mixed Users list (array) or the SQL query if $getSQL was set to true
4340
     */
4341
    public static function getUsersFollowedByUser(
4342
        $userId,
4343
        $userStatus = null,
4344
        $getOnlyUserId = false,
4345
        $getSql = false,
4346
        $getCount = false,
4347
        $from = null,
4348
        $numberItems = null,
4349
        $column = null,
4350
        $direction = null,
4351
        $active = null,
4352
        $lastConnectionDate = null,
4353
        $status = null,
4354
        $keyword = null,
4355
        $checkSessionVisibility = false
4356
    ) {
4357
        // Database Table Definitions
4358
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4359
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4360
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4361
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4362
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4363
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4364
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4365
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4366
4367
        $userId = (int) $userId;
4368
        $limitCondition = '';
4369
4370
        if (isset($from) && isset($numberItems)) {
4371
            $from = (int) $from;
4372
            $numberItems = (int) $numberItems;
4373
            $limitCondition = "LIMIT $from, $numberItems";
4374
        }
4375
4376
        $column = Database::escape_string($column);
4377
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
4378
4379
        $userConditions = '';
4380
        if (!empty($userStatus)) {
4381
            $userConditions .= ' AND u.status = '.intval($userStatus);
4382
        }
4383
4384
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
4385
        if ($getOnlyUserId) {
4386
            $select = " SELECT DISTINCT u.id user_id";
4387
        }
4388
4389
        $masterSelect = "SELECT DISTINCT * FROM ";
4390
4391
        if ($getCount) {
4392
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
4393
            $select = " SELECT DISTINCT(u.id) user_id";
4394
        }
4395
4396
        if (!is_null($active)) {
4397
            $active = intval($active);
4398
            $userConditions .= " AND u.active = $active ";
4399
        }
4400
4401
        if (!empty($keyword)) {
4402
            $keyword = trim(Database::escape_string($keyword));
4403
            $keywordParts = array_filter(explode(' ', $keyword));
4404
            $extraKeyword = '';
4405
            if (!empty($keywordParts)) {
4406
                $keywordPartsFixed = Database::escape_string(implode('%', $keywordParts));
4407
                if (!empty($keywordPartsFixed)) {
4408
                    $extraKeyword .= " OR
4409
                        CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keywordPartsFixed%' OR
4410
                        CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keywordPartsFixed%' ";
4411
                }
4412
            }
4413
            $userConditions .= " AND (
4414
                u.username LIKE '%$keyword%' OR
4415
                u.firstname LIKE '%$keyword%' OR
4416
                u.lastname LIKE '%$keyword%' OR
4417
                u.official_code LIKE '%$keyword%' OR
4418
                u.email LIKE '%$keyword%' OR
4419
                CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keyword%' OR
4420
                CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keyword%'
4421
                $extraKeyword
4422
            )";
4423
        }
4424
4425
        if (!empty($lastConnectionDate)) {
4426
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
4427
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
4428
        }
4429
4430
        $sessionConditionsCoach = null;
4431
        $dateCondition = '';
4432
        $drhConditions = null;
4433
        $teacherSelect = null;
4434
        $urlId = api_get_current_access_url_id();
4435
4436
        switch ($status) {
4437
            case DRH:
4438
                $drhConditions .= " AND
4439
                    friend_user_id = '$userId' AND
4440
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4441
                ";
4442
                break;
4443
            case COURSEMANAGER:
4444
                $drhConditions .= " AND
4445
                    friend_user_id = '$userId' AND
4446
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4447
                ";
4448
4449
                $sessionConditionsTeacher = " AND
4450
                    (scu.status = ".SessionEntity::COURSE_COACH." AND scu.user_id = '$userId')
4451
                ";
4452
4453
                if ($checkSessionVisibility) {
4454
                    $today = api_strtotime('now', 'UTC');
4455
                    $today = date('Y-m-d', $today);
4456
                    $dateCondition = "
4457
                        AND
4458
                        (
4459
                            (s.access_start_date <= '$today' AND '$today' <= s.access_end_date) OR
4460
                            (s.access_start_date IS NULL AND s.access_end_date IS NULL) OR
4461
                            (s.access_start_date <= '$today' AND s.access_end_date IS NULL) OR
4462
                            ('$today' <= s.access_end_date AND s.access_start_date IS NULL)
4463
                        )
4464
					";
4465
                }
4466
4467
                // Use $tbl_session_rel_course_rel_user instead of $tbl_session_rel_user
4468
                /*
4469
                INNER JOIN $tbl_session_rel_user sru
4470
                ON (sru.user_id = u.id)
4471
                INNER JOIN $tbl_session_rel_course_rel_user scu
4472
                ON (scu.user_id = u.id AND scu.c_id IS NOT NULL AND visibility = 1)*/
4473
                $teacherSelect =
4474
                "UNION ALL (
4475
                        $select
4476
                        FROM $tbl_user u
4477
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
4478
                        WHERE
4479
                            (
4480
                                sru.session_id IN (
4481
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
4482
                                    $tbl_session_rel_access_url session_rel_access_rel_user
4483
                                    ON session_rel_access_rel_user.session_id = s.id
4484
                                    INNER JOIN $tbl_session_rel_user sru ON s.id = sru.session_id
4485
                                    WHERE access_url_id = ".$urlId."
4486
                                        AND (sru.relation_type = ".SessionEntity::GENERAL_COACH."
4487
                                        AND sru.user_id = $userId)
4488
                                ) OR sru.session_id IN (
4489
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
4490
                                    INNER JOIN $tbl_session_rel_access_url url
4491
                                    ON (url.session_id = s.id)
4492
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
4493
                                    ON (scu.session_id = s.id)
4494
                                    WHERE access_url_id = ".$urlId."
4495
                                    $sessionConditionsTeacher
4496
                                    $dateCondition
4497
                                )
4498
                            )
4499
                            $userConditions
4500
                    )
4501
                    UNION ALL(
4502
                        $select
4503
                        FROM $tbl_user u
4504
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
4505
                        WHERE cu.c_id IN (
4506
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
4507
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
4508
                        )
4509
                        $userConditions
4510
                    )"
4511
                ;
4512
                break;
4513
            case STUDENT_BOSS:
4514
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".UserRelUser::USER_RELATION_TYPE_BOSS;
4515
                break;
4516
            case HRM_REQUEST:
4517
                $drhConditions .= " AND
4518
                    friend_user_id = '$userId' AND
4519
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_HRM_REQUEST."'
4520
                ";
4521
                break;
4522
        }
4523
4524
        $join = null;
4525
        $sql = " $masterSelect
4526
                (
4527
                    (
4528
                        $select
4529
                        FROM $tbl_user u
4530
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
4531
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
4532
                        $join
4533
                        WHERE
4534
                            access_url_id = ".$urlId."
4535
                            $drhConditions
4536
                            $userConditions
4537
                    )
4538
                    $teacherSelect
4539
4540
                ) as t1";
4541
4542
        if ($getSql) {
4543
            return $sql;
4544
        }
4545
        if ($getCount) {
4546
            $result = Database::query($sql);
4547
            $row = Database::fetch_array($result);
4548
4549
            return $row['count'];
4550
        }
4551
4552
        $orderBy = null;
4553
        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...
4554
            if (api_is_western_name_order()) {
4555
                $orderBy .= " ORDER BY firstname, lastname ";
4556
            } else {
4557
                $orderBy .= " ORDER BY lastname, firstname ";
4558
            }
4559
4560
            if (!empty($column) && !empty($direction)) {
4561
                // Fixing order due the UNIONs
4562
                $column = str_replace('u.', '', $column);
4563
                $orderBy = " ORDER BY `$column` $direction ";
4564
            }
4565
        }
4566
4567
        $sql .= $orderBy;
4568
        $sql .= $limitCondition;
4569
4570
        $result = Database::query($sql);
4571
        $users = [];
4572
        if (Database::num_rows($result) > 0) {
4573
            while ($row = Database::fetch_array($result)) {
4574
                $users[$row['user_id']] = $row;
4575
            }
4576
        }
4577
4578
        return $users;
4579
    }
4580
4581
    /**
4582
     * Subscribes users to human resource manager (Dashboard feature).
4583
     *
4584
     * @param int   $hr_dept_id
4585
     * @param array $users_id
4586
     * @param bool  $deleteOtherAssignedUsers
4587
     */
4588
    public static function subscribeUsersToHRManager(
4589
        $hr_dept_id,
4590
        $users_id,
4591
        $deleteOtherAssignedUsers = true
4592
    ): void {
4593
        self::subscribeUsersToUser(
4594
            $hr_dept_id,
4595
            $users_id,
4596
            UserRelUser::USER_RELATION_TYPE_RRHH,
4597
            false,
4598
            $deleteOtherAssignedUsers
4599
        );
4600
    }
4601
4602
    /**
4603
     * Register request to assign users to HRM.
4604
     *
4605
     * @param int   $hrmId   The HRM ID
4606
     * @param array $usersId The users IDs
4607
     */
4608
    public static function requestUsersToHRManager($hrmId, $usersId): void
4609
    {
4610
        self::subscribeUsersToUser(
4611
            $hrmId,
4612
            $usersId,
4613
            UserRelUser::USER_RELATION_TYPE_HRM_REQUEST,
4614
            false,
4615
            false
4616
        );
4617
    }
4618
4619
    /**
4620
     * Remove the requests for assign a user to a HRM.
4621
     *
4622
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
4623
     */
4624
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
4625
    {
4626
        $users = implode(', ', $usersId);
4627
        Database::getManager()
4628
            ->createQuery('
4629
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
4630
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
4631
            ')
4632
            ->execute(['hrm_id' => $hrmId, 'relation_type' => UserRelUser::USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
4633
    }
4634
4635
    /**
4636
     * Add subscribed users to a user by relation type.
4637
     *
4638
     * @param int   $userId                   The user id
4639
     * @param array $subscribedUsersId        The id of subscribed users
4640
     * @param int   $relationType             The relation type
4641
     * @param bool  $deleteUsersBeforeInsert
4642
     * @param bool  $deleteOtherAssignedUsers
4643
     */
4644
    public static function subscribeUsersToUser(
4645
        $userId,
4646
        $subscribedUsersId,
4647
        $relationType,
4648
        $deleteUsersBeforeInsert = false,
4649
        $deleteOtherAssignedUsers = true
4650
    ): void {
4651
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4652
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4653
4654
        $userId = (int) $userId;
4655
        $relationType = (int) $relationType;
4656
4657
        if ($deleteOtherAssignedUsers) {
4658
            if (api_get_multiple_access_url()) {
4659
                // Deleting assigned users to hrm_id
4660
                $sql = "SELECT s.user_id
4661
                        FROM $userRelUserTable s
4662
                        INNER JOIN $userRelAccessUrlTable a
4663
                        ON (a.user_id = s.user_id)
4664
                        WHERE
4665
                            friend_user_id = $userId AND
4666
                            relation_type = $relationType AND
4667
                            access_url_id = ".api_get_current_access_url_id();
4668
            } else {
4669
                $sql = "SELECT user_id
4670
                        FROM $userRelUserTable
4671
                        WHERE
4672
                            friend_user_id = $userId AND
4673
                            relation_type = $relationType";
4674
            }
4675
            $result = Database::query($sql);
4676
4677
            if (Database::num_rows($result) > 0) {
4678
                while ($row = Database::fetch_array($result)) {
4679
                    $sql = "DELETE FROM $userRelUserTable
4680
                            WHERE
4681
                                user_id = {$row['user_id']} AND
4682
                                friend_user_id = $userId AND
4683
                                relation_type = $relationType";
4684
                    Database::query($sql);
4685
                }
4686
            }
4687
        }
4688
4689
        if ($deleteUsersBeforeInsert) {
4690
            $sql = "DELETE FROM $userRelUserTable
4691
                    WHERE
4692
                        user_id = $userId AND
4693
                        relation_type = $relationType";
4694
            Database::query($sql);
4695
        }
4696
4697
        // Inserting new user list.
4698
        if (is_array($subscribedUsersId)) {
4699
            foreach ($subscribedUsersId as $subscribedUserId) {
4700
                $subscribedUserId = (int) $subscribedUserId;
4701
                $sql = "SELECT id
4702
                        FROM $userRelUserTable
4703
                        WHERE
4704
                            user_id = $subscribedUserId AND
4705
                            friend_user_id = $userId AND
4706
                            relation_type = $relationType";
4707
4708
                $result = Database::query($sql);
4709
                $num = Database::num_rows($result);
4710
                if (0 === $num) {
4711
                    $userRelUser = (new UserRelUser())
4712
                        ->setUser(api_get_user_entity($subscribedUserId))
4713
                        ->setFriend(api_get_user_entity($userId))
4714
                        ->setRelationType($relationType)
4715
                    ;
4716
                    $em = Database::getManager();
4717
                    $em->persist($userRelUser);
4718
                    $em->flush();
4719
                }
4720
            }
4721
        }
4722
    }
4723
4724
    /**
4725
     * This function checks if a user is followed by provided human resources managers.
4726
     *
4727
     * @param int $user_id
4728
     * @param int $hr_dept_id Human resources manager
4729
     *
4730
     * @return bool
4731
     * @throws Exception
4732
     * @throws \Doctrine\DBAL\Exception
4733
     */
4734
    public static function is_user_followed_by_drh(int $user_id, int $hr_dept_id): bool
4735
    {
4736
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4737
        $result = false;
4738
4739
        $sql = "SELECT user_id FROM $tbl_user_rel_user
4740
                WHERE
4741
                    user_id = $user_id AND
4742
                    friend_user_id = $hr_dept_id AND
4743
                    relation_type = ".UserRelUser::USER_RELATION_TYPE_RRHH;
4744
        $rs = Database::query($sql);
4745
        if (Database::num_rows($rs) > 0) {
4746
            $result = true;
4747
        }
4748
4749
        return $result;
4750
    }
4751
4752
    /**
4753
     * Return the user id of teacher or session administrator.
4754
     *
4755
     * @param array $courseInfo
4756
     *
4757
     * @return int The user id, or 0 if the session ID was negative
4758
     * @throws Exception
4759
     * @throws \Doctrine\DBAL\Exception
4760
     */
4761
    public static function get_user_id_of_course_admin_or_session_admin(array $courseInfo): int
4762
    {
4763
        $session = api_get_session_id();
4764
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4765
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4766
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4767
4768
        if (empty($courseInfo)) {
4769
            return 0;
4770
        }
4771
4772
        $courseId = $courseInfo['real_id'];
4773
4774
        if (0 == $session) {
4775
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
4776
                    INNER JOIN '.$table_course_user.' ru
4777
                    ON ru.user_id = u.id
4778
                    WHERE
4779
                        ru.status = 1 AND
4780
                        ru.c_id = "'.$courseId.'" ';
4781
            $rs = Database::query($sql);
4782
            $num_rows = Database::num_rows($rs);
4783
            if (1 == $num_rows) {
4784
                $row = Database::fetch_array($rs);
4785
4786
                return (int) $row['uid'];
4787
            } else {
4788
                $my_num_rows = $num_rows;
4789
4790
                return (int) Database::result($rs, $my_num_rows - 1, 'uid');
4791
            }
4792
        } elseif ($session > 0) {
4793
            $sql = 'SELECT u.id as uid FROM '.$table_user.' u
4794
                    INNER JOIN '.$table_session_course_user.' sru
4795
                    ON sru.user_id = u.id
4796
                    WHERE
4797
                        sru.c_id = '.$courseId.' AND
4798
                        sru.status = '.SessionEntity::COURSE_COACH;
4799
            $rs = Database::query($sql);
4800
            if (Database::num_rows($rs) > 0) {
4801
                $row = Database::fetch_assoc($rs);
4802
4803
                return (int) $row['uid'];
4804
            }
4805
        }
4806
4807
        return 0;
4808
    }
4809
4810
    /**
4811
     * Determines if a user is a gradebook certified.
4812
     *
4813
     * @param int $cat_id  The category id of gradebook
4814
     * @param int $user_id The user id
4815
     *
4816
     * @return bool
4817
     */
4818
    public static function is_user_certified($cat_id, $user_id)
4819
    {
4820
        $cat_id = (int) $cat_id;
4821
        $user_id = (int) $user_id;
4822
4823
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4824
        $sql = 'SELECT path_certificate
4825
                FROM '.$table.'
4826
                WHERE
4827
                    cat_id = "'.$cat_id.'" AND
4828
                    user_id = "'.$user_id.'"';
4829
        $rs = Database::query($sql);
4830
        $row = Database::fetch_array($rs);
4831
4832
        if (!isset($row['path_certificate']) || '' == $row['path_certificate'] || is_null($row['path_certificate'])) {
4833
            return false;
4834
        }
4835
4836
        return true;
4837
    }
4838
4839
    /**
4840
     * Gets the info about a gradebook certificate for a user by course.
4841
     *
4842
     * @param array $course_info The course code
4843
     * @param int   $session_id
4844
     * @param int   $user_id     The user id
4845
     *
4846
     * @return array if there is not information return false
4847
     */
4848
    public static function get_info_gradebook_certificate($course_info, $session_id, $user_id)
4849
    {
4850
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4851
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
4852
        $session_id = (int) $session_id;
4853
        $user_id = (int) $user_id;
4854
        $courseId = $course_info['real_id'];
4855
4856
        if (empty($session_id)) {
4857
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
4858
        } else {
4859
            $session_condition = " AND session_id = $session_id";
4860
        }
4861
4862
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.'
4863
                WHERE cat_id = (
4864
                    SELECT id FROM '.$tbl_grade_category.'
4865
                    WHERE
4866
                        c_id = "'.$courseId.'" '.$session_condition.'
4867
                    LIMIT 1
4868
                ) AND user_id='.$user_id;
4869
4870
        $rs = Database::query($sql);
4871
        if (Database::num_rows($rs) > 0) {
4872
            $row = Database::fetch_assoc($rs);
4873
            $score = $row['score_certificate'];
4874
            $category_id = $row['cat_id'];
4875
            $cat = Category::load($category_id);
4876
            $displayscore = ScoreDisplay::instance();
4877
            if (isset($cat) && $displayscore->is_custom()) {
4878
                $grade = $displayscore->display_score(
4879
                    [$score, $cat[0]->get_weight()],
4880
                    SCORE_DIV_PERCENT_WITH_CUSTOM
4881
                );
4882
            } else {
4883
                $grade = $displayscore->display_score(
4884
                    [$score, $cat[0]->get_weight()]
4885
                );
4886
            }
4887
            $row['grade'] = $grade;
4888
4889
            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...
4890
        }
4891
4892
        return false;
4893
    }
4894
4895
    /**
4896
     * This function check if the user is a coach inside session course.
4897
     *
4898
     * @param int $user_id    User id
4899
     * @param int $courseId
4900
     * @param int $session_id
4901
     *
4902
     * @return bool True if the user is a coach
4903
     */
4904
    public static function is_session_course_coach($user_id, $courseId, $session_id)
4905
    {
4906
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4907
        // Protect data
4908
        $user_id = intval($user_id);
4909
        $courseId = intval($courseId);
4910
        $session_id = intval($session_id);
4911
        $result = false;
4912
4913
        $sql = "SELECT session_id FROM $table
4914
                WHERE
4915
                  session_id = $session_id AND
4916
                  c_id = $courseId AND
4917
                  user_id = $user_id AND
4918
                  status = ".SessionEntity::COURSE_COACH;
4919
        $res = Database::query($sql);
4920
4921
        if (Database::num_rows($res) > 0) {
4922
            $result = true;
4923
        }
4924
4925
        return $result;
4926
    }
4927
4928
    /**
4929
     * This function returns an icon path that represents the favicon of the website of which the url given.
4930
     * Defaults to the current Chamilo favicon.
4931
     *
4932
     * @param string $url1 URL of website where to look for favicon.ico
4933
     * @param string $url2 Optional second URL of website where to look for favicon.ico
4934
     *
4935
     * @return string Path of icon to load
4936
     */
4937
    public static function get_favicon_from_url($url1, $url2 = null)
4938
    {
4939
        $icon_link = '';
4940
        $url = $url1;
4941
        if (empty($url1)) {
4942
            $url = $url2;
4943
            if (empty($url)) {
4944
                $url = api_get_access_url(api_get_current_access_url_id());
4945
                $url = $url[0];
4946
            }
4947
        }
4948
        if (!empty($url)) {
4949
            $pieces = parse_url($url);
4950
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
4951
        }
4952
4953
        return $icon_link;
4954
    }
4955
4956
    public static function addUserAsAdmin(User $user)
4957
    {
4958
        $userId = $user->getId();
4959
4960
        if (!self::is_admin($userId)) {
4961
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
4962
            $sql = "INSERT INTO $table SET user_id = $userId";
4963
            Database::query($sql);
4964
        }
4965
4966
        $user->addRole('ROLE_ADMIN');
4967
        self::getRepository()->updateUser($user, true);
4968
    }
4969
4970
    public static function removeUserAdmin(User $user)
4971
    {
4972
        $userId = (int) $user->getId();
4973
        if (self::is_admin($userId)) {
4974
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
4975
            $sql = "DELETE FROM $table WHERE user_id = $userId";
4976
            Database::query($sql);
4977
            $user->removeRole('ROLE_ADMIN');
4978
            self::getRepository()->updateUser($user, true);
4979
        }
4980
    }
4981
4982
    /**
4983
     * @param string $from
4984
     * @param string $to
4985
     */
4986
    public static function update_all_user_languages($from, $to)
4987
    {
4988
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4989
        $from = Database::escape_string($from);
4990
        $to = Database::escape_string($to);
4991
4992
        if (!empty($to) && !empty($from)) {
4993
            $sql = "UPDATE $table_user SET language = '$to'
4994
                    WHERE language = '$from'";
4995
            Database::query($sql);
4996
        }
4997
    }
4998
4999
    /**
5000
     * Subscribe boss to students.
5001
     *
5002
     * @param int   $bossId                   The boss id
5003
     * @param array $usersId                  The users array
5004
     * @param bool  $deleteOtherAssignedUsers
5005
     */
5006
    public static function subscribeBossToUsers($bossId, $usersId, $deleteOtherAssignedUsers = true): void
5007
    {
5008
        self::subscribeUsersToUser(
5009
            $bossId,
5010
            $usersId,
5011
            UserRelUser::USER_RELATION_TYPE_BOSS,
5012
            false,
5013
            $deleteOtherAssignedUsers
5014
        );
5015
    }
5016
5017
    /**
5018
     * @param int $userId
5019
     *
5020
     * @return bool
5021
     */
5022
    public static function removeAllBossFromStudent($userId)
5023
    {
5024
        $userId = (int) $userId;
5025
5026
        if (empty($userId)) {
5027
            return false;
5028
        }
5029
5030
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5031
        $sql = "DELETE FROM $userRelUserTable
5032
                WHERE user_id = $userId AND relation_type = ".UserRelUser::USER_RELATION_TYPE_BOSS;
5033
        Database::query($sql);
5034
5035
        return true;
5036
    }
5037
5038
    /**
5039
     * Subscribe boss to students, if $bossList is empty then the boss list will be empty too.
5040
     *
5041
     * @param int   $studentId
5042
     * @param array $bossList
5043
     * @param bool  $sendNotification
5044
     *
5045
     * @return mixed Affected rows or false on failure
5046
     */
5047
    public static function subscribeUserToBossList(
5048
        $studentId,
5049
        $bossList,
5050
        $sendNotification = false
5051
    ) {
5052
        $inserted = 0;
5053
        if (!empty($bossList)) {
5054
            sort($bossList);
5055
            $studentId = (int) $studentId;
5056
            $studentInfo = api_get_user_info($studentId);
5057
5058
            if (empty($studentInfo)) {
5059
                return false;
5060
            }
5061
5062
            $previousBossList = self::getStudentBossList($studentId);
5063
            $previousBossList = !empty($previousBossList) ? array_column($previousBossList, 'boss_id') : [];
5064
            sort($previousBossList);
5065
5066
            // Boss list is the same, nothing changed.
5067
            if ($bossList == $previousBossList) {
5068
                return false;
5069
            }
5070
5071
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5072
            self::removeAllBossFromStudent($studentId);
5073
5074
            foreach ($bossList as $bossId) {
5075
                $bossId = (int) $bossId;
5076
                $bossInfo = api_get_user_info($bossId);
5077
5078
                if (empty($bossInfo)) {
5079
                    continue;
5080
                }
5081
5082
                $bossLanguage = $bossInfo['locale'];
5083
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
5084
                        VALUES ($studentId, $bossId, ".UserRelUser::USER_RELATION_TYPE_BOSS.")";
5085
                $insertId = Database::query($sql);
5086
5087
                if ($insertId) {
5088
                    if ($sendNotification) {
5089
                        $name = $studentInfo['complete_name'];
5090
                        $url = api_get_path(WEB_CODE_PATH).'my_space/myStudents.php?student='.$studentId;
5091
                        $url = Display::url($url, $url);
5092
                        $subject = sprintf(get_lang('You have been assigned the learner %s', $bossLanguage), $name);
5093
                        $message = sprintf(get_lang('You have been assigned the learner %s with url %s', $bossLanguage), $name, $url);
5094
                        MessageManager::send_message_simple(
5095
                            $bossId,
5096
                            $subject,
5097
                            $message
5098
                        );
5099
                    }
5100
                    $inserted++;
5101
                }
5102
            }
5103
        } else {
5104
            self::removeAllBossFromStudent($studentId);
5105
        }
5106
5107
        return $inserted;
5108
    }
5109
5110
    /**
5111
     * Get users followed by student boss.
5112
     *
5113
     * @param int    $userId
5114
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
5115
     * @param bool   $getOnlyUserId
5116
     * @param bool   $getSql
5117
     * @param bool   $getCount
5118
     * @param int    $from
5119
     * @param int    $numberItems
5120
     * @param int    $column
5121
     * @param string $direction
5122
     * @param int    $active
5123
     * @param string $lastConnectionDate
5124
     *
5125
     * @return array users
5126
     */
5127
    public static function getUsersFollowedByStudentBoss(
5128
        $userId,
5129
        $userStatus = 0,
5130
        $getOnlyUserId = false,
5131
        $getSql = false,
5132
        $getCount = false,
5133
        $from = null,
5134
        $numberItems = null,
5135
        $column = null,
5136
        $direction = null,
5137
        $active = null,
5138
        $lastConnectionDate = null
5139
    ) {
5140
        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...
5141
            $userId,
5142
            $userStatus,
5143
            $getOnlyUserId,
5144
            $getSql,
5145
            $getCount,
5146
            $from,
5147
            $numberItems,
5148
            $column,
5149
            $direction,
5150
            $active,
5151
            $lastConnectionDate,
5152
            STUDENT_BOSS
5153
        );
5154
    }
5155
5156
    /**
5157
     * @return array
5158
     */
5159
    public static function getOfficialCodeGrouped()
5160
    {
5161
        $user = Database::get_main_table(TABLE_MAIN_USER);
5162
        $sql = "SELECT DISTINCT official_code
5163
                FROM $user
5164
                GROUP BY official_code";
5165
        $result = Database::query($sql);
5166
        $values = Database::store_result($result, 'ASSOC');
5167
        $result = [];
5168
        foreach ($values as $value) {
5169
            $result[$value['official_code']] = $value['official_code'];
5170
        }
5171
5172
        return $result;
5173
    }
5174
5175
    /**
5176
     * @param string $officialCode
5177
     *
5178
     * @return array
5179
     */
5180
    public static function getUsersByOfficialCode($officialCode)
5181
    {
5182
        $user = Database::get_main_table(TABLE_MAIN_USER);
5183
        $officialCode = Database::escape_string($officialCode);
5184
5185
        $sql = "SELECT DISTINCT id
5186
                FROM $user
5187
                WHERE official_code = '$officialCode'
5188
                ";
5189
        $result = Database::query($sql);
5190
5191
        $users = [];
5192
        while ($row = Database::fetch_array($result)) {
5193
            $users[] = $row['id'];
5194
        }
5195
5196
        return $users;
5197
    }
5198
5199
    /**
5200
     * Calc the expended time (in seconds) by a user in a course.
5201
     *
5202
     * @param int    $userId    The user id
5203
     * @param int    $courseId  The course id
5204
     * @param int    $sessionId Optional. The session id
5205
     * @param string $from      Optional. From date
5206
     * @param string $until     Optional. Until date
5207
     *
5208
     * @return int The time
5209
     */
5210
    public static function getTimeSpentInCourses(
5211
        $userId,
5212
        $courseId,
5213
        $sessionId = 0,
5214
        $from = '',
5215
        $until = ''
5216
    ) {
5217
        $userId = (int) $userId;
5218
        $sessionId = (int) $sessionId;
5219
5220
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5221
        $whereConditions = [
5222
            'user_id = ? ' => $userId,
5223
            'AND c_id = ? ' => $courseId,
5224
            'AND session_id = ? ' => $sessionId,
5225
        ];
5226
5227
        if (!empty($from) && !empty($until)) {
5228
            $whereConditions["AND (login_course_date >= '?' "] = $from;
5229
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
5230
        }
5231
5232
        $trackResult = Database::select(
5233
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
5234
            $trackCourseAccessTable,
5235
            [
5236
                'where' => $whereConditions,
5237
            ],
5238
            'first'
5239
        );
5240
5241
        if (false != $trackResult) {
5242
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
5243
        }
5244
5245
        return 0;
5246
    }
5247
5248
    /**
5249
     * Get the boss user ID from a followed user id.
5250
     *
5251
     * @param $userId
5252
     *
5253
     * @return bool
5254
     */
5255
    public static function getFirstStudentBoss($userId)
5256
    {
5257
        $userId = (int) $userId;
5258
        if ($userId > 0) {
5259
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5260
            $row = Database::select(
5261
                'DISTINCT friend_user_id AS boss_id',
5262
                $userRelTable,
5263
                [
5264
                    'where' => [
5265
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
5266
                            $userId,
5267
                            UserRelUser::USER_RELATION_TYPE_BOSS,
5268
                        ],
5269
                    ],
5270
                ]
5271
            );
5272
            if (!empty($row)) {
5273
                return $row[0]['boss_id'];
5274
            }
5275
        }
5276
5277
        return false;
5278
    }
5279
5280
    /**
5281
     * Get the boss user ID from a followed user id.
5282
     *
5283
     * @param int $userId student id
5284
     *
5285
     * @return array
5286
     */
5287
    public static function getStudentBossList($userId)
5288
    {
5289
        $userId = (int) $userId;
5290
5291
        if ($userId > 0) {
5292
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5293
5294
            return Database::select(
5295
                'DISTINCT friend_user_id AS boss_id',
5296
                $userRelTable,
5297
                [
5298
                    'where' => [
5299
                        'user_id = ? AND relation_type = ? ' => [
5300
                            $userId,
5301
                            UserRelUser::USER_RELATION_TYPE_BOSS,
5302
                        ],
5303
                    ],
5304
                ]
5305
            );
5306
        }
5307
5308
        return [];
5309
    }
5310
5311
    /**
5312
     * @param int $bossId
5313
     * @param int $studentId
5314
     *
5315
     * @return bool
5316
     */
5317
    public static function userIsBossOfStudent($bossId, $studentId)
5318
    {
5319
        $result = false;
5320
        $bossList = self::getStudentBossList($studentId);
5321
        if (!empty($bossList)) {
5322
            $bossList = array_column($bossList, 'boss_id');
5323
            if (in_array($bossId, $bossList)) {
5324
                $result = true;
5325
            }
5326
        }
5327
5328
        return $result;
5329
    }
5330
5331
    /**
5332
     * Displays the name of the user and makes the link to the user profile.
5333
     *
5334
     * @param array $userInfo
5335
     *
5336
     * @return string
5337
     */
5338
    public static function getUserProfileLink($userInfo)
5339
    {
5340
        if (isset($userInfo) && isset($userInfo['user_id'])) {
5341
            return Display::url(
5342
                $userInfo['complete_name_with_username'],
5343
                $userInfo['profile_url']
5344
            );
5345
        }
5346
5347
        return get_lang('Anonymous');
5348
    }
5349
5350
    /**
5351
     * Get users whose name matches $firstname and $lastname.
5352
     *
5353
     * @param string $firstname Firstname to search
5354
     * @param string $lastname  Lastname to search
5355
     *
5356
     * @return array The user list
5357
     */
5358
    public static function getUsersByName($firstname, $lastname)
5359
    {
5360
        $firstname = Database::escape_string($firstname);
5361
        $lastname = Database::escape_string($lastname);
5362
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
5363
5364
        $sql = <<<SQL
5365
            SELECT id, username, lastname, firstname
5366
            FROM $userTable
5367
            WHERE
5368
                firstname LIKE '$firstname%' AND
5369
                lastname LIKE '$lastname%'
5370
SQL;
5371
        $result = Database::query($sql);
5372
        $users = [];
5373
        while ($resultData = Database::fetch_object($result)) {
5374
            $users[] = $resultData;
5375
        }
5376
5377
        return $users;
5378
    }
5379
5380
    /**
5381
     * @param int $optionSelected
5382
     *
5383
     * @return string
5384
     */
5385
    public static function getUserSubscriptionTab($optionSelected = 1)
5386
    {
5387
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
5388
        if (('true' === $allowAdmin && api_is_allowed_to_edit()) ||
5389
            api_is_platform_admin()
5390
        ) {
5391
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
5392
5393
            $headers = [
5394
                [
5395
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
5396
                    'content' => get_lang('Learners'),
5397
                ],
5398
                [
5399
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
5400
                    'content' => get_lang('Trainers'),
5401
                ],
5402
                /*[
5403
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
5404
                    'content' => get_lang('Learners'),
5405
                ],
5406
                [
5407
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
5408
                    'content' => get_lang('Trainers'),
5409
                ],*/
5410
                [
5411
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
5412
                    'content' => get_lang('Groups'),
5413
                ],
5414
                [
5415
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
5416
                    'content' => get_lang('Classes'),
5417
                ],
5418
            ];
5419
5420
            return Display::tabsOnlyLink($headers, $optionSelected);
5421
        }
5422
5423
        return '';
5424
    }
5425
5426
    /**
5427
     * Make sure this function is protected because it does NOT check password!
5428
     *
5429
     * This function defines globals.
5430
     *
5431
     * @param int  $userId
5432
     * @param bool $checkIfUserCanLoginAs
5433
     *
5434
     * @return bool
5435
     *
5436
     * @author Evie Embrechts
5437
     * @author Yannick Warnier <[email protected]>
5438
     */
5439
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
5440
    {
5441
        $userId = (int) $userId;
5442
        $userInfo = api_get_user_info($userId);
5443
5444
        // Check if the user is allowed to 'login_as'
5445
        $canLoginAs = true;
5446
        if ($checkIfUserCanLoginAs) {
5447
            $canLoginAs = api_can_login_as($userId);
5448
        }
5449
5450
        if (!$canLoginAs || empty($userInfo)) {
5451
            return false;
5452
        }
5453
5454
        if ($userId) {
5455
            $logInfo = [
5456
                'tool' => 'logout',
5457
                'tool_id' => 0,
5458
                'tool_id_detail' => 0,
5459
                'action' => '',
5460
                'info' => 'Change user (login as)',
5461
            ];
5462
            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

5462
            Event::/** @scrutinizer ignore-call */ 
5463
                   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...
5463
5464
            // Logout the current user
5465
            self::loginDelete(api_get_user_id());
5466
5467
            return true;
5468
5469
            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...
5470
            Session::erase('is_platformAdmin');
5471
            Session::erase('is_allowedCreateCourse');
5472
            Session::erase('_uid');
5473
5474
            // Cleaning session variables
5475
            $_user['firstName'] = $userInfo['firstname'];
5476
            $_user['lastName'] = $userInfo['lastname'];
5477
            $_user['mail'] = $userInfo['email'];
5478
            $_user['official_code'] = $userInfo['official_code'];
5479
            $_user['picture_uri'] = $userInfo['picture_uri'];
5480
            $_user['user_id'] = $userId;
5481
            $_user['id'] = $userId;
5482
            $_user['status'] = $userInfo['status'];
5483
5484
            // Filling session variables with new data
5485
            Session::write('_uid', $userId);
5486
            Session::write('_user', $userInfo);
5487
            Session::write('is_platformAdmin', (bool) self::is_admin($userId));
5488
            Session::write('is_allowedCreateCourse', 1 == $userInfo['status']);
5489
            // will be useful later to know if the user is actually an admin or not (example reporting)
5490
            Session::write('login_as', true);
5491
            $logInfo = [
5492
                'tool' => 'login',
5493
                'tool_id' => 0,
5494
                'tool_id_detail' => 0,
5495
                'info' => $userId,
5496
            ];
5497
            Event::registerLog($logInfo);
5498
5499
            return true;
5500
        }
5501
5502
        return false;
5503
    }
5504
5505
    /**
5506
     * Remove all login records from the track_e_online stats table,
5507
     * for the given user ID.
5508
     *
5509
     * @param int $userId User ID
5510
     */
5511
    public static function loginDelete($userId)
5512
    {
5513
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5514
        $userId = (int) $userId;
5515
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
5516
        Database::query($query);
5517
    }
5518
5519
    /**
5520
     * Login as first admin user registered in the platform.
5521
     *
5522
     * @return array
5523
     */
5524
    public static function logInAsFirstAdmin()
5525
    {
5526
        $adminList = self::get_all_administrators();
5527
5528
        if (!empty($adminList)) {
5529
            $userInfo = current($adminList);
5530
            if (!empty($userInfo)) {
5531
                $result = self::loginAsUser($userInfo['user_id'], false);
5532
                if ($result && api_is_platform_admin()) {
5533
                    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...
5534
                }
5535
            }
5536
        }
5537
5538
        return [];
5539
    }
5540
5541
    /**
5542
     * Check if user is teacher of a student based in their courses.
5543
     *
5544
     * @param $teacherId
5545
     * @param $studentId
5546
     *
5547
     * @return array
5548
     */
5549
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
5550
    {
5551
        $courses = CourseManager::getCoursesFollowedByUser(
5552
            $teacherId,
5553
            COURSEMANAGER
5554
        );
5555
        if (empty($courses)) {
5556
            return false;
5557
        }
5558
5559
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
5560
        if (empty($coursesFromUser)) {
5561
            return false;
5562
        }
5563
5564
        $coursesCodeList = array_column($courses, 'code');
5565
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
5566
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
5567
        $commonCourses = array_filter($commonCourses);
5568
5569
        if (!empty($commonCourses)) {
5570
            return $commonCourses;
5571
        }
5572
5573
        return [];
5574
    }
5575
5576
    /**
5577
     * @param int $teacherId
5578
     * @param int $studentId
5579
     *
5580
     * @return bool
5581
     */
5582
    public static function isTeacherOfStudent($teacherId, $studentId)
5583
    {
5584
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
5585
            $teacherId,
5586
            $studentId
5587
        );
5588
5589
        if (!empty($courses)) {
5590
            return true;
5591
        }
5592
5593
        return false;
5594
    }
5595
5596
    /**
5597
     * Send user confirmation mail.
5598
     *
5599
     * @throws Exception
5600
     */
5601
    public static function sendUserConfirmationMail(User $user)
5602
    {
5603
        $uniqueId = api_get_unique_id();
5604
        $user->setConfirmationToken($uniqueId);
5605
5606
        Database::getManager()->persist($user);
5607
        Database::getManager()->flush();
5608
5609
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
5610
5611
        // Check if the user was originally set for an automated subscription to a course or session
5612
        $courseCodeToRedirect = Session::read('course_redirect');
5613
        $sessionToRedirect = Session::read('session_redirect');
5614
        if (!empty($courseCodeToRedirect)) {
5615
            $url .= '&c='.$courseCodeToRedirect;
5616
        }
5617
        if (!empty($sessionToRedirect)) {
5618
            $url .= '&s='.$sessionToRedirect;
5619
        }
5620
        $mailSubject = get_lang('Registration confirmation');
5621
        $mailBody = get_lang('To complete your platform registration you need confirm your account by clicking the following link')
5622
            .PHP_EOL
5623
            .Display::url($url, $url);
5624
5625
        api_mail_html(
5626
            self::formatUserFullName($user),
5627
            $user->getEmail(),
5628
            $mailSubject,
5629
            $mailBody
5630
        );
5631
        Display::addFlash(Display::return_message(get_lang('Check your e-mail and follow the instructions.')));
5632
    }
5633
5634
    /**
5635
     * Anonymize a user. Replace personal info by anonymous info.
5636
     *
5637
     * @param int  $userId   User id
5638
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
5639
     *
5640
     * @throws \Exception
5641
     *
5642
     * @return bool
5643
     * @assert (0) === false
5644
     */
5645
    public static function anonymize($userId, $deleteIP = true)
5646
    {
5647
        global $debug;
5648
5649
        $userId = (int) $userId;
5650
5651
        if (empty($userId)) {
5652
            return false;
5653
        }
5654
5655
        $em = Database::getManager();
5656
        $user = api_get_user_entity($userId);
5657
        $uniqueId = uniqid('anon', true);
5658
        $user
5659
            ->setFirstname($uniqueId)
5660
            ->setLastname($uniqueId)
5661
            ->setBiography('')
5662
            ->setAddress('')
5663
            //->setCurriculumItems(null)
5664
            ->setDateOfBirth(null)
5665
            ->setCompetences('')
5666
            ->setDiplomas('')
5667
            ->setOpenarea('')
5668
            ->setTeach('')
5669
            ->setProductions(null)
5670
            ->setOpenid('')
5671
            ->setEmailCanonical($uniqueId.'@example.com')
5672
            ->setEmail($uniqueId.'@example.com')
5673
            ->setUsername($uniqueId)
5674
            ->setUsernameCanonical($uniqueId)
5675
            ->setPhone('')
5676
            ->setOfficialCode('')
5677
        ;
5678
5679
        self::deleteUserPicture($userId);
5680
        self::cleanUserRequestsOfRemoval($userId);
5681
5682
        // The IP address is a border-case personal data, as it does
5683
        // not directly allow for personal identification (it is not
5684
        // a completely safe value in most countries - the IP could
5685
        // be used by neighbours and crackers)
5686
        if ($deleteIP) {
5687
            $substitute = '127.0.0.1';
5688
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
5689
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
5690
            $res = Database::query($sql);
5691
            if (false === $res && $debug > 0) {
5692
                error_log("Could not anonymize IP address for user $userId ($sql)");
5693
            }
5694
5695
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5696
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5697
            $res = Database::query($sql);
5698
            if (false === $res && $debug > 0) {
5699
                error_log("Could not anonymize IP address for user $userId ($sql)");
5700
            }
5701
5702
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
5703
            $sql = "UPDATE $table SET user_ip = '$substitute' WHERE exe_user_id = $userId";
5704
            $res = Database::query($sql);
5705
            if (false === $res && $debug > 0) {
5706
                error_log("Could not anonymize IP address for user $userId ($sql)");
5707
            }
5708
5709
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
5710
            $sql = "UPDATE $table SET user_ip = '$substitute' WHERE login_user_id = $userId";
5711
            $res = Database::query($sql);
5712
            if (false === $res && $debug > 0) {
5713
                error_log("Could not anonymize IP address for user $userId ($sql)");
5714
            }
5715
5716
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5717
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
5718
            $res = Database::query($sql);
5719
            if (false === $res && $debug > 0) {
5720
                error_log("Could not anonymize IP address for user $userId ($sql)");
5721
            }
5722
5723
            $table = Database::get_course_table(TABLE_WIKI);
5724
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5725
            $res = Database::query($sql);
5726
            if (false === $res && $debug > 0) {
5727
                error_log("Could not anonymize IP address for user $userId ($sql)");
5728
            }
5729
5730
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
5731
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
5732
            $res = Database::query($sql);
5733
            if (false === $res && $debug > 0) {
5734
                error_log("Could not anonymize IP address for user $userId ($sql)");
5735
            }
5736
5737
            $table = Database::get_course_table(TABLE_WIKI);
5738
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5739
            $res = Database::query($sql);
5740
            if (false === $res && $debug > 0) {
5741
                error_log("Could not anonymize IP address for user $userId ($sql)");
5742
            }
5743
        }
5744
5745
        $extraFieldRepository = $em->getRepository(EntityExtraField::class);
5746
        $autoRemoveFields = $extraFieldRepository->findBy([
5747
            'autoRemove' => 1,
5748
            'itemType' => EntityExtraField::USER_FIELD_TYPE
5749
        ]);
5750
5751
        foreach ($autoRemoveFields as $field) {
5752
            $extraFieldValueRepository = $em->getRepository(EntityExtraFieldValues::class);
5753
            $extraFieldValue = $extraFieldValueRepository->findOneBy([
5754
                'field' => $field,
5755
                'itemId' => $userId
5756
            ]);
5757
5758
            if ($extraFieldValue) {
5759
                $em->remove($extraFieldValue);
5760
            }
5761
        }
5762
5763
        $em->persist($user);
5764
        $em->flush();
5765
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
5766
5767
        return true;
5768
    }
5769
5770
    /**
5771
     * @param int $userId
5772
     *
5773
     * @throws Exception
5774
     *
5775
     * @return string
5776
     */
5777
    public static function anonymizeUserWithVerification($userId)
5778
    {
5779
        $allowDelete = ('true' === api_get_setting('session.allow_delete_user_for_session_admin'));
5780
5781
        $message = '';
5782
        if (api_is_platform_admin() ||
5783
            ($allowDelete && api_is_session_admin())
5784
        ) {
5785
            $userToUpdateInfo = api_get_user_info($userId);
5786
            $currentUserId = api_get_user_id();
5787
5788
            if ($userToUpdateInfo &&
5789
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
5790
            ) {
5791
                if ($userId != $currentUserId &&
5792
                    self::anonymize($userId)
5793
                ) {
5794
                    $message = Display::return_message(
5795
                        sprintf(get_lang("User %s's information anonymized."), $userToUpdateInfo['complete_name_with_username']),
5796
                        'confirmation'
5797
                    );
5798
                } else {
5799
                    $message = Display::return_message(
5800
                        sprintf(get_lang("We could not anonymize user %s's information. Please try again or check the logs."), $userToUpdateInfo['complete_name_with_username']),
5801
                        'error'
5802
                    );
5803
                }
5804
            } else {
5805
                $message = Display::return_message(
5806
                    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']),
5807
                    'error'
5808
                );
5809
            }
5810
        }
5811
5812
        return $message;
5813
    }
5814
5815
    /**
5816
     * @param int $userId
5817
     *
5818
     * @throws Exception
5819
     *
5820
     * @return string
5821
     */
5822
    public static function deleteUserWithVerification($userId, bool $destroy = false)
5823
    {
5824
        $allowDelete = ('true' === api_get_setting('session.allow_delete_user_for_session_admin'));
5825
        $message = Display::return_message(get_lang('You cannot delete this user'), 'error');
5826
        $userToUpdateInfo = api_get_user_info($userId);
5827
5828
        // User must exist.
5829
        if (empty($userToUpdateInfo)) {
5830
            return $message;
5831
        }
5832
5833
        $currentUserId = api_get_user_id();
5834
5835
        // Cannot delete myself.
5836
        if ($userId == $currentUserId) {
5837
            return $message;
5838
        }
5839
5840
        if (api_is_platform_admin() ||
5841
            ($allowDelete && api_is_session_admin())
5842
        ) {
5843
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
5844
                if (self::delete_user($userId, $destroy)) {
5845
                    $message = Display::return_message(
5846
                        get_lang('The user has been deleted').': '.$userToUpdateInfo['complete_name_with_username'],
5847
                        'confirmation'
5848
                    );
5849
                } else {
5850
                    $message = Display::return_message(get_lang('This user cannot be deleted because he is still teacher in a course. You can either remove his teacher status from these courses and then delete his account, or disable his account instead of deleting it.'), 'error');
5851
                }
5852
            }
5853
        }
5854
5855
        return $message;
5856
    }
5857
5858
    /**
5859
     * @return array
5860
     */
5861
    public static function createDataPrivacyExtraFields()
5862
    {
5863
        self::create_extra_field(
5864
            'request_for_legal_agreement_consent_removal_justification',
5865
            1, //text
5866
            'Request for legal agreement consent removal justification	',
5867
            ''
5868
        );
5869
5870
        self::create_extra_field(
5871
            'request_for_delete_account_justification',
5872
            1, //text
5873
            'Request for delete account justification',
5874
            ''
5875
        );
5876
5877
        $extraFieldId = self::create_extra_field(
5878
            'request_for_legal_agreement_consent_removal',
5879
            1, //text
5880
            'Request for legal agreement consent removal',
5881
            ''
5882
        );
5883
5884
        $extraFieldIdDeleteAccount = self::create_extra_field(
5885
            'request_for_delete_account',
5886
            1, //text
5887
            'Request for delete user account',
5888
            ''
5889
        );
5890
5891
        return [
5892
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
5893
            'delete_legal' => $extraFieldId,
5894
        ];
5895
    }
5896
5897
    /**
5898
     * @param int $userId
5899
     */
5900
    public static function cleanUserRequestsOfRemoval($userId)
5901
    {
5902
        $userId = (int) $userId;
5903
5904
        $extraFieldValue = new ExtraFieldValue('user');
5905
        $extraFieldsToDelete = [
5906
            'legal_accept',
5907
            'request_for_legal_agreement_consent_removal',
5908
            'request_for_legal_agreement_consent_removal_justification',
5909
            'request_for_delete_account_justification', // just in case delete also this
5910
            'request_for_delete_account',
5911
        ];
5912
5913
        foreach ($extraFieldsToDelete as $variable) {
5914
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
5915
                $userId,
5916
                $variable
5917
            );
5918
            if ($value && isset($value['id'])) {
5919
                $extraFieldValue->delete($value['id']);
5920
            }
5921
        }
5922
    }
5923
5924
    /**
5925
     * @param int $searchYear
5926
     *
5927
     * @throws Exception
5928
     *
5929
     * @return array
5930
     */
5931
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
5932
    {
5933
        $timezone = new DateTimeZone(api_get_timezone());
5934
5935
        $sessions = [];
5936
        if (DRH == $userInfo['status']) {
5937
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
5938
        } elseif (api_is_platform_admin(true)) {
5939
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
5940
        } else {
5941
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
5942
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
5943
5944
            foreach ($sessionsByCategory as $sessionsInCategory) {
5945
                $sessions = array_merge($sessions, $sessionsInCategory);
5946
            }
5947
        }
5948
5949
        $sessions = array_map(
5950
            function ($sessionInfo) {
5951
                if (!isset($sessionInfo['session_id'])) {
5952
                    $sessionInfo['session_id'] = $sessionInfo['id'];
5953
                }
5954
                if (!isset($sessionInfo['session_name'])) {
5955
                    $sessionInfo['session_name'] = $sessionInfo['name'];
5956
                }
5957
5958
                return $sessionInfo;
5959
            },
5960
            $sessions
5961
        );
5962
5963
        $calendarSessions = [];
5964
5965
        foreach ($sessions as $sessionInfo) {
5966
            if (!empty($sessionInfo['duration'])) {
5967
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
5968
                    $sessionInfo['session_id'],
5969
                    $userInfo['id']
5970
                );
5971
5972
                if (empty($courseAccess)) {
5973
                    continue;
5974
                }
5975
5976
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
5977
                $lastAccessDate = clone $firstAcessDate;
5978
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
5979
5980
                $firstAccessYear = (int) $firstAcessDate->format('Y');
5981
                $lastAccessYear = (int) $lastAccessDate->format('Y');
5982
5983
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
5984
                    $calendarSessions[$sessionInfo['session_id']] = [
5985
                        'name' => $sessionInfo['session_name'],
5986
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
5987
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
5988
                    ];
5989
                }
5990
5991
                continue;
5992
            }
5993
5994
            $accessStartDate = !empty($sessionInfo['access_start_date'])
5995
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
5996
                : null;
5997
            $accessEndDate = !empty($sessionInfo['access_end_date'])
5998
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
5999
                : null;
6000
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
6001
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
6002
6003
            $isValid = false;
6004
6005
            if ($accessStartYear && $accessEndYear) {
6006
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
6007
                    $isValid = true;
6008
                }
6009
            }
6010
6011
            if ($accessStartYear && !$accessEndYear) {
6012
                if ($accessStartYear == $searchYear) {
6013
                    $isValid = true;
6014
                }
6015
            }
6016
6017
            if (!$accessStartYear && $accessEndYear) {
6018
                if ($accessEndYear == $searchYear) {
6019
                    $isValid = true;
6020
                }
6021
            }
6022
6023
            if ($isValid) {
6024
                $calendarSessions[$sessionInfo['session_id']] = [
6025
                    'name' => $sessionInfo['session_name'],
6026
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
6027
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
6028
                ];
6029
            }
6030
        }
6031
6032
        return $calendarSessions;
6033
    }
6034
6035
    /**
6036
     * Get sessions info for planification calendar.
6037
     *
6038
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
6039
     * @param int   $searchYear
6040
     *
6041
     * @throws Exception
6042
     *
6043
     * @return array
6044
     */
6045
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
6046
    {
6047
        $timezone = new DateTimeZone(api_get_timezone());
6048
        $calendar = [];
6049
6050
        foreach ($sessionsList as $sessionId => $sessionInfo) {
6051
            $startDate = $sessionInfo['access_start_date']
6052
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6053
                : null;
6054
            $endDate = $sessionInfo['access_end_date']
6055
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6056
                : null;
6057
6058
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
6059
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
6060
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
6061
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
6062
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
6063
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
6064
6065
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
6066
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
6067
6068
            $calendar[] = [
6069
                'id' => $sessionId,
6070
                'name' => $sessionInfo['name'],
6071
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
6072
                'start_in_last_year' => $startYear < $searchYear,
6073
                'end_in_next_year' => $endYear > $searchYear,
6074
                'no_start' => !$startWeek,
6075
                'no_end' => !$endWeek,
6076
                'start' => $start,
6077
                'duration' => $duration > 0 ? $duration : 1,
6078
            ];
6079
        }
6080
6081
        usort(
6082
            $calendar,
6083
            function ($sA, $sB) {
6084
                if ($sA['start'] == $sB['start']) {
6085
                    return 0;
6086
                }
6087
6088
                if ($sA['start'] < $sB['start']) {
6089
                    return -1;
6090
                }
6091
6092
                return 1;
6093
            }
6094
        );
6095
6096
        return $calendar;
6097
    }
6098
6099
    /**
6100
     * Return the user's full name. Optionally with the username.
6101
     */
6102
    public static function formatUserFullName(User $user, bool $includeUsername = false): string
6103
    {
6104
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
6105
6106
        if ($includeUsername && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
6107
            $username = $user->getUsername();
6108
6109
            return "$fullName ($username)";
6110
        }
6111
6112
        return $fullName;
6113
    }
6114
6115
    /**
6116
     * @param int $userId
6117
     *
6118
     * @return array
6119
     */
6120
    public static function getUserCareers($userId)
6121
    {
6122
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6123
        $tableCareer = Database::get_main_table(TABLE_CAREER);
6124
        $userId = (int) $userId;
6125
6126
        $sql = "SELECT c.id, c.title
6127
                FROM $table uc
6128
                INNER JOIN $tableCareer c
6129
                ON uc.career_id = c.id
6130
                WHERE user_id = $userId
6131
                ORDER BY uc.created_at
6132
                ";
6133
        $result = Database::query($sql);
6134
6135
        return Database::store_result($result, 'ASSOC');
6136
    }
6137
6138
    /**
6139
     * @param int $userId
6140
     * @param int $careerId
6141
     */
6142
    public static function addUserCareer($userId, $careerId)
6143
    {
6144
        if ('true' !== api_get_setting('profile.allow_career_users')) {
6145
            return false;
6146
        }
6147
6148
        if (false === self::userHasCareer($userId, $careerId)) {
6149
            $params = ['user_id' => $userId, 'career_id' => $careerId, 'created_at' => api_get_utc_datetime(), 'updated_at' => api_get_utc_datetime()];
6150
            $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6151
            Database::insert($table, $params);
6152
        }
6153
6154
        return true;
6155
    }
6156
6157
    /**
6158
     * @param int   $userCareerId
6159
     * @param array $data
6160
     *
6161
     * @return bool
6162
     */
6163
    public static function updateUserCareer($userCareerId, $data)
6164
    {
6165
        if ('true' !== api_get_setting('profile.allow_career_users')) {
6166
            return false;
6167
        }
6168
6169
        $params = ['extra_data' => $data, 'updated_at' => api_get_utc_datetime()];
6170
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6171
        Database::update(
6172
            $table,
6173
            $params,
6174
            ['id = ?' => (int) $userCareerId]
6175
        );
6176
6177
        return true;
6178
    }
6179
6180
    /**
6181
     * @param int $userId
6182
     * @param int $careerId
6183
     *
6184
     * @return array
6185
     */
6186
    public static function getUserCareer($userId, $careerId)
6187
    {
6188
        $userId = (int) $userId;
6189
        $careerId = (int) $careerId;
6190
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6191
6192
        $sql = "SELECT * FROM $table WHERE user_id = $userId AND career_id = $careerId";
6193
        $result = Database::query($sql);
6194
6195
        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...
6196
    }
6197
6198
    /**
6199
     * @param int $userId
6200
     * @param int $careerId
6201
     *
6202
     * @return bool
6203
     */
6204
    public static function userHasCareer($userId, $careerId)
6205
    {
6206
        $userId = (int) $userId;
6207
        $careerId = (int) $careerId;
6208
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6209
6210
        $sql = "SELECT id FROM $table WHERE user_id = $userId AND career_id = $careerId";
6211
        $result = Database::query($sql);
6212
6213
        return Database::num_rows($result) > 0;
6214
    }
6215
6216
    /**
6217
     * Disables or enables a user.
6218
     *
6219
     * @param int $user_id
6220
     * @param int $active  Enable or disable
6221
     *
6222
     * @return bool True on success, false on failure
6223
     * @assert (-1,0) === false
6224
     * @assert (1,1) === true
6225
     */
6226
    public static function change_active_state($user_id, $active)
6227
    {
6228
        $user_id = (int) $user_id;
6229
        $active = (int) $active;
6230
6231
        if (empty($user_id)) {
6232
            return false;
6233
        }
6234
6235
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6236
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
6237
        $r = Database::query($sql);
6238
        $ev = LOG_USER_DISABLE;
6239
        if (1 == $active) {
6240
            $ev = LOG_USER_ENABLE;
6241
        }
6242
        if (false !== $r) {
6243
            Event::addEvent($ev, LOG_USER_ID, $user_id);
6244
        }
6245
6246
        return $r;
6247
    }
6248
6249
    /**
6250
     * Get either a Gravatar URL or complete image tag for a specified email address.
6251
     *
6252
     * @param string $email The email address
6253
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
6254
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
6255
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
6256
     * @param bool   $img   True to return a complete IMG tag False for just the URL
6257
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
6258
     *
6259
     * @return string containing either just a URL or a complete image tag
6260
     * @source http://gravatar.com/site/implement/images/php/
6261
     */
6262
    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...
6263
        $email,
6264
        $s = 80,
6265
        $d = 'mm',
6266
        $r = 'g',
6267
        $img = false,
6268
        $atts = []
6269
    ) {
6270
        $url = 'http://www.gravatar.com/avatar/';
6271
        if (!empty($_SERVER['HTTPS'])) {
6272
            $url = 'https://secure.gravatar.com/avatar/';
6273
        }
6274
        $url .= md5(strtolower(trim($email)));
6275
        $url .= "?s=$s&d=$d&r=$r";
6276
        if ($img) {
6277
            $url = '<img src="'.$url.'"';
6278
            foreach ($atts as $key => $val) {
6279
                $url .= ' '.$key.'="'.$val.'"';
6280
            }
6281
            $url .= ' />';
6282
        }
6283
6284
        return $url;
6285
    }
6286
6287
    /**
6288
     * Count users in courses and if they have certificate.
6289
     * This function is resource intensive.
6290
     *
6291
     * @return array
6292
     * @throws Exception
6293
     * @throws \Doctrine\DBAL\Exception
6294
     */
6295
    public static function countUsersWhoFinishedCourses()
6296
    {
6297
        $courses = [];
6298
        $currentAccessUrlId = api_get_current_access_url_id();
6299
        $sql = "SELECT course.code, course.id as cid, cru.user_id
6300
                FROM course_rel_user cru
6301
                    JOIN course ON cru.c_id = course.id
6302
                    JOIN access_url_rel_user auru on cru.user_id = auru.user_id
6303
                    JOIN access_url_rel_course ON course.id = access_url_rel_course.c_id
6304
                WHERE access_url_rel_course.access_url_id = $currentAccessUrlId
6305
                ORDER BY course.code
6306
        ";
6307
        $res = Database::query($sql);
6308
        if (Database::num_rows($res) > 0) {
6309
            while ($row = Database::fetch_array($res)) {
6310
                if (!isset($courses[$row['code']])) {
6311
                    $courses[$row['code']] = [
6312
                        'subscribed' => 0,
6313
                        'finished' => 0,
6314
                    ];
6315
                }
6316
                $courses[$row['code']]['subscribed']++;
6317
                $entityManager = Database::getManager();
6318
                $repository = $entityManager->getRepository(GradebookCategory::class);
6319
                //todo check when have more than 1 gradebook
6320
                /** @var GradebookCategory $gradebook */
6321
                $gradebook = $repository->findOneBy(['course' => $row['cid']]);
6322
                if (!empty($gradebook)) {
6323
                    $finished = 0;
6324
                    Database::getManager()->persist($gradebook);
6325
                    $certificateRepo = $entityManager->getRepository(\Chamilo\CoreBundle\Entity\GradebookCertificate::class);
6326
                    $finished = $certificateRepo->getCertificateByUserId($gradebook->getId(), $row['user_id']);
6327
                    if (!empty($finished)) {
6328
                        $courses[$row['code']]['finished']++;
6329
                    }
6330
                }
6331
            }
6332
        }
6333
        return $courses;
6334
    }
6335
6336
    /**
6337
     * Count users in sessions and if they have certificate.
6338
     * This function is resource intensive.
6339
     *
6340
     * @return array
6341
     * @throws Exception
6342
     * @throws \Doctrine\DBAL\Exception
6343
     */
6344
    public static function countUsersWhoFinishedCoursesInSessions()
6345
    {
6346
        $coursesInSessions = [];
6347
        $currentAccessUrlId = api_get_current_access_url_id();
6348
        $sql = "SELECT course.code, srcru.session_id, srcru.user_id, session.title
6349
                FROM session_rel_course_rel_user srcru
6350
                    JOIN course ON srcru.c_id = course.id
6351
                    JOIN access_url_rel_session aurs on srcru.session_id = aurs.session_id
6352
                    JOIN session ON srcru.session_id = session.id
6353
                WHERE aurs.access_url_id = $currentAccessUrlId
6354
                ORDER BY course.code, session.title
6355
        ";
6356
        $res = Database::query($sql);
6357
        if (Database::num_rows($res) > 0) {
6358
            while ($row = Database::fetch_array($res)) {
6359
                $index = $row['code'].' ('.$row['title'].')';
6360
                if (!isset($coursesInSessions[$index])) {
6361
                    $coursesInSessions[$index] = [
6362
                        'subscribed' => 0,
6363
                        'finished' => 0,
6364
                    ];
6365
                }
6366
                $coursesInSessions[$index]['subscribed']++;
6367
                $entityManager = Database::getManager();
6368
                $repository = $entityManager->getRepository(GradebookCategory::class);
6369
                /** @var GradebookCategory $gradebook */
6370
                $gradebook = $repository->findOneBy(
6371
                    [
6372
                        'course' => $row['cid'],
6373
                        'sessionId' => $row['session_id'],
6374
                    ]
6375
                );
6376
                if (!empty($gradebook)) {
6377
                    $finished = 0;
6378
                    Database::getManager()->persist($gradebook);
6379
                    $certificateRepo = $entityManager->getRepository(\Chamilo\CoreBundle\Entity\GradebookCertificate::class);
6380
                    $finished = $certificateRepo->getCertificateByUserId($gradebook->getId(), $row['user_id']);
6381
                    if (!empty($finished)) {
6382
                        $coursesInSessions[$index]['finished']++;
6383
                    }
6384
                }
6385
            }
6386
        }
6387
        return $coursesInSessions;
6388
    }
6389
6390
    public static function redirectToResetPassword($userId): void
6391
    {
6392
        if ('true' !== api_get_setting('platform.force_renew_password_at_first_login')) {
6393
            return;
6394
        }
6395
        $askPassword = self::get_extra_user_data_by_field(
6396
            $userId,
6397
            'ask_new_password'
6398
        );
6399
        if (!empty($askPassword) && isset($askPassword['ask_new_password']) &&
6400
            1 === (int) $askPassword['ask_new_password']
6401
        ) {
6402
            $uniqueId = api_get_unique_id();
6403
            $userObj = api_get_user_entity($userId);
6404
            $userObj->setConfirmationToken($uniqueId);
6405
            $userObj->setPasswordRequestedAt(new \DateTime());
6406
            Database::getManager()->persist($userObj);
6407
            Database::getManager()->flush();
6408
            $url = api_get_path(WEB_CODE_PATH).'auth/reset.php?token='.$uniqueId;
6409
            api_location($url);
6410
        }
6411
    }
6412
6413
}
6414