Passed
Push — master ( 65d552...4599b6 )
by Angel Fernando Quiroz
10:41
created

UserManager::update_api_key()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 26
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return false;
}

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

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

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

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

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

    return false;
}

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

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

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

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

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

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

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

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

3956
            $form->addHeader(get_lang('Results and feedback').' "'./** @scrutinizer ignore-type */ $query.'"');
Loading history...
3957
        }
3958
3959
        $form->addText(
3960
            'q',
3961
            get_lang('Users, Groups'),
3962
            false,
3963
            [
3964
                'id' => 'q',
3965
            ]
3966
        );
3967
        $options = [
3968
            0 => get_lang('Select'),
3969
            1 => get_lang('User'),
3970
            2 => get_lang('Group'),
3971
        ];
3972
        $form->addSelect(
3973
            'search_type',
3974
            get_lang('Type'),
3975
            $options,
3976
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
3977
        );
3978
3979
        // Extra fields
3980
        $extraFields = self::getExtraFilterableFields();
3981
        $defaults = [];
3982
        if (is_array($extraFields) && count($extraFields) > 0) {
3983
            foreach ($extraFields as $extraField) {
3984
                $varName = 'field_'.$extraField['variable'];
3985
                $options = [
3986
                    0 => get_lang('Select'),
3987
                ];
3988
                foreach ($extraField['data'] as $option) {
3989
                    if (isset($_GET[$varName])) {
3990
                        if ($_GET[$varName] == $option[1]) {
3991
                            $defaults[$option[1]] = true;
3992
                        }
3993
                    }
3994
3995
                    $options[$option[1]] = $option[1];
3996
                }
3997
                $form->addSelect($varName, $extraField['name'], $options);
3998
            }
3999
        }
4000
4001
        $defaults['search_type'] = (int) $searchType;
4002
        $defaults['q'] = $query;
4003
4004
        if (!empty($defaultParams)) {
4005
            $defaults = array_merge($defaults, $defaultParams);
4006
        }
4007
        $form->setDefaults($defaults);
4008
        $form->addButtonSearch(get_lang('Search'));
4009
4010
        $js = '<script>
4011
        extra_field_toogle();
4012
        function extra_field_toogle() {
4013
            if (jQuery("select[name=search_type]").val() != "1") {
4014
                jQuery(".extra_field").hide();
4015
            } else {
4016
                jQuery(".extra_field").show();
4017
            }
4018
        }
4019
        </script>';
4020
4021
        return $js.$form->returnForm();
4022
    }
4023
4024
    /**
4025
     * @param int $userId
4026
     *
4027
     * @return array
4028
     */
4029
    public static function getDrhListFromUser($userId)
4030
    {
4031
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4032
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4033
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4034
        $userId = (int) $userId;
4035
4036
        $orderBy = null;
4037
        if (api_is_western_name_order()) {
4038
            $orderBy .= ' ORDER BY firstname, lastname ';
4039
        } else {
4040
            $orderBy .= ' ORDER BY lastname, firstname ';
4041
        }
4042
4043
        $sql = "SELECT u.id, username, u.firstname, u.lastname
4044
                FROM $tblUser u
4045
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
4046
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
4047
                WHERE
4048
                    access_url_id = ".api_get_current_access_url_id()." AND
4049
                    uru.user_id = '$userId' AND
4050
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4051
                    $orderBy
4052
                ";
4053
        $result = Database::query($sql);
4054
4055
        return Database::store_result($result);
4056
    }
4057
4058
    /**
4059
     * get users followed by human resource manager.
4060
     *
4061
     * @param int    $userId
4062
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
4063
     * @param bool   $getOnlyUserId
4064
     * @param bool   $getSql
4065
     * @param bool   $getCount
4066
     * @param int    $from
4067
     * @param int    $numberItems
4068
     * @param int    $column
4069
     * @param string $direction
4070
     * @param int    $active
4071
     * @param string $lastConnectionDate
4072
     *
4073
     * @return array users
4074
     */
4075
    public static function get_users_followed_by_drh(
4076
        $userId,
4077
        $userStatus = 0,
4078
        $getOnlyUserId = false,
4079
        $getSql = false,
4080
        $getCount = false,
4081
        $from = null,
4082
        $numberItems = null,
4083
        $column = null,
4084
        $direction = null,
4085
        $active = null,
4086
        $lastConnectionDate = null
4087
    ) {
4088
        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...
4089
            $userId,
4090
            $userStatus,
4091
            $getOnlyUserId,
4092
            $getSql,
4093
            $getCount,
4094
            $from,
4095
            $numberItems,
4096
            $column,
4097
            $direction,
4098
            $active,
4099
            $lastConnectionDate,
4100
            DRH
4101
        );
4102
    }
4103
4104
    /**
4105
     * Get users followed by human resource manager.
4106
     *
4107
     * @param int    $userId
4108
     * @param int    $userStatus             Filter users by status (STUDENT, COURSEMANAGER, etc)
4109
     * @param bool   $getOnlyUserId
4110
     * @param bool   $getSql
4111
     * @param bool   $getCount
4112
     * @param int    $from
4113
     * @param int    $numberItems
4114
     * @param int    $column
4115
     * @param string $direction
4116
     * @param int    $active
4117
     * @param string $lastConnectionDate
4118
     * @param int    $status                 the function is called by who? COURSEMANAGER, DRH?
4119
     * @param string $keyword
4120
     * @param bool   $checkSessionVisibility
4121
     *
4122
     * @return mixed Users list (array) or the SQL query if $getSQL was set to true
4123
     */
4124
    public static function getUsersFollowedByUser(
4125
        $userId,
4126
        $userStatus = null,
4127
        $getOnlyUserId = false,
4128
        $getSql = false,
4129
        $getCount = false,
4130
        $from = null,
4131
        $numberItems = null,
4132
        $column = null,
4133
        $direction = null,
4134
        $active = null,
4135
        $lastConnectionDate = null,
4136
        $status = null,
4137
        $keyword = null,
4138
        $checkSessionVisibility = false
4139
    ) {
4140
        // Database Table Definitions
4141
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4142
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4143
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4144
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4145
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4146
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4147
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4148
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4149
4150
        $userId = (int) $userId;
4151
        $limitCondition = '';
4152
4153
        if (isset($from) && isset($numberItems)) {
4154
            $from = (int) $from;
4155
            $numberItems = (int) $numberItems;
4156
            $limitCondition = "LIMIT $from, $numberItems";
4157
        }
4158
4159
        $column = Database::escape_string($column);
4160
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
4161
4162
        $userConditions = '';
4163
        if (!empty($userStatus)) {
4164
            $userConditions .= ' AND u.status = '.intval($userStatus);
4165
        }
4166
4167
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
4168
        if ($getOnlyUserId) {
4169
            $select = " SELECT DISTINCT u.id user_id";
4170
        }
4171
4172
        $masterSelect = "SELECT DISTINCT * FROM ";
4173
4174
        if ($getCount) {
4175
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
4176
            $select = " SELECT DISTINCT(u.id) user_id";
4177
        }
4178
4179
        if (!is_null($active)) {
4180
            $active = intval($active);
4181
            $userConditions .= " AND u.active = $active ";
4182
        }
4183
4184
        if (!empty($keyword)) {
4185
            $keyword = trim(Database::escape_string($keyword));
4186
            $keywordParts = array_filter(explode(' ', $keyword));
4187
            $extraKeyword = '';
4188
            if (!empty($keywordParts)) {
4189
                $keywordPartsFixed = Database::escape_string(implode('%', $keywordParts));
4190
                if (!empty($keywordPartsFixed)) {
4191
                    $extraKeyword .= " OR
4192
                        CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keywordPartsFixed%' OR
4193
                        CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keywordPartsFixed%' ";
4194
                }
4195
            }
4196
            $userConditions .= " AND (
4197
                u.username LIKE '%$keyword%' OR
4198
                u.firstname LIKE '%$keyword%' OR
4199
                u.lastname LIKE '%$keyword%' OR
4200
                u.official_code LIKE '%$keyword%' OR
4201
                u.email LIKE '%$keyword%' OR
4202
                CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keyword%' OR
4203
                CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keyword%'
4204
                $extraKeyword
4205
            )";
4206
        }
4207
4208
        if (!empty($lastConnectionDate)) {
4209
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
4210
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
4211
        }
4212
4213
        $sessionConditionsCoach = null;
4214
        $dateCondition = '';
4215
        $drhConditions = null;
4216
        $teacherSelect = null;
4217
        $urlId = api_get_current_access_url_id();
4218
4219
        switch ($status) {
4220
            case DRH:
4221
                $drhConditions .= " AND
4222
                    friend_user_id = '$userId' AND
4223
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4224
                ";
4225
                break;
4226
            case COURSEMANAGER:
4227
                $drhConditions .= " AND
4228
                    friend_user_id = '$userId' AND
4229
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4230
                ";
4231
4232
                $sessionConditionsTeacher = " AND
4233
                    (scu.status = ".SessionEntity::COURSE_COACH." AND scu.user_id = '$userId')
4234
                ";
4235
4236
                if ($checkSessionVisibility) {
4237
                    $today = api_strtotime('now', 'UTC');
4238
                    $today = date('Y-m-d', $today);
4239
                    $dateCondition = "
4240
                        AND
4241
                        (
4242
                            (s.access_start_date <= '$today' AND '$today' <= s.access_end_date) OR
4243
                            (s.access_start_date IS NULL AND s.access_end_date IS NULL) OR
4244
                            (s.access_start_date <= '$today' AND s.access_end_date IS NULL) OR
4245
                            ('$today' <= s.access_end_date AND s.access_start_date IS NULL)
4246
                        )
4247
					";
4248
                }
4249
4250
                // Use $tbl_session_rel_course_rel_user instead of $tbl_session_rel_user
4251
                /*
4252
                INNER JOIN $tbl_session_rel_user sru
4253
                ON (sru.user_id = u.id)
4254
                INNER JOIN $tbl_session_rel_course_rel_user scu
4255
                ON (scu.user_id = u.id AND scu.c_id IS NOT NULL AND visibility = 1)*/
4256
                $teacherSelect =
4257
                "UNION ALL (
4258
                        $select
4259
                        FROM $tbl_user u
4260
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
4261
                        WHERE
4262
                            (
4263
                                sru.session_id IN (
4264
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
4265
                                    $tbl_session_rel_access_url session_rel_access_rel_user
4266
                                    ON session_rel_access_rel_user.session_id = s.id
4267
                                    INNER JOIN $tbl_session_rel_user sru ON s.id = sru.session_id
4268
                                    WHERE access_url_id = ".$urlId."
4269
                                        AND (sru.relation_type = ".SessionEntity::GENERAL_COACH."
4270
                                        AND sru.user_id = $userId)
4271
                                ) OR sru.session_id IN (
4272
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
4273
                                    INNER JOIN $tbl_session_rel_access_url url
4274
                                    ON (url.session_id = s.id)
4275
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
4276
                                    ON (scu.session_id = s.id)
4277
                                    WHERE access_url_id = ".$urlId."
4278
                                    $sessionConditionsTeacher
4279
                                    $dateCondition
4280
                                )
4281
                            )
4282
                            $userConditions
4283
                    )
4284
                    UNION ALL(
4285
                        $select
4286
                        FROM $tbl_user u
4287
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
4288
                        WHERE cu.c_id IN (
4289
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
4290
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
4291
                        )
4292
                        $userConditions
4293
                    )"
4294
                ;
4295
                break;
4296
            case STUDENT_BOSS:
4297
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".UserRelUser::USER_RELATION_TYPE_BOSS;
4298
                break;
4299
            case HRM_REQUEST:
4300
                $drhConditions .= " AND
4301
                    friend_user_id = '$userId' AND
4302
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_HRM_REQUEST."'
4303
                ";
4304
                break;
4305
        }
4306
4307
        $join = null;
4308
        $sql = " $masterSelect
4309
                (
4310
                    (
4311
                        $select
4312
                        FROM $tbl_user u
4313
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
4314
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
4315
                        $join
4316
                        WHERE
4317
                            access_url_id = ".$urlId."
4318
                            $drhConditions
4319
                            $userConditions
4320
                    )
4321
                    $teacherSelect
4322
4323
                ) as t1";
4324
4325
        if ($getSql) {
4326
            return $sql;
4327
        }
4328
        if ($getCount) {
4329
            $result = Database::query($sql);
4330
            $row = Database::fetch_array($result);
4331
4332
            return $row['count'];
4333
        }
4334
4335
        $orderBy = null;
4336
        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...
4337
            if (api_is_western_name_order()) {
4338
                $orderBy .= " ORDER BY firstname, lastname ";
4339
            } else {
4340
                $orderBy .= " ORDER BY lastname, firstname ";
4341
            }
4342
4343
            if (!empty($column) && !empty($direction)) {
4344
                // Fixing order due the UNIONs
4345
                $column = str_replace('u.', '', $column);
4346
                $orderBy = " ORDER BY `$column` $direction ";
4347
            }
4348
        }
4349
4350
        $sql .= $orderBy;
4351
        $sql .= $limitCondition;
4352
4353
        $result = Database::query($sql);
4354
        $users = [];
4355
        if (Database::num_rows($result) > 0) {
4356
            while ($row = Database::fetch_array($result)) {
4357
                $users[$row['user_id']] = $row;
4358
            }
4359
        }
4360
4361
        return $users;
4362
    }
4363
4364
    /**
4365
     * Subscribes users to human resource manager (Dashboard feature).
4366
     *
4367
     * @param int   $hr_dept_id
4368
     * @param array $users_id
4369
     * @param bool  $deleteOtherAssignedUsers
4370
     */
4371
    public static function subscribeUsersToHRManager(
4372
        $hr_dept_id,
4373
        $users_id,
4374
        $deleteOtherAssignedUsers = true
4375
    ): void {
4376
        self::subscribeUsersToUser(
4377
            $hr_dept_id,
4378
            $users_id,
4379
            UserRelUser::USER_RELATION_TYPE_RRHH,
4380
            false,
4381
            $deleteOtherAssignedUsers
4382
        );
4383
    }
4384
4385
    /**
4386
     * Register request to assign users to HRM.
4387
     *
4388
     * @param int   $hrmId   The HRM ID
4389
     * @param array $usersId The users IDs
4390
     */
4391
    public static function requestUsersToHRManager($hrmId, $usersId): void
4392
    {
4393
        self::subscribeUsersToUser(
4394
            $hrmId,
4395
            $usersId,
4396
            UserRelUser::USER_RELATION_TYPE_HRM_REQUEST,
4397
            false,
4398
            false
4399
        );
4400
    }
4401
4402
    /**
4403
     * Remove the requests for assign a user to a HRM.
4404
     *
4405
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
4406
     */
4407
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
4408
    {
4409
        $users = implode(', ', $usersId);
4410
        Database::getManager()
4411
            ->createQuery('
4412
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
4413
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
4414
            ')
4415
            ->execute(['hrm_id' => $hrmId, 'relation_type' => UserRelUser::USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
4416
    }
4417
4418
    /**
4419
     * Add subscribed users to a user by relation type.
4420
     *
4421
     * @param int   $userId                   The user id
4422
     * @param array $subscribedUsersId        The id of subscribed users
4423
     * @param int   $relationType             The relation type
4424
     * @param bool  $deleteUsersBeforeInsert
4425
     * @param bool  $deleteOtherAssignedUsers
4426
     */
4427
    public static function subscribeUsersToUser(
4428
        $userId,
4429
        $subscribedUsersId,
4430
        $relationType,
4431
        $deleteUsersBeforeInsert = false,
4432
        $deleteOtherAssignedUsers = true
4433
    ): void {
4434
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4435
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4436
4437
        $userId = (int) $userId;
4438
        $relationType = (int) $relationType;
4439
4440
        if ($deleteOtherAssignedUsers) {
4441
            if (api_get_multiple_access_url()) {
4442
                // Deleting assigned users to hrm_id
4443
                $sql = "SELECT s.user_id
4444
                        FROM $userRelUserTable s
4445
                        INNER JOIN $userRelAccessUrlTable a
4446
                        ON (a.user_id = s.user_id)
4447
                        WHERE
4448
                            friend_user_id = $userId AND
4449
                            relation_type = $relationType AND
4450
                            access_url_id = ".api_get_current_access_url_id();
4451
            } else {
4452
                $sql = "SELECT user_id
4453
                        FROM $userRelUserTable
4454
                        WHERE
4455
                            friend_user_id = $userId AND
4456
                            relation_type = $relationType";
4457
            }
4458
            $result = Database::query($sql);
4459
4460
            if (Database::num_rows($result) > 0) {
4461
                while ($row = Database::fetch_array($result)) {
4462
                    $sql = "DELETE FROM $userRelUserTable
4463
                            WHERE
4464
                                user_id = {$row['user_id']} AND
4465
                                friend_user_id = $userId AND
4466
                                relation_type = $relationType";
4467
                    Database::query($sql);
4468
                }
4469
            }
4470
        }
4471
4472
        if ($deleteUsersBeforeInsert) {
4473
            $sql = "DELETE FROM $userRelUserTable
4474
                    WHERE
4475
                        user_id = $userId AND
4476
                        relation_type = $relationType";
4477
            Database::query($sql);
4478
        }
4479
4480
        // Inserting new user list.
4481
        if (is_array($subscribedUsersId)) {
4482
            foreach ($subscribedUsersId as $subscribedUserId) {
4483
                $subscribedUserId = (int) $subscribedUserId;
4484
                $sql = "SELECT id
4485
                        FROM $userRelUserTable
4486
                        WHERE
4487
                            user_id = $subscribedUserId AND
4488
                            friend_user_id = $userId AND
4489
                            relation_type = $relationType";
4490
4491
                $result = Database::query($sql);
4492
                $num = Database::num_rows($result);
4493
                if (0 === $num) {
4494
                    $userRelUser = (new UserRelUser())
4495
                        ->setUser(api_get_user_entity($subscribedUserId))
4496
                        ->setFriend(api_get_user_entity($userId))
4497
                        ->setRelationType($relationType)
4498
                    ;
4499
                    $em = Database::getManager();
4500
                    $em->persist($userRelUser);
4501
                    $em->flush();
4502
                }
4503
            }
4504
        }
4505
    }
4506
4507
    /**
4508
     * This function checks if a user is followed by provided human resources managers.
4509
     *
4510
     * @param int $user_id
4511
     * @param int $hr_dept_id Human resources manager
4512
     *
4513
     * @return bool
4514
     * @throws Exception
4515
     * @throws \Doctrine\DBAL\Exception
4516
     */
4517
    public static function is_user_followed_by_drh(int $user_id, int $hr_dept_id): bool
4518
    {
4519
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4520
        $result = false;
4521
4522
        $sql = "SELECT user_id FROM $tbl_user_rel_user
4523
                WHERE
4524
                    user_id = $user_id AND
4525
                    friend_user_id = $hr_dept_id AND
4526
                    relation_type = ".UserRelUser::USER_RELATION_TYPE_RRHH;
4527
        $rs = Database::query($sql);
4528
        if (Database::num_rows($rs) > 0) {
4529
            $result = true;
4530
        }
4531
4532
        return $result;
4533
    }
4534
4535
    /**
4536
     * Return the user id of teacher or session administrator.
4537
     *
4538
     * @param array $courseInfo
4539
     *
4540
     * @return int The user id, or 0 if the session ID was negative
4541
     * @throws Exception
4542
     * @throws \Doctrine\DBAL\Exception
4543
     */
4544
    public static function get_user_id_of_course_admin_or_session_admin(array $courseInfo): int
4545
    {
4546
        $session = api_get_session_id();
4547
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4548
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4549
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4550
4551
        if (empty($courseInfo)) {
4552
            return 0;
4553
        }
4554
4555
        $courseId = $courseInfo['real_id'];
4556
4557
        if (0 == $session) {
4558
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
4559
                    INNER JOIN '.$table_course_user.' ru
4560
                    ON ru.user_id = u.id
4561
                    WHERE
4562
                        ru.status = 1 AND
4563
                        ru.c_id = "'.$courseId.'" ';
4564
            $rs = Database::query($sql);
4565
            $num_rows = Database::num_rows($rs);
4566
            if (1 == $num_rows) {
4567
                $row = Database::fetch_array($rs);
4568
4569
                return (int) $row['uid'];
4570
            } else {
4571
                $my_num_rows = $num_rows;
4572
4573
                return (int) Database::result($rs, $my_num_rows - 1, 'uid');
4574
            }
4575
        } elseif ($session > 0) {
4576
            $sql = 'SELECT u.id as uid FROM '.$table_user.' u
4577
                    INNER JOIN '.$table_session_course_user.' sru
4578
                    ON sru.user_id = u.id
4579
                    WHERE
4580
                        sru.c_id = '.$courseId.' AND
4581
                        sru.status = '.SessionEntity::COURSE_COACH;
4582
            $rs = Database::query($sql);
4583
            if (Database::num_rows($rs) > 0) {
4584
                $row = Database::fetch_assoc($rs);
4585
4586
                return (int) $row['uid'];
4587
            }
4588
        }
4589
4590
        return 0;
4591
    }
4592
4593
    /**
4594
     * Determines if a user is a gradebook certified.
4595
     *
4596
     * @param int $cat_id  The category id of gradebook
4597
     * @param int $user_id The user id
4598
     *
4599
     * @return bool
4600
     */
4601
    public static function is_user_certified($cat_id, $user_id)
4602
    {
4603
        $cat_id = (int) $cat_id;
4604
        $user_id = (int) $user_id;
4605
4606
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4607
        $sql = 'SELECT path_certificate
4608
                FROM '.$table.'
4609
                WHERE
4610
                    cat_id = "'.$cat_id.'" AND
4611
                    user_id = "'.$user_id.'"';
4612
        $rs = Database::query($sql);
4613
        $row = Database::fetch_array($rs);
4614
4615
        if (!isset($row['path_certificate']) || '' == $row['path_certificate'] || is_null($row['path_certificate'])) {
4616
            return false;
4617
        }
4618
4619
        return true;
4620
    }
4621
4622
    /**
4623
     * Gets the info about a gradebook certificate for a user by course.
4624
     *
4625
     * @param array $course_info The course code
4626
     * @param int   $session_id
4627
     * @param int   $user_id     The user id
4628
     *
4629
     * @return array if there is not information return false
4630
     */
4631
    public static function get_info_gradebook_certificate($course_info, $session_id, $user_id)
4632
    {
4633
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4634
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
4635
        $session_id = (int) $session_id;
4636
        $user_id = (int) $user_id;
4637
        $courseId = $course_info['real_id'];
4638
4639
        if (empty($session_id)) {
4640
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
4641
        } else {
4642
            $session_condition = " AND session_id = $session_id";
4643
        }
4644
4645
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.'
4646
                WHERE cat_id = (
4647
                    SELECT id FROM '.$tbl_grade_category.'
4648
                    WHERE
4649
                        c_id = "'.$courseId.'" '.$session_condition.'
4650
                    LIMIT 1
4651
                ) AND user_id='.$user_id;
4652
4653
        $rs = Database::query($sql);
4654
        if (Database::num_rows($rs) > 0) {
4655
            $row = Database::fetch_assoc($rs);
4656
            $score = $row['score_certificate'];
4657
            $category_id = $row['cat_id'];
4658
            $cat = Category::load($category_id);
4659
            $displayscore = ScoreDisplay::instance();
4660
            if (isset($cat) && $displayscore->is_custom()) {
4661
                $grade = $displayscore->display_score(
4662
                    [$score, $cat[0]->get_weight()],
4663
                    SCORE_DIV_PERCENT_WITH_CUSTOM
4664
                );
4665
            } else {
4666
                $grade = $displayscore->display_score(
4667
                    [$score, $cat[0]->get_weight()]
4668
                );
4669
            }
4670
            $row['grade'] = $grade;
4671
4672
            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...
4673
        }
4674
4675
        return false;
4676
    }
4677
4678
    /**
4679
     * This function check if the user is a coach inside session course.
4680
     *
4681
     * @param int $user_id    User id
4682
     * @param int $courseId
4683
     * @param int $session_id
4684
     *
4685
     * @return bool True if the user is a coach
4686
     */
4687
    public static function is_session_course_coach($user_id, $courseId, $session_id)
4688
    {
4689
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4690
        // Protect data
4691
        $user_id = intval($user_id);
4692
        $courseId = intval($courseId);
4693
        $session_id = intval($session_id);
4694
        $result = false;
4695
4696
        $sql = "SELECT session_id FROM $table
4697
                WHERE
4698
                  session_id = $session_id AND
4699
                  c_id = $courseId AND
4700
                  user_id = $user_id AND
4701
                  status = ".SessionEntity::COURSE_COACH;
4702
        $res = Database::query($sql);
4703
4704
        if (Database::num_rows($res) > 0) {
4705
            $result = true;
4706
        }
4707
4708
        return $result;
4709
    }
4710
4711
    /**
4712
     * This function returns an icon path that represents the favicon of the website of which the url given.
4713
     * Defaults to the current Chamilo favicon.
4714
     *
4715
     * @param string $url1 URL of website where to look for favicon.ico
4716
     * @param string $url2 Optional second URL of website where to look for favicon.ico
4717
     *
4718
     * @return string Path of icon to load
4719
     */
4720
    public static function get_favicon_from_url($url1, $url2 = null)
4721
    {
4722
        $icon_link = '';
4723
        $url = $url1;
4724
        if (empty($url1)) {
4725
            $url = $url2;
4726
            if (empty($url)) {
4727
                $url = api_get_access_url(api_get_current_access_url_id());
4728
                $url = $url[0];
4729
            }
4730
        }
4731
        if (!empty($url)) {
4732
            $pieces = parse_url($url);
4733
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
4734
        }
4735
4736
        return $icon_link;
4737
    }
4738
4739
    public static function addUserAsAdmin(User $user)
4740
    {
4741
        $userId = $user->getId();
4742
4743
        if (!self::is_admin($userId)) {
4744
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
4745
            $sql = "INSERT INTO $table SET user_id = $userId";
4746
            Database::query($sql);
4747
        }
4748
4749
        $user->addRole('ROLE_ADMIN');
4750
        self::getRepository()->updateUser($user, true);
4751
    }
4752
4753
    public static function removeUserAdmin(User $user)
4754
    {
4755
        $userId = (int) $user->getId();
4756
        if (self::is_admin($userId)) {
4757
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
4758
            $sql = "DELETE FROM $table WHERE user_id = $userId";
4759
            Database::query($sql);
4760
            $user->removeRole('ROLE_ADMIN');
4761
            self::getRepository()->updateUser($user, true);
4762
        }
4763
    }
4764
4765
    /**
4766
     * @param string $from
4767
     * @param string $to
4768
     */
4769
    public static function update_all_user_languages($from, $to)
4770
    {
4771
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4772
        $from = Database::escape_string($from);
4773
        $to = Database::escape_string($to);
4774
4775
        if (!empty($to) && !empty($from)) {
4776
            $sql = "UPDATE $table_user SET language = '$to'
4777
                    WHERE language = '$from'";
4778
            Database::query($sql);
4779
        }
4780
    }
4781
4782
    /**
4783
     * Subscribe boss to students.
4784
     *
4785
     * @param int   $bossId                   The boss id
4786
     * @param array $usersId                  The users array
4787
     * @param bool  $deleteOtherAssignedUsers
4788
     */
4789
    public static function subscribeBossToUsers($bossId, $usersId, $deleteOtherAssignedUsers = true): void
4790
    {
4791
        self::subscribeUsersToUser(
4792
            $bossId,
4793
            $usersId,
4794
            UserRelUser::USER_RELATION_TYPE_BOSS,
4795
            false,
4796
            $deleteOtherAssignedUsers
4797
        );
4798
    }
4799
4800
    /**
4801
     * @param int $userId
4802
     *
4803
     * @return bool
4804
     */
4805
    public static function removeAllBossFromStudent($userId)
4806
    {
4807
        $userId = (int) $userId;
4808
4809
        if (empty($userId)) {
4810
            return false;
4811
        }
4812
4813
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4814
        $sql = "DELETE FROM $userRelUserTable
4815
                WHERE user_id = $userId AND relation_type = ".UserRelUser::USER_RELATION_TYPE_BOSS;
4816
        Database::query($sql);
4817
4818
        return true;
4819
    }
4820
4821
    /**
4822
     * Subscribe boss to students, if $bossList is empty then the boss list will be empty too.
4823
     *
4824
     * @param int   $studentId
4825
     * @param array $bossList
4826
     * @param bool  $sendNotification
4827
     *
4828
     * @return mixed Affected rows or false on failure
4829
     */
4830
    public static function subscribeUserToBossList(
4831
        $studentId,
4832
        $bossList,
4833
        $sendNotification = false
4834
    ) {
4835
        $inserted = 0;
4836
        if (!empty($bossList)) {
4837
            sort($bossList);
4838
            $studentId = (int) $studentId;
4839
            $studentInfo = api_get_user_info($studentId);
4840
4841
            if (empty($studentInfo)) {
4842
                return false;
4843
            }
4844
4845
            $previousBossList = self::getStudentBossList($studentId);
4846
            $previousBossList = !empty($previousBossList) ? array_column($previousBossList, 'boss_id') : [];
4847
            sort($previousBossList);
4848
4849
            // Boss list is the same, nothing changed.
4850
            if ($bossList == $previousBossList) {
4851
                return false;
4852
            }
4853
4854
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4855
            self::removeAllBossFromStudent($studentId);
4856
4857
            foreach ($bossList as $bossId) {
4858
                $bossId = (int) $bossId;
4859
                $bossInfo = api_get_user_info($bossId);
4860
4861
                if (empty($bossInfo)) {
4862
                    continue;
4863
                }
4864
4865
                $bossLanguage = $bossInfo['locale'];
4866
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
4867
                        VALUES ($studentId, $bossId, ".UserRelUser::USER_RELATION_TYPE_BOSS.")";
4868
                $insertId = Database::query($sql);
4869
4870
                if ($insertId) {
4871
                    if ($sendNotification) {
4872
                        $name = $studentInfo['complete_name'];
4873
                        $url = api_get_path(WEB_CODE_PATH).'my_space/myStudents.php?student='.$studentId;
4874
                        $url = Display::url($url, $url);
4875
                        $subject = sprintf(get_lang('You have been assigned the learner %s', $bossLanguage), $name);
4876
                        $message = sprintf(get_lang('You have been assigned the learner %s with url %s', $bossLanguage), $name, $url);
4877
                        MessageManager::send_message_simple(
4878
                            $bossId,
4879
                            $subject,
4880
                            $message
4881
                        );
4882
                    }
4883
                    $inserted++;
4884
                }
4885
            }
4886
        } else {
4887
            self::removeAllBossFromStudent($studentId);
4888
        }
4889
4890
        return $inserted;
4891
    }
4892
4893
    /**
4894
     * Get users followed by student boss.
4895
     *
4896
     * @param int    $userId
4897
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
4898
     * @param bool   $getOnlyUserId
4899
     * @param bool   $getSql
4900
     * @param bool   $getCount
4901
     * @param int    $from
4902
     * @param int    $numberItems
4903
     * @param int    $column
4904
     * @param string $direction
4905
     * @param int    $active
4906
     * @param string $lastConnectionDate
4907
     *
4908
     * @return array users
4909
     */
4910
    public static function getUsersFollowedByStudentBoss(
4911
        $userId,
4912
        $userStatus = 0,
4913
        $getOnlyUserId = false,
4914
        $getSql = false,
4915
        $getCount = false,
4916
        $from = null,
4917
        $numberItems = null,
4918
        $column = null,
4919
        $direction = null,
4920
        $active = null,
4921
        $lastConnectionDate = null
4922
    ) {
4923
        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...
4924
            $userId,
4925
            $userStatus,
4926
            $getOnlyUserId,
4927
            $getSql,
4928
            $getCount,
4929
            $from,
4930
            $numberItems,
4931
            $column,
4932
            $direction,
4933
            $active,
4934
            $lastConnectionDate,
4935
            STUDENT_BOSS
4936
        );
4937
    }
4938
4939
    /**
4940
     * @return array
4941
     */
4942
    public static function getOfficialCodeGrouped()
4943
    {
4944
        $user = Database::get_main_table(TABLE_MAIN_USER);
4945
        $sql = "SELECT DISTINCT official_code
4946
                FROM $user
4947
                GROUP BY official_code";
4948
        $result = Database::query($sql);
4949
        $values = Database::store_result($result, 'ASSOC');
4950
        $result = [];
4951
        foreach ($values as $value) {
4952
            $result[$value['official_code']] = $value['official_code'];
4953
        }
4954
4955
        return $result;
4956
    }
4957
4958
    /**
4959
     * @param string $officialCode
4960
     *
4961
     * @return array
4962
     */
4963
    public static function getUsersByOfficialCode($officialCode)
4964
    {
4965
        $user = Database::get_main_table(TABLE_MAIN_USER);
4966
        $officialCode = Database::escape_string($officialCode);
4967
4968
        $sql = "SELECT DISTINCT id
4969
                FROM $user
4970
                WHERE official_code = '$officialCode'
4971
                ";
4972
        $result = Database::query($sql);
4973
4974
        $users = [];
4975
        while ($row = Database::fetch_array($result)) {
4976
            $users[] = $row['id'];
4977
        }
4978
4979
        return $users;
4980
    }
4981
4982
    /**
4983
     * Calc the expended time (in seconds) by a user in a course.
4984
     *
4985
     * @param int    $userId    The user id
4986
     * @param int    $courseId  The course id
4987
     * @param int    $sessionId Optional. The session id
4988
     * @param string $from      Optional. From date
4989
     * @param string $until     Optional. Until date
4990
     *
4991
     * @return int The time
4992
     */
4993
    public static function getTimeSpentInCourses(
4994
        $userId,
4995
        $courseId,
4996
        $sessionId = 0,
4997
        $from = '',
4998
        $until = ''
4999
    ) {
5000
        $userId = (int) $userId;
5001
        $sessionId = (int) $sessionId;
5002
5003
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5004
        $whereConditions = [
5005
            'user_id = ? ' => $userId,
5006
            'AND c_id = ? ' => $courseId,
5007
            'AND session_id = ? ' => $sessionId,
5008
        ];
5009
5010
        if (!empty($from) && !empty($until)) {
5011
            $whereConditions["AND (login_course_date >= '?' "] = $from;
5012
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
5013
        }
5014
5015
        $trackResult = Database::select(
5016
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
5017
            $trackCourseAccessTable,
5018
            [
5019
                'where' => $whereConditions,
5020
            ],
5021
            'first'
5022
        );
5023
5024
        if (false != $trackResult) {
5025
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
5026
        }
5027
5028
        return 0;
5029
    }
5030
5031
    /**
5032
     * Get the boss user ID from a followed user id.
5033
     *
5034
     * @param $userId
5035
     *
5036
     * @return bool
5037
     */
5038
    public static function getFirstStudentBoss($userId)
5039
    {
5040
        $userId = (int) $userId;
5041
        if ($userId > 0) {
5042
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5043
            $row = Database::select(
5044
                'DISTINCT friend_user_id AS boss_id',
5045
                $userRelTable,
5046
                [
5047
                    'where' => [
5048
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
5049
                            $userId,
5050
                            UserRelUser::USER_RELATION_TYPE_BOSS,
5051
                        ],
5052
                    ],
5053
                ]
5054
            );
5055
            if (!empty($row)) {
5056
                return $row[0]['boss_id'];
5057
            }
5058
        }
5059
5060
        return false;
5061
    }
5062
5063
    /**
5064
     * Get the boss user ID from a followed user id.
5065
     *
5066
     * @param int $userId student id
5067
     *
5068
     * @return array
5069
     */
5070
    public static function getStudentBossList($userId)
5071
    {
5072
        $userId = (int) $userId;
5073
5074
        if ($userId > 0) {
5075
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5076
5077
            return Database::select(
5078
                'DISTINCT friend_user_id AS boss_id',
5079
                $userRelTable,
5080
                [
5081
                    'where' => [
5082
                        'user_id = ? AND relation_type = ? ' => [
5083
                            $userId,
5084
                            UserRelUser::USER_RELATION_TYPE_BOSS,
5085
                        ],
5086
                    ],
5087
                ]
5088
            );
5089
        }
5090
5091
        return [];
5092
    }
5093
5094
    /**
5095
     * @param int $bossId
5096
     * @param int $studentId
5097
     *
5098
     * @return bool
5099
     */
5100
    public static function userIsBossOfStudent($bossId, $studentId)
5101
    {
5102
        $result = false;
5103
        $bossList = self::getStudentBossList($studentId);
5104
        if (!empty($bossList)) {
5105
            $bossList = array_column($bossList, 'boss_id');
5106
            if (in_array($bossId, $bossList)) {
5107
                $result = true;
5108
            }
5109
        }
5110
5111
        return $result;
5112
    }
5113
5114
    /**
5115
     * Displays the name of the user and makes the link to the user profile.
5116
     *
5117
     * @param array $userInfo
5118
     *
5119
     * @return string
5120
     */
5121
    public static function getUserProfileLink($userInfo)
5122
    {
5123
        if (isset($userInfo) && isset($userInfo['user_id'])) {
5124
            return Display::url(
5125
                $userInfo['complete_name_with_username'],
5126
                $userInfo['profile_url']
5127
            );
5128
        }
5129
5130
        return get_lang('Anonymous');
5131
    }
5132
5133
    /**
5134
     * Get users whose name matches $firstname and $lastname.
5135
     *
5136
     * @param string $firstname Firstname to search
5137
     * @param string $lastname  Lastname to search
5138
     *
5139
     * @return array The user list
5140
     */
5141
    public static function getUsersByName($firstname, $lastname)
5142
    {
5143
        $firstname = Database::escape_string($firstname);
5144
        $lastname = Database::escape_string($lastname);
5145
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
5146
5147
        $sql = <<<SQL
5148
            SELECT id, username, lastname, firstname
5149
            FROM $userTable
5150
            WHERE
5151
                firstname LIKE '$firstname%' AND
5152
                lastname LIKE '$lastname%'
5153
SQL;
5154
        $result = Database::query($sql);
5155
        $users = [];
5156
        while ($resultData = Database::fetch_object($result)) {
5157
            $users[] = $resultData;
5158
        }
5159
5160
        return $users;
5161
    }
5162
5163
    /**
5164
     * @param int $optionSelected
5165
     *
5166
     * @return string
5167
     */
5168
    public static function getUserSubscriptionTab($optionSelected = 1)
5169
    {
5170
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
5171
        if (('true' === $allowAdmin && api_is_allowed_to_edit()) ||
5172
            api_is_platform_admin()
5173
        ) {
5174
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
5175
5176
            $headers = [
5177
                [
5178
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
5179
                    'content' => get_lang('Learners'),
5180
                ],
5181
                [
5182
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
5183
                    'content' => get_lang('Trainers'),
5184
                ],
5185
                /*[
5186
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
5187
                    'content' => get_lang('Learners'),
5188
                ],
5189
                [
5190
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
5191
                    'content' => get_lang('Trainers'),
5192
                ],*/
5193
                [
5194
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
5195
                    'content' => get_lang('Groups'),
5196
                ],
5197
                [
5198
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
5199
                    'content' => get_lang('Classes'),
5200
                ],
5201
            ];
5202
5203
            return Display::tabsOnlyLink($headers, $optionSelected);
5204
        }
5205
5206
        return '';
5207
    }
5208
5209
    /**
5210
     * Make sure this function is protected because it does NOT check password!
5211
     *
5212
     * This function defines globals.
5213
     *
5214
     * @param int  $userId
5215
     * @param bool $checkIfUserCanLoginAs
5216
     *
5217
     * @return bool
5218
     *
5219
     * @author Evie Embrechts
5220
     * @author Yannick Warnier <[email protected]>
5221
     */
5222
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
5223
    {
5224
        $userId = (int) $userId;
5225
        $userInfo = api_get_user_info($userId);
5226
5227
        // Check if the user is allowed to 'login_as'
5228
        $canLoginAs = true;
5229
        if ($checkIfUserCanLoginAs) {
5230
            $canLoginAs = api_can_login_as($userId);
5231
        }
5232
5233
        if (!$canLoginAs || empty($userInfo)) {
5234
            return false;
5235
        }
5236
5237
        if ($userId) {
5238
            $logInfo = [
5239
                'tool' => 'logout',
5240
                'tool_id' => 0,
5241
                'tool_id_detail' => 0,
5242
                'action' => '',
5243
                'info' => 'Change user (login as)',
5244
            ];
5245
            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

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