Passed
Push — master ( d578ca...a531df )
by Yannick
09:55
created

UserManager::delete_user()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 42
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 24
c 0
b 0
f 0
nc 5
nop 2
dl 0
loc 42
rs 9.2248
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()) {
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
                    $emailBody = $tpl->render('@ChamiloCore/Mailer/Legacy/new_user_second_email_confirmation.html.twig');
464
465
                    if (!empty($emailBodyTemplate) &&
466
                        isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
467
                        !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
468
                    ) {
469
                        $emailBody = $mailTemplateManager->parseTemplate(
470
                            $emailTemplate['new_user_second_email_confirmation.tpl'],
471
                            $userInfo
472
                        );
473
                    }
474
475
                    api_mail_html(
476
                        $recipient_name,
477
                        $email,
478
                        $emailSubject,
479
                        $emailBody,
480
                        $sender_name,
481
                        $email_admin,
482
                        null,
483
                        null,
484
                        null,
485
                        $creatorEmail
486
                    );
487
                } else {
488
                    if (!empty($emailBodyTemplate)) {
489
                        $emailBody = $emailBodyTemplate;
490
                    }
491
                    $sendToInbox = ('true' === api_get_setting('registration.send_inscription_msg_to_inbox'));
492
                    if ($sendToInbox) {
493
                        $adminList = self::get_all_administrators();
494
                        $senderId = 1;
495
                        if (!empty($adminList)) {
496
                            $adminInfo = current($adminList);
497
                            $senderId = $adminInfo['user_id'];
498
                        }
499
500
                        MessageManager::send_message_simple(
501
                            $userId,
502
                            $emailSubject,
503
                            $emailBody,
504
                            $senderId
505
                        );
506
                    } else {
507
                        api_mail_html(
508
                            $recipient_name,
509
                            $email,
510
                            $emailSubject,
511
                            $emailBody,
512
                            $sender_name,
513
                            $email_admin,
514
                            null,
515
                            null,
516
                            null,
517
                            $creatorEmail
518
                        );
519
                    }
520
                }
521
522
                $notification = api_get_setting('profile.send_notification_when_user_added', true);
523
                if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
524
                    foreach ($notification['admins'] as $adminId) {
525
                        $emailSubjectToAdmin = get_lang('The user has been added').': '.
526
                            api_get_person_name($firstName, $lastName);
527
                        MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
528
                    }
529
                }
530
531
                if ($sendEmailToAllAdmins) {
532
                    $adminList = self::get_all_administrators();
533
                    // variables for the default template
534
                    $renderer = FormValidator::getDefaultRenderer();
535
                    // Form template
536
                    $elementTemplate = ' {label}: {element} <br />';
537
                    $renderer->setElementTemplate($elementTemplate);
538
                    /** @var FormValidator $form */
539
                    $form->freeze(null, $elementTemplate);
540
                    $form->removeElement('submit');
541
                    $formData = $form->returnForm();
542
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
543
                    $params = [
544
                        'complete_name' => stripslashes(api_get_person_name($firstName, $lastName)),
545
                        'user_added' => $user,
546
                        'link' => Display::url($url, $url),
547
                        'form' => $formData,
548
                        'user_language' => $user->getLocale(),
549
                    ];
550
                    $emailBody = $tpl->render(
551
                        '@ChamiloCore/Mailer/Legacy/content_registration_platform_to_admin.html.twig',
552
                        $params
553
                    );
554
555
                    if (!empty($emailBodyTemplate) &&
556
                        isset($emailTemplate['content_registration_platform_to_admin.tpl']) &&
557
                        !empty($emailTemplate['content_registration_platform_to_admin.tpl'])
558
                    ) {
559
                        $emailBody = $mailTemplateManager->parseTemplate(
560
                            $emailTemplate['content_registration_platform_to_admin.tpl'],
561
                            $userInfo
562
                        );
563
                    }
564
565
                    $subject = get_lang('The user has been added');
566
                    foreach ($adminList as $adminId => $data) {
567
                        MessageManager::send_message_simple(
568
                            $adminId,
569
                            $subject,
570
                            $emailBody,
571
                            $userId
572
                        );
573
                    }
574
                }
575
            }
576
            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

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

954
            $emailsubject = '['./** @scrutinizer ignore-type */ api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
Loading history...
955
            $sender_name = api_get_person_name(
956
                api_get_setting('administratorName'),
957
                api_get_setting('administratorSurname'),
958
                null,
959
                PERSON_NAME_EMAIL_ADDRESS
960
            );
961
            $email_admin = api_get_setting('emailAdministrator');
962
            $url = api_get_path(WEB_PATH);
963
            if (api_is_multiple_url_enabled()) {
964
                $access_url_id = api_get_current_access_url_id();
965
                if (-1 != $access_url_id) {
966
                    $url = api_get_access_url($access_url_id);
967
                    $url = $url['url'];
968
                }
969
            }
970
971
            $tplContent = new Template(
972
                null,
973
                false,
974
                false,
975
                false,
976
                false,
977
                false
978
            );
979
            // variables for the default template
980
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
981
            $tplContent->assign('login_name', $username);
982
983
            $originalPassword = '';
984
            if ($reset_password > 0) {
985
                $originalPassword = stripslashes($original_password);
986
            }
987
            $tplContent->assign('original_password', $originalPassword);
988
            $tplContent->assign('portal_url', $url);
989
990
            $emailBody = $tplContent->fetch('@ChamiloCore/Mailer/Legacy/user_edit_content.html.twig');
991
992
            $mailTemplateManager = new MailTemplateManager();
993
994
            if (!empty($emailTemplate) &&
995
                isset($emailTemplate['user_edit_content.tpl']) &&
996
                !empty($emailTemplate['user_edit_content.tpl'])
997
            ) {
998
                $userInfo = api_get_user_info($user_id);
999
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], $userInfo);
1000
            }
1001
1002
            $creatorInfo = api_get_user_info($creator_id);
1003
            $creatorEmail = $creatorInfo['email'] ?? '';
1004
1005
            api_mail_html(
1006
                $recipient_name,
1007
                $email,
1008
                $emailsubject,
1009
                $emailBody,
1010
                $sender_name,
1011
                $email_admin,
1012
                null,
1013
                null,
1014
                null,
1015
                null,
1016
                $creatorEmail
1017
            );
1018
        }
1019
1020
        return $user->getId();
1021
    }
1022
1023
    /**
1024
     * Disables a user.
1025
     *
1026
     * @param int User id
1027
     *
1028
     * @return bool
1029
     *
1030
     * @uses \UserManager::change_active_state() to actually disable the user
1031
     * @assert (0) === false
1032
     */
1033
    public static function disable($user_id)
1034
    {
1035
        if (empty($user_id)) {
1036
            return false;
1037
        }
1038
        self::change_active_state($user_id, 0);
1039
1040
        return true;
1041
    }
1042
1043
    /**
1044
     * Enable a user.
1045
     *
1046
     * @param int User id
1047
     *
1048
     * @return bool
1049
     *
1050
     * @uses \UserManager::change_active_state() to actually disable the user
1051
     * @assert (0) === false
1052
     */
1053
    public static function enable($user_id)
1054
    {
1055
        if (empty($user_id)) {
1056
            return false;
1057
        }
1058
        self::change_active_state($user_id, 1);
1059
1060
        return true;
1061
    }
1062
1063
    /**
1064
     * Returns the user's id based on the original id and field name in
1065
     * the extra fields. Returns 0 if no user was found. This function is
1066
     * mostly useful in the context of a web services-based sinchronization.
1067
     *
1068
     * @param string Original user id
1069
     * @param string Original field name
1070
     *
1071
     * @return int User id
1072
     * @assert ('0','---') === 0
1073
     */
1074
    public static function get_user_id_from_original_id(
1075
        $original_user_id_value,
1076
        $original_user_id_name
1077
    ) {
1078
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1079
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1080
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1081
1082
        $original_user_id_name = Database::escape_string($original_user_id_name);
1083
        $original_user_id_value = Database::escape_string($original_user_id_value);
1084
1085
        $sql = "SELECT item_id as user_id
1086
                FROM $t_uf uf
1087
                INNER JOIN $t_ufv ufv
1088
                ON ufv.field_id = uf.id
1089
                WHERE
1090
                    variable = '$original_user_id_name' AND
1091
                    field_value = '$original_user_id_value' AND
1092
                    item_type = $extraFieldType
1093
                ";
1094
        $res = Database::query($sql);
1095
        $row = Database::fetch_object($res);
1096
        if ($row) {
1097
            return $row->user_id;
1098
        }
1099
1100
        return 0;
1101
    }
1102
1103
    /**
1104
     * Check if a username is available.
1105
     *
1106
     * @param string $username the wanted username
1107
     *
1108
     * @return bool true if the wanted username is available
1109
     * @assert ('') === false
1110
     * @assert ('xyzxyzxyz') === true
1111
     */
1112
    public static function is_username_available($username)
1113
    {
1114
        if (empty($username)) {
1115
            return false;
1116
        }
1117
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1118
        $sql = "SELECT username FROM $table_user
1119
                WHERE username = '".Database::escape_string($username)."'";
1120
        $res = Database::query($sql);
1121
1122
        return 0 == Database::num_rows($res);
1123
    }
1124
1125
    /**
1126
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1127
     *
1128
     * @param string $firstname the first name of the user
1129
     * @param string $lastname  the last name of the user
1130
     *
1131
     * @return string suggests a username that contains only ASCII-letters and digits,
1132
     *                without check for uniqueness within the system
1133
     *
1134
     * @author Julio Montoya Armas
1135
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1136
     * @assert ('','') === false
1137
     * @assert ('a','b') === 'ab'
1138
     */
1139
    public static function create_username($firstname, $lastname)
1140
    {
1141
        if (empty($firstname) && empty($lastname)) {
1142
            return false;
1143
        }
1144
1145
        // The first letter only.
1146
        $firstname = api_substr(
1147
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1148
            0,
1149
            1
1150
        );
1151
        //Looking for a space in the lastname
1152
        $pos = api_strpos($lastname, ' ');
1153
        if (false !== $pos) {
1154
            $lastname = api_substr($lastname, 0, $pos);
1155
        }
1156
1157
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1158
        $username = $firstname.$lastname;
1159
        if (empty($username)) {
1160
            $username = 'user';
1161
        }
1162
1163
        $username = URLify::transliterate($username);
1164
1165
        return strtolower(substr($username, 0, User::USERNAME_MAX_LENGTH - 3));
1166
    }
1167
1168
    /**
1169
     * Creates a unique username, using:
1170
     * 1. the first name and the last name of a user;
1171
     * 2. an already created username but not checked for uniqueness yet.
1172
     *
1173
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1174
     *                          parameter is treated as username which is to be checked f
1175
     *                          or uniqueness and to be modified when it is necessary.
1176
     * @param string $lastname  the last name of the user
1177
     *
1178
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1179
     *                Note: When the method is called several times with same parameters,
1180
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1181
     *
1182
     * @author Ivan Tcholakov, 2009
1183
     */
1184
    public static function create_unique_username($firstname, $lastname = null)
1185
    {
1186
        if (is_null($lastname)) {
1187
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1188
            // For making this method tolerant of mistakes,
1189
            // let us transliterate and purify the suggested input username anyway.
1190
            // So, instead of the sentence $username = $firstname; we place the following:
1191
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1192
        } else {
1193
            $username = self::create_username($firstname, $lastname);
1194
        }
1195
        if (!self::is_username_available($username)) {
1196
            $i = 2;
1197
            $temp_username = substr($username, 0, User::USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1198
            while (!self::is_username_available($temp_username)) {
1199
                $i++;
1200
                $temp_username = substr($username, 0, User::USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1201
            }
1202
            $username = $temp_username;
1203
        }
1204
1205
        $username = URLify::transliterate($username);
1206
1207
        return $username;
1208
    }
1209
1210
    /**
1211
     * Modifies a given username accordingly to the specification for valid characters and length.
1212
     *
1213
     * @param $username string          The input username
1214
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1215
     *                     otherwise compliance may be partial. The default value is FALSE.
1216
     *
1217
     * @return string the resulting purified username
1218
     */
1219
    public static function purify_username($username, $strict = false)
1220
    {
1221
        if ($strict) {
1222
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1223
            // into ASCII letters in order they not to be totally removed.
1224
            // 2. Applying the strict purifier.
1225
            // 3. Length limitation.
1226
            $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);
1227
            $return = URLify::transliterate($return);
1228
1229
            // We want everything transliterate() does except converting @ to '(at)'. This is a hack to avoid this.
1230
            $return = str_replace(' (at) ', '@', $return);
1231
1232
            return $return;
1233
        }
1234
1235
        // 1. Applying the shallow purifier.
1236
        // 2. Length limitation.
1237
        return substr(
1238
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1239
            0,
1240
            User::USERNAME_MAX_LENGTH
1241
        );
1242
    }
1243
1244
    /**
1245
     * Checks whether the user id exists in the database.
1246
     *
1247
     * @param int $userId User id
1248
     *
1249
     * @return bool True if user id was found, false otherwise
1250
     */
1251
    public static function is_user_id_valid($userId)
1252
    {
1253
        $resultData = Database::select(
1254
            'COUNT(1) AS count',
1255
            Database::get_main_table(TABLE_MAIN_USER),
1256
            [
1257
                'where' => ['id = ?' => (int) $userId],
1258
            ],
1259
            'first'
1260
        );
1261
1262
        if (false === $resultData) {
1263
            return false;
1264
        }
1265
1266
        return $resultData['count'] > 0;
1267
    }
1268
1269
    /**
1270
     * Checks whether a given username matches to the specification strictly.
1271
     * The empty username is assumed here as invalid.
1272
     * Mostly this function is to be used in the user interface built-in validation routines
1273
     * for providing feedback while usernames are enterd manually.
1274
     *
1275
     * @param string $username the input username
1276
     *
1277
     * @return bool returns TRUE if the username is valid, FALSE otherwise
1278
     */
1279
    public static function is_username_valid($username)
1280
    {
1281
        return !empty($username) && $username == self::purify_username($username, true);
1282
    }
1283
1284
    /**
1285
     * Checks whether a username is empty. If the username contains whitespace characters,
1286
     * such as spaces, tabulators, newlines, etc.,
1287
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
1288
     *
1289
     * @param string $username the given username
1290
     *
1291
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1292
     */
1293
    public static function is_username_empty($username)
1294
    {
1295
        return 0 == strlen(self::purify_username($username, false));
1296
    }
1297
1298
    /**
1299
     * Checks whether a username is too long or not.
1300
     *
1301
     * @param string $username the given username, it should contain only ASCII-letters and digits
1302
     *
1303
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1304
     */
1305
    public static function is_username_too_long($username)
1306
    {
1307
        return strlen($username) > User::USERNAME_MAX_LENGTH;
1308
    }
1309
1310
    /**
1311
     * Get the users by ID.
1312
     *
1313
     * @param array  $ids    student ids
1314
     * @param bool   $active
1315
     * @param string $order
1316
     * @param string $limit
1317
     *
1318
     * @return array $result student information
1319
     */
1320
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
1321
    {
1322
        if (empty($ids)) {
1323
            return [];
1324
        }
1325
1326
        $ids = is_array($ids) ? $ids : [$ids];
1327
        $ids = array_map('intval', $ids);
1328
        $ids = implode(',', $ids);
1329
1330
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
1331
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
1332
        if (!is_null($active)) {
1333
            $sql .= ' AND active='.($active ? '1' : '0');
1334
        }
1335
1336
        if (!is_null($order)) {
1337
            $order = Database::escape_string($order);
1338
            $sql .= ' ORDER BY '.$order;
1339
        }
1340
1341
        if (!is_null($limit)) {
1342
            $limit = Database::escape_string($limit);
1343
            $sql .= ' LIMIT '.$limit;
1344
        }
1345
1346
        $rs = Database::query($sql);
1347
        $result = [];
1348
        while ($row = Database::fetch_array($rs)) {
1349
            $result[] = $row;
1350
        }
1351
1352
        return $result;
1353
    }
1354
1355
    /**
1356
     * Get a list of users of which the given conditions match with an = 'cond'.
1357
     *
1358
     * @param array $conditions a list of condition (example : status=>STUDENT)
1359
     * @param array $order_by   a list of fields on which sort
1360
     *
1361
     * @return array an array with all users of the platform
1362
     *
1363
     * @todo security filter order by
1364
     */
1365
    public static function get_user_list(
1366
        $conditions = [],
1367
        $order_by = [],
1368
        $limit_from = false,
1369
        $limit_to = false,
1370
        $idCampus = null
1371
    ) {
1372
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1373
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1374
        $return_array = [];
1375
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1376
1377
        if (api_is_multiple_url_enabled()) {
1378
            if ($idCampus) {
1379
                $urlId = $idCampus;
1380
            } else {
1381
                $urlId = api_get_current_access_url_id();
1382
            }
1383
            $sql .= " INNER JOIN $userUrlTable url_user
1384
                      ON (user.id = url_user.user_id)
1385
                      WHERE url_user.access_url_id = $urlId";
1386
        } else {
1387
            $sql .= " WHERE 1=1 ";
1388
        }
1389
1390
        if (count($conditions) > 0) {
1391
            foreach ($conditions as $field => $value) {
1392
                $field = Database::escape_string($field);
1393
                $value = Database::escape_string($value);
1394
                $sql .= " AND $field = '$value'";
1395
            }
1396
        }
1397
1398
        if (count($order_by) > 0) {
1399
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1400
        }
1401
1402
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1403
            $limit_from = (int) $limit_from;
1404
            $limit_to = (int) $limit_to;
1405
            $sql .= " LIMIT $limit_from, $limit_to";
1406
        }
1407
        $sql_result = Database::query($sql);
1408
        while ($result = Database::fetch_array($sql_result)) {
1409
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1410
            $return_array[] = $result;
1411
        }
1412
1413
        return $return_array;
1414
    }
1415
1416
    public static function getUserListExtraConditions(
1417
        $conditions = [],
1418
        $order_by = [],
1419
        $limit_from = false,
1420
        $limit_to = false,
1421
        $idCampus = null,
1422
        $extraConditions = '',
1423
        $getCount = false
1424
    ) {
1425
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1426
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1427
        $return_array = [];
1428
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1429
1430
        if ($getCount) {
1431
            $sql = "SELECT count(user.id) count FROM $user_table user ";
1432
        }
1433
1434
        if (api_is_multiple_url_enabled()) {
1435
            if ($idCampus) {
1436
                $urlId = $idCampus;
1437
            } else {
1438
                $urlId = api_get_current_access_url_id();
1439
            }
1440
            $sql .= " INNER JOIN $userUrlTable url_user
1441
                      ON (user.user_id = url_user.user_id)
1442
                      WHERE url_user.access_url_id = $urlId";
1443
        } else {
1444
            $sql .= " WHERE 1=1 ";
1445
        }
1446
1447
        $sql .= " AND status <> ".ANONYMOUS." ";
1448
1449
        if (count($conditions) > 0) {
1450
            foreach ($conditions as $field => $value) {
1451
                $field = Database::escape_string($field);
1452
                $value = Database::escape_string($value);
1453
                $sql .= " AND $field = '$value'";
1454
            }
1455
        }
1456
1457
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
1458
1459
        if (!empty($order_by) && count($order_by) > 0) {
1460
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1461
        }
1462
1463
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1464
            $limit_from = (int) $limit_from;
1465
            $limit_to = (int) $limit_to;
1466
            $sql .= " LIMIT $limit_from, $limit_to";
1467
        }
1468
1469
        $sql_result = Database::query($sql);
1470
1471
        if ($getCount) {
1472
            $result = Database::fetch_array($sql_result);
1473
1474
            return $result['count'];
1475
        }
1476
1477
        while ($result = Database::fetch_array($sql_result)) {
1478
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1479
            $return_array[] = $result;
1480
        }
1481
1482
        return $return_array;
1483
    }
1484
1485
    /**
1486
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
1487
     *
1488
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
1489
     * @param array  $order_by         a list of fields on which sort
1490
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
1491
     * @param string $condition        Whether we want the filters to be combined by AND or OR
1492
     * @param array  $onlyThisUserList
1493
     *
1494
     * @return array an array with all users of the platform
1495
     *
1496
     * @todo optional course code parameter, optional sorting parameters...
1497
     * @todo security filter order_by
1498
     */
1499
    public static function getUserListLike(
1500
        $conditions = [],
1501
        $order_by = [],
1502
        $simple_like = false,
1503
        $condition = 'AND',
1504
        $onlyThisUserList = []
1505
    ) {
1506
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1507
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1508
        $return_array = [];
1509
        $sql_query = "SELECT user.id FROM $user_table user ";
1510
1511
        if (api_is_multiple_url_enabled()) {
1512
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
1513
        }
1514
1515
        $sql_query .= ' WHERE 1 = 1 ';
1516
        if (count($conditions) > 0) {
1517
            $temp_conditions = [];
1518
            foreach ($conditions as $field => $value) {
1519
                $field = Database::escape_string($field);
1520
                $value = Database::escape_string($value);
1521
                if ($simple_like) {
1522
                    $temp_conditions[] = $field." LIKE '$value%'";
1523
                } else {
1524
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
1525
                }
1526
            }
1527
            if (!empty($temp_conditions)) {
1528
                $sql_query .= ' AND '.implode(' '.$condition.' ', $temp_conditions);
1529
            }
1530
1531
            if (api_is_multiple_url_enabled()) {
1532
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1533
            }
1534
        } else {
1535
            if (api_is_multiple_url_enabled()) {
1536
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1537
            }
1538
        }
1539
1540
        if (!empty($onlyThisUserList)) {
1541
            $onlyThisUserListToString = implode("','", $onlyThisUserList);
1542
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
1543
        }
1544
1545
        if (count($order_by) > 0) {
1546
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1547
        }
1548
1549
        $sql_result = Database::query($sql_query);
1550
        while ($result = Database::fetch_array($sql_result)) {
1551
            $userInfo = api_get_user_info($result['id']);
1552
            $return_array[] = $userInfo;
1553
        }
1554
1555
        return $return_array;
1556
    }
1557
1558
    /**
1559
     * Get user path from user ID (returns an array).
1560
     * The return format is a complete path to a folder ending with "/"
1561
     * In case the first level of subdirectory of users/ does not exist, the
1562
     * function will attempt to create it. Probably not the right place to do it
1563
     * but at least it avoids headaches in many other places.
1564
     *
1565
     * @param int    $id   User ID
1566
     * @param string $type Type of path to return (can be 'system', 'web', 'last')
1567
     *
1568
     * @return string User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
1569
     */
1570
    public static function getUserPathById($id, $type)
1571
    {
1572
        $id = (int) $id;
1573
        if (!$id) {
1574
            return null;
1575
        }
1576
1577
        $userPath = "users/$id/";
1578
        if (api_get_setting('split_users_upload_directory') === 'true') {
1579
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
1580
            // In exceptional cases, on some portals, the intermediate base user
1581
            // directory might not have been created. Make sure it is before
1582
            // going further.
1583
1584
            $rootPath = api_get_path(SYS_PATH).'../app/upload/users/'.substr((string) $id, 0, 1);
1585
            if (!is_dir($rootPath)) {
1586
                $perm = api_get_permissions_for_new_directories();
1587
                try {
1588
                    mkdir($rootPath, $perm);
1589
                } catch (Exception $e) {
1590
                    error_log($e->getMessage());
1591
                }
1592
            }
1593
        }
1594
        switch ($type) {
1595
            case 'system': // Base: absolute system path.
1596
                $userPath = api_get_path(SYS_PATH).'../app/upload/'.$userPath;
1597
                break;
1598
            case 'web': // Base: absolute web path.
1599
                $userPath = api_get_path(WEB_PATH).'../app/upload/'.$userPath;
1600
                break;
1601
            case 'last': // Only the last part starting with users/
1602
                break;
1603
        }
1604
1605
        return $userPath;
1606
    }
1607
1608
    /**
1609
     * Gets the current user image.
1610
     *
1611
     * @param string $userId
1612
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
1613
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
1614
     * @param bool   $addRandomId
1615
     * @param array  $userInfo    to avoid query the DB
1616
     *
1617
     * @todo add gravatar support
1618
     * @todo replace $userId with User entity
1619
     *
1620
     * @return string
1621
     */
1622
    public static function getUserPicture(
1623
        $userId,
1624
        int $size = USER_IMAGE_SIZE_MEDIUM,
1625
        $addRandomId = true,
1626
        $userInfo = []
1627
    ) {
1628
        $user = api_get_user_entity($userId);
1629
        $illustrationRepo = Container::getIllustrationRepository();
1630
1631
        switch ($size) {
1632
            case USER_IMAGE_SIZE_SMALL:
1633
                $width = 32;
1634
                break;
1635
            case USER_IMAGE_SIZE_MEDIUM:
1636
                $width = 64;
1637
                break;
1638
            case USER_IMAGE_SIZE_BIG:
1639
                $width = 128;
1640
                break;
1641
            case USER_IMAGE_SIZE_ORIGINAL:
1642
            default:
1643
                $width = 0;
1644
                break;
1645
        }
1646
1647
        $url = $illustrationRepo->getIllustrationUrl($user);
1648
        $params = [];
1649
        if (!empty($width)) {
1650
            $params['w'] = $width;
1651
        }
1652
1653
        if ($addRandomId) {
1654
            $params['rand'] = uniqid('u_', true);
1655
        }
1656
1657
        $paramsToString = '';
1658
        if (!empty($params)) {
1659
            $paramsToString = '?'.http_build_query($params);
1660
        }
1661
1662
        return $url.$paramsToString;
1663
1664
        /*
1665
        // Make sure userInfo is defined. Otherwise, define it!
1666
        if (empty($userInfo) || !is_array($userInfo) || 0 == count($userInfo)) {
1667
            if (empty($user_id)) {
1668
                return '';
1669
            } else {
1670
                $userInfo = api_get_user_info($user_id);
1671
            }
1672
        }
1673
1674
        $imageWebPath = self::get_user_picture_path_by_id(
1675
            $user_id,
1676
            'web',
1677
            $userInfo
1678
        );
1679
        $pictureWebFile = $imageWebPath['file'];
1680
        $pictureWebDir = $imageWebPath['dir'];
1681
1682
        $pictureAnonymousSize = '128';
1683
        $gravatarSize = 22;
1684
        $realSizeName = 'small_';
1685
1686
        switch ($size) {
1687
            case USER_IMAGE_SIZE_SMALL:
1688
                $pictureAnonymousSize = '32';
1689
                $realSizeName = 'small_';
1690
                $gravatarSize = 32;
1691
                break;
1692
            case USER_IMAGE_SIZE_MEDIUM:
1693
                $pictureAnonymousSize = '64';
1694
                $realSizeName = 'medium_';
1695
                $gravatarSize = 64;
1696
                break;
1697
            case USER_IMAGE_SIZE_ORIGINAL:
1698
                $pictureAnonymousSize = '128';
1699
                $realSizeName = '';
1700
                $gravatarSize = 128;
1701
                break;
1702
            case USER_IMAGE_SIZE_BIG:
1703
                $pictureAnonymousSize = '128';
1704
                $realSizeName = 'big_';
1705
                $gravatarSize = 128;
1706
                break;
1707
        }
1708
1709
        $gravatarEnabled = api_get_setting('gravatar_enabled');
1710
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
1711
        if ('unknown.jpg' == $pictureWebFile || empty($pictureWebFile)) {
1712
            if ('true' === $gravatarEnabled) {
1713
                $file = self::getGravatar(
1714
                    $imageWebPath['email'],
1715
                    $gravatarSize,
1716
                    api_get_setting('gravatar_type')
1717
                );
1718
1719
                if ($addRandomId) {
1720
                    $file .= '&rand='.uniqid();
1721
                }
1722
1723
                return $file;
1724
            }
1725
1726
            return $anonymousPath;
1727
        }
1728
1729
        if ($addRandomId) {
1730
            $picture .= '?rand='.uniqid();
1731
        }
1732
1733
        return $picture;*/
1734
    }
1735
1736
    /**
1737
     * Creates new user photos in various sizes of a user, or deletes user photos.
1738
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
1739
     *
1740
     * @param int    $user_id the user internal identification number
1741
     * @param string $file    The common file name for the newly created photos.
1742
     *                        It will be checked and modified for compatibility with the file system.
1743
     *                        If full name is provided, path component is ignored.
1744
     *                        If an empty name is provided, then old user photos are deleted only,
1745
     *
1746
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
1747
     *
1748
     * @param string $source_file    the full system name of the image from which user photos will be created
1749
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
1750
     *
1751
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
1752
     *              When deletion is requested returns empty string.
1753
     *              In case of internal error or negative validation returns FALSE.
1754
     */
1755
    public static function update_user_picture($userId, UploadedFile $file, string $crop = '')
1756
    {
1757
        if (empty($userId) || empty($file)) {
1758
            return false;
1759
        }
1760
1761
        $repo = Container::getUserRepository();
1762
        $user = $repo->find($userId);
1763
        if ($user) {
1764
            $repoIllustration = Container::getIllustrationRepository();
1765
            $repoIllustration->addIllustration($user, $user, $file, $crop);
1766
        }
1767
    }
1768
1769
    /**
1770
     * Deletes user photos.
1771
     *
1772
     * @param int $userId the user internal identification number
1773
     *
1774
     * @return mixed returns empty string on success, FALSE on error
1775
     */
1776
    public static function deleteUserPicture($userId)
1777
    {
1778
        $repo = Container::getUserRepository();
1779
        $user = $repo->find($userId);
1780
        if ($user) {
1781
            $illustrationRepo = Container::getIllustrationRepository();
1782
            $illustrationRepo->deleteIllustration($user);
1783
        }
1784
    }
1785
1786
    /**
1787
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
1788
     * doesn't have any.
1789
     *
1790
     * If there has been a request to remove a production, the function will return
1791
     * without building the list unless forced to do so by the optional second
1792
     * parameter. This increases performance by avoiding to read through the
1793
     * productions on the filesystem before the removal request has been carried
1794
     * out because they'll have to be re-read afterwards anyway.
1795
     *
1796
     * @deprecated This method is being removed from chamilo 2.0
1797
     * @param int  $user_id    User id
1798
     * @param bool $force      Optional parameter to force building after a removal request
1799
     * @param bool $showDelete
1800
     *
1801
     * @return string A string containing the XHTML code to display the production list, or FALSE
1802
     */
1803
    public static function build_production_list($user_id, $force = false, $showDelete = false)
1804
    {
1805
        if (!$force && !empty($_POST['remove_production'])) {
1806
            return true; // postpone reading from the filesystem
1807
        }
1808
1809
        $productions = self::get_user_productions($user_id);
1810
1811
        if (empty($productions)) {
1812
            return false;
1813
        }
1814
1815
        return false;
1816
1817
        $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...
1818
        $del_image = Display::returnIconPath('delete.png');
1819
        $add_image = Display::returnIconPath('archive.png');
1820
        $del_text = get_lang('Delete');
1821
        $production_list = '';
1822
        if (count($productions) > 0) {
1823
            $production_list = '<div class="files-production"><ul id="productions">';
1824
            foreach ($productions as $file) {
1825
                $production_list .= '<li>
1826
                    <img src="'.$add_image.'" />
1827
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
1828
                        '.htmlentities($file).'
1829
                    </a>';
1830
                if ($showDelete) {
1831
                    $production_list .= '&nbsp;&nbsp;
1832
                        <input
1833
                            style="width:16px;"
1834
                            type="image"
1835
                            name="remove_production['.urlencode($file).']"
1836
                            src="'.$del_image.'"
1837
                            alt="'.$del_text.'"
1838
                            title="'.$del_text.' '.htmlentities($file).'"
1839
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
1840
                }
1841
            }
1842
            $production_list .= '</ul></div>';
1843
        }
1844
1845
        return $production_list;
1846
    }
1847
1848
    /**
1849
     * Returns an array with the user's productions.
1850
     *
1851
     * @param int $user_id User id
1852
     *
1853
     * @return array An array containing the user's productions
1854
     */
1855
    public static function get_user_productions($user_id)
1856
    {
1857
        return [];
1858
1859
        $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...
1860
        $productions = [];
1861
1862
        if (is_dir($production_repository)) {
1863
            $handle = opendir($production_repository);
1864
            while ($file = readdir($handle)) {
1865
                if ('.' == $file ||
1866
                    '..' == $file ||
1867
                    '.htaccess' == $file ||
1868
                    is_dir($production_repository.$file)
1869
                ) {
1870
                    // skip current/parent directory and .htaccess
1871
                    continue;
1872
                }
1873
1874
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
1875
                    // User's photos should not be listed as productions.
1876
                    continue;
1877
                }
1878
                $productions[] = $file;
1879
            }
1880
        }
1881
1882
        return $productions;
1883
    }
1884
1885
    /**
1886
     * Remove a user production.
1887
     *
1888
     * @param int    $user_id    User id
1889
     * @param string $production The production to remove
1890
     *
1891
     * @return bool
1892
     */
1893
    public static function remove_user_production($user_id, $production)
1894
    {
1895
        throw new Exception('remove_user_production');
1896
        /*$production_path = self::get_user_picture_path_by_id($user_id, 'system');
1897
        $production_file = $production_path['dir'].$production;
1898
        if (is_file($production_file)) {
1899
            unlink($production_file);
1900
1901
            return true;
1902
        }
1903
1904
        return false;*/
1905
    }
1906
1907
    /**
1908
     * Update an extra field value for a given user.
1909
     *
1910
     * @param int    $userId   User ID
1911
     * @param string $variable Field variable name
1912
     * @param string $value    Field value
1913
     *
1914
     * @return bool true if field updated, false otherwise
1915
     */
1916
    public static function update_extra_field_value($userId, $variable, $value = '')
1917
    {
1918
        $extraFieldValue = new ExtraFieldValue('user');
1919
        $params = [
1920
            'item_id' => $userId,
1921
            'variable' => $variable,
1922
            'field_value' => $value,
1923
        ];
1924
1925
        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...
1926
    }
1927
1928
    /**
1929
     * Get an array of extra fields with field details (type, default value and options).
1930
     *
1931
     * @param    int    Offset (from which row)
1932
     * @param    int    Number of items
1933
     * @param    int    Column on which sorting is made
1934
     * @param    string    Sorting direction
1935
     * @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...
1936
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
1937
     *
1938
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
1939
     */
1940
    public static function get_extra_fields(
1941
        $from = 0,
1942
        $number_of_items = 0,
1943
        $column = 5,
1944
        $direction = 'ASC',
1945
        $all_visibility = true,
1946
        $field_filter = null
1947
    ) {
1948
        $fields = [];
1949
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1950
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
1951
        $columns = [
1952
            'id',
1953
            'variable',
1954
            'value_type',
1955
            'display_text',
1956
            'default_value',
1957
            'field_order',
1958
            'filter',
1959
        ];
1960
        $column = (int) $column;
1961
        $sort_direction = '';
1962
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
1963
            $sort_direction = strtoupper($direction);
1964
        }
1965
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1966
        $sqlf = "SELECT * FROM $t_uf WHERE item_type = $extraFieldType ";
1967
        if (!$all_visibility) {
1968
            $sqlf .= " AND visible_to_self = 1 ";
1969
        }
1970
        if (!is_null($field_filter)) {
1971
            $field_filter = (int) $field_filter;
1972
            $sqlf .= " AND filter = $field_filter ";
1973
        }
1974
        $sqlf .= " ORDER BY `".$columns[$column]."` $sort_direction ";
1975
        if (0 != $number_of_items) {
1976
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
1977
        }
1978
        $resf = Database::query($sqlf);
1979
        if (Database::num_rows($resf) > 0) {
1980
            while ($rowf = Database::fetch_array($resf)) {
1981
                $fields[$rowf['id']] = [
1982
                    0 => $rowf['id'],
1983
                    1 => $rowf['variable'],
1984
                    2 => $rowf['value_type'],
1985
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
1986
                    4 => $rowf['default_value'],
1987
                    5 => $rowf['field_order'],
1988
                    6 => $rowf['visible_to_self'],
1989
                    7 => $rowf['changeable'],
1990
                    8 => $rowf['filter'],
1991
                    9 => [],
1992
                    10 => '<a name="'.$rowf['id'].'"></a>',
1993
                ];
1994
1995
                $sqlo = "SELECT * FROM $t_ufo
1996
                         WHERE field_id = ".$rowf['id']."
1997
                         ORDER BY option_order ASC";
1998
                $reso = Database::query($sqlo);
1999
                if (Database::num_rows($reso) > 0) {
2000
                    while ($rowo = Database::fetch_array($reso)) {
2001
                        $fields[$rowf['id']][9][$rowo['id']] = [
2002
                            0 => $rowo['id'],
2003
                            1 => $rowo['option_value'],
2004
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2005
                            3 => $rowo['option_order'],
2006
                        ];
2007
                    }
2008
                }
2009
            }
2010
        }
2011
2012
        return $fields;
2013
    }
2014
2015
    /**
2016
     * Creates a new extra field.
2017
     *
2018
     * @param string $variable    Field's internal variable name
2019
     * @param int    $fieldType   Field's type
2020
     * @param string $displayText Field's language var name
2021
     * @param string $default     Field's default value
2022
     *
2023
     * @return int
2024
     */
2025
    public static function create_extra_field(
2026
        $variable,
2027
        $valueType,
2028
        $displayText,
2029
        $default
2030
    ) {
2031
        $extraField = new ExtraField('user');
2032
        $params = [
2033
            'variable' => $variable,
2034
            'value_type' => $valueType,
2035
            'display_text' => $displayText,
2036
            'default_value' => $default,
2037
        ];
2038
2039
        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...
2040
    }
2041
2042
    /**
2043
     * Check if a field is available.
2044
     *
2045
     * @param string $variable
2046
     *
2047
     * @return bool
2048
     */
2049
    public static function is_extra_field_available($variable)
2050
    {
2051
        $extraField = new ExtraField('user');
2052
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2053
2054
        return !empty($data) ? true : false;
2055
    }
2056
2057
    /**
2058
     * Gets user extra fields data.
2059
     *
2060
     * @param    int    User ID
2061
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2062
     * @param    bool    Whether to return invisible fields as well
2063
     * @param    bool    Whether to split multiple-selection fields or not
2064
     *
2065
     * @return array Array of fields => value for the given user
2066
     */
2067
    public static function get_extra_user_data(
2068
        $user_id,
2069
        $prefix = false,
2070
        $allVisibility = true,
2071
        $splitMultiple = false,
2072
        $fieldFilter = null
2073
    ) {
2074
        $user_id = (int) $user_id;
2075
2076
        if (empty($user_id)) {
2077
            return [];
2078
        }
2079
2080
        $extra_data = [];
2081
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2082
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2083
        $sql = "SELECT f.id as id, f.variable as fvar, f.value_type as type
2084
                FROM $t_uf f
2085
                WHERE
2086
                    item_type = ".EntityExtraField::USER_FIELD_TYPE."
2087
                ";
2088
        $filter_cond = '';
2089
2090
        if (!$allVisibility) {
2091
            if (isset($fieldFilter)) {
2092
                $fieldFilter = (int) $fieldFilter;
2093
                $filter_cond .= " AND filter = $fieldFilter ";
2094
            }
2095
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2096
        } else {
2097
            if (isset($fieldFilter)) {
2098
                $fieldFilter = (int) $fieldFilter;
2099
                $sql .= " AND filter = $fieldFilter ";
2100
            }
2101
        }
2102
2103
        $sql .= ' ORDER BY f.field_order';
2104
2105
        $res = Database::query($sql);
2106
        if (Database::num_rows($res) > 0) {
2107
            while ($row = Database::fetch_array($res)) {
2108
                if (self::USER_FIELD_TYPE_TAG == $row['type']) {
2109
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2110
                    $extra_data['extra_'.$row['fvar']] = $tags;
2111
                } else {
2112
                    $sqlu = "SELECT field_value as fval
2113
                            FROM $t_ufv
2114
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2115
                    $resu = Database::query($sqlu);
2116
                    // get default value
2117
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2118
                               WHERE id=".$row['id'];
2119
                    $res_df = Database::query($sql_df);
2120
2121
                    if (Database::num_rows($resu) > 0) {
2122
                        $rowu = Database::fetch_array($resu);
2123
                        $fval = $rowu['fval'];
2124
                        if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2125
                            $fval = explode(';', $rowu['fval']);
2126
                        }
2127
                    } else {
2128
                        $row_df = Database::fetch_array($res_df);
2129
                        $fval = $row_df['fval_df'];
2130
                    }
2131
                    // We get here (and fill the $extra_data array) even if there
2132
                    // is no user with data (we fill it with default values)
2133
                    if ($prefix) {
2134
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2135
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2136
                        } else {
2137
                            $extra_data['extra_'.$row['fvar']] = $fval;
2138
                        }
2139
                    } else {
2140
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2141
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2142
                        } else {
2143
                            $extra_data[$row['fvar']] = $fval;
2144
                        }
2145
                    }
2146
                }
2147
            }
2148
        }
2149
2150
        return $extra_data;
2151
    }
2152
2153
    /** Get extra user data by field.
2154
     * @param int    user ID
2155
     * @param string the internal variable name of the field
2156
     *
2157
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2158
     */
2159
    public static function get_extra_user_data_by_field(
2160
        $user_id,
2161
        $field_variable,
2162
        $prefix = false,
2163
        $all_visibility = true,
2164
        $splitmultiple = false
2165
    ) {
2166
        $user_id = (int) $user_id;
2167
2168
        if (empty($user_id)) {
2169
            return [];
2170
        }
2171
2172
        $extra_data = [];
2173
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2174
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2175
2176
        $sql = "SELECT f.id as id, f.variable as fvar, f.value_type as type
2177
                FROM $t_uf f
2178
                WHERE f.variable = '$field_variable' ";
2179
2180
        if (!$all_visibility) {
2181
            $sql .= " AND f.visible_to_self = 1 ";
2182
        }
2183
2184
        $sql .= " AND item_type = ".EntityExtraField::USER_FIELD_TYPE;
2185
        $sql .= " ORDER BY f.field_order ";
2186
2187
        $res = Database::query($sql);
2188
        if (Database::num_rows($res) > 0) {
2189
            while ($row = Database::fetch_array($res)) {
2190
                $sqlu = "SELECT field_value as fval FROM $t_ufv v
2191
                         INNER JOIN $t_uf f
2192
                         ON (v.field_id = f.id)
2193
                         WHERE
2194
                            item_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2195
                            field_id = ".$row['id']." AND
2196
                            item_id = ".$user_id;
2197
                $resu = Database::query($sqlu);
2198
                $fval = '';
2199
                if (Database::num_rows($resu) > 0) {
2200
                    $rowu = Database::fetch_array($resu);
2201
                    $fval = $rowu['fval'];
2202
                    if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2203
                        $fval = explode(';', $rowu['fval']);
2204
                    }
2205
                }
2206
                if ($prefix) {
2207
                    $extra_data['extra_'.$row['fvar']] = $fval;
2208
                } else {
2209
                    $extra_data[$row['fvar']] = $fval;
2210
                }
2211
            }
2212
        }
2213
2214
        return $extra_data;
2215
    }
2216
2217
    /**
2218
     * Get the extra field information for a certain field (the options as well).
2219
     *
2220
     * @param int $variable The name of the field we want to know everything about
2221
     *
2222
     * @return array Array containing all the information about the extra profile field
2223
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2224
     *               as returned by the database)
2225
     *
2226
     * @author Julio Montoya
2227
     *
2228
     * @since v1.8.6
2229
     */
2230
    public static function get_extra_field_information_by_name($variable)
2231
    {
2232
        $extraField = new ExtraField('user');
2233
2234
        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...
2235
    }
2236
2237
    /**
2238
     * Get the extra field information for user tag (the options as well).
2239
     *
2240
     * @param int $variable The name of the field we want to know everything about
2241
     *
2242
     * @return array Array containing all the information about the extra profile field
2243
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2244
     *               as returned by the database)
2245
     *
2246
     * @author José Loguercio
2247
     *
2248
     * @since v1.11.0
2249
     */
2250
    public static function get_extra_field_tags_information_by_name($variable)
2251
    {
2252
        $extraField = new ExtraField('user');
2253
2254
        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...
2255
    }
2256
2257
    /**
2258
     * Get all the extra field information of a certain field (also the options).
2259
     *
2260
     * @param int $fieldId the ID of the field we want to know everything of
2261
     *
2262
     * @return array $return containing all th information about the extra profile field
2263
     *
2264
     * @author Julio Montoya
2265
     *
2266
     * @deprecated
2267
     * @since v1.8.6
2268
     */
2269
    public static function get_extra_field_information($fieldId)
2270
    {
2271
        $extraField = new ExtraField('user');
2272
2273
        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...
2274
    }
2275
2276
    /**
2277
     * Get extra user data by value.
2278
     *
2279
     * @param string $variable       the internal variable name of the field
2280
     * @param string $value          the internal value of the field
2281
     * @param bool   $all_visibility
2282
     *
2283
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2284
     */
2285
    public static function get_extra_user_data_by_value($variable, $value, $all_visibility = true)
2286
    {
2287
        $extraFieldValue = new ExtraFieldValue('user');
2288
        $extraField = new ExtraField('user');
2289
2290
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2291
2292
        if (false === $info) {
2293
            return [];
2294
        }
2295
2296
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2297
            $variable,
2298
            $value,
2299
            false,
2300
            false,
2301
            true
2302
        );
2303
2304
        $result = [];
2305
        if (!empty($data)) {
2306
            foreach ($data as $item) {
2307
                $result[] = $item['item_id'];
2308
            }
2309
        }
2310
2311
        return $result;
2312
    }
2313
2314
    /**
2315
     * Get extra user data by tags value.
2316
     *
2317
     * @param int    $fieldId the ID of the field we want to know everything of
2318
     * @param string $tag     the tag name for search
2319
     *
2320
     * @return array with extra data info of a user
2321
     *
2322
     * @author José Loguercio
2323
     *
2324
     * @since v1.11.0
2325
     */
2326
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2327
    {
2328
        $extraField = new ExtraField('user');
2329
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2330
        $array = [];
2331
        foreach ($result as $index => $user) {
2332
            $array[] = $user['user_id'];
2333
        }
2334
2335
        return $array;
2336
    }
2337
2338
    /**
2339
     * Get extra user data by field variable.
2340
     *
2341
     * @param string $variable field variable
2342
     *
2343
     * @return array data
2344
     */
2345
    public static function get_extra_user_data_by_field_variable($variable)
2346
    {
2347
        $extraInfo = self::get_extra_field_information_by_name($variable);
2348
        $field_id = (int) $extraInfo['id'];
2349
2350
        $extraField = new ExtraFieldValue('user');
2351
        $data = $extraField->getValuesByFieldId($field_id);
2352
2353
        if (!empty($data)) {
2354
            foreach ($data as $row) {
2355
                $user_id = $row['item_id'];
2356
                $data[$user_id] = $row;
2357
            }
2358
        }
2359
2360
        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...
2361
    }
2362
2363
    /**
2364
     * Get extra user data tags by field variable.
2365
     *
2366
     * @param string $variable field variable
2367
     *
2368
     * @return array
2369
     */
2370
    public static function get_extra_user_data_for_tags($variable)
2371
    {
2372
        $data = self::get_extra_field_tags_information_by_name($variable);
2373
2374
        return $data;
2375
    }
2376
2377
    /**
2378
     * Gives a list of [session_category][session_id] for the current user.
2379
     *
2380
     * @param int  $user_id
2381
     * @param bool $is_time_over                 whether to fill the first element or not
2382
     *                                           (to give space for courses out of categories)
2383
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2384
     * @param bool $ignoreTimeLimit              ignore time start/end
2385
     * @param bool $getCount
2386
     *
2387
     * @return array list of statuses [session_category][session_id]
2388
     *
2389
     * @todo ensure multiple access urls are managed correctly
2390
     */
2391
    public static function get_sessions_by_category(
2392
        $user_id,
2393
        $is_time_over = true,
2394
        $ignore_visibility_for_admins = false,
2395
        $ignoreTimeLimit = false,
2396
        $getCount = false
2397
    ) {
2398
        $user_id = (int) $user_id;
2399
2400
        if (empty($user_id)) {
2401
            return [];
2402
        }
2403
2404
        // Get the list of sessions per user
2405
        $now = new DateTime('now', new DateTimeZone('UTC'));
2406
2407
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2408
        // join would not catch session-courses where the user is general
2409
        // session coach but which do not have students nor coaches registered
2410
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
2411
2412
        if (!$getCount) {
2413
            $dqlSelect = " DISTINCT
2414
                s.id,
2415
                s.title,
2416
                s.accessStartDate AS access_start_date,
2417
                s.accessEndDate AS access_end_date,
2418
                s.duration,
2419
                sc.id AS session_category_id,
2420
                sc.title AS session_category_title,
2421
                sc.dateStart AS session_category_date_start,
2422
                sc.dateEnd AS session_category_date_end,
2423
                s.coachAccessStartDate AS coach_access_start_date,
2424
                s.coachAccessEndDate AS coach_access_end_date,
2425
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
2426
                , s.position AS position
2427
            ";
2428
        }
2429
2430
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
2431
        // is awfully inefficient for large sets of data (1m25s for 58K
2432
        // sessions, BT#14115) but executing a similar query twice and grouping
2433
        // the results afterwards in PHP takes about 1/1000th of the time
2434
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
2435
        $dqlStudent = "SELECT $dqlSelect
2436
            FROM ChamiloCoreBundle:Session AS s
2437
            LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2438
            INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2439
            LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2440
            WHERE scu.user = :user AND url.url = :url ";
2441
        $dqlCoach = "SELECT $dqlSelect
2442
            FROM ChamiloCoreBundle:Session AS s
2443
            INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2444
            LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2445
            INNER JOIN ChamiloCoreBundle:SessionRelUser AS su WITH su.session = s
2446
            WHERE (su.user = :user AND su.relationType = ".SessionEntity::GENERAL_COACH.") AND url.url = :url ";
2447
2448
        // Default order
2449
        $order = 'ORDER BY sc.title, s.title';
2450
2451
        // Order by date if showing all sessions
2452
        $showAllSessions = ('true' === api_get_setting('course.show_all_sessions_on_my_course_page'));
2453
        if ($showAllSessions) {
2454
            $order = 'ORDER BY s.accessStartDate';
2455
        }
2456
2457
        // Order by position
2458
        if ('true' === api_get_setting('session.session_list_order')) {
2459
            $order = 'ORDER BY s.position';
2460
        }
2461
2462
        // Order by dates according to settings
2463
        $orderBySettings = api_get_setting('session.my_courses_session_order', true);
2464
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
2465
            $field = $orderBySettings['field'];
2466
            $orderSetting = $orderBySettings['order'];
2467
            switch ($field) {
2468
                case 'start_date':
2469
                    $order = " ORDER BY s.accessStartDate $orderSetting";
2470
                    break;
2471
                case 'end_date':
2472
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
2473
                    if ('asc' == $orderSetting) {
2474
                        // Put null values at the end
2475
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
2476
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
2477
                    }
2478
                    break;
2479
                case 'name':
2480
                case 'title':
2481
                    $order = " ORDER BY s.title $orderSetting ";
2482
                    break;
2483
            }
2484
        }
2485
2486
        $dqlStudent .= $order;
2487
        $dqlCoach .= $order;
2488
2489
        $accessUrlId = api_get_current_access_url_id();
2490
        $dqlStudent = Database::getManager()
2491
            ->createQuery($dqlStudent)
2492
            ->setParameters(
2493
                ['user' => $user_id, 'url' => $accessUrlId]
2494
            )
2495
        ;
2496
        $dqlCoach = Database::getManager()
2497
            ->createQuery($dqlCoach)
2498
            ->setParameters(
2499
                ['user' => $user_id, 'url' => $accessUrlId]
2500
            )
2501
        ;
2502
2503
        if ($getCount) {
2504
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
2505
        }
2506
2507
        $sessionDataStudent = $dqlStudent->getResult();
2508
        $sessionDataCoach = $dqlCoach->getResult();
2509
2510
        $sessionData = [];
2511
        // First fill $sessionData with student sessions
2512
        if (!empty($sessionDataStudent)) {
2513
            foreach ($sessionDataStudent as $row) {
2514
                $sessionData[$row['id']] = $row;
2515
            }
2516
        }
2517
        // Overwrite session data of the user as a student with session data
2518
        // of the user as a coach.
2519
        // There shouldn't be such duplicate rows, but just in case...
2520
        if (!empty($sessionDataCoach)) {
2521
            foreach ($sessionDataCoach as $row) {
2522
                $sessionData[$row['id']] = $row;
2523
            }
2524
        }
2525
2526
        $collapsable = ('true' === api_get_setting('session.allow_user_session_collapsable'));
2527
2528
2529
2530
        $extraField = new ExtraFieldValue('session');
2531
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
2532
2533
        if (empty($sessionData)) {
2534
            return [];
2535
        }
2536
        $categories = [];
2537
        foreach ($sessionData as $row) {
2538
            $session_id = $row['id'];
2539
            $coachList = SessionManager::getCoachesBySession($session_id);
2540
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
2541
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
2542
            $courseList = self::get_courses_list_by_session($user_id, $session_id);
2543
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
2544
2545
            // User portal filters:
2546
            if (false === $ignoreTimeLimit) {
2547
                if ($is_time_over) {
2548
                    // History
2549
                    if ($row['duration']) {
2550
                        if ($daysLeft >= 0) {
2551
                            continue;
2552
                        }
2553
                    } else {
2554
                        if (empty($row['access_end_date'])) {
2555
                            continue;
2556
                        } else {
2557
                            if ($row['access_end_date'] > $now) {
2558
                                continue;
2559
                            }
2560
                        }
2561
                    }
2562
                } else {
2563
                    // Current user portal
2564
                    $isGeneralCoach = api_get_session_entity($row['id'])->hasUserAsGeneralCoach(api_get_user_entity($user_id));
2565
                    $isCoachOfCourse = in_array($user_id, $coachList);
2566
2567
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
2568
                        // Teachers can access the session depending in the access_coach date
2569
                    } else {
2570
                        if ($row['duration']) {
2571
                            if ($daysLeft <= 0) {
2572
                                continue;
2573
                            }
2574
                        } else {
2575
                            if (isset($row['access_end_date']) &&
2576
                                !empty($row['access_end_date'])
2577
                            ) {
2578
                                if ($row['access_end_date'] <= $now) {
2579
                                    continue;
2580
                                }
2581
                            }
2582
                        }
2583
                    }
2584
                }
2585
            }
2586
2587
            $categories[$row['session_category_id']]['session_category'] = [
2588
                'id' => $row['session_category_id'],
2589
                'name' => $row['session_category_title'],
2590
                'date_start' => $categoryStart,
2591
                'date_end' => $categoryEnd,
2592
            ];
2593
2594
            $visibility = api_get_session_visibility(
2595
                $session_id,
2596
                null,
2597
                $ignore_visibility_for_admins
2598
            );
2599
2600
            if (SESSION_VISIBLE != $visibility) {
2601
                // Course Coach session visibility.
2602
                $blockedCourseCount = 0;
2603
                $closedVisibilityList = [
2604
                    COURSE_VISIBILITY_CLOSED,
2605
                    COURSE_VISIBILITY_HIDDEN,
2606
                ];
2607
2608
                foreach ($courseList as $course) {
2609
                    // Checking session visibility
2610
                    $sessionCourseVisibility = api_get_session_visibility(
2611
                        $session_id,
2612
                        $course['real_id'],
2613
                        $ignore_visibility_for_admins
2614
                    );
2615
2616
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
2617
                    if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
2618
                        $blockedCourseCount++;
2619
                    }
2620
                }
2621
2622
                // If all courses are blocked then no show in the list.
2623
                if ($blockedCourseCount === count($courseList)) {
2624
                    $visibility = SESSION_INVISIBLE;
2625
                } else {
2626
                    $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...
2627
                }
2628
            }
2629
2630
            switch ($visibility) {
2631
                case SESSION_VISIBLE_READ_ONLY:
2632
                case SESSION_VISIBLE:
2633
                case SESSION_AVAILABLE:
2634
                    break;
2635
                case SESSION_INVISIBLE:
2636
                    if (false === $ignore_visibility_for_admins) {
2637
                        continue 2;
2638
                    }
2639
            }
2640
2641
            $collapsed = '';
2642
            $collapsedAction = '';
2643
            if ($collapsable) {
2644
                $collapsableData = SessionManager::getCollapsableData(
2645
                    $user_id,
2646
                    $session_id,
2647
                    $extraField,
2648
                    $collapsableLink
2649
                );
2650
                $collapsed = $collapsableData['collapsed'];
2651
                $collapsedAction = $collapsableData['collapsable_link'];
2652
            }
2653
2654
            $categories[$row['session_category_id']]['sessions'][] = [
2655
                'session_name' => $row['title'],
2656
                'session_id' => $row['id'],
2657
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
2658
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
2659
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
2660
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
2661
                'courses' => $courseList,
2662
                'collapsed' => $collapsed,
2663
                'collapsable_link' => $collapsedAction,
2664
                'duration' => $row['duration'],
2665
            ];
2666
        }
2667
2668
        return $categories;
2669
    }
2670
2671
    /**
2672
     * Gives a list of [session_id-course_code] => [status] for the current user.
2673
     *
2674
     * @param int $user_id
2675
     * @param int $sessionLimit
2676
     *
2677
     * @return array list of statuses (session_id-course_code => status)
2678
     */
2679
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
2680
    {
2681
        // Database Table Definitions
2682
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2683
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2684
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2685
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
2686
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2687
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2688
2689
        $user_id = (int) $user_id;
2690
2691
        if (empty($user_id)) {
2692
            return [];
2693
        }
2694
2695
        $sessionRepo = Container::getSessionRepository();
2696
2697
        $user = api_get_user_entity($user_id);
2698
        $url = null;
2699
        $formattedUserName = Container::$container->get(NameConvention::class)->getPersonName($user);
2700
2701
        // We filter the courses from the URL
2702
        $join_access_url = $where_access_url = '';
2703
        if (api_get_multiple_access_url()) {
2704
            $access_url_id = api_get_current_access_url_id();
2705
            if (-1 != $access_url_id) {
2706
                $url = api_get_url_entity($access_url_id);
2707
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2708
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
2709
                $where_access_url = " AND access_url_id = $access_url_id ";
2710
            }
2711
        }
2712
2713
        // Courses in which we subscribed out of any session
2714
2715
        $sql = "SELECT
2716
                    course.code,
2717
                    course_rel_user.status course_rel_status,
2718
                    course_rel_user.sort sort,
2719
                    course_rel_user.user_course_cat user_course_cat
2720
                 FROM $tbl_course_user course_rel_user
2721
                 LEFT JOIN $tbl_course course
2722
                 ON course.id = course_rel_user.c_id
2723
                 $join_access_url
2724
                 WHERE
2725
                    course_rel_user.user_id = '".$user_id."' AND
2726
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
2727
                    $where_access_url
2728
                 ORDER BY course_rel_user.sort, course.title ASC";
2729
2730
        $course_list_sql_result = Database::query($sql);
2731
        $personal_course_list = [];
2732
        if (Database::num_rows($course_list_sql_result) > 0) {
2733
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
2734
                $course_info = api_get_course_info($result_row['code']);
2735
                $result_row['course_info'] = $course_info;
2736
                $personal_course_list[] = $result_row;
2737
            }
2738
        }
2739
2740
        $coachCourseConditions = '';
2741
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
2742
        if (api_is_allowed_to_create_course()) {
2743
            $sessionListFromCourseCoach = [];
2744
            $sql = " SELECT DISTINCT session_id
2745
                    FROM $tbl_session_course_user
2746
                    WHERE user_id = $user_id AND status = ".SessionEntity::COURSE_COACH;
2747
2748
            $result = Database::query($sql);
2749
            if (Database::num_rows($result)) {
2750
                $result = Database::store_result($result);
2751
                foreach ($result as $session) {
2752
                    $sessionListFromCourseCoach[] = $session['session_id'];
2753
                }
2754
            }
2755
            if (!empty($sessionListFromCourseCoach)) {
2756
                $condition = implode("','", $sessionListFromCourseCoach);
2757
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
2758
            }
2759
        }
2760
2761
        // Get the list of sessions where the user is subscribed
2762
        // This is divided into two different queries
2763
        $sessions = [];
2764
        $sessionLimitRestriction = '';
2765
        if (!empty($sessionLimit)) {
2766
            $sessionLimit = (int) $sessionLimit;
2767
            $sessionLimitRestriction = "LIMIT $sessionLimit";
2768
        }
2769
2770
        $sql = "SELECT DISTINCT s.id, s.title, access_start_date, access_end_date
2771
                FROM $tbl_session_user su INNER JOIN $tbl_session s
2772
                ON (s.id = su.session_id)
2773
                WHERE (
2774
                    su.user_id = $user_id AND
2775
                    su.relation_type = ".SessionEntity::STUDENT."
2776
                )
2777
                $coachCourseConditions
2778
                ORDER BY access_start_date, access_end_date, s.title
2779
                $sessionLimitRestriction
2780
        ";
2781
2782
        $result = Database::query($sql);
2783
        if (Database::num_rows($result) > 0) {
2784
            while ($row = Database::fetch_assoc($result)) {
2785
                $sessions[$row['id']] = $row;
2786
            }
2787
        }
2788
2789
        $sql = "SELECT DISTINCT
2790
                s.id, s.title, s.access_start_date, s.access_end_date
2791
                FROM $tbl_session s
2792
                INNER JOIN $tbl_session_user sru ON sru.session_id = s.id
2793
                WHERE (
2794
                    sru.user_id = $user_id AND sru.relation_type = ".SessionEntity::GENERAL_COACH."
2795
                )
2796
                $coachCourseConditions
2797
                ORDER BY s.access_start_date, s.access_end_date, s.title";
2798
2799
        $result = Database::query($sql);
2800
        if (Database::num_rows($result) > 0) {
2801
            while ($row = Database::fetch_assoc($result)) {
2802
                if (empty($sessions[$row['id']])) {
2803
                    $sessions[$row['id']] = $row;
2804
                }
2805
            }
2806
        }
2807
2808
        if (api_is_allowed_to_create_course()) {
2809
            foreach ($sessions as $enreg) {
2810
                $session_id = $enreg['id'];
2811
                $session_visibility = api_get_session_visibility($session_id);
2812
                $session = api_get_session_entity($session_id);
2813
2814
                if (SESSION_INVISIBLE == $session_visibility) {
2815
                    continue;
2816
                }
2817
2818
                $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
2819
                    $user,
2820
                    $session,
2821
                    SessionEntity::GENERAL_COACH,
2822
                    $url
2823
                );
2824
                $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
2825
                    $user,
2826
                    $session,
2827
                    SessionEntity::COURSE_COACH,
2828
                    $url
2829
                );
2830
2831
                // This query is horribly slow when more than a few thousand
2832
                // users and just a few sessions to which they are subscribed
2833
                $coursesInSession = array_map(
2834
                    function (SessionRelCourse $courseInSession) {
2835
                        $course = $courseInSession->getCourse();
2836
2837
                        return [
2838
                            'code' => $course->getCode(),
2839
                            'i' => $course->getTitle(),
2840
                            'l' => $course->getCourseLanguage(),
2841
                            'sort' => 1,
2842
                        ];
2843
                    },
2844
                    array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
2845
                );
2846
2847
                foreach ($coursesInSession as $result_row) {
2848
                    $result_row['t'] = $formattedUserName;
2849
                    $result_row['email'] = $user->getEmail();
2850
                    $result_row['access_start_date'] = $session->getAccessStartDate()?->format('Y-m-d H:i:s');
2851
                    $result_row['access_end_date'] = $session->getAccessEndDate()?->format('Y-m-d H:i:s');
2852
                    $result_row['session_id'] = $session->getId();
2853
                    $result_row['session_name'] = $session->getTitle();
2854
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
2855
                    $key = $result_row['session_id'].' - '.$result_row['code'];
2856
                    $personal_course_list[$key] = $result_row;
2857
                }
2858
            }
2859
        }
2860
2861
        foreach ($sessions as $enreg) {
2862
            $session_id = $enreg['id'];
2863
            $session_visibility = api_get_session_visibility($session_id);
2864
            if (SESSION_INVISIBLE == $session_visibility) {
2865
                continue;
2866
            }
2867
2868
            /* This query is very similar to the above query,
2869
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
2870
            $sql = "SELECT DISTINCT
2871
                course.code code,
2872
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
2873
                email,
2874
                course.course_language l,
2875
                1 sort,
2876
                access_start_date,
2877
                access_end_date,
2878
                session.id as session_id,
2879
                session.title as session_name,
2880
                IF((session_course_user.user_id = 3 AND session_course_user.status = ".SessionEntity::COURSE_COACH."),'2', '5')
2881
            FROM $tbl_session_course_user as session_course_user
2882
            INNER JOIN $tbl_course AS course
2883
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
2884
            INNER JOIN $tbl_session as session
2885
            ON session_course_user.session_id = session.id
2886
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
2887
            WHERE session_course_user.user_id = $user_id
2888
            ORDER BY i";
2889
2890
            $course_list_sql_result = Database::query($sql);
2891
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
2892
                $result_row['course_info'] = api_get_course_info($result_row['code']);
2893
                $key = $result_row['session_id'].' - '.$result_row['code'];
2894
                if (!isset($personal_course_list[$key])) {
2895
                    $personal_course_list[$key] = $result_row;
2896
                }
2897
            }
2898
        }
2899
2900
        return $personal_course_list;
2901
    }
2902
2903
    /**
2904
     * Gives a list of courses for the given user in the given session.
2905
     *
2906
     * @param int $user_id
2907
     * @param int $session_id
2908
     *
2909
     * @return array list of statuses (session_id-course_code => status)
2910
     */
2911
    public static function get_courses_list_by_session($user_id, $session_id)
2912
    {
2913
        // Database Table Definitions
2914
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
2915
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2916
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
2917
2918
        $user_id = (int) $user_id;
2919
        $session_id = (int) $session_id;
2920
2921
        $sessionRepo = Container::getSessionRepository();
2922
2923
        $user = api_get_user_entity($user_id);
2924
        $session = api_get_session_entity($session_id);
2925
        $url = null;
2926
2927
        // We filter the courses from the URL
2928
        $join_access_url = $where_access_url = '';
2929
        if (api_get_multiple_access_url()) {
2930
            $urlId = api_get_current_access_url_id();
2931
            if (-1 != $urlId) {
2932
                $url = api_get_url_entity($urlId);
2933
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
2934
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
2935
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
2936
            }
2937
        }
2938
2939
        /* This query is very similar to the query below, but it will check the
2940
        session_rel_course_user table if there are courses registered
2941
        to our user or not */
2942
        $sql = "SELECT DISTINCT
2943
                    c.title,
2944
                    c.visibility,
2945
                    c.id as real_id,
2946
                    c.code as course_code,
2947
                    sc.position,
2948
                    c.unsubscribe
2949
                FROM $tbl_session_course_user as scu
2950
                INNER JOIN $tbl_session_course sc
2951
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
2952
                INNER JOIN $tableCourse as c
2953
                ON (scu.c_id = c.id)
2954
                $join_access_url
2955
                WHERE
2956
                    scu.user_id = $user_id AND
2957
                    scu.session_id = $session_id
2958
                    $where_access_url
2959
                ORDER BY sc.position ASC";
2960
2961
        $myCourseList = [];
2962
        $courses = [];
2963
        $result = Database::query($sql);
2964
        if (Database::num_rows($result) > 0) {
2965
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
2966
                $result_row['status'] = 5;
2967
                if (!in_array($result_row['real_id'], $courses)) {
2968
                    $position = $result_row['position'];
2969
                    if (!isset($myCourseList[$position])) {
2970
                        $myCourseList[$position] = $result_row;
2971
                    } else {
2972
                        $myCourseList[] = $result_row;
2973
                    }
2974
                    $courses[] = $result_row['real_id'];
2975
                }
2976
            }
2977
        }
2978
2979
        if (api_is_allowed_to_create_course()) {
2980
            $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
2981
                $user,
2982
                $session,
2983
                SessionEntity::GENERAL_COACH,
2984
                $url
2985
            );
2986
            $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
2987
                $user,
2988
                $session,
2989
                SessionEntity::COURSE_COACH,
2990
                $url
2991
            );
2992
2993
            $coursesInSession = array_map(
2994
                function (SessionRelCourse $courseInSession) {
2995
                    $course = $courseInSession->getCourse();
2996
2997
                    return [
2998
                        'title' => $course->getTitle(),
2999
                        'visibility' => $course->getVisibility(),
3000
                        'real_id' => $course->getId(),
3001
                        'course_code' => $course->getCode(),
3002
                        'position' => $courseInSession->getPosition(),
3003
                        'unsubscribe' => $course->getUnsubscribe(),
3004
                    ];
3005
                },
3006
                array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
3007
            );
3008
3009
            foreach ($coursesInSession as $result_row) {
3010
                $result_row['status'] = 2;
3011
                if (!in_array($result_row['real_id'], $courses)) {
3012
                    $position = $result_row['position'];
3013
                    if (!isset($myCourseList[$position])) {
3014
                        $myCourseList[$position] = $result_row;
3015
                    } else {
3016
                        $myCourseList[] = $result_row;
3017
                    }
3018
                    $courses[] = $result_row['real_id'];
3019
                }
3020
            }
3021
        }
3022
3023
        if (api_is_drh()) {
3024
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3025
            $sessionList = array_keys($sessionList);
3026
            if (in_array($session_id, $sessionList)) {
3027
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3028
                if (!empty($courseList)) {
3029
                    foreach ($courseList as $course) {
3030
                        if (!in_array($course['id'], $courses)) {
3031
                            $position = $course['position'];
3032
                            if (!isset($myCourseList[$position])) {
3033
                                $myCourseList[$position] = $course;
3034
                            } else {
3035
                                $myCourseList[] = $course;
3036
                            }
3037
                        }
3038
                    }
3039
                }
3040
            }
3041
        } else {
3042
            //check if user is general coach for this session
3043
            if ($session && $session->hasUserAsGeneralCoach($user)) {
3044
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3045
                if (!empty($courseList)) {
3046
                    foreach ($courseList as $course) {
3047
                        if (!in_array($course['id'], $courses)) {
3048
                            $position = $course['position'];
3049
                            if (!isset($myCourseList[$position])) {
3050
                                $myCourseList[$position] = $course;
3051
                            } else {
3052
                                $myCourseList[] = $course;
3053
                            }
3054
                        }
3055
                    }
3056
                }
3057
            }
3058
        }
3059
3060
        if (!empty($myCourseList)) {
3061
            ksort($myCourseList);
3062
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
3063
            if (empty($checkPosition)) {
3064
                // The session course list doesn't have any position,
3065
                // then order the course list by course code
3066
                $list = array_column($myCourseList, 'course_code');
3067
                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

3067
                array_multisort($myCourseList, /** @scrutinizer ignore-type */ SORT_ASC, $list);
Loading history...
3068
            }
3069
        }
3070
3071
        return $myCourseList;
3072
    }
3073
3074
    /**
3075
     * Get user id from a username.
3076
     *
3077
     * @param string $username
3078
     *
3079
     * @return int User ID (or false if not found)
3080
     */
3081
    public static function get_user_id_from_username($username)
3082
    {
3083
        if (empty($username)) {
3084
            return false;
3085
        }
3086
        $username = trim($username);
3087
        $username = Database::escape_string($username);
3088
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3089
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3090
        $res = Database::query($sql);
3091
3092
        if (false === $res) {
3093
            return false;
3094
        }
3095
        if (1 !== Database::num_rows($res)) {
3096
            return false;
3097
        }
3098
        $row = Database::fetch_array($res);
3099
3100
        return $row['id'];
3101
    }
3102
3103
    /**
3104
     * Get the users files upload from his share_folder.
3105
     *
3106
     * @param string $user_id      User ID
3107
     * @param string $course       course directory
3108
     * @param string $resourceType resource type: images, all
3109
     *
3110
     * @return string
3111
     */
3112
    /*public static function get_user_upload_files_by_course(
3113
        $user_id,
3114
        $course,
3115
        $resourceType = 'all'
3116
    ) {
3117
        $return = '';
3118
        $user_id = (int) $user_id;
3119
3120
        if (!empty($user_id) && !empty($course)) {
3121
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3122
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3123
            $file_list = [];
3124
3125
            if (is_dir($path)) {
3126
                $handle = opendir($path);
3127
                while ($file = readdir($handle)) {
3128
                    if ('.' == $file || '..' == $file || '.htaccess' == $file || is_dir($path.$file)) {
3129
                        continue; // skip current/parent directory and .htaccess
3130
                    }
3131
                    $file_list[] = $file;
3132
                }
3133
                if (count($file_list) > 0) {
3134
                    $return = "<h4>$course</h4>";
3135
                    $return .= '<ul class="thumbnails">';
3136
                }
3137
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
3138
                foreach ($file_list as $file) {
3139
                    if ('all' == $resourceType) {
3140
                        $return .= '<li>
3141
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
3142
                    } elseif ('images' == $resourceType) {
3143
                        //get extension
3144
                        $ext = explode('.', $file);
3145
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
3146
                            $return .= '<li class="span2">
3147
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
3148
                                                <img src="'.$web_path.urlencode($file).'" >
3149
                                            </a>
3150
                                        </li>';
3151
                        }
3152
                    }
3153
                }
3154
                if (count($file_list) > 0) {
3155
                    $return .= '</ul>';
3156
                }
3157
            }
3158
        }
3159
3160
        return $return;
3161
    }*/
3162
3163
    /**
3164
     * Gets the API key (or keys) and return them into an array.
3165
     *
3166
     * @param int     Optional user id (defaults to the result of api_get_user_id())
3167
     * @param string $api_service
3168
     *
3169
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
3170
     */
3171
    public static function get_api_keys($user_id = null, $api_service = 'default')
3172
    {
3173
        if ($user_id != strval(intval($user_id))) {
3174
            return false;
3175
        }
3176
        if (empty($user_id)) {
3177
            $user_id = api_get_user_id();
3178
        }
3179
        if (false === $user_id) {
3180
            return false;
3181
        }
3182
        $service_name = Database::escape_string($api_service);
3183
        if (false === is_string($service_name)) {
3184
            return false;
3185
        }
3186
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3187
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
3188
        $res = Database::query($sql);
3189
        if (false === $res) {
3190
            return false;
3191
        } //error during query
3192
        $num = Database::num_rows($res);
3193
        if (0 == $num) {
3194
            return false;
3195
        }
3196
        $list = [];
3197
        while ($row = Database::fetch_array($res)) {
3198
            $list[$row['id']] = $row['api_key'];
3199
        }
3200
3201
        return $list;
3202
    }
3203
3204
    /**
3205
     * Adds a new API key to the users' account.
3206
     *
3207
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
3208
     * @param string $api_service
3209
     *
3210
     * @return bool True on success, false on failure
3211
     */
3212
    public static function add_api_key($user_id = null, $api_service = 'default')
3213
    {
3214
        if ($user_id != strval(intval($user_id))) {
3215
            return false;
3216
        }
3217
        if (empty($user_id)) {
3218
            $user_id = api_get_user_id();
3219
        }
3220
        if (false === $user_id) {
3221
            return false;
3222
        }
3223
        $service_name = Database::escape_string($api_service);
3224
        if (false === is_string($service_name)) {
3225
            return false;
3226
        }
3227
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3228
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
3229
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
3230
        $res = Database::query($sql);
3231
        if (false === $res) {
3232
            return false;
3233
        } //error during query
3234
        $num = Database::insert_id();
3235
3236
        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...
3237
    }
3238
3239
    /**
3240
     * Deletes an API key from the user's account.
3241
     *
3242
     * @param   int     API key's internal ID
3243
     *
3244
     * @return bool True on success, false on failure
3245
     */
3246
    public static function delete_api_key($key_id)
3247
    {
3248
        if ($key_id != strval(intval($key_id))) {
3249
            return false;
3250
        }
3251
        if (false === $key_id) {
3252
            return false;
3253
        }
3254
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3255
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
3256
        $res = Database::query($sql);
3257
        if (false === $res) {
3258
            return false;
3259
        } //error during query
3260
        $num = Database::num_rows($res);
3261
        if (1 !== $num) {
3262
            return false;
3263
        }
3264
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
3265
        $res = Database::query($sql);
3266
        if (false === $res) {
3267
            return false;
3268
        } //error during query
3269
3270
        return true;
3271
    }
3272
3273
    /**
3274
     * Regenerate an API key from the user's account.
3275
     *
3276
     * @param   int     user ID (defaults to the results of api_get_user_id())
3277
     * @param   string  API key's internal ID
3278
     *
3279
     * @return int num
3280
     */
3281
    public static function update_api_key($user_id, $api_service)
3282
    {
3283
        if ($user_id != strval(intval($user_id))) {
3284
            return false;
3285
        }
3286
        if (false === $user_id) {
3287
            return false;
3288
        }
3289
        $service_name = Database::escape_string($api_service);
3290
        if (false === is_string($service_name)) {
3291
            return false;
3292
        }
3293
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3294
        $sql = "SELECT id FROM $t_api
3295
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3296
        $res = Database::query($sql);
3297
        $num = Database::num_rows($res);
3298
        if (1 == $num) {
3299
            $id_key = Database::fetch_array($res, 'ASSOC');
3300
            self::delete_api_key($id_key['id']);
3301
            $num = self::add_api_key($user_id, $api_service);
3302
        } elseif (0 == $num) {
3303
            $num = self::add_api_key($user_id, $api_service);
3304
        }
3305
3306
        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...
3307
    }
3308
3309
    /**
3310
     * @param   int     user ID (defaults to the results of api_get_user_id())
3311
     * @param   string    API key's internal ID
3312
     *
3313
     * @return int row ID, or return false if not found
3314
     */
3315
    public static function get_api_key_id($user_id, $api_service)
3316
    {
3317
        if ($user_id != strval(intval($user_id))) {
3318
            return false;
3319
        }
3320
        if (false === $user_id) {
3321
            return false;
3322
        }
3323
        if (empty($api_service)) {
3324
            return false;
3325
        }
3326
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3327
        $api_service = Database::escape_string($api_service);
3328
        $sql = "SELECT id FROM $t_api
3329
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3330
        $res = Database::query($sql);
3331
        if (Database::num_rows($res) < 1) {
3332
            return false;
3333
        }
3334
        $row = Database::fetch_array($res, 'ASSOC');
3335
3336
        return $row['id'];
3337
    }
3338
3339
    /**
3340
     * Checks if a user_id is platform admin.
3341
     *
3342
     * @param   int user ID
3343
     *
3344
     * @return bool True if is admin, false otherwise
3345
     *
3346
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
3347
     */
3348
    public static function is_admin($user_id)
3349
    {
3350
        $user_id = (int) $user_id;
3351
        if (empty($user_id)) {
3352
            return false;
3353
        }
3354
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3355
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3356
        $res = Database::query($sql);
3357
3358
        return 1 === Database::num_rows($res);
3359
    }
3360
3361
    /**
3362
     * Get the total count of users.
3363
     *
3364
     * @param int $status        Status of users to be counted
3365
     * @param int $access_url_id Access URL ID (optional)
3366
     * @param int $active
3367
     *
3368
     * @return mixed Number of users or false on error
3369
     */
3370
    public static function get_number_of_users($status = 0, $access_url_id = 1, $active = null)
3371
    {
3372
        $t_u = Database::get_main_table(TABLE_MAIN_USER);
3373
        $t_a = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3374
3375
        if (api_is_multiple_url_enabled()) {
3376
            $sql = "SELECT count(u.id)
3377
                    FROM $t_u u
3378
                    INNER JOIN $t_a url_user
3379
                    ON (u.id = url_user.user_id)
3380
                    WHERE url_user.access_url_id = $access_url_id
3381
            ";
3382
        } else {
3383
            $sql = "SELECT count(u.id)
3384
                    FROM $t_u u
3385
                    WHERE 1 = 1 ";
3386
        }
3387
3388
        $status = (int) $status;
3389
        if (!empty($status) && $status > 0) {
3390
            $sql .= " AND u.status = $status ";
3391
        }
3392
3393
        if (null !== $active) {
3394
            $active = (int) $active;
3395
            $sql .= " AND u.active = $active ";
3396
        }
3397
3398
        $res = Database::query($sql);
3399
        if (1 === Database::num_rows($res)) {
3400
            return (int) Database::result($res, 0, 0);
3401
        }
3402
3403
        return false;
3404
    }
3405
3406
    /**
3407
     * Gets the tags of a specific field_id
3408
     * USER TAGS.
3409
     *
3410
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
3411
     *
3412
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
3413
     *    Called it "books" for example.
3414
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
3415
     * 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
3416
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
3417
     * 5. Test and enjoy.
3418
     *
3419
     * @param string $tag
3420
     * @param int    $field_id      field_id
3421
     * @param string $return_format how we are going to result value in array or in a string (json)
3422
     * @param $limit
3423
     *
3424
     * @return mixed
3425
     */
3426
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
3427
    {
3428
        // database table definition
3429
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3430
        $field_id = (int) $field_id;
3431
        $limit = (int) $limit;
3432
        $tag = trim(Database::escape_string($tag));
3433
3434
        // all the information of the field
3435
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
3436
                WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag LIMIT $limit";
3437
        $result = Database::query($sql);
3438
        $return = [];
3439
        if (Database::num_rows($result) > 0) {
3440
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3441
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
3442
            }
3443
        }
3444
        if ('json' === $return_format) {
3445
            $return = json_encode($return);
3446
        }
3447
3448
        return $return;
3449
    }
3450
3451
    /**
3452
     * @param int $field_id
3453
     * @param int $limit
3454
     *
3455
     * @return array
3456
     */
3457
    public static function get_top_tags($field_id, $limit = 100)
3458
    {
3459
        // database table definition
3460
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3461
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3462
        $field_id = (int) $field_id;
3463
        $limit = (int) $limit;
3464
        // all the information of the field
3465
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
3466
                INNER JOIN $table_user_tag ut
3467
                ON (ut.id = uv.tag_id)
3468
                WHERE field_id = $field_id
3469
                GROUP BY tag_id
3470
                ORDER BY count DESC
3471
                LIMIT $limit";
3472
        $result = Database::query($sql);
3473
        $return = [];
3474
        if (Database::num_rows($result) > 0) {
3475
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3476
                $return[] = $row;
3477
            }
3478
        }
3479
3480
        return $return;
3481
    }
3482
3483
    /**
3484
     * Get user's tags.
3485
     *
3486
     * @param int $user_id
3487
     * @param int $field_id
3488
     *
3489
     * @return array
3490
     */
3491
    public static function get_user_tags($user_id, $field_id)
3492
    {
3493
        // database table definition
3494
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3495
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3496
        $field_id = (int) $field_id;
3497
        $user_id = (int) $user_id;
3498
3499
        // all the information of the field
3500
        $sql = "SELECT ut.id, tag, count
3501
                FROM $table_user_tag ut
3502
                INNER JOIN $table_user_tag_values uv
3503
                ON (uv.tag_id=ut.ID)
3504
                WHERE field_id = $field_id AND user_id = $user_id
3505
                ORDER BY tag";
3506
        $result = Database::query($sql);
3507
        $return = [];
3508
        if (Database::num_rows($result) > 0) {
3509
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3510
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3511
            }
3512
        }
3513
3514
        return $return;
3515
    }
3516
3517
    /**
3518
     * Get user's tags.
3519
     *
3520
     * @param int  $user_id
3521
     * @param int  $field_id
3522
     * @param bool $show_links show links or not
3523
     *
3524
     * @return string
3525
     */
3526
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
3527
    {
3528
        // database table definition
3529
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3530
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3531
        $field_id = (int) $field_id;
3532
        $user_id = (int) $user_id;
3533
3534
        // all the information of the field
3535
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
3536
                INNER JOIN $table_user_tag_values uv
3537
                ON (uv.tag_id = ut.id)
3538
                WHERE field_id = $field_id AND user_id = $user_id
3539
                ORDER BY tag";
3540
3541
        $result = Database::query($sql);
3542
        $return = [];
3543
        if (Database::num_rows($result) > 0) {
3544
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3545
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3546
            }
3547
        }
3548
        $user_tags = $return;
3549
        $tag_tmp = [];
3550
        foreach ($user_tags as $tag) {
3551
            if ($show_links) {
3552
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
3553
                    $tag['tag'].
3554
                '</a>';
3555
            } else {
3556
                $tag_tmp[] = $tag['tag'];
3557
            }
3558
        }
3559
3560
        if (is_array($user_tags) && count($user_tags) > 0) {
3561
            return implode(', ', $tag_tmp);
3562
        } else {
3563
            return '';
3564
        }
3565
    }
3566
3567
    /**
3568
     * Get the tag id.
3569
     *
3570
     * @param int $tag
3571
     * @param int $field_id
3572
     *
3573
     * @return int returns 0 if fails otherwise the tag id
3574
     */
3575
    public static function get_tag_id($tag, $field_id)
3576
    {
3577
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3578
        $tag = Database::escape_string($tag);
3579
        $field_id = (int) $field_id;
3580
        //with COLLATE latin1_bin to select query in a case sensitive mode
3581
        $sql = "SELECT id FROM $table_user_tag
3582
                WHERE tag LIKE '$tag' AND field_id = $field_id";
3583
        $result = Database::query($sql);
3584
        if (Database::num_rows($result) > 0) {
3585
            $row = Database::fetch_array($result, 'ASSOC');
3586
3587
            return $row['id'];
3588
        } else {
3589
            return 0;
3590
        }
3591
    }
3592
3593
    /**
3594
     * Get the tag id.
3595
     *
3596
     * @param int $tag_id
3597
     * @param int $field_id
3598
     *
3599
     * @return int 0 if fails otherwise the tag id
3600
     */
3601
    public static function get_tag_id_from_id($tag_id, $field_id)
3602
    {
3603
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3604
        $tag_id = (int) $tag_id;
3605
        $field_id = (int) $field_id;
3606
        $sql = "SELECT id FROM $table_user_tag
3607
                WHERE id = '$tag_id' AND field_id = $field_id";
3608
        $result = Database::query($sql);
3609
        if (Database::num_rows($result) > 0) {
3610
            $row = Database::fetch_array($result, 'ASSOC');
3611
3612
            return $row['id'];
3613
        } else {
3614
            return false;
3615
        }
3616
    }
3617
3618
    /**
3619
     * Adds a user-tag value.
3620
     *
3621
     * @param mixed $tag
3622
     * @param int   $user_id
3623
     * @param int   $field_id field id of the tag
3624
     *
3625
     * @return bool True if the tag was inserted or updated. False otherwise.
3626
     *              The return value doesn't take into account *values* added to the tag.
3627
     *              Only the creation/update of the tag field itself.
3628
     */
3629
    public static function add_tag($tag, $user_id, $field_id)
3630
    {
3631
        // database table definition
3632
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3633
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3634
        $tag = trim(Database::escape_string($tag));
3635
        $user_id = (int) $user_id;
3636
        $field_id = (int) $field_id;
3637
3638
        $tag_id = self::get_tag_id($tag, $field_id);
3639
3640
        /* IMPORTANT
3641
         *  @todo we don't create tags with numbers
3642
         *
3643
         */
3644
3645
        //this is a new tag
3646
        if (0 == $tag_id) {
3647
            //the tag doesn't exist
3648
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
3649
            Database::query($sql);
3650
            $last_insert_id = Database::insert_id();
3651
        } else {
3652
            //the tag exists we update it
3653
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
3654
            Database::query($sql);
3655
            $last_insert_id = $tag_id;
3656
        }
3657
3658
        if (!empty($last_insert_id) && (0 != $last_insert_id)) {
3659
            //we insert the relationship user-tag
3660
            $sql = "SELECT tag_id FROM $table_user_tag_values
3661
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
3662
            $result = Database::query($sql);
3663
            //if the relationship does not exist we create it
3664
            if (0 == Database::num_rows($result)) {
3665
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
3666
                Database::query($sql);
3667
            }
3668
3669
            return true;
3670
        }
3671
3672
        return false;
3673
    }
3674
3675
    /**
3676
     * Deletes an user tag.
3677
     *
3678
     * @param int $user_id
3679
     * @param int $field_id
3680
     */
3681
    public static function delete_user_tags($user_id, $field_id)
3682
    {
3683
        // database table definition
3684
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3685
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3686
        $user_id = (int) $user_id;
3687
3688
        $tags = self::get_user_tags($user_id, $field_id);
3689
        if (is_array($tags) && count($tags) > 0) {
3690
            foreach ($tags as $key => $tag) {
3691
                if ($tag['count'] > '0') {
3692
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
3693
                    Database::query($sql);
3694
                }
3695
                $sql = "DELETE FROM $table_user_tag_values
3696
                        WHERE user_id = $user_id AND tag_id = $key";
3697
                Database::query($sql);
3698
            }
3699
        }
3700
    }
3701
3702
    /**
3703
     * Process the tag list comes from the UserManager::update_extra_field_value() function.
3704
     *
3705
     * @param array $tags     the tag list that will be added
3706
     * @param int   $user_id
3707
     * @param int   $field_id
3708
     *
3709
     * @return bool
3710
     */
3711
    public static function process_tags($tags, $user_id, $field_id)
3712
    {
3713
        // We loop the tags and add it to the DB
3714
        if (is_array($tags)) {
3715
            foreach ($tags as $tag) {
3716
                self::add_tag($tag, $user_id, $field_id);
3717
            }
3718
        } else {
3719
            self::add_tag($tags, $user_id, $field_id);
3720
        }
3721
3722
        return true;
3723
    }
3724
3725
    /**
3726
     * Returns a list of all administrators.
3727
     *
3728
     * @return array
3729
     */
3730
    public static function get_all_administrators()
3731
    {
3732
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3733
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
3734
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3735
        $access_url_id = api_get_current_access_url_id();
3736
        if (api_get_multiple_access_url()) {
3737
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3738
                    FROM $tbl_url_rel_user as url
3739
                    INNER JOIN $table_admin as admin
3740
                    ON (admin.user_id=url.user_id)
3741
                    INNER JOIN $table_user u
3742
                    ON (u.id=admin.user_id)
3743
                    WHERE access_url_id ='".$access_url_id."'";
3744
        } else {
3745
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3746
                    FROM $table_admin as admin
3747
                    INNER JOIN $table_user u
3748
                    ON (u.id=admin.user_id)";
3749
        }
3750
        $sql .= !str_contains($sql, 'WHERE') ? ' WHERE u.active <> '.USER_SOFT_DELETED : ' AND u.active <> '.USER_SOFT_DELETED;
3751
        $result = Database::query($sql);
3752
        $return = [];
3753
        if (Database::num_rows($result) > 0) {
3754
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3755
                $return[$row['user_id']] = $row;
3756
            }
3757
        }
3758
3759
        return $return;
3760
    }
3761
3762
    /**
3763
     * Search an user (tags, first name, last name and email ).
3764
     *
3765
     * @param string $tag
3766
     * @param int    $field_id        field id of the tag
3767
     * @param int    $from            where to start in the query
3768
     * @param int    $number_of_items
3769
     * @param bool   $getCount        get count or not
3770
     *
3771
     * @return array
3772
     */
3773
    public static function get_all_user_tags(
3774
        $tag,
3775
        $field_id = 0,
3776
        $from = 0,
3777
        $number_of_items = 10,
3778
        $getCount = false
3779
    ) {
3780
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
3781
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3782
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3783
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3784
3785
        $field_id = intval($field_id);
3786
        $from = intval($from);
3787
        $number_of_items = intval($number_of_items);
3788
3789
        $where_field = "";
3790
        $where_extra_fields = self::get_search_form_where_extra_fields();
3791
        if (0 != $field_id) {
3792
            $where_field = " field_id = $field_id AND ";
3793
        }
3794
3795
        // all the information of the field
3796
        if ($getCount) {
3797
            $select = "SELECT count(DISTINCT u.id) count";
3798
        } else {
3799
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, tag, picture_uri";
3800
        }
3801
3802
        $sql = " $select
3803
                FROM $user_table u
3804
                INNER JOIN $access_url_rel_user_table url_rel_user
3805
                ON (u.id = url_rel_user.user_id)
3806
                LEFT JOIN $table_user_tag_values uv
3807
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
3808
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
3809
                WHERE
3810
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
3811
                    (
3812
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
3813
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
3814
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
3815
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
3816
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
3817
                     )
3818
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
3819
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
3820
3821
        $keyword_active = true;
3822
        // only active users
3823
        if ($keyword_active) {
3824
            $sql .= " AND u.active='1'";
3825
        }
3826
        // avoid anonymous
3827
        $sql .= " AND u.status <> 6 ";
3828
        $sql .= " ORDER BY username";
3829
        $sql .= " LIMIT $from , $number_of_items";
3830
3831
        $result = Database::query($sql);
3832
        $return = [];
3833
3834
        if (Database::num_rows($result) > 0) {
3835
            if ($getCount) {
3836
                $row = Database::fetch_array($result, 'ASSOC');
3837
3838
                return $row['count'];
3839
            }
3840
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3841
                $return[$row['id']] = $row;
3842
            }
3843
        }
3844
3845
        return $return;
3846
    }
3847
3848
    /**
3849
     * Get extra filterable user fields (only type select).
3850
     *
3851
     * @return array Array of extra fields as [int => ['name' => ..., 'variable' => ..., 'data' => ...]] (
3852
     *               or empty array if no extra field)
3853
     */
3854
    public static function getExtraFilterableFields()
3855
    {
3856
        $extraFieldList = self::get_extra_fields();
3857
        $fields = [];
3858
        if (is_array($extraFieldList)) {
3859
            foreach ($extraFieldList as $extraField) {
3860
                // If is enabled to filter and is a "<select>" field type
3861
                if (1 == $extraField[8] && 4 == $extraField[2]) {
3862
                    $fields[] = [
3863
                        'name' => $extraField[3],
3864
                        'variable' => $extraField[1],
3865
                        'data' => $extraField[9],
3866
                    ];
3867
                }
3868
            }
3869
        }
3870
3871
        return $fields;
3872
    }
3873
3874
    /**
3875
     * Get extra where clauses for finding users based on extra filterable user fields (type select).
3876
     *
3877
     * @return string With AND clauses based on user's ID which have the values to search in extra user fields
3878
     *                (or empty if no extra field exists)
3879
     */
3880
    public static function get_search_form_where_extra_fields()
3881
    {
3882
        $useExtraFields = false;
3883
        $extraFields = self::getExtraFilterableFields();
3884
        $extraFieldResult = [];
3885
        if (is_array($extraFields) && count($extraFields) > 0) {
3886
            foreach ($extraFields as $extraField) {
3887
                $varName = 'field_'.$extraField['variable'];
3888
                if (self::is_extra_field_available($extraField['variable'])) {
3889
                    if (isset($_GET[$varName]) && '0' != $_GET[$varName]) {
3890
                        $useExtraFields = true;
3891
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
3892
                            $extraField['variable'],
3893
                            $_GET[$varName]
3894
                        );
3895
                    }
3896
                }
3897
            }
3898
        }
3899
3900
        if ($useExtraFields) {
3901
            $finalResult = [];
3902
            if (count($extraFieldResult) > 1) {
3903
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
3904
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
3905
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
3906
                    }
3907
                }
3908
            } else {
3909
                $finalResult = $extraFieldResult[0];
3910
            }
3911
3912
            if (is_array($finalResult) && count($finalResult) > 0) {
3913
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
3914
            } else {
3915
                //no results
3916
                $whereFilter = " AND u.id  = -1 ";
3917
            }
3918
3919
            return $whereFilter;
3920
        }
3921
3922
        return '';
3923
    }
3924
3925
    /**
3926
     * Show the search form.
3927
     *
3928
     * @param string $query the value of the search box
3929
     *
3930
     * @throws Exception
3931
     *
3932
     * @return string HTML form
3933
     */
3934
    public static function get_search_form($query, $defaultParams = [])
3935
    {
3936
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
3937
        $form = new FormValidator(
3938
            'search_user',
3939
            'get',
3940
            api_get_path(WEB_PATH).'main/social/search.php',
3941
            '',
3942
            [],
3943
            FormValidator::LAYOUT_HORIZONTAL
3944
        );
3945
3946
        $query = Security::remove_XSS($query);
3947
3948
        if (!empty($query)) {
3949
            $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

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

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