Passed
Push — master ( 21b2ac...c8a0b7 )
by
unknown
16:13 queued 08:02
created

UserManager::getUserListLike()   D

Complexity

Conditions 14
Paths 288

Size

Total Lines 70
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 14
eloc 40
nc 288
nop 7
dl 0
loc 70
rs 4.3333
c 1
b 0
f 0

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

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

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

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

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

986
            $emailsubject = '['./** @scrutinizer ignore-type */ api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
Loading history...
987
            $sender_name = api_get_person_name(
988
                api_get_setting('administratorName'),
989
                api_get_setting('administratorSurname'),
990
                null,
991
                PERSON_NAME_EMAIL_ADDRESS
992
            );
993
            $email_admin = api_get_setting('emailAdministrator');
994
            $url = api_get_path(WEB_PATH);
995
            if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

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

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

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

1414
        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...
1415
            if ($idCampus) {
1416
                $urlId = $idCampus;
1417
            } else {
1418
                $urlId = api_get_current_access_url_id();
1419
            }
1420
            $sql .= " INNER JOIN $userUrlTable url_user
1421
                      ON (user.id = url_user.user_id)
1422
                      WHERE url_user.access_url_id = $urlId";
1423
        } else {
1424
            $sql .= " WHERE 1=1 ";
1425
        }
1426
1427
        if (count($conditions) > 0) {
1428
            foreach ($conditions as $field => $value) {
1429
                $field = Database::escape_string($field);
1430
                $value = Database::escape_string($value);
1431
                $sql .= " AND $field = '$value'";
1432
            }
1433
        }
1434
1435
        if (count($order_by) > 0) {
1436
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1437
        }
1438
1439
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1440
            $limit_from = (int) $limit_from;
1441
            $limit_to = (int) $limit_to;
1442
            $sql .= " LIMIT $limit_from, $limit_to";
1443
        }
1444
        $sql_result = Database::query($sql);
1445
        while ($result = Database::fetch_array($sql_result)) {
1446
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1447
            $return_array[] = $result;
1448
        }
1449
1450
        return $return_array;
1451
    }
1452
1453
    public static function getUserListExtraConditions(
1454
        $conditions = [],
1455
        $order_by = [],
1456
        $limit_from = false,
1457
        $limit_to = false,
1458
        $idCampus = null,
1459
        $extraConditions = '',
1460
        $getCount = false
1461
    ) {
1462
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1463
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1464
        $return_array = [];
1465
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1466
1467
        if ($getCount) {
1468
            $sql = "SELECT count(user.id) count FROM $user_table user ";
1469
        }
1470
1471
        if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

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

1471
        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...
1472
            if ($idCampus) {
1473
                $urlId = $idCampus;
1474
            } else {
1475
                $urlId = api_get_current_access_url_id();
1476
            }
1477
            $sql .= " INNER JOIN $userUrlTable url_user
1478
                      ON (user.id = url_user.user_id)
1479
                      WHERE url_user.access_url_id = $urlId";
1480
        } else {
1481
            $sql .= " WHERE 1=1 ";
1482
        }
1483
1484
        $sql .= " AND status <> ".ANONYMOUS." ";
1485
1486
        if (count($conditions) > 0) {
1487
            foreach ($conditions as $field => $value) {
1488
                $field = Database::escape_string($field);
1489
                $value = Database::escape_string($value);
1490
                $sql .= " AND $field = '$value'";
1491
            }
1492
        }
1493
1494
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
1495
1496
        if (!empty($order_by) && count($order_by) > 0) {
1497
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1498
        }
1499
1500
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1501
            $limit_from = (int) $limit_from;
1502
            $limit_to = (int) $limit_to;
1503
            $sql .= " LIMIT $limit_from, $limit_to";
1504
        }
1505
1506
        $sql_result = Database::query($sql);
1507
1508
        if ($getCount) {
1509
            $result = Database::fetch_array($sql_result);
1510
1511
            return $result['count'];
1512
        }
1513
1514
        while ($result = Database::fetch_array($sql_result)) {
1515
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1516
            $return_array[] = $result;
1517
        }
1518
1519
        return $return_array;
1520
    }
1521
1522
    /**
1523
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
1524
     *
1525
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
1526
     * @param array  $order_by         a list of fields on which sort
1527
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
1528
     * @param string $condition        Whether we want the filters to be combined by AND or OR
1529
     * @param array  $onlyThisUserList
1530
     *
1531
     * @return array an array with all users of the platform
1532
     *
1533
     * @todo optional course code parameter, optional sorting parameters...
1534
     * @todo security filter order_by
1535
     */
1536
    public static function getUserListLike(
1537
        $conditions = [],
1538
        $order_by = [],
1539
        $simple_like = false,
1540
        $condition = 'AND',
1541
        $onlyThisUserList = [],
1542
        int $limit = 0,
1543
        int $offset = 0
1544
    ) {
1545
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1546
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1547
        $return_array = [];
1548
        $sql_query = "SELECT user.id, user.username, user.firstname, user.lastname, user.official_code, user.status
1549
                  FROM $user_table user ";
1550
1551
        if (api_get_multiple_access_url()) {
1552
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
1553
        }
1554
1555
        $sql_query .= ' WHERE 1 = 1 ';
1556
        if (count($conditions) > 0) {
1557
            $temp_conditions = [];
1558
            foreach ($conditions as $field => $value) {
1559
                $field = Database::escape_string($field);
1560
                $value = Database::escape_string($value);
1561
                if ($simple_like) {
1562
                    $temp_conditions[] = "$field LIKE '$value%'";
1563
                } else {
1564
                    if (in_array($field, ['user.id', 'user.status'])) {
1565
                        $temp_conditions[] = "$field = '$value'";
1566
                    } else {
1567
                        $temp_conditions[] = "$field LIKE '%$value%'";
1568
                    }
1569
                }
1570
            }
1571
            if (!empty($temp_conditions)) {
1572
                $sql_query .= ' AND '.implode(" $condition ", $temp_conditions);
1573
            }
1574
1575
            if (api_get_multiple_access_url()) {
1576
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1577
            }
1578
        } else {
1579
            if (api_get_multiple_access_url()) {
1580
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1581
            }
1582
        }
1583
1584
        if (!empty($onlyThisUserList)) {
1585
            $onlyThisUserListToString = implode("','", array_map('intval', $onlyThisUserList));
1586
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
1587
        }
1588
1589
        if (!empty($order_by)) {
1590
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1591
        }
1592
1593
        if ($limit > 0) {
1594
            $sql_query .= ' LIMIT '.intval($limit);
1595
            if ($offset > 0) {
1596
                $sql_query .= ' OFFSET '.intval($offset);
1597
            }
1598
        }
1599
1600
        $sql_result = Database::query($sql_query);
1601
        while ($result = Database::fetch_array($sql_result)) {
1602
            $return_array[] = $result;
1603
        }
1604
1605
        return $return_array;
1606
    }
1607
1608
    /**
1609
     * Get user path from user ID (returns an array).
1610
     * The return format is a complete path to a folder ending with "/"
1611
     * In case the first level of subdirectory of users/ does not exist, the
1612
     * function will attempt to create it. Probably not the right place to do it
1613
     * but at least it avoids headaches in many other places.
1614
     *
1615
     * @param int    $id   User ID
1616
     * @param string $type Type of path to return (can be 'system', 'web', 'last')
1617
     *
1618
     * @return string User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
1619
     */
1620
    public static function getUserPathById($id, $type)
1621
    {
1622
        $id = (int) $id;
1623
        if (!$id) {
1624
            return null;
1625
        }
1626
1627
        $userPath = "users/$id/";
1628
        if (api_get_setting('split_users_upload_directory') === 'true') {
1629
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
1630
            // In exceptional cases, on some portals, the intermediate base user
1631
            // directory might not have been created. Make sure it is before
1632
            // going further.
1633
1634
            $rootPath = api_get_path(SYS_PATH).'../app/upload/users/'.substr((string) $id, 0, 1);
1635
            if (!is_dir($rootPath)) {
1636
                $perm = api_get_permissions_for_new_directories();
1637
                try {
1638
                    mkdir($rootPath, $perm);
1639
                } catch (Exception $e) {
1640
                    error_log($e->getMessage());
1641
                }
1642
            }
1643
        }
1644
        switch ($type) {
1645
            case 'system': // Base: absolute system path.
1646
                $userPath = api_get_path(SYS_PATH).'../app/upload/'.$userPath;
1647
                break;
1648
            case 'web': // Base: absolute web path.
1649
                $userPath = api_get_path(WEB_PATH).'../app/upload/'.$userPath;
1650
                break;
1651
            case 'last': // Only the last part starting with users/
1652
                break;
1653
        }
1654
1655
        return $userPath;
1656
    }
1657
1658
    /**
1659
     * Gets the current user image.
1660
     *
1661
     * @param string $userId
1662
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
1663
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
1664
     * @param bool   $addRandomId
1665
     * @param array  $userInfo    to avoid query the DB
1666
     *
1667
     * @todo add gravatar support
1668
     * @todo replace $userId with User entity
1669
     *
1670
     * @return string
1671
     */
1672
    public static function getUserPicture(
1673
        $userId,
1674
        int $size = USER_IMAGE_SIZE_MEDIUM,
1675
        $addRandomId = true,
1676
        $userInfo = []
1677
    ) {
1678
        $user = api_get_user_entity($userId);
1679
        $illustrationRepo = Container::getIllustrationRepository();
1680
1681
        switch ($size) {
1682
            case USER_IMAGE_SIZE_SMALL:
1683
                $width = 32;
1684
                break;
1685
            case USER_IMAGE_SIZE_MEDIUM:
1686
                $width = 64;
1687
                break;
1688
            case USER_IMAGE_SIZE_BIG:
1689
                $width = 128;
1690
                break;
1691
            case USER_IMAGE_SIZE_ORIGINAL:
1692
            default:
1693
                $width = 0;
1694
                break;
1695
        }
1696
1697
        $url = $illustrationRepo->getIllustrationUrl($user);
1698
        $params = [];
1699
        if (!empty($width)) {
1700
            $params['w'] = $width;
1701
        }
1702
1703
        if ($addRandomId) {
1704
            $params['rand'] = uniqid('u_', true);
1705
        }
1706
1707
        $paramsToString = '';
1708
        if (!empty($params)) {
1709
            $paramsToString = '?'.http_build_query($params);
1710
        }
1711
1712
        return $url.$paramsToString;
1713
1714
        /*
1715
        // Make sure userInfo is defined. Otherwise, define it!
1716
        if (empty($userInfo) || !is_array($userInfo) || 0 == count($userInfo)) {
1717
            if (empty($user_id)) {
1718
                return '';
1719
            } else {
1720
                $userInfo = api_get_user_info($user_id);
1721
            }
1722
        }
1723
1724
        $imageWebPath = self::get_user_picture_path_by_id(
1725
            $user_id,
1726
            'web',
1727
            $userInfo
1728
        );
1729
        $pictureWebFile = $imageWebPath['file'];
1730
        $pictureWebDir = $imageWebPath['dir'];
1731
1732
        $pictureAnonymousSize = '128';
1733
        $gravatarSize = 22;
1734
        $realSizeName = 'small_';
1735
1736
        switch ($size) {
1737
            case USER_IMAGE_SIZE_SMALL:
1738
                $pictureAnonymousSize = '32';
1739
                $realSizeName = 'small_';
1740
                $gravatarSize = 32;
1741
                break;
1742
            case USER_IMAGE_SIZE_MEDIUM:
1743
                $pictureAnonymousSize = '64';
1744
                $realSizeName = 'medium_';
1745
                $gravatarSize = 64;
1746
                break;
1747
            case USER_IMAGE_SIZE_ORIGINAL:
1748
                $pictureAnonymousSize = '128';
1749
                $realSizeName = '';
1750
                $gravatarSize = 128;
1751
                break;
1752
            case USER_IMAGE_SIZE_BIG:
1753
                $pictureAnonymousSize = '128';
1754
                $realSizeName = 'big_';
1755
                $gravatarSize = 128;
1756
                break;
1757
        }
1758
1759
        $gravatarEnabled = api_get_setting('gravatar_enabled');
1760
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
1761
        if ('unknown.jpg' == $pictureWebFile || empty($pictureWebFile)) {
1762
            if ('true' === $gravatarEnabled) {
1763
                $file = self::getGravatar(
1764
                    $imageWebPath['email'],
1765
                    $gravatarSize,
1766
                    api_get_setting('gravatar_type')
1767
                );
1768
1769
                if ($addRandomId) {
1770
                    $file .= '&rand='.uniqid();
1771
                }
1772
1773
                return $file;
1774
            }
1775
1776
            return $anonymousPath;
1777
        }
1778
1779
        if ($addRandomId) {
1780
            $picture .= '?rand='.uniqid();
1781
        }
1782
1783
        return $picture;*/
1784
    }
1785
1786
    /**
1787
     * Creates new user photos in various sizes of a user, or deletes user photos.
1788
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
1789
     *
1790
     * @param int    $user_id the user internal identification number
1791
     * @param string $file    The common file name for the newly created photos.
1792
     *                        It will be checked and modified for compatibility with the file system.
1793
     *                        If full name is provided, path component is ignored.
1794
     *                        If an empty name is provided, then old user photos are deleted only,
1795
     *
1796
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
1797
     *
1798
     * @param string $source_file    the full system name of the image from which user photos will be created
1799
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
1800
     *
1801
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
1802
     *              When deletion is requested returns empty string.
1803
     *              In case of internal error or negative validation returns FALSE.
1804
     */
1805
    public static function update_user_picture($userId, UploadedFile $file, string $crop = '')
1806
    {
1807
        if (empty($userId) || empty($file)) {
1808
            return false;
1809
        }
1810
1811
        $repo = Container::getUserRepository();
1812
        $user = $repo->find($userId);
1813
        if ($user) {
1814
            $repoIllustration = Container::getIllustrationRepository();
1815
            $repoIllustration->addIllustration($user, $user, $file, $crop);
1816
        }
1817
    }
1818
1819
    /**
1820
     * Deletes user photos.
1821
     *
1822
     * @param int $userId the user internal identification number
1823
     *
1824
     * @return mixed returns empty string on success, FALSE on error
1825
     */
1826
    public static function deleteUserPicture($userId)
1827
    {
1828
        $repo = Container::getUserRepository();
1829
        $user = $repo->find($userId);
1830
        if ($user) {
1831
            $illustrationRepo = Container::getIllustrationRepository();
1832
            $illustrationRepo->deleteIllustration($user);
1833
        }
1834
    }
1835
1836
    /**
1837
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
1838
     * doesn't have any.
1839
     *
1840
     * If there has been a request to remove a production, the function will return
1841
     * without building the list unless forced to do so by the optional second
1842
     * parameter. This increases performance by avoiding to read through the
1843
     * productions on the filesystem before the removal request has been carried
1844
     * out because they'll have to be re-read afterwards anyway.
1845
     *
1846
     * @deprecated This method is being removed from chamilo 2.0
1847
     * @param int  $user_id    User id
1848
     * @param bool $force      Optional parameter to force building after a removal request
1849
     * @param bool $showDelete
1850
     *
1851
     * @return string A string containing the XHTML code to display the production list, or FALSE
1852
     */
1853
    public static function build_production_list($user_id, $force = false, $showDelete = false)
1854
    {
1855
        if (!$force && !empty($_POST['remove_production'])) {
1856
            return true; // postpone reading from the filesystem
1857
        }
1858
1859
        $productions = self::get_user_productions($user_id);
1860
1861
        if (empty($productions)) {
1862
            return false;
1863
        }
1864
1865
        return false;
1866
1867
        $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...
1868
        $del_image = Display::returnIconPath('delete.png');
1869
        $add_image = Display::returnIconPath('archive.png');
1870
        $del_text = get_lang('Delete');
1871
        $production_list = '';
1872
        if (count($productions) > 0) {
1873
            $production_list = '<div class="files-production"><ul id="productions">';
1874
            foreach ($productions as $file) {
1875
                $production_list .= '<li>
1876
                    <img src="'.$add_image.'" />
1877
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
1878
                        '.htmlentities($file).'
1879
                    </a>';
1880
                if ($showDelete) {
1881
                    $production_list .= '&nbsp;&nbsp;
1882
                        <input
1883
                            style="width:16px;"
1884
                            type="image"
1885
                            name="remove_production['.urlencode($file).']"
1886
                            src="'.$del_image.'"
1887
                            alt="'.$del_text.'"
1888
                            title="'.$del_text.' '.htmlentities($file).'"
1889
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
1890
                }
1891
            }
1892
            $production_list .= '</ul></div>';
1893
        }
1894
1895
        return $production_list;
1896
    }
1897
1898
    /**
1899
     * Returns an array with the user's productions.
1900
     *
1901
     * @param int $user_id User id
1902
     *
1903
     * @return array An array containing the user's productions
1904
     */
1905
    public static function get_user_productions($user_id)
1906
    {
1907
        return [];
1908
1909
        $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...
1910
        $productions = [];
1911
1912
        if (is_dir($production_repository)) {
1913
            $handle = opendir($production_repository);
1914
            while ($file = readdir($handle)) {
1915
                if ('.' == $file ||
1916
                    '..' == $file ||
1917
                    '.htaccess' == $file ||
1918
                    is_dir($production_repository.$file)
1919
                ) {
1920
                    // skip current/parent directory and .htaccess
1921
                    continue;
1922
                }
1923
1924
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
1925
                    // User's photos should not be listed as productions.
1926
                    continue;
1927
                }
1928
                $productions[] = $file;
1929
            }
1930
        }
1931
1932
        return $productions;
1933
    }
1934
1935
    /**
1936
     * Remove a user production.
1937
     *
1938
     * @param int    $user_id    User id
1939
     * @param string $production The production to remove
1940
     *
1941
     * @return bool
1942
     */
1943
    public static function remove_user_production($user_id, $production)
1944
    {
1945
        throw new Exception('remove_user_production');
1946
        /*$production_path = self::get_user_picture_path_by_id($user_id, 'system');
1947
        $production_file = $production_path['dir'].$production;
1948
        if (is_file($production_file)) {
1949
            unlink($production_file);
1950
1951
            return true;
1952
        }
1953
1954
        return false;*/
1955
    }
1956
1957
    /**
1958
     * Update an extra field value for a given user.
1959
     *
1960
     * @param int    $userId   User ID
1961
     * @param string $variable Field variable name
1962
     * @param string $value    Field value
1963
     *
1964
     * @return bool true if field updated, false otherwise
1965
     */
1966
    public static function update_extra_field_value($userId, $variable, $value = '')
1967
    {
1968
        $extraFieldValue = new ExtraFieldValue('user');
1969
        $params = [
1970
            'item_id' => $userId,
1971
            'variable' => $variable,
1972
            'field_value' => $value,
1973
        ];
1974
1975
        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...
1976
    }
1977
1978
    /**
1979
     * Get an array of extra fields with field details (type, default value and options).
1980
     *
1981
     * @param    int    Offset (from which row)
1982
     * @param    int    Number of items
1983
     * @param    int    Column on which sorting is made
1984
     * @param    string    Sorting direction
1985
     * @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...
1986
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
1987
     *
1988
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
1989
     */
1990
    public static function get_extra_fields(
1991
        $from = 0,
1992
        $number_of_items = 0,
1993
        $column = 5,
1994
        $direction = 'ASC',
1995
        $all_visibility = true,
1996
        $field_filter = null
1997
    ) {
1998
        $fields = [];
1999
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2000
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2001
        $columns = [
2002
            'id',
2003
            'variable',
2004
            'value_type',
2005
            'display_text',
2006
            'default_value',
2007
            'field_order',
2008
            'filter',
2009
        ];
2010
        $column = (int) $column;
2011
        $sort_direction = '';
2012
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2013
            $sort_direction = strtoupper($direction);
2014
        }
2015
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2016
        $sqlf = "SELECT * FROM $t_uf WHERE item_type = $extraFieldType ";
2017
        if (!$all_visibility) {
2018
            $sqlf .= " AND visible_to_self = 1 ";
2019
        }
2020
        if (!is_null($field_filter)) {
2021
            $field_filter = (int) $field_filter;
2022
            $sqlf .= " AND filter = $field_filter ";
2023
        }
2024
        $sqlf .= " ORDER BY `".$columns[$column]."` $sort_direction ";
2025
        if (0 != $number_of_items) {
2026
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2027
        }
2028
        $resf = Database::query($sqlf);
2029
        if (Database::num_rows($resf) > 0) {
2030
            while ($rowf = Database::fetch_array($resf)) {
2031
                $fields[$rowf['id']] = [
2032
                    0 => $rowf['id'],
2033
                    1 => $rowf['variable'],
2034
                    2 => $rowf['value_type'],
2035
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2036
                    4 => $rowf['default_value'],
2037
                    5 => $rowf['field_order'],
2038
                    6 => $rowf['visible_to_self'],
2039
                    7 => $rowf['changeable'],
2040
                    8 => $rowf['filter'],
2041
                    9 => [],
2042
                    10 => '<a name="'.$rowf['id'].'"></a>',
2043
                ];
2044
2045
                $sqlo = "SELECT * FROM $t_ufo
2046
                         WHERE field_id = ".$rowf['id']."
2047
                         ORDER BY option_order ASC";
2048
                $reso = Database::query($sqlo);
2049
                if (Database::num_rows($reso) > 0) {
2050
                    while ($rowo = Database::fetch_array($reso)) {
2051
                        $fields[$rowf['id']][9][$rowo['id']] = [
2052
                            0 => $rowo['id'],
2053
                            1 => $rowo['option_value'],
2054
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2055
                            3 => $rowo['option_order'],
2056
                        ];
2057
                    }
2058
                }
2059
            }
2060
        }
2061
2062
        return $fields;
2063
    }
2064
2065
    /**
2066
     * Creates a new extra field.
2067
     *
2068
     * @param string $variable    Field's internal variable name
2069
     * @param int    $fieldType   Field's type
2070
     * @param string $displayText Field's language var name
2071
     * @param string $default     Field's default value
2072
     *
2073
     * @return int
2074
     */
2075
    public static function create_extra_field(
2076
        $variable,
2077
        $valueType,
2078
        $displayText,
2079
        $default
2080
    ) {
2081
        $extraField = new ExtraField('user');
2082
        $params = [
2083
            'variable' => $variable,
2084
            'value_type' => $valueType,
2085
            'display_text' => $displayText,
2086
            'default_value' => $default,
2087
        ];
2088
2089
        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...
2090
    }
2091
2092
    /**
2093
     * Check if a field is available.
2094
     *
2095
     * @param string $variable
2096
     *
2097
     * @return bool
2098
     */
2099
    public static function is_extra_field_available($variable)
2100
    {
2101
        $extraField = new ExtraField('user');
2102
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2103
2104
        return !empty($data) ? true : false;
2105
    }
2106
2107
    /**
2108
     * Gets user extra fields data.
2109
     *
2110
     * @param    int    User ID
2111
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2112
     * @param    bool    Whether to return invisible fields as well
2113
     * @param    bool    Whether to split multiple-selection fields or not
2114
     *
2115
     * @return array Array of fields => value for the given user
2116
     */
2117
    public static function get_extra_user_data(
2118
        $user_id,
2119
        $prefix = false,
2120
        $allVisibility = true,
2121
        $splitMultiple = false,
2122
        $fieldFilter = null
2123
    ) {
2124
        $user_id = (int) $user_id;
2125
2126
        if (empty($user_id)) {
2127
            return [];
2128
        }
2129
2130
        $extra_data = [];
2131
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2132
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2133
        $sql = "SELECT f.id as id, f.variable as fvar, f.value_type as type
2134
                FROM $t_uf f
2135
                WHERE
2136
                    item_type = ".EntityExtraField::USER_FIELD_TYPE."
2137
                ";
2138
        $filter_cond = '';
2139
2140
        if (!$allVisibility) {
2141
            if (isset($fieldFilter)) {
2142
                $fieldFilter = (int) $fieldFilter;
2143
                $filter_cond .= " AND filter = $fieldFilter ";
2144
            }
2145
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2146
        } else {
2147
            if (isset($fieldFilter)) {
2148
                $fieldFilter = (int) $fieldFilter;
2149
                $sql .= " AND filter = $fieldFilter ";
2150
            }
2151
        }
2152
2153
        $sql .= ' ORDER BY f.field_order';
2154
2155
        $res = Database::query($sql);
2156
        if (Database::num_rows($res) > 0) {
2157
            while ($row = Database::fetch_array($res)) {
2158
                if (self::USER_FIELD_TYPE_TAG == $row['type']) {
2159
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2160
                    $extra_data['extra_'.$row['fvar']] = $tags;
2161
                } else {
2162
                    $sqlu = "SELECT field_value as fval
2163
                            FROM $t_ufv
2164
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2165
                    $resu = Database::query($sqlu);
2166
                    // get default value
2167
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2168
                               WHERE id=".$row['id'];
2169
                    $res_df = Database::query($sql_df);
2170
2171
                    if (Database::num_rows($resu) > 0) {
2172
                        $rowu = Database::fetch_array($resu);
2173
                        $fval = $rowu['fval'];
2174
                        if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2175
                            $fval = explode(';', $rowu['fval']);
2176
                        }
2177
                    } else {
2178
                        $row_df = Database::fetch_array($res_df);
2179
                        $fval = $row_df['fval_df'];
2180
                    }
2181
                    // We get here (and fill the $extra_data array) even if there
2182
                    // is no user with data (we fill it with default values)
2183
                    if ($prefix) {
2184
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2185
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2186
                        } else {
2187
                            $extra_data['extra_'.$row['fvar']] = $fval;
2188
                        }
2189
                    } else {
2190
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2191
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2192
                        } else {
2193
                            $extra_data[$row['fvar']] = $fval;
2194
                        }
2195
                    }
2196
                }
2197
            }
2198
        }
2199
2200
        return $extra_data;
2201
    }
2202
2203
    /** Get extra user data by field.
2204
     * @param int    user ID
2205
     * @param string the internal variable name of the field
2206
     *
2207
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2208
     */
2209
    public static function get_extra_user_data_by_field(
2210
        $user_id,
2211
        $field_variable,
2212
        $prefix = false,
2213
        $all_visibility = true,
2214
        $splitmultiple = false
2215
    ) {
2216
        $user_id = (int) $user_id;
2217
2218
        if (empty($user_id)) {
2219
            return [];
2220
        }
2221
2222
        $extra_data = [];
2223
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2224
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2225
2226
        $sql = "SELECT f.id as id, f.variable as fvar, f.value_type as type
2227
                FROM $t_uf f
2228
                WHERE f.variable = '$field_variable' ";
2229
2230
        if (!$all_visibility) {
2231
            $sql .= " AND f.visible_to_self = 1 ";
2232
        }
2233
2234
        $sql .= " AND item_type = ".EntityExtraField::USER_FIELD_TYPE;
2235
        $sql .= " ORDER BY f.field_order ";
2236
2237
        $res = Database::query($sql);
2238
        if (Database::num_rows($res) > 0) {
2239
            while ($row = Database::fetch_array($res)) {
2240
                $sqlu = "SELECT field_value as fval FROM $t_ufv v
2241
                         INNER JOIN $t_uf f
2242
                         ON (v.field_id = f.id)
2243
                         WHERE
2244
                            item_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2245
                            field_id = ".$row['id']." AND
2246
                            item_id = ".$user_id;
2247
                $resu = Database::query($sqlu);
2248
                $fval = '';
2249
                if (Database::num_rows($resu) > 0) {
2250
                    $rowu = Database::fetch_array($resu);
2251
                    $fval = $rowu['fval'];
2252
                    if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2253
                        $fval = explode(';', $rowu['fval']);
2254
                    }
2255
                }
2256
                if ($prefix) {
2257
                    $extra_data['extra_'.$row['fvar']] = $fval;
2258
                } else {
2259
                    $extra_data[$row['fvar']] = $fval;
2260
                }
2261
            }
2262
        }
2263
2264
        return $extra_data;
2265
    }
2266
2267
    /**
2268
     * Get the extra field information for a certain field (the options as well).
2269
     *
2270
     * @param int $variable The name of the field we want to know everything about
2271
     *
2272
     * @return array Array containing all the information about the extra profile field
2273
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2274
     *               as returned by the database)
2275
     *
2276
     * @author Julio Montoya
2277
     *
2278
     * @since v1.8.6
2279
     */
2280
    public static function get_extra_field_information_by_name($variable)
2281
    {
2282
        $extraField = new ExtraField('user');
2283
2284
        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...
2285
    }
2286
2287
    /**
2288
     * Get the extra field information for user tag (the options as well).
2289
     *
2290
     * @param int $variable The name of the field we want to know everything about
2291
     *
2292
     * @return array Array containing all the information about the extra profile field
2293
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2294
     *               as returned by the database)
2295
     *
2296
     * @author José Loguercio
2297
     *
2298
     * @since v1.11.0
2299
     */
2300
    public static function get_extra_field_tags_information_by_name($variable)
2301
    {
2302
        $extraField = new ExtraField('user');
2303
2304
        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...
2305
    }
2306
2307
    /**
2308
     * Get all the extra field information of a certain field (also the options).
2309
     *
2310
     * @param int $fieldId the ID of the field we want to know everything of
2311
     *
2312
     * @return array $return containing all th information about the extra profile field
2313
     *
2314
     * @author Julio Montoya
2315
     *
2316
     * @deprecated
2317
     * @since v1.8.6
2318
     */
2319
    public static function get_extra_field_information($fieldId)
2320
    {
2321
        $extraField = new ExtraField('user');
2322
2323
        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...
2324
    }
2325
2326
    /**
2327
     * Get extra user data by value.
2328
     *
2329
     * @param string $variable       the internal variable name of the field
2330
     * @param string $value          the internal value of the field
2331
     * @param bool   $all_visibility
2332
     *
2333
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2334
     */
2335
    public static function get_extra_user_data_by_value($variable, $value, $all_visibility = true)
2336
    {
2337
        $extraFieldValue = new ExtraFieldValue('user');
2338
        $extraField = new ExtraField('user');
2339
2340
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2341
2342
        if (false === $info) {
2343
            return [];
2344
        }
2345
2346
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2347
            $variable,
2348
            $value,
2349
            false,
2350
            false,
2351
            true
2352
        );
2353
2354
        $result = [];
2355
        if (!empty($data)) {
2356
            foreach ($data as $item) {
2357
                $result[] = $item['item_id'];
2358
            }
2359
        }
2360
2361
        return $result;
2362
    }
2363
2364
    /**
2365
     * Get extra user data by tags value.
2366
     *
2367
     * @param int    $fieldId the ID of the field we want to know everything of
2368
     * @param string $tag     the tag name for search
2369
     *
2370
     * @return array with extra data info of a user
2371
     *
2372
     * @author José Loguercio
2373
     *
2374
     * @since v1.11.0
2375
     */
2376
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2377
    {
2378
        $extraField = new ExtraField('user');
2379
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2380
        $array = [];
2381
        foreach ($result as $index => $user) {
2382
            $array[] = $user['user_id'];
2383
        }
2384
2385
        return $array;
2386
    }
2387
2388
    /**
2389
     * Get extra user data by field variable.
2390
     *
2391
     * @param string $variable field variable
2392
     *
2393
     * @return array data
2394
     */
2395
    public static function get_extra_user_data_by_field_variable($variable)
2396
    {
2397
        $extraInfo = self::get_extra_field_information_by_name($variable);
2398
        $field_id = (int) $extraInfo['id'];
2399
2400
        $extraField = new ExtraFieldValue('user');
2401
        $data = $extraField->getValuesByFieldId($field_id);
2402
2403
        if (!empty($data)) {
2404
            foreach ($data as $row) {
2405
                $user_id = $row['item_id'];
2406
                $data[$user_id] = $row;
2407
            }
2408
        }
2409
2410
        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...
2411
    }
2412
2413
    /**
2414
     * Get extra user data tags by field variable.
2415
     *
2416
     * @param string $variable field variable
2417
     *
2418
     * @return array
2419
     */
2420
    public static function get_extra_user_data_for_tags($variable)
2421
    {
2422
        $data = self::get_extra_field_tags_information_by_name($variable);
2423
2424
        return $data;
2425
    }
2426
2427
    /**
2428
     * Gives a list of [session_category][session_id] for the current user.
2429
     *
2430
     * @param int  $user_id
2431
     * @param bool $is_time_over                 whether to fill the first element or not
2432
     *                                           (to give space for courses out of categories)
2433
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2434
     * @param bool $ignoreTimeLimit              ignore time start/end
2435
     * @param bool $getCount
2436
     *
2437
     * @return array list of statuses [session_category][session_id]
2438
     *
2439
     * @todo ensure multiple access urls are managed correctly
2440
     */
2441
    public static function get_sessions_by_category(
2442
        $user_id,
2443
        $is_time_over = true,
2444
        $ignore_visibility_for_admins = false,
2445
        $ignoreTimeLimit = false,
2446
        $getCount = false
2447
    ) {
2448
        $user_id = (int) $user_id;
2449
2450
        if (empty($user_id)) {
2451
            return [];
2452
        }
2453
2454
        // Get the list of sessions per user
2455
        $now = new DateTime('now', new DateTimeZone('UTC'));
2456
2457
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2458
        // join would not catch session-courses where the user is general
2459
        // session coach but which do not have students nor coaches registered
2460
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
2461
2462
        if (!$getCount) {
2463
            $dqlSelect = " DISTINCT
2464
                s.id,
2465
                s.title,
2466
                s.accessStartDate AS access_start_date,
2467
                s.accessEndDate AS access_end_date,
2468
                s.duration,
2469
                sc.id AS session_category_id,
2470
                sc.title AS session_category_title,
2471
                sc.dateStart AS session_category_date_start,
2472
                sc.dateEnd AS session_category_date_end,
2473
                s.coachAccessStartDate AS coach_access_start_date,
2474
                s.coachAccessEndDate AS coach_access_end_date,
2475
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
2476
                , s.position AS position
2477
            ";
2478
        }
2479
2480
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
2481
        // is awfully inefficient for large sets of data (1m25s for 58K
2482
        // sessions, BT#14115) but executing a similar query twice and grouping
2483
        // the results afterwards in PHP takes about 1/1000th of the time
2484
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
2485
        $dqlStudent = "SELECT $dqlSelect
2486
            FROM ChamiloCoreBundle:Session AS s
2487
            LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2488
            INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2489
            LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2490
            WHERE scu.user = :user AND url.url = :url ";
2491
        $dqlCoach = "SELECT $dqlSelect
2492
            FROM ChamiloCoreBundle:Session AS s
2493
            INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2494
            LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2495
            INNER JOIN ChamiloCoreBundle:SessionRelUser AS su WITH su.session = s
2496
            WHERE (su.user = :user AND su.relationType = ".SessionEntity::GENERAL_COACH.") AND url.url = :url ";
2497
2498
        // Default order
2499
        $order = 'ORDER BY sc.title, s.title';
2500
2501
        // Order by date if showing all sessions
2502
        $showAllSessions = ('true' === api_get_setting('course.show_all_sessions_on_my_course_page'));
2503
        if ($showAllSessions) {
2504
            $order = 'ORDER BY s.accessStartDate';
2505
        }
2506
2507
        // Order by position
2508
        if ('true' === api_get_setting('session.session_list_order')) {
2509
            $order = 'ORDER BY s.position';
2510
        }
2511
2512
        // Order by dates according to settings
2513
        $orderBySettings = api_get_setting('session.my_courses_session_order', true);
2514
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
2515
            $field = $orderBySettings['field'];
2516
            $orderSetting = $orderBySettings['order'];
2517
            switch ($field) {
2518
                case 'start_date':
2519
                    $order = " ORDER BY s.accessStartDate $orderSetting";
2520
                    break;
2521
                case 'end_date':
2522
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
2523
                    if ('asc' == $orderSetting) {
2524
                        // Put null values at the end
2525
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
2526
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
2527
                    }
2528
                    break;
2529
                case 'name':
2530
                case 'title':
2531
                    $order = " ORDER BY s.title $orderSetting ";
2532
                    break;
2533
            }
2534
        }
2535
2536
        $dqlStudent .= $order;
2537
        $dqlCoach .= $order;
2538
2539
        $accessUrlId = api_get_current_access_url_id();
2540
        $dqlStudent = Database::getManager()
2541
            ->createQuery($dqlStudent)
2542
            ->setParameters(
2543
                ['user' => $user_id, 'url' => $accessUrlId]
2544
            )
2545
        ;
2546
        $dqlCoach = Database::getManager()
2547
            ->createQuery($dqlCoach)
2548
            ->setParameters(
2549
                ['user' => $user_id, 'url' => $accessUrlId]
2550
            )
2551
        ;
2552
2553
        if ($getCount) {
2554
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
2555
        }
2556
2557
        $sessionDataStudent = $dqlStudent->getResult();
2558
        $sessionDataCoach = $dqlCoach->getResult();
2559
2560
        $sessionData = [];
2561
        // First fill $sessionData with student sessions
2562
        if (!empty($sessionDataStudent)) {
2563
            foreach ($sessionDataStudent as $row) {
2564
                $sessionData[$row['id']] = $row;
2565
            }
2566
        }
2567
        // Overwrite session data of the user as a student with session data
2568
        // of the user as a coach.
2569
        // There shouldn't be such duplicate rows, but just in case...
2570
        if (!empty($sessionDataCoach)) {
2571
            foreach ($sessionDataCoach as $row) {
2572
                $sessionData[$row['id']] = $row;
2573
            }
2574
        }
2575
2576
        $collapsable = ('true' === api_get_setting('session.allow_user_session_collapsable'));
2577
2578
2579
2580
        $extraField = new ExtraFieldValue('session');
2581
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
2582
2583
        if (empty($sessionData)) {
2584
            return [];
2585
        }
2586
        $categories = [];
2587
        foreach ($sessionData as $row) {
2588
            $session_id = $row['id'];
2589
            $coachList = SessionManager::getCoachesBySession($session_id);
2590
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
2591
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
2592
            $courseList = self::get_courses_list_by_session($user_id, $session_id);
2593
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
2594
2595
            // User portal filters:
2596
            if (false === $ignoreTimeLimit) {
2597
                if ($is_time_over) {
2598
                    // History
2599
                    if ($row['duration']) {
2600
                        if ($daysLeft >= 0) {
2601
                            continue;
2602
                        }
2603
                    } else {
2604
                        if (empty($row['access_end_date'])) {
2605
                            continue;
2606
                        } else {
2607
                            if ($row['access_end_date'] > $now) {
2608
                                continue;
2609
                            }
2610
                        }
2611
                    }
2612
                } else {
2613
                    // Current user portal
2614
                    $isGeneralCoach = api_get_session_entity($row['id'])->hasUserAsGeneralCoach(api_get_user_entity($user_id));
2615
                    $isCoachOfCourse = in_array($user_id, $coachList);
2616
2617
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
2618
                        // Teachers can access the session depending in the access_coach date
2619
                    } else {
2620
                        if ($row['duration']) {
2621
                            if ($daysLeft <= 0) {
2622
                                continue;
2623
                            }
2624
                        } else {
2625
                            if (isset($row['access_end_date']) &&
2626
                                !empty($row['access_end_date'])
2627
                            ) {
2628
                                if ($row['access_end_date'] <= $now) {
2629
                                    continue;
2630
                                }
2631
                            }
2632
                        }
2633
                    }
2634
                }
2635
            }
2636
2637
            $categories[$row['session_category_id']]['session_category'] = [
2638
                'id' => $row['session_category_id'],
2639
                'name' => $row['session_category_title'],
2640
                'date_start' => $categoryStart,
2641
                'date_end' => $categoryEnd,
2642
            ];
2643
2644
            $visibility = api_get_session_visibility(
2645
                $session_id,
2646
                null,
2647
                $ignore_visibility_for_admins
2648
            );
2649
2650
            if (SESSION_VISIBLE != $visibility) {
2651
                // Course Coach session visibility.
2652
                $blockedCourseCount = 0;
2653
                $closedVisibilityList = [
2654
                    COURSE_VISIBILITY_CLOSED,
2655
                    COURSE_VISIBILITY_HIDDEN,
2656
                ];
2657
2658
                foreach ($courseList as $course) {
2659
                    // Checking session visibility
2660
                    $sessionCourseVisibility = api_get_session_visibility(
2661
                        $session_id,
2662
                        $course['real_id'],
2663
                        $ignore_visibility_for_admins
2664
                    );
2665
2666
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
2667
                    if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
2668
                        $blockedCourseCount++;
2669
                    }
2670
                }
2671
2672
                // If all courses are blocked then no show in the list.
2673
                if ($blockedCourseCount === count($courseList)) {
2674
                    $visibility = SESSION_INVISIBLE;
2675
                } else {
2676
                    $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...
2677
                }
2678
            }
2679
2680
            switch ($visibility) {
2681
                case SESSION_VISIBLE_READ_ONLY:
2682
                case SESSION_VISIBLE:
2683
                case SESSION_AVAILABLE:
2684
                    break;
2685
                case SESSION_INVISIBLE:
2686
                    if (false === $ignore_visibility_for_admins) {
2687
                        continue 2;
2688
                    }
2689
            }
2690
2691
            $collapsed = '';
2692
            $collapsedAction = '';
2693
            if ($collapsable) {
2694
                $collapsableData = SessionManager::getCollapsableData(
2695
                    $user_id,
2696
                    $session_id,
2697
                    $extraField,
2698
                    $collapsableLink
2699
                );
2700
                $collapsed = $collapsableData['collapsed'];
2701
                $collapsedAction = $collapsableData['collapsable_link'];
2702
            }
2703
2704
            $categories[$row['session_category_id']]['sessions'][] = [
2705
                'session_name' => $row['title'],
2706
                'session_id' => $row['id'],
2707
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
2708
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
2709
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
2710
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
2711
                'courses' => $courseList,
2712
                'collapsed' => $collapsed,
2713
                'collapsable_link' => $collapsedAction,
2714
                'duration' => $row['duration'],
2715
            ];
2716
        }
2717
2718
        return $categories;
2719
    }
2720
2721
    /**
2722
     * Gives a list of [session_id-course_code] => [status] for the current user.
2723
     *
2724
     * @param int $user_id
2725
     * @param int $sessionLimit
2726
     *
2727
     * @return array list of statuses (session_id-course_code => status)
2728
     */
2729
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
2730
    {
2731
        // Database Table Definitions
2732
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2733
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2734
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2735
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
2736
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2737
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2738
2739
        $user_id = (int) $user_id;
2740
2741
        if (empty($user_id)) {
2742
            return [];
2743
        }
2744
2745
        $sessionRepo = Container::getSessionRepository();
2746
2747
        $user = api_get_user_entity($user_id);
2748
        $url = null;
2749
        $formattedUserName = Container::$container->get(NameConvention::class)->getPersonName($user);
2750
2751
        // We filter the courses from the URL
2752
        $join_access_url = $where_access_url = '';
2753
        if (api_get_multiple_access_url()) {
2754
            $access_url_id = api_get_current_access_url_id();
2755
            if (-1 != $access_url_id) {
2756
                $url = api_get_url_entity($access_url_id);
2757
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2758
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
2759
                $where_access_url = " AND access_url_id = $access_url_id ";
2760
            }
2761
        }
2762
2763
        // Courses in which we subscribed out of any session
2764
2765
        $sql = "SELECT
2766
                    course.code,
2767
                    course_rel_user.status course_rel_status,
2768
                    course_rel_user.sort sort,
2769
                    course_rel_user.user_course_cat user_course_cat
2770
                 FROM $tbl_course_user course_rel_user
2771
                 LEFT JOIN $tbl_course course
2772
                 ON course.id = course_rel_user.c_id
2773
                 $join_access_url
2774
                 WHERE
2775
                    course_rel_user.user_id = '".$user_id."' AND
2776
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
2777
                    $where_access_url
2778
                 ORDER BY course_rel_user.sort, course.title ASC";
2779
2780
        $course_list_sql_result = Database::query($sql);
2781
        $personal_course_list = [];
2782
        if (Database::num_rows($course_list_sql_result) > 0) {
2783
            while ($result_row = Database::fetch_assoc($course_list_sql_result)) {
2784
                $course_info = api_get_course_info($result_row['code']);
2785
                $result_row['course_info'] = $course_info;
2786
                $personal_course_list[] = $result_row;
2787
            }
2788
        }
2789
2790
        $coachCourseConditions = '';
2791
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
2792
        if (api_is_allowed_to_create_course()) {
2793
            $sessionListFromCourseCoach = [];
2794
            $sql = " SELECT DISTINCT session_id
2795
                    FROM $tbl_session_course_user
2796
                    WHERE user_id = $user_id AND status = ".SessionEntity::COURSE_COACH;
2797
2798
            $result = Database::query($sql);
2799
            if (Database::num_rows($result)) {
2800
                $result = Database::store_result($result);
2801
                foreach ($result as $session) {
2802
                    $sessionListFromCourseCoach[] = $session['session_id'];
2803
                }
2804
            }
2805
            if (!empty($sessionListFromCourseCoach)) {
2806
                $condition = implode("','", $sessionListFromCourseCoach);
2807
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
2808
            }
2809
        }
2810
2811
        // Get the list of sessions where the user is subscribed
2812
        // This is divided into two different queries
2813
        $sessions = [];
2814
        $sessionLimitRestriction = '';
2815
        if (!empty($sessionLimit)) {
2816
            $sessionLimit = (int) $sessionLimit;
2817
            $sessionLimitRestriction = "LIMIT $sessionLimit";
2818
        }
2819
2820
        $sql = "SELECT DISTINCT s.id, s.title, access_start_date, access_end_date
2821
                FROM $tbl_session_user su INNER JOIN $tbl_session s
2822
                ON (s.id = su.session_id)
2823
                WHERE (
2824
                    su.user_id = $user_id AND
2825
                    su.relation_type = ".SessionEntity::STUDENT."
2826
                )
2827
                $coachCourseConditions
2828
                ORDER BY access_start_date, access_end_date, s.title
2829
                $sessionLimitRestriction
2830
        ";
2831
2832
        $result = Database::query($sql);
2833
        if (Database::num_rows($result) > 0) {
2834
            while ($row = Database::fetch_assoc($result)) {
2835
                $sessions[$row['id']] = $row;
2836
            }
2837
        }
2838
2839
        $sql = "SELECT DISTINCT
2840
                s.id, s.title, s.access_start_date, s.access_end_date
2841
                FROM $tbl_session s
2842
                INNER JOIN $tbl_session_user sru ON sru.session_id = s.id
2843
                WHERE (
2844
                    sru.user_id = $user_id AND sru.relation_type = ".SessionEntity::GENERAL_COACH."
2845
                )
2846
                $coachCourseConditions
2847
                ORDER BY s.access_start_date, s.access_end_date, s.title";
2848
2849
        $result = Database::query($sql);
2850
        if (Database::num_rows($result) > 0) {
2851
            while ($row = Database::fetch_assoc($result)) {
2852
                if (empty($sessions[$row['id']])) {
2853
                    $sessions[$row['id']] = $row;
2854
                }
2855
            }
2856
        }
2857
2858
        if (api_is_allowed_to_create_course()) {
2859
            foreach ($sessions as $enreg) {
2860
                $session_id = $enreg['id'];
2861
                $session_visibility = api_get_session_visibility($session_id);
2862
                $session = api_get_session_entity($session_id);
2863
2864
                if (SESSION_INVISIBLE == $session_visibility) {
2865
                    continue;
2866
                }
2867
2868
                $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
2869
                    $user,
2870
                    $session,
2871
                    SessionEntity::GENERAL_COACH,
2872
                    $url
2873
                );
2874
                $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
2875
                    $user,
2876
                    $session,
2877
                    SessionEntity::COURSE_COACH,
2878
                    $url
2879
                );
2880
2881
                // This query is horribly slow when more than a few thousand
2882
                // users and just a few sessions to which they are subscribed
2883
                $coursesInSession = array_map(
2884
                    function (SessionRelCourse $courseInSession) {
2885
                        $course = $courseInSession->getCourse();
2886
2887
                        return [
2888
                            'code' => $course->getCode(),
2889
                            'i' => $course->getTitle(),
2890
                            'l' => $course->getCourseLanguage(),
2891
                            'sort' => 1,
2892
                        ];
2893
                    },
2894
                    array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
2895
                );
2896
2897
                foreach ($coursesInSession as $result_row) {
2898
                    $result_row['t'] = $formattedUserName;
2899
                    $result_row['email'] = $user->getEmail();
2900
                    $result_row['access_start_date'] = $session->getAccessStartDate()?->format('Y-m-d H:i:s');
2901
                    $result_row['access_end_date'] = $session->getAccessEndDate()?->format('Y-m-d H:i:s');
2902
                    $result_row['session_id'] = $session->getId();
2903
                    $result_row['session_name'] = $session->getTitle();
2904
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
2905
                    $key = $result_row['session_id'].' - '.$result_row['code'];
2906
                    $personal_course_list[$key] = $result_row;
2907
                }
2908
            }
2909
        }
2910
2911
        foreach ($sessions as $enreg) {
2912
            $session_id = $enreg['id'];
2913
            $session_visibility = api_get_session_visibility($session_id);
2914
            if (SESSION_INVISIBLE == $session_visibility) {
2915
                continue;
2916
            }
2917
2918
            /* This query is very similar to the above query,
2919
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
2920
            $sql = "SELECT DISTINCT
2921
                course.code code,
2922
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
2923
                email,
2924
                course.course_language l,
2925
                1 sort,
2926
                access_start_date,
2927
                access_end_date,
2928
                session.id as session_id,
2929
                session.title as session_name,
2930
                IF((session_course_user.user_id = 3 AND session_course_user.status = ".SessionEntity::COURSE_COACH."),'2', '5')
2931
            FROM $tbl_session_course_user as session_course_user
2932
            INNER JOIN $tbl_course AS course
2933
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
2934
            INNER JOIN $tbl_session as session
2935
            ON session_course_user.session_id = session.id
2936
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
2937
            WHERE session_course_user.user_id = $user_id
2938
            ORDER BY i";
2939
2940
            $course_list_sql_result = Database::query($sql);
2941
            while ($result_row = Database::fetch_assoc($course_list_sql_result)) {
2942
                $result_row['course_info'] = api_get_course_info($result_row['code']);
2943
                $key = $result_row['session_id'].' - '.$result_row['code'];
2944
                if (!isset($personal_course_list[$key])) {
2945
                    $personal_course_list[$key] = $result_row;
2946
                }
2947
            }
2948
        }
2949
2950
        return $personal_course_list;
2951
    }
2952
2953
    /**
2954
     * Gives a list of courses for the given user in the given session.
2955
     *
2956
     * @param int $user_id
2957
     * @param int $session_id
2958
     *
2959
     * @return array list of statuses (session_id-course_code => status)
2960
     */
2961
    public static function get_courses_list_by_session($user_id, $session_id)
2962
    {
2963
        // Database Table Definitions
2964
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
2965
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2966
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
2967
2968
        $user_id = (int) $user_id;
2969
        $session_id = (int) $session_id;
2970
2971
        $sessionRepo = Container::getSessionRepository();
2972
2973
        $user = api_get_user_entity($user_id);
2974
        $session = api_get_session_entity($session_id);
2975
        $url = null;
2976
2977
        // We filter the courses from the URL
2978
        $join_access_url = $where_access_url = '';
2979
        if (api_get_multiple_access_url()) {
2980
            $urlId = api_get_current_access_url_id();
2981
            if (-1 != $urlId) {
2982
                $url = api_get_url_entity($urlId);
2983
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
2984
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
2985
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
2986
            }
2987
        }
2988
2989
        /* This query is very similar to the query below, but it will check the
2990
        session_rel_course_user table if there are courses registered
2991
        to our user or not */
2992
        $sql = "SELECT DISTINCT
2993
                    c.title,
2994
                    c.visibility,
2995
                    c.id as real_id,
2996
                    c.code as course_code,
2997
                    sc.position,
2998
                    c.unsubscribe
2999
                FROM $tbl_session_course_user as scu
3000
                INNER JOIN $tbl_session_course sc
3001
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3002
                INNER JOIN $tableCourse as c
3003
                ON (scu.c_id = c.id)
3004
                $join_access_url
3005
                WHERE
3006
                    scu.user_id = $user_id AND
3007
                    scu.session_id = $session_id
3008
                    $where_access_url
3009
                ORDER BY sc.position ASC";
3010
3011
        $myCourseList = [];
3012
        $courses = [];
3013
        $result = Database::query($sql);
3014
        if (Database::num_rows($result) > 0) {
3015
            while ($result_row = Database::fetch_assoc($result)) {
3016
                $result_row['status'] = 5;
3017
                if (!in_array($result_row['real_id'], $courses)) {
3018
                    $position = $result_row['position'];
3019
                    if (!isset($myCourseList[$position])) {
3020
                        $myCourseList[$position] = $result_row;
3021
                    } else {
3022
                        $myCourseList[] = $result_row;
3023
                    }
3024
                    $courses[] = $result_row['real_id'];
3025
                }
3026
            }
3027
        }
3028
3029
        if (api_is_allowed_to_create_course()) {
3030
            $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
3031
                $user,
3032
                $session,
3033
                SessionEntity::GENERAL_COACH,
3034
                $url
3035
            );
3036
            $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
3037
                $user,
3038
                $session,
3039
                SessionEntity::COURSE_COACH,
3040
                $url
3041
            );
3042
3043
            $coursesInSession = array_map(
3044
                function (SessionRelCourse $courseInSession) {
3045
                    $course = $courseInSession->getCourse();
3046
3047
                    return [
3048
                        'title' => $course->getTitle(),
3049
                        'visibility' => $course->getVisibility(),
3050
                        'real_id' => $course->getId(),
3051
                        'course_code' => $course->getCode(),
3052
                        'position' => $courseInSession->getPosition(),
3053
                        'unsubscribe' => $course->getUnsubscribe(),
3054
                    ];
3055
                },
3056
                array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
3057
            );
3058
3059
            foreach ($coursesInSession as $result_row) {
3060
                $result_row['status'] = 2;
3061
                if (!in_array($result_row['real_id'], $courses)) {
3062
                    $position = $result_row['position'];
3063
                    if (!isset($myCourseList[$position])) {
3064
                        $myCourseList[$position] = $result_row;
3065
                    } else {
3066
                        $myCourseList[] = $result_row;
3067
                    }
3068
                    $courses[] = $result_row['real_id'];
3069
                }
3070
            }
3071
        }
3072
3073
        if (api_is_drh()) {
3074
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3075
            $sessionList = array_keys($sessionList);
3076
            if (in_array($session_id, $sessionList)) {
3077
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3078
                if (!empty($courseList)) {
3079
                    foreach ($courseList as $course) {
3080
                        if (!in_array($course['id'], $courses)) {
3081
                            $position = $course['position'];
3082
                            if (!isset($myCourseList[$position])) {
3083
                                $myCourseList[$position] = $course;
3084
                            } else {
3085
                                $myCourseList[] = $course;
3086
                            }
3087
                        }
3088
                    }
3089
                }
3090
            }
3091
        } else {
3092
            //check if user is general coach for this session
3093
            if ($session && $session->hasUserAsGeneralCoach($user)) {
3094
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3095
                if (!empty($courseList)) {
3096
                    foreach ($courseList as $course) {
3097
                        if (!in_array($course['id'], $courses)) {
3098
                            $position = $course['position'];
3099
                            if (!isset($myCourseList[$position])) {
3100
                                $myCourseList[$position] = $course;
3101
                            } else {
3102
                                $myCourseList[] = $course;
3103
                            }
3104
                        }
3105
                    }
3106
                }
3107
            }
3108
        }
3109
3110
        if (!empty($myCourseList)) {
3111
            ksort($myCourseList);
3112
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
3113
            if (empty($checkPosition)) {
3114
                // The session course list doesn't have any position,
3115
                // then order the course list by course code
3116
                $list = array_column($myCourseList, 'course_code');
3117
                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

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

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

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

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