Passed
Push — 1.11.x ( bce6cd...c146d9 )
by Angel Fernando Quiroz
12:25
created

main/inc/lib/usermanager.lib.php (4 issues)

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
6
use Chamilo\CoreBundle\Entity\Repository\AccessUrlRepository;
7
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
8
use Chamilo\CoreBundle\Entity\SkillRelUser;
9
use Chamilo\CoreBundle\Entity\SkillRelUserComment;
10
use Chamilo\UserBundle\Entity\User;
11
use Chamilo\UserBundle\Repository\UserRepository;
12
use ChamiloSession as Session;
13
use Symfony\Component\Filesystem\Filesystem;
14
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
15
16
/**
17
 * Class UserManager.
18
 *
19
 * This library provides functions for user management.
20
 * Include/require it in your code to use its functionality.
21
 *
22
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
23
 */
24
class UserManager
25
{
26
    // This constants are deprecated use the constants located in ExtraField
27
    public const USER_FIELD_TYPE_TEXT = 1;
28
    public const USER_FIELD_TYPE_TEXTAREA = 2;
29
    public const USER_FIELD_TYPE_RADIO = 3;
30
    public const USER_FIELD_TYPE_SELECT = 4;
31
    public const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
32
    public const USER_FIELD_TYPE_DATE = 6;
33
    public const USER_FIELD_TYPE_DATETIME = 7;
34
    public const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
35
    public const USER_FIELD_TYPE_DIVIDER = 9;
36
    public const USER_FIELD_TYPE_TAG = 10;
37
    public const USER_FIELD_TYPE_TIMEZONE = 11;
38
    public const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
39
    public const USER_FIELD_TYPE_FILE = 13;
40
    public const USER_FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
41
42
    private static $encryptionMethod;
43
44
    /**
45
     * Constructor.
46
     *
47
     * @assert () === null
48
     */
49
    public function __construct()
50
    {
51
    }
52
53
    /**
54
     * Repository is use to query the DB, selects, etc.
55
     *
56
     * @return UserRepository
57
     */
58
    public static function getRepository()
59
    {
60
        /** @var UserRepository $userRepository */
61
62
        return Database::getManager()->getRepository('ChamiloUserBundle:User');
63
    }
64
65
    /**
66
     * Create/update/delete methods are available in the UserManager
67
     * (based in the Sonata\UserBundle\Entity\UserManager).
68
     *
69
     * @return Chamilo\UserBundle\Entity\Manager\UserManager
70
     */
71
    public static function getManager()
72
    {
73
        static $userManager;
74
75
        if (!isset($userManager)) {
76
            $encoderFactory = self::getEncoderFactory();
77
            $passwordUpdater = new \FOS\UserBundle\Util\PasswordUpdater($encoderFactory);
78
            $canonicalFieldUpdater = new \FOS\UserBundle\Util\CanonicalFieldsUpdater(
79
                new \FOS\UserBundle\Util\Canonicalizer(), new \FOS\UserBundle\Util\Canonicalizer()
80
            );
81
            $userManager = new Chamilo\UserBundle\Entity\Manager\UserManager(
82
                $passwordUpdater,
83
                $canonicalFieldUpdater,
84
                Database::getManager(),
85
                'Chamilo\\UserBundle\\Entity\\User'
86
            );
87
        }
88
89
        return $userManager;
90
    }
91
92
    /**
93
     * @param string $encryptionMethod
94
     */
95
    public static function setPasswordEncryption($encryptionMethod)
96
    {
97
        self::$encryptionMethod = $encryptionMethod;
98
    }
99
100
    /**
101
     * @return bool|mixed
102
     */
103
    public static function getPasswordEncryption()
104
    {
105
        $encryptionMethod = self::$encryptionMethod;
106
        if (empty($encryptionMethod)) {
107
            $encryptionMethod = api_get_configuration_value('password_encryption');
108
        }
109
110
        return $encryptionMethod;
111
    }
112
113
    /**
114
     * Validates the password.
115
     *
116
     * @param $encoded
117
     * @param $raw
118
     * @param $salt
119
     *
120
     * @return bool
121
     */
122
    public static function isPasswordValid($encoded, $raw, $salt)
123
    {
124
        $encoder = new \Chamilo\UserBundle\Security\Encoder(self::getPasswordEncryption());
125
126
        return $encoder->isPasswordValid($encoded, $raw, $salt);
127
    }
128
129
    /**
130
     * @param string $raw
131
     *
132
     * @return string
133
     */
134
    public static function encryptPassword($raw, User $user)
135
    {
136
        $encoder = self::getEncoder($user);
137
138
        return $encoder->encodePassword(
139
            $raw,
140
            $user->getSalt()
141
        );
142
    }
143
144
    /**
145
     * @param int    $userId
146
     * @param string $password
147
     */
148
    public static function updatePassword($userId, $password)
149
    {
150
        $repository = self::getRepository();
151
        /** @var User $user */
152
        $user = $repository->find($userId);
153
        $userManager = self::getManager();
154
        $user->setPlainPassword($password);
155
        $userManager->updateUser($user, true);
156
        Event::addEvent(LOG_USER_PASSWORD_UPDATE, LOG_USER_ID, $userId);
157
    }
158
159
    /**
160
     * Creates a new user for the platform.
161
     *
162
     * @author Hugues Peeters <[email protected]>,
163
     * @author Roan Embrechts <[email protected]>
164
     *
165
     * @param string        $firstName
166
     * @param string        $lastName
167
     * @param int           $status                  (1 for course tutor, 5 for student, 6 for anonymous)
168
     * @param string        $email
169
     * @param string        $loginName
170
     * @param string        $password
171
     * @param string        $official_code           Any official code (optional)
172
     * @param string        $language                User language    (optional)
173
     * @param string        $phone                   Phone number    (optional)
174
     * @param string        $picture_uri             Picture URI        (optional)
175
     * @param string        $authSource              Authentication source (defaults to 'platform', dependind on constant)
176
     * @param string        $expirationDate          Account expiration date (optional, defaults to null)
177
     * @param int           $active                  Whether the account is enabled or disabled by default
178
     * @param int           $hr_dept_id              The department of HR in which the user is registered (defaults to 0)
179
     * @param array         $extra                   Extra fields (prefix labels with "extra_")
180
     * @param string        $encrypt_method          Used if password is given encrypted. Set to an empty string by default
181
     * @param bool          $send_mail
182
     * @param bool          $isAdmin
183
     * @param string        $address
184
     * @param bool          $sendEmailToAllAdmins
185
     * @param FormValidator $form
186
     * @param int           $creatorId
187
     * @param array         $emailTemplate
188
     * @param string        $redirectToURLAfterLogin
189
     *
190
     * @return mixed new user id - if the new user creation succeeds, false otherwise
191
     * @desc The function tries to retrieve user id from the session.
192
     * If it exists, the current user id is the creator id. If a problem arises,
193
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
194
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
195
     */
196
    public static function create_user(
197
        $firstName,
198
        $lastName,
199
        $status,
200
        $email,
201
        $loginName,
202
        $password,
203
        $official_code = '',
204
        $language = '',
205
        $phone = '',
206
        $picture_uri = '',
207
        $authSource = PLATFORM_AUTH_SOURCE,
208
        $expirationDate = null,
209
        $active = 1,
210
        $hr_dept_id = 0,
211
        $extra = [],
212
        $encrypt_method = '',
213
        $send_mail = false,
214
        $isAdmin = false,
215
        $address = '',
216
        $sendEmailToAllAdmins = false,
217
        $form = null,
218
        $creatorId = 0,
219
        $emailTemplate = [],
220
        $redirectToURLAfterLogin = ''
221
    ) {
222
        $creatorId = empty($creatorId) ? api_get_user_id() : 0;
223
        $creatorInfo = api_get_user_info($creatorId);
224
        $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
225
226
        $hook = HookCreateUser::create();
227
        if (!empty($hook)) {
228
            $hook->notifyCreateUser(HOOK_EVENT_TYPE_PRE);
229
        }
230
231
        if ('true' === api_get_setting('registration', 'email')) {
232
            // Force email validation.
233
            if (false === api_valid_email($email)) {
234
                Display::addFlash(
235
                   Display::return_message(get_lang('PleaseEnterValidEmail').' - '.$email, 'warning')
236
               );
237
238
                return false;
239
            }
240
        } else {
241
            // Allow empty email. If email is set, check if is valid.
242
            if (!empty($email) && false === api_valid_email($email)) {
243
                Display::addFlash(
244
                    Display::return_message(get_lang('PleaseEnterValidEmail').' - '.$email, 'warning')
245
                );
246
247
                return false;
248
            }
249
        }
250
251
        if ('true' === api_get_setting('login_is_email')) {
252
            if (false === api_valid_email($loginName)) {
253
                Display::addFlash(
254
                    Display::return_message(get_lang('PleaseEnterValidEmail').' - '.$loginName, 'warning')
255
                );
256
257
                return false;
258
            }
259
        } else {
260
            if (false === self::is_username_valid($loginName)) {
261
                Display::addFlash(
262
                    Display::return_message(get_lang('UsernameWrong').' - '.$loginName, 'warning')
263
                );
264
265
                return false;
266
            }
267
        }
268
269
        // First check wether the login already exists
270
        if (!self::is_username_available($loginName)) {
271
            Display::addFlash(
272
                Display::return_message(get_lang('LoginAlreadyTaken').' - '.$loginName, 'warning')
273
            );
274
275
            return false;
276
        }
277
278
        global $_configuration;
279
        $original_password = $password;
280
281
        $access_url_id = 1;
282
        if (api_get_multiple_access_url()) {
283
            $access_url_id = api_get_current_access_url_id();
284
        } else {
285
            // In some cases, the first access_url ID might be different from 1
286
            // for example when using a DB cluster or hacking the DB manually.
287
            // In this case, we want the first row, not necessarily "1".
288
            $dbm = Database::getManager();
289
            /** @var AccessUrlRepository $accessUrlRepository */
290
            $accessUrlRepository = $dbm->getRepository('ChamiloCoreBundle:AccessUrl');
291
            $accessUrl = $accessUrlRepository->getFirstId();
292
            if (!empty($accessUrl[0]) && !empty($accessUrl[0][1])) {
293
                $access_url_id = $accessUrl[0][1];
294
            }
295
        }
296
297
        if (isset($_configuration[$access_url_id]) &&
298
            is_array($_configuration[$access_url_id]) &&
299
            isset($_configuration[$access_url_id]['hosting_limit_users']) &&
300
            $_configuration[$access_url_id]['hosting_limit_users'] > 0) {
301
            $num = self::get_number_of_users(null, $access_url_id);
302
            if ($num >= $_configuration[$access_url_id]['hosting_limit_users']) {
303
                api_warn_hosting_contact('hosting_limit_users');
304
                Display::addFlash(
305
                    Display::return_message(
306
                        get_lang('PortalUsersLimitReached'),
307
                        'warning'
308
                    )
309
                );
310
311
                return false;
312
            }
313
        }
314
315
        if ($status === 1 &&
316
            isset($_configuration[$access_url_id]) &&
317
            is_array($_configuration[$access_url_id]) &&
318
            isset($_configuration[$access_url_id]['hosting_limit_teachers']) &&
319
            $_configuration[$access_url_id]['hosting_limit_teachers'] > 0
320
        ) {
321
            $num = self::get_number_of_users(1, $access_url_id);
322
            if ($num >= $_configuration[$access_url_id]['hosting_limit_teachers']) {
323
                Display::addFlash(
324
                    Display::return_message(
325
                        get_lang('PortalTeachersLimitReached'),
326
                        'warning'
327
                    )
328
                );
329
                api_warn_hosting_contact('hosting_limit_teachers');
330
331
                return false;
332
            }
333
        }
334
335
        if (empty($password)) {
336
            if ($authSource === PLATFORM_AUTH_SOURCE) {
337
                Display::addFlash(
338
                    Display::return_message(
339
                        get_lang('ThisFieldIsRequired').': '.get_lang(
340
                            'Password'
341
                        ),
342
                        'warning'
343
                    )
344
                );
345
346
                return false;
347
            }
348
349
            // We use the authSource as password.
350
            // The real validation will be by processed by the auth
351
            // source not Chamilo
352
            $password = $authSource;
353
        }
354
355
        // database table definition
356
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
357
358
        // Checking the user language
359
        $languages = api_get_languages();
360
        $language = strtolower($language);
361
362
        if (isset($languages['folder'])) {
363
            if (!in_array($language, $languages['folder'])) {
364
                $language = api_get_setting('platformLanguage');
365
            }
366
        }
367
368
        $currentDate = api_get_utc_datetime();
369
        $now = new DateTime();
370
371
        if (empty($expirationDate) || $expirationDate == '0000-00-00 00:00:00') {
372
            // Default expiration date
373
            // if there is a default duration of a valid account then
374
            // we have to change the expiration_date accordingly
375
            // Accept 0000-00-00 00:00:00 as a null value to avoid issues with
376
            // third party code using this method with the previous (pre-1.10)
377
            // value of 0000...
378
            if (api_get_setting('account_valid_duration') != '') {
379
                $expirationDate = new DateTime($currentDate);
380
                $days = (int) api_get_setting('account_valid_duration');
381
                $expirationDate->modify('+'.$days.' day');
382
            }
383
        } else {
384
            $expirationDate = api_get_utc_datetime($expirationDate);
385
            $expirationDate = new \DateTime($expirationDate, new DateTimeZone('UTC'));
386
        }
387
388
        $userManager = self::getManager();
389
390
        /** @var User $user */
391
        $user = $userManager->createUser();
392
393
        $user
394
            ->setLastname($lastName)
395
            ->setFirstname($firstName)
396
            ->setUsername($loginName)
397
            ->setStatus($status)
398
            ->setPlainPassword($password)
399
            ->setEmail($email)
400
            ->setOfficialCode($official_code)
401
            ->setPictureUri($picture_uri)
402
            ->setCreatorId($creatorId)
403
            ->setAuthSource($authSource)
404
            ->setPhone($phone)
405
            ->setAddress($address)
406
            ->setLanguage($language)
407
            ->setRegistrationDate($now)
408
            ->setHrDeptId($hr_dept_id)
409
            ->setActive($active)
410
            ->setEnabled($active)
411
        ;
412
413
        if (!empty($expirationDate)) {
414
            $user->setExpirationDate($expirationDate);
415
        }
416
417
        $userManager->updateUser($user);
418
        $userId = $user->getId();
419
420
        if (!empty($userId)) {
421
            $return = $userId;
422
            $sql = "UPDATE $table_user SET user_id = $return WHERE id = $return";
423
            Database::query($sql);
424
425
            if ($isAdmin) {
426
                self::addUserAsAdmin($user);
427
            }
428
429
            if (api_get_multiple_access_url()) {
430
                UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
431
            } else {
432
                //we are adding by default the access_url_user table with access_url_id = 1
433
                UrlManager::add_user_to_url($userId, 1);
434
            }
435
436
            $extra['item_id'] = $userId;
437
438
            if (is_array($extra) && count($extra) > 0) {
439
                $userFieldValue = new ExtraFieldValue('user');
440
                // Force saving of extra fields (otherwise, if the current
441
                // user is not admin, fields not visible to the user - most
442
                // of them - are just ignored)
443
                $userFieldValue->saveFieldValues(
444
                    $extra,
445
                    true,
446
                    null,
447
                    null,
448
                    null,
449
                    true
450
                );
451
            } else {
452
                // Create notify settings by default
453
                self::update_extra_field_value(
454
                    $userId,
455
                    'mail_notify_invitation',
456
                    '1'
457
                );
458
                self::update_extra_field_value(
459
                    $userId,
460
                    'mail_notify_message',
461
                    '1'
462
                );
463
                self::update_extra_field_value(
464
                    $userId,
465
                    'mail_notify_group_message',
466
                    '1'
467
                );
468
            }
469
470
            self::update_extra_field_value(
471
                $userId,
472
                'already_logged_in',
473
                'false'
474
            );
475
476
            if (!empty($redirectToURLAfterLogin) && api_get_configuration_value('plugin_redirection_enabled')) {
477
                RedirectionPlugin::insert($userId, $redirectToURLAfterLogin);
478
            }
479
480
            if (!empty($email) && $send_mail) {
481
                $recipient_name = api_get_person_name(
482
                    $firstName,
483
                    $lastName,
484
                    null,
485
                    PERSON_NAME_EMAIL_ADDRESS
486
                );
487
                $tplSubject = new Template(
488
                    null,
489
                    false,
490
                    false,
491
                    false,
492
                    false,
493
                    false
494
                );
495
                $layoutSubject = $tplSubject->get_template('mail/subject_registration_platform.tpl');
496
                $emailSubject = $tplSubject->fetch($layoutSubject);
497
                $sender_name = api_get_person_name(
498
                    api_get_setting('administratorName'),
499
                    api_get_setting('administratorSurname'),
500
                    null,
501
                    PERSON_NAME_EMAIL_ADDRESS
502
                );
503
                $email_admin = api_get_setting('emailAdministrator');
504
505
                $url = api_get_path(WEB_PATH);
506
                if (api_is_multiple_url_enabled()) {
507
                    $access_url_id = api_get_current_access_url_id();
508
                    if ($access_url_id != -1) {
509
                        $urlInfo = api_get_access_url($access_url_id);
510
                        if ($urlInfo) {
511
                            $url = $urlInfo['url'];
512
                        }
513
                    }
514
                }
515
516
                $tplContent = new Template(
517
                    null,
518
                    false,
519
                    false,
520
                    false,
521
                    false,
522
                    false
523
                );
524
                // variables for the default template
525
                $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
526
                $tplContent->assign('login_name', $loginName);
527
                $tplContent->assign('original_password', stripslashes($original_password));
528
                $tplContent->assign('mailWebPath', $url);
529
                $tplContent->assign('new_user', $user);
530
531
                $layoutContent = $tplContent->get_template('mail/content_registration_platform.tpl');
532
                $emailBody = $tplContent->fetch($layoutContent);
533
534
                $userInfo = api_get_user_info($userId);
535
                $mailTemplateManager = new MailTemplateManager();
536
537
                /* MANAGE EVENT WITH MAIL */
538
                if (EventsMail::check_if_using_class('user_registration')) {
539
                    $values["about_user"] = $return;
540
                    $values["password"] = $original_password;
541
                    $values["send_to"] = [$return];
542
                    $values["prior_lang"] = null;
543
                    EventsDispatcher::events('user_registration', $values);
544
                } else {
545
                    $phoneNumber = isset($extra['mobile_phone_number']) ? $extra['mobile_phone_number'] : null;
546
                    $additionalParameters = [
547
                        'smsType' => SmsPlugin::WELCOME_LOGIN_PASSWORD,
548
                        'userId' => $return,
549
                        'mobilePhoneNumber' => $phoneNumber,
550
                        'password' => $original_password,
551
                    ];
552
553
                    $emailBodyTemplate = '';
554
                    if (!empty($emailTemplate)) {
555
                        if (isset($emailTemplate['content_registration_platform.tpl']) &&
556
                            !empty($emailTemplate['content_registration_platform.tpl'])
557
                        ) {
558
                            $emailBodyTemplate = $mailTemplateManager->parseTemplate(
559
                                $emailTemplate['content_registration_platform.tpl'],
560
                                $userInfo
561
                            );
562
                        }
563
                    }
564
565
                    $twoEmail = api_get_configuration_value('send_two_inscription_confirmation_mail');
566
                    if ($twoEmail === true) {
567
                        $layoutContent = $tplContent->get_template('mail/new_user_first_email_confirmation.tpl');
568
                        $emailBody = $tplContent->fetch($layoutContent);
569
570
                        if (!empty($emailTemplate) &&
571
                            isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
572
                            !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
573
                        ) {
574
                            $emailBody = $mailTemplateManager->parseTemplate(
575
                                $emailTemplate['new_user_first_email_confirmation.tpl'],
576
                                $userInfo
577
                            );
578
                        }
579
580
                        api_mail_html(
581
                            $recipient_name,
582
                            $email,
583
                            $emailSubject,
584
                            $emailBody,
585
                            $sender_name,
586
                            $email_admin,
587
                            null,
588
                            null,
589
                            null,
590
                            $additionalParameters,
591
                            $creatorEmail
592
                        );
593
594
                        $layoutContent = $tplContent->get_template('mail/new_user_second_email_confirmation.tpl');
595
                        $emailBody = $tplContent->fetch($layoutContent);
596
597
                        if (!empty($emailTemplate) &&
598
                            isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
599
                            !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
600
                        ) {
601
                            $emailBody = $mailTemplateManager->parseTemplate(
602
                                $emailTemplate['new_user_second_email_confirmation.tpl'],
603
                                $userInfo
604
                            );
605
                        }
606
607
                        api_mail_html(
608
                            $recipient_name,
609
                            $email,
610
                            $emailSubject,
611
                            $emailBody,
612
                            $sender_name,
613
                            $email_admin,
614
                            null,
615
                            null,
616
                            null,
617
                            $additionalParameters,
618
                            $creatorEmail
619
                        );
620
                    } else {
621
                        if (!empty($emailBodyTemplate)) {
622
                            $emailBody = $emailBodyTemplate;
623
                        }
624
                        $sendToInbox = api_get_configuration_value('send_inscription_msg_to_inbox');
625
                        if ($sendToInbox) {
626
                            $adminList = self::get_all_administrators();
627
                            $senderId = 1;
628
                            if (!empty($adminList)) {
629
                                $adminInfo = current($adminList);
630
                                $senderId = $adminInfo['user_id'];
631
                            }
632
633
                            MessageManager::send_message_simple(
634
                                $userId,
635
                                $emailSubject,
636
                                $emailBody,
637
                                $senderId
638
                            );
639
                        } else {
640
                            api_mail_html(
641
                                $recipient_name,
642
                                $email,
643
                                $emailSubject,
644
                                $emailBody,
645
                                $sender_name,
646
                                $email_admin,
647
                                null,
648
                                null,
649
                                null,
650
                                $additionalParameters,
651
                                $creatorEmail
652
                            );
653
                        }
654
                    }
655
656
                    $notification = api_get_configuration_value('send_notification_when_user_added');
657
                    if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
658
                        foreach ($notification['admins'] as $adminId) {
659
                            $emailSubjectToAdmin = get_lang('UserAdded').': '.api_get_person_name($firstName, $lastName);
660
                            MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
661
                        }
662
                    }
663
                }
664
665
                if ($sendEmailToAllAdmins) {
666
                    $adminList = self::get_all_administrators();
667
668
                    $tplContent = new Template(
669
                        null,
670
                        false,
671
                        false,
672
                        false,
673
                        false,
674
                        false
675
                    );
676
                    // variables for the default template
677
                    $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
678
                    $tplContent->assign('user_added', $user);
679
                    $renderer = FormValidator::getDefaultRenderer();
680
                    // Form template
681
                    $elementTemplate = ' {label}: {element} <br />';
682
                    $renderer->setElementTemplate($elementTemplate);
683
                    /** @var FormValidator $form */
684
                    $form->freeze(null, $elementTemplate);
685
                    $form->removeElement('submit');
686
                    $formData = $form->returnForm();
687
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
688
                    $tplContent->assign('link', Display::url($url, $url));
689
                    $tplContent->assign('form', $formData);
690
691
                    $layoutContent = $tplContent->get_template('mail/content_registration_platform_to_admin.tpl');
692
                    $emailBody = $tplContent->fetch($layoutContent);
693
694
                    if (!empty($emailTemplate) &&
695
                        isset($emailTemplate['content_registration_platform_to_admin.tpl']) &&
696
                        !empty($emailTemplate['content_registration_platform_to_admin.tpl'])
697
                    ) {
698
                        $emailBody = $mailTemplateManager->parseTemplate(
699
                            $emailTemplate['content_registration_platform_to_admin.tpl'],
700
                            $userInfo
701
                        );
702
                    }
703
704
                    $subject = get_lang('UserAdded');
705
                    foreach ($adminList as $adminId => $data) {
706
                        MessageManager::send_message_simple(
707
                            $adminId,
708
                            $subject,
709
                            $emailBody,
710
                            $userId
711
                        );
712
                    }
713
                }
714
                /* ENDS MANAGE EVENT WITH MAIL */
715
            }
716
717
            if (!empty($hook)) {
718
                $hook->setEventData([
719
                    'return' => $userId,
720
                    'originalPassword' => $original_password,
721
                ]);
722
                $hook->notifyCreateUser(HOOK_EVENT_TYPE_POST);
723
            }
724
            Event::addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId);
725
        } else {
726
            Display::addFlash(
727
                Display::return_message(get_lang('ErrorContactPlatformAdmin'))
728
            );
729
730
            return false;
731
        }
732
733
        return $return;
734
    }
735
736
    /**
737
     * Ensure the CAS-authenticated user exists in the database.
738
     *
739
     * @param $casUser string the CAS user identifier
740
     *
741
     * @throws Exception if more than one user share the same CAS user identifier
742
     *
743
     * @return string|bool the recognised user login name or false if not found
744
     */
745
    public static function casUserLoginName($casUser)
746
    {
747
        $loginName = false;
748
749
        // look inside the casUser extra field
750
        if (UserManager::is_extra_field_available('cas_user')) {
751
            $valueModel = new ExtraFieldValue('user');
752
            $itemList = $valueModel->get_item_id_from_field_variable_and_field_value(
753
                'cas_user',
754
                $casUser,
755
                false,
756
                false,
757
                true
758
            );
759
            if (false !== $itemList) {
760
                // at least one user has $casUser in the 'cas_user' extra field
761
                // we attempt to load each candidate user because there might be deleted ones
762
                // (extra field values of a deleted user might remain)
763
                foreach ($itemList as $item) {
764
                    $userId = intval($item['item_id']);
765
                    $user = UserManager::getRepository()->find($userId);
766
                    if (!is_null($user)) {
767
                        if (false === $loginName) {
768
                            $loginName = $user->getUsername();
769
                        } else {
770
                            throw new Exception(get_lang('MoreThanOneUserMatched'));
771
                        }
772
                    }
773
                }
774
            }
775
        }
776
777
        if (false === $loginName) {
778
            // no matching 'cas_user' extra field value, or no such extra field
779
            // falling back to the old behaviour: $casUser must be the login name
780
            $userId = UserManager::get_user_id_from_username($casUser);
781
            if (false !== $userId) {
782
                $loginName = $casUser;
783
            }
784
        }
785
786
        return $loginName;
787
    }
788
789
    /**
790
     * Checks the availability of extra field 'cas_user'
791
     * and creates it if missing.
792
     *
793
     * @throws Exception on failure
794
     */
795
    public static function ensureCASUserExtraFieldExists()
796
    {
797
        if (!self::is_extra_field_available('cas_user')) {
798
            $extraField = new ExtraField('user');
799
            if (false === $extraField->save(
800
                    [
801
                        'variable' => 'cas_user',
802
                        'field_type' => ExtraField::FIELD_TYPE_TEXT,
803
                        'display_text' => get_lang('CAS User Identifier'),
804
                        'visible_to_self' => true,
805
                        'filter' => true,
806
                    ]
807
                )) {
808
                throw new Exception(get_lang('FailedToCreateExtraFieldCasUser'));
809
            }
810
811
            $rules = api_get_configuration_value('cas_user_map');
812
            if (!empty($rules) && isset($rules['extra'])) {
813
                foreach ($rules['extra'] as $extra) {
814
                    $extraField->save(
815
                        [
816
                            'variable' => $extra,
817
                            'field_type' => ExtraField::FIELD_TYPE_TEXT,
818
                            'display_text' => $extra,
819
                            'visible_to_self' => false,
820
                            'filter' => false,
821
                        ]
822
                    );
823
                }
824
            }
825
        }
826
    }
827
828
    /**
829
     * Create a CAS-authenticated user from scratch, from its CAS user identifier, with temporary default values.
830
     *
831
     * @param string $casUser the CAS user identifier
832
     *
833
     * @throws Exception on error
834
     *
835
     * @return string the login name of the new user
836
     */
837
    public static function createCASAuthenticatedUserFromScratch($casUser)
838
    {
839
        self::ensureCASUserExtraFieldExists();
840
841
        $loginName = 'cas_user_'.$casUser;
842
        $defaultValue = get_lang('EditInProfile');
843
        $defaultEmailValue = get_lang('EditInProfile');
844
        require_once __DIR__.'/../../auth/external_login/functions.inc.php';
845
        if ('true' === api_get_setting('login_is_email')) {
846
            $defaultEmailValue = $casUser;
847
        }
848
        $userId = external_add_user(
849
            [
850
                'username' => $loginName,
851
                'auth_source' => CAS_AUTH_SOURCE,
852
                'firstname' => $defaultValue,
853
                'lastname' => $defaultValue,
854
                'email' => $defaultEmailValue,
855
            ]
856
        );
857
        if (false === $userId) {
858
            throw new Exception(get_lang('FailedUserCreation'));
859
        }
860
        // Not checking function update_extra_field_value return value because not reliable
861
        self::update_extra_field_value($userId, 'cas_user', $casUser);
862
863
        return $loginName;
864
    }
865
866
    public static function updateCasUser($_user)
867
    {
868
        $rules = api_get_configuration_value('cas_user_map');
869
870
        if (empty($_user)) {
871
            return false;
872
        }
873
874
        if (!empty($rules)) {
875
            $userEntity = api_get_user_entity($_user['id']);
876
            $attributes = phpCAS::getAttributes();
877
            if (isset($rules['fields'])) {
878
                $isAdmin = false;
879
                foreach ($rules['fields'] as $field => $attributeName) {
880
                    if (!isset($attributes[$attributeName])) {
881
                        continue;
882
                    }
883
                    $value = $attributes[$attributeName];
884
                    // Check replace.
885
                    if (isset($rules['replace'][$attributeName])) {
886
                        $value = $rules['replace'][$attributeName][$value];
887
                    }
888
889
                    switch ($field) {
890
                        case 'email':
891
                            $userEntity->setEmail($value);
892
                            break;
893
                        case 'firstname':
894
                            $userEntity->setFirstname($value);
895
                            break;
896
                        case 'lastname':
897
                            $userEntity->setLastname($value);
898
                            break;
899
                        case 'active':
900
                            $userEntity->setActive('false' === $value);
901
                            break;
902
                        case 'status':
903
                            if (PLATFORM_ADMIN === (int) $value) {
904
                                $value = COURSEMANAGER;
905
                                $isAdmin = true;
906
                            }
907
                            $userEntity->setStatus($value);
908
                            break;
909
                    }
910
911
                    Database::getManager()->persist($userEntity);
912
                    Database::getManager()->flush();
913
914
                    if ($isAdmin) {
915
                        self::addUserAsAdmin($userEntity);
916
                    }
917
                }
918
            }
919
920
            if (isset($rules['extra'])) {
921
                foreach ($rules['extra'] as $variable) {
922
                    if (isset($attributes[$variable])) {
923
                        self::update_extra_field_value(
924
                            $_user['id'],
925
                            $variable,
926
                            $attributes[$variable]
927
                        );
928
                    }
929
                }
930
            }
931
        }
932
    }
933
934
    /**
935
     * Create a CAS-authenticated user from LDAP, from its CAS user identifier.
936
     *
937
     * @param $casUser
938
     *
939
     * @throws Exception
940
     *
941
     * @return string login name of the new user
942
     */
943
    public static function createCASAuthenticatedUserFromLDAP($casUser)
944
    {
945
        self::ensureCASUserExtraFieldExists();
946
947
        require_once __DIR__.'/../../auth/external_login/ldap.inc.php';
948
        $login = extldapCasUserLogin($casUser);
949
        if (false !== $login) {
950
            $ldapUser = extldap_authenticate($login, 'nopass', true);
951
            if (false !== $ldapUser) {
952
                require_once __DIR__.'/../../auth/external_login/functions.inc.php';
953
                $user = extldap_get_chamilo_user($ldapUser);
954
                $user['username'] = $login;
955
                $user['auth_source'] = CAS_AUTH_SOURCE;
956
                $userId = external_add_user($user);
957
                if (false !== $userId) {
958
                    // Not checking function update_extra_field_value return value because not reliable
959
                    self::update_extra_field_value($userId, 'cas_user', $casUser);
960
961
                    return $login;
962
                } else {
963
                    throw new Exception('Could not create the new user '.$login);
964
                }
965
            } else {
966
                throw new Exception('Could not load the new user from LDAP using its login '.$login);
0 ignored issues
show
Are you sure $login of type string|true 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

966
                throw new Exception('Could not load the new user from LDAP using its login './** @scrutinizer ignore-type */ $login);
Loading history...
967
            }
968
        } else {
969
            throw new Exception('Could not find the new user from LDAP using its cas user identifier '.$casUser);
970
        }
971
    }
972
973
    /**
974
     * updates user record in database from its LDAP record
975
     * copies relevant LDAP attribute values : firstname, lastname and email.
976
     *
977
     * @param $login string the user login name
978
     *
979
     * @throws Exception when the user login name is not found in the LDAP or in the database
980
     */
981
    public static function updateUserFromLDAP($login)
982
    {
983
        require_once __DIR__.'/../../auth/external_login/ldap.inc.php';
984
985
        $ldapUser = extldap_authenticate($login, 'nopass', true);
986
        if (false === $ldapUser) {
987
            throw new Exception(get_lang('NoSuchUserInLDAP'));
988
        }
989
990
        $user = extldap_get_chamilo_user($ldapUser);
991
        $userInfo = api_get_user_info_from_username($login);
992
        if (false === $userInfo) {
993
            throw new Exception(get_lang('NoSuchUserInInternalDatabase'));
994
        }
995
996
        $userId = UserManager::update_user(
997
            $userInfo['user_id'],
998
            $user["firstname"],
999
            $user["lastname"],
1000
            $login,
1001
            null,
1002
            $userInfo['auth_source'],
1003
            $user["email"],
1004
            $userInfo['status'],
1005
            $userInfo['official_code'],
1006
            $userInfo['phone'],
1007
            $userInfo['picture_uri'],
1008
            $userInfo['expiration_date'],
1009
            $userInfo['active'],
1010
            $userInfo['creator_id'],
1011
            $userInfo['hr_dept_id'],
1012
            null,
1013
            $userInfo['language']
1014
        );
1015
        if (false === $userId) {
1016
            throw new Exception(get_lang('CouldNotUpdateUser'));
1017
        }
1018
    }
1019
1020
    /**
1021
     * Can user be deleted? This function checks whether there's a course
1022
     * in which the given user is the
1023
     * only course administrator. If that is the case, the user can't be
1024
     * deleted because the course would remain without a course admin.
1025
     *
1026
     * @param int $user_id The user id
1027
     *
1028
     * @return bool true if user can be deleted
1029
     *
1030
     * @assert (null) === false
1031
     * @assert (-1) === false
1032
     * @assert ('abc') === false
1033
     */
1034
    public static function canDeleteUser($user_id)
1035
    {
1036
        $deny = api_get_configuration_value('deny_delete_users');
1037
1038
        if ($deny) {
1039
            return false;
1040
        }
1041
1042
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1043
        $user_id = (int) $user_id;
1044
1045
        if (empty($user_id)) {
1046
            return false;
1047
        }
1048
1049
        $res = Database::query(
1050
            "SELECT c_id FROM $table_course_user WHERE status = 1 AND user_id = $user_id"
1051
        );
1052
        while ($course = Database::fetch_assoc($res)) {
1053
            $sql = Database::query(
1054
                "SELECT COUNT(id) number FROM $table_course_user WHERE status = 1 AND c_id = {$course['c_id']}"
1055
            );
1056
            $res2 = Database::fetch_assoc($sql);
1057
1058
            if ($res2['number'] == 1) {
1059
                return false;
1060
            }
1061
        }
1062
1063
        return true;
1064
    }
1065
1066
    /**
1067
     * Delete a user from the platform, and all its belongings. This is a
1068
     * very dangerous function that should only be accessible by
1069
     * super-admins. Other roles should only be able to disable a user,
1070
     * which removes access to the platform but doesn't delete anything.
1071
     *
1072
     * @param int The ID of th user to be deleted
1073
     *
1074
     * @throws Exception
1075
     *
1076
     * @return bool true if user is successfully deleted, false otherwise
1077
     * @assert (null) === false
1078
     * @assert ('abc') === false
1079
     */
1080
    public static function delete_user($user_id)
1081
    {
1082
        $user_id = (int) $user_id;
1083
1084
        if (empty($user_id)) {
1085
            return false;
1086
        }
1087
1088
        if (!self::canDeleteUser($user_id)) {
1089
            return false;
1090
        }
1091
1092
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1093
        $usergroup_rel_user = Database::get_main_table(TABLE_USERGROUP_REL_USER);
1094
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1095
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
1096
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
1097
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
1098
        $table_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
1099
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1100
        $table_group = Database::get_course_table(TABLE_GROUP_USER);
1101
        $table_work = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
1102
1103
        // Unsubscribe the user from all groups in all his courses
1104
        $sql = "SELECT c.id
1105
                FROM $table_course c
1106
                INNER JOIN $table_course_user cu
1107
                ON (c.id = cu.c_id)
1108
                WHERE
1109
                    cu.user_id = '".$user_id."' AND
1110
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
1111
                ";
1112
1113
        $res = Database::query($sql);
1114
        while ($course = Database::fetch_object($res)) {
1115
            $sql = "DELETE FROM $table_group
1116
                    WHERE c_id = {$course->id} AND user_id = $user_id";
1117
            Database::query($sql);
1118
        }
1119
1120
        // Unsubscribe user from usergroup_rel_user
1121
        $sql = "DELETE FROM $usergroup_rel_user WHERE user_id = '".$user_id."'";
1122
        Database::query($sql);
1123
1124
        // Unsubscribe user from all courses
1125
        $sql = "DELETE FROM $table_course_user WHERE user_id = '".$user_id."'";
1126
        Database::query($sql);
1127
1128
        // Unsubscribe user from all courses in sessions
1129
        $sql = "DELETE FROM $table_session_course_user WHERE user_id = '".$user_id."'";
1130
        Database::query($sql);
1131
1132
        // If the user was added as a id_coach then set the current admin as coach see BT#
1133
        $currentUserId = api_get_user_id();
1134
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
1135
                WHERE id_coach = '".$user_id."'";
1136
        Database::query($sql);
1137
1138
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
1139
                WHERE session_admin_id = '".$user_id."'";
1140
        Database::query($sql);
1141
1142
        // Unsubscribe user from all sessions
1143
        $sql = "DELETE FROM $table_session_user
1144
                WHERE user_id = '".$user_id."'";
1145
        Database::query($sql);
1146
1147
        if (api_get_configuration_value('plugin_redirection_enabled')) {
1148
            RedirectionPlugin::deleteUserRedirection($user_id);
1149
        }
1150
1151
        $user_info = api_get_user_info($user_id);
1152
1153
        try {
1154
            self::deleteUserFiles($user_id);
1155
        } catch (Exception $exception) {
1156
            error_log('Delete user exception: '.$exception->getMessage());
1157
        }
1158
1159
        // Delete the personal course categories
1160
        $course_cat_table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
1161
        $sql = "DELETE FROM $course_cat_table WHERE user_id = '".$user_id."'";
1162
        Database::query($sql);
1163
1164
        // Delete user from the admin table
1165
        $sql = "DELETE FROM $table_admin WHERE user_id = '".$user_id."'";
1166
        Database::query($sql);
1167
1168
        // Delete the personal agenda-items from this user
1169
        $agenda_table = Database::get_main_table(TABLE_PERSONAL_AGENDA);
1170
        $sql = "DELETE FROM $agenda_table WHERE user = '".$user_id."'";
1171
        Database::query($sql);
1172
1173
        $gradebook_results_table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_RESULT);
1174
        $sql = 'DELETE FROM '.$gradebook_results_table.' WHERE user_id = '.$user_id;
1175
        Database::query($sql);
1176
1177
        $extraFieldValue = new ExtraFieldValue('user');
1178
        $extraFieldValue->deleteValuesByItem($user_id);
1179
1180
        UrlManager::deleteUserFromAllUrls($user_id);
1181
1182
        if (api_get_setting('allow_social_tool') === 'true') {
1183
            $userGroup = new UserGroup();
1184
            //Delete user from portal groups
1185
            $group_list = $userGroup->get_groups_by_user($user_id);
1186
            if (!empty($group_list)) {
1187
                foreach ($group_list as $group_id => $data) {
1188
                    $userGroup->delete_user_rel_group($user_id, $group_id);
1189
                }
1190
            }
1191
1192
            // Delete user from friend lists
1193
            SocialManager::remove_user_rel_user($user_id, true);
1194
        }
1195
1196
        // Removing survey invitation
1197
        SurveyManager::delete_all_survey_invitations_by_user($user_id);
1198
1199
        // Delete students works
1200
        $sql = "DELETE FROM $table_work WHERE user_id = $user_id AND c_id <> 0";
1201
        Database::query($sql);
1202
1203
        $sql = "UPDATE c_item_property SET to_user_id = NULL
1204
                WHERE to_user_id = '".$user_id."'";
1205
        Database::query($sql);
1206
1207
        $sql = "UPDATE c_item_property SET insert_user_id = NULL
1208
                WHERE insert_user_id = '".$user_id."'";
1209
        Database::query($sql);
1210
1211
        $sql = "UPDATE c_item_property SET lastedit_user_id = NULL
1212
                WHERE lastedit_user_id = '".$user_id."'";
1213
        Database::query($sql);
1214
1215
        // Skills
1216
        $em = Database::getManager();
1217
1218
        $criteria = ['user' => $user_id];
1219
        $skills = $em->getRepository('ChamiloCoreBundle:SkillRelUser')->findBy($criteria);
1220
        if ($skills) {
1221
            /** @var SkillRelUser $skill */
1222
            foreach ($skills as $skill) {
1223
                $comments = $skill->getComments();
1224
                if ($comments) {
1225
                    /** @var SkillRelUserComment $comment */
1226
                    foreach ($comments as $comment) {
1227
                        $em->remove($comment);
1228
                    }
1229
                }
1230
                $em->remove($skill);
1231
            }
1232
            $em->flush();
1233
        }
1234
1235
        // ExtraFieldSavedSearch
1236
        $criteria = ['user' => $user_id];
1237
        $searchList = $em->getRepository('ChamiloCoreBundle:ExtraFieldSavedSearch')->findBy($criteria);
1238
        if ($searchList) {
1239
            foreach ($searchList as $search) {
1240
                $em->remove($search);
1241
            }
1242
            $em->flush();
1243
        }
1244
1245
        $connection = Database::getManager()->getConnection();
1246
        $tableExists = $connection->getSchemaManager()->tablesExist(['plugin_bbb_room']);
1247
        if ($tableExists) {
1248
            // Delete user from database
1249
            $sql = "DELETE FROM plugin_bbb_room WHERE participant_id = $user_id";
1250
            Database::query($sql);
1251
        }
1252
1253
        // Delete user/ticket relationships :(
1254
        $tableExists = $connection->getSchemaManager()->tablesExist(['ticket_ticket']);
1255
        if ($tableExists) {
1256
            TicketManager::deleteUserFromTicketSystem($user_id);
1257
        }
1258
1259
        $tableExists = $connection->getSchemaManager()->tablesExist(['c_lp_category_user']);
1260
        if ($tableExists) {
1261
            $sql = "DELETE FROM c_lp_category_user WHERE user_id = $user_id";
1262
            Database::query($sql);
1263
        }
1264
1265
        $app_plugin = new AppPlugin();
1266
        $app_plugin->performActionsWhenDeletingItem('user', $user_id);
1267
1268
        // Delete user from database
1269
        $sql = "DELETE FROM $table_user WHERE id = '".$user_id."'";
1270
        Database::query($sql);
1271
1272
        // Add event to system log
1273
        $user_id_manager = api_get_user_id();
1274
1275
        Event::addEvent(
1276
            LOG_USER_DELETE,
1277
            LOG_USER_ID,
1278
            $user_id,
1279
            api_get_utc_datetime(),
1280
            $user_id_manager
1281
        );
1282
1283
        Event::addEvent(
1284
            LOG_USER_DELETE,
1285
            LOG_USER_OBJECT,
1286
            $user_info,
1287
            api_get_utc_datetime(),
1288
            $user_id_manager
1289
        );
1290
        $cacheAvailable = api_get_configuration_value('apc');
1291
        if ($cacheAvailable === true) {
1292
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1293
            if (apcu_exists($apcVar)) {
1294
                apcu_delete($apcVar);
1295
            }
1296
        }
1297
1298
        return true;
1299
    }
1300
1301
    /**
1302
     * Deletes users completely. Can be called either as:
1303
     * - UserManager::delete_users(1, 2, 3); or
1304
     * - UserManager::delete_users(array(1, 2, 3));.
1305
     *
1306
     * @param array|int $ids
1307
     *
1308
     * @return bool True if at least one user was successfuly deleted. False otherwise.
1309
     *
1310
     * @author Laurent Opprecht
1311
     *
1312
     * @uses \UserManager::delete_user() to actually delete each user
1313
     * @assert (null) === false
1314
     * @assert (-1) === false
1315
     * @assert (array(-1)) === false
1316
     */
1317
    public static function delete_users($ids = [])
1318
    {
1319
        $result = false;
1320
        $ids = is_array($ids) ? $ids : func_get_args();
1321
        if (!is_array($ids) || count($ids) == 0) {
1322
            return false;
1323
        }
1324
        $ids = array_map('intval', $ids);
1325
        foreach ($ids as $id) {
1326
            if (empty($id) || $id < 1) {
1327
                continue;
1328
            }
1329
            $deleted = self::delete_user($id);
1330
            $result = $deleted || $result;
1331
        }
1332
1333
        return $result;
1334
    }
1335
1336
    /**
1337
     * Disable users. Can be called either as:
1338
     * - UserManager::deactivate_users(1, 2, 3);
1339
     * - UserManager::deactivate_users(array(1, 2, 3));.
1340
     *
1341
     * @param array|int $ids
1342
     *
1343
     * @return bool
1344
     *
1345
     * @author Laurent Opprecht
1346
     * @assert (null) === false
1347
     * @assert (array(-1)) === false
1348
     */
1349
    public static function deactivate_users($ids = [])
1350
    {
1351
        if (empty($ids)) {
1352
            return false;
1353
        }
1354
1355
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1356
1357
        $ids = is_array($ids) ? $ids : func_get_args();
1358
        $ids = array_map('intval', $ids);
1359
        $ids = implode(',', $ids);
1360
1361
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
1362
        $r = Database::query($sql);
1363
        if ($r !== false) {
1364
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
1365
1366
            return true;
1367
        }
1368
1369
        return false;
1370
    }
1371
1372
    /**
1373
     * Enable users. Can be called either as:
1374
     * - UserManager::activate_users(1, 2, 3);
1375
     * - UserManager::activate_users(array(1, 2, 3));.
1376
     *
1377
     * @param array|int IDs of the users to enable
1378
     *
1379
     * @return bool
1380
     *
1381
     * @author Laurent Opprecht
1382
     * @assert (null) === false
1383
     * @assert (array(-1)) === false
1384
     */
1385
    public static function activate_users($ids = [])
1386
    {
1387
        if (empty($ids)) {
1388
            return false;
1389
        }
1390
1391
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1392
1393
        $ids = is_array($ids) ? $ids : func_get_args();
1394
        $ids = array_map('intval', $ids);
1395
        $ids = implode(',', $ids);
1396
1397
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
1398
        $r = Database::query($sql);
1399
        if ($r !== false) {
1400
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
1401
1402
            return true;
1403
        }
1404
1405
        return false;
1406
    }
1407
1408
    /**
1409
     * Update user information with new openid.
1410
     *
1411
     * @param int    $user_id
1412
     * @param string $openid
1413
     *
1414
     * @return bool true if the user information was updated
1415
     * @assert (false,'') === false
1416
     * @assert (-1,'') === false
1417
     */
1418
    public static function update_openid($user_id, $openid)
1419
    {
1420
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1421
        if ($user_id != strval(intval($user_id))) {
1422
            return false;
1423
        }
1424
        if ($user_id === false) {
1425
            return false;
1426
        }
1427
        $sql = "UPDATE $table_user SET
1428
                openid='".Database::escape_string($openid)."'";
1429
        $sql .= " WHERE id= $user_id";
1430
1431
        if (Database::query($sql) !== false) {
1432
            return true;
1433
        }
1434
1435
        return false;
1436
    }
1437
1438
    /**
1439
     * Update user information with all the parameters passed to this function.
1440
     *
1441
     * @param int    $user_id         The ID of the user to be updated
1442
     * @param string $firstname       The user's firstname
1443
     * @param string $lastname        The user's lastname
1444
     * @param string $username        The user's username (login)
1445
     * @param string $password        The user's password
1446
     * @param string $auth_source     The authentication source (default: "platform")
1447
     * @param string $email           The user's e-mail address
1448
     * @param int    $status          The user's status
1449
     * @param string $official_code   The user's official code (usually just an internal institutional code)
1450
     * @param string $phone           The user's phone number
1451
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
1452
     * @param string $expiration_date The date at which this user will be automatically disabled
1453
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
1454
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
1455
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
1456
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
1457
     * @param string $language        The language to which the user account will be set
1458
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
1459
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
1460
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
1461
     * @param string $address
1462
     * @param array  $emailTemplate
1463
     *
1464
     * @return bool|int False on error, or the user ID if the user information was updated
1465
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
1466
     */
1467
    public static function update_user(
1468
        $user_id,
1469
        $firstname,
1470
        $lastname,
1471
        $username,
1472
        $password,
1473
        $auth_source,
1474
        $email,
1475
        $status,
1476
        $official_code,
1477
        $phone,
1478
        $picture_uri,
1479
        $expiration_date,
1480
        $active,
1481
        $creator_id = null,
1482
        $hr_dept_id = 0,
1483
        $extra = null,
1484
        $language = 'english',
1485
        $encrypt_method = '',
1486
        $send_email = false,
1487
        $reset_password = 0,
1488
        $address = null,
1489
        $emailTemplate = []
1490
    ) {
1491
        $hook = HookUpdateUser::create();
1492
        if (!empty($hook)) {
1493
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_PRE);
1494
        }
1495
        $original_password = $password;
1496
        $user_id = (int) $user_id;
1497
        $creator_id = (int) $creator_id;
1498
1499
        if (empty($user_id)) {
1500
            return false;
1501
        }
1502
1503
        $userManager = self::getManager();
1504
        /** @var User $user */
1505
        $user = self::getRepository()->find($user_id);
1506
1507
        if (empty($user)) {
1508
            return false;
1509
        }
1510
1511
        if ($reset_password == 0) {
1512
            $password = null;
1513
            $auth_source = $user->getAuthSource();
1514
        } elseif ($reset_password == 1) {
1515
            $original_password = $password = api_generate_password();
1516
            $auth_source = PLATFORM_AUTH_SOURCE;
1517
        } elseif ($reset_password == 2) {
1518
            //$password = $password;
1519
            $auth_source = PLATFORM_AUTH_SOURCE;
1520
        } elseif ($reset_password == 3) {
1521
            //$password = $password;
1522
            //$auth_source = $auth_source;
1523
        }
1524
1525
        // Checking the user language
1526
        $languages = api_get_languages();
1527
        if (!in_array($language, $languages['folder'])) {
1528
            $language = api_get_setting('platformLanguage');
1529
        }
1530
1531
        $change_active = 0;
1532
        $isUserActive = $user->getActive();
1533
        if ($isUserActive != $active) {
1534
            $change_active = 1;
1535
        }
1536
1537
        $originalUsername = $user->getUsername();
1538
1539
        // If username is different from original then check if it exists.
1540
        if ($originalUsername !== $username) {
1541
            $available = self::is_username_available($username);
1542
            if ($available === false) {
1543
                return false;
1544
            }
1545
        }
1546
1547
        if (!empty($expiration_date)) {
1548
            $expiration_date = api_get_utc_datetime($expiration_date);
1549
            $expiration_date = new \DateTime(
1550
                $expiration_date,
1551
                new DateTimeZone('UTC')
1552
            );
1553
        }
1554
1555
        $user
1556
            ->setLastname($lastname)
1557
            ->setFirstname($firstname)
1558
            ->setUsername($username)
1559
            ->setStatus($status)
1560
            ->setAuthSource($auth_source)
1561
            ->setLanguage($language)
1562
            ->setEmail($email)
1563
            ->setOfficialCode($official_code)
1564
            ->setPhone($phone)
1565
            ->setAddress($address)
1566
            ->setPictureUri($picture_uri)
1567
            ->setExpirationDate($expiration_date)
1568
            ->setActive($active)
1569
            ->setEnabled($active)
1570
            ->setHrDeptId($hr_dept_id)
1571
        ;
1572
1573
        if (!is_null($password)) {
1574
            $user->setPlainPassword($password);
1575
            Event::addEvent(LOG_USER_PASSWORD_UPDATE, LOG_USER_ID, $user_id);
1576
        }
1577
1578
        $userManager->updateUser($user, true);
1579
        Event::addEvent(LOG_USER_UPDATE, LOG_USER_ID, $user_id);
1580
1581
        if ($change_active == 1) {
1582
            if ($active == 1) {
1583
                $event_title = LOG_USER_ENABLE;
1584
            } else {
1585
                $event_title = LOG_USER_DISABLE;
1586
            }
1587
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
1588
        }
1589
1590
        if (is_array($extra) && count($extra) > 0) {
1591
            $res = true;
1592
            foreach ($extra as $fname => $fvalue) {
1593
                $res = $res && self::update_extra_field_value(
1594
                    $user_id,
1595
                    $fname,
1596
                    $fvalue
1597
                );
1598
            }
1599
        }
1600
1601
        if (!empty($email) && $send_email) {
1602
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
1603
            $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('YourReg').' '.api_get_setting('siteName');
1604
            $sender_name = api_get_person_name(
1605
                api_get_setting('administratorName'),
1606
                api_get_setting('administratorSurname'),
1607
                null,
1608
                PERSON_NAME_EMAIL_ADDRESS
1609
            );
1610
            $email_admin = api_get_setting('emailAdministrator');
1611
            $url = api_get_path(WEB_PATH);
1612
            if (api_is_multiple_url_enabled()) {
1613
                $access_url_id = api_get_current_access_url_id();
1614
                if ($access_url_id != -1) {
1615
                    $url = api_get_access_url($access_url_id);
1616
                    $url = $url['url'];
1617
                }
1618
            }
1619
1620
            $tplContent = new Template(
1621
                null,
1622
                false,
1623
                false,
1624
                false,
1625
                false,
1626
                false
1627
            );
1628
            // variables for the default template
1629
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
1630
            $tplContent->assign('login_name', $username);
1631
1632
            $originalPassword = '';
1633
            if ($reset_password > 0) {
1634
                $originalPassword = stripslashes($original_password);
1635
            }
1636
            $tplContent->assign('original_password', $originalPassword);
1637
            $tplContent->assign('portal_url', $url);
1638
1639
            $layoutContent = $tplContent->get_template('mail/user_edit_content.tpl');
1640
            $emailBody = $tplContent->fetch($layoutContent);
1641
1642
            $mailTemplateManager = new MailTemplateManager();
1643
1644
            if (!empty($emailTemplate) &&
1645
                isset($emailTemplate['user_edit_content.tpl']) &&
1646
                !empty($emailTemplate['user_edit_content.tpl'])
1647
            ) {
1648
                $userInfo = api_get_user_info($user_id);
1649
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], $userInfo);
1650
            }
1651
1652
            $creatorInfo = api_get_user_info($creator_id);
1653
            $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
1654
1655
            api_mail_html(
1656
                $recipient_name,
1657
                $email,
1658
                $emailsubject,
1659
                $emailBody,
1660
                $sender_name,
1661
                $email_admin,
1662
                null,
1663
                null,
1664
                null,
1665
                null,
1666
                $creatorEmail
1667
            );
1668
        }
1669
1670
        if (!empty($hook)) {
1671
            $hook->setEventData(['user' => $user]);
1672
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_POST);
1673
        }
1674
1675
        $cacheAvailable = api_get_configuration_value('apc');
1676
        if ($cacheAvailable === true) {
1677
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1678
            if (apcu_exists($apcVar)) {
1679
                apcu_delete($apcVar);
1680
            }
1681
        }
1682
1683
        return $user->getId();
1684
    }
1685
1686
    /**
1687
     * Disables a user.
1688
     *
1689
     * @param int User id
1690
     *
1691
     * @return bool
1692
     *
1693
     * @uses \UserManager::change_active_state() to actually disable the user
1694
     * @assert (0) === false
1695
     */
1696
    public static function disable($user_id)
1697
    {
1698
        if (empty($user_id)) {
1699
            return false;
1700
        }
1701
        self::change_active_state($user_id, 0);
1702
1703
        return true;
1704
    }
1705
1706
    /**
1707
     * Enable a user.
1708
     *
1709
     * @param int User id
1710
     *
1711
     * @return bool
1712
     *
1713
     * @uses \UserManager::change_active_state() to actually disable the user
1714
     * @assert (0) === false
1715
     */
1716
    public static function enable($user_id)
1717
    {
1718
        if (empty($user_id)) {
1719
            return false;
1720
        }
1721
        self::change_active_state($user_id, 1);
1722
1723
        return true;
1724
    }
1725
1726
    /**
1727
     * Returns the user's id based on the original id and field name in
1728
     * the extra fields. Returns 0 if no user was found. This function is
1729
     * mostly useful in the context of a web services-based sinchronization.
1730
     *
1731
     * @param string Original user id
1732
     * @param string Original field name
1733
     *
1734
     * @return int User id
1735
     * @assert ('0','---') === 0
1736
     */
1737
    public static function get_user_id_from_original_id(
1738
        $original_user_id_value,
1739
        $original_user_id_name
1740
    ) {
1741
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1742
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1743
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1744
1745
        $original_user_id_name = Database::escape_string($original_user_id_name);
1746
        $original_user_id_value = Database::escape_string($original_user_id_value);
1747
1748
        $sql = "SELECT item_id as user_id
1749
                FROM $t_uf uf
1750
                INNER JOIN $t_ufv ufv
1751
                ON ufv.field_id = uf.id
1752
                WHERE
1753
                    variable = '$original_user_id_name' AND
1754
                    value = '$original_user_id_value' AND
1755
                    extra_field_type = $extraFieldType
1756
                ";
1757
        $res = Database::query($sql);
1758
        $row = Database::fetch_object($res);
1759
        if ($row) {
1760
            return $row->user_id;
1761
        }
1762
1763
        return 0;
1764
    }
1765
1766
    /**
1767
     * Check if a username is available.
1768
     *
1769
     * @param string $username the wanted username
1770
     *
1771
     * @return bool true if the wanted username is available
1772
     * @assert ('') === false
1773
     * @assert ('xyzxyzxyz') === true
1774
     */
1775
    public static function is_username_available($username)
1776
    {
1777
        if (empty($username)) {
1778
            return false;
1779
        }
1780
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1781
        $sql = "SELECT username FROM $table_user
1782
                WHERE username = '".Database::escape_string($username)."'";
1783
        $res = Database::query($sql);
1784
1785
        return Database::num_rows($res) == 0;
1786
    }
1787
1788
    /**
1789
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1790
     *
1791
     * @param string $firstname the first name of the user
1792
     * @param string $lastname  the last name of the user
1793
     *
1794
     * @return string suggests a username that contains only ASCII-letters and digits,
1795
     *                without check for uniqueness within the system
1796
     *
1797
     * @author Julio Montoya Armas
1798
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1799
     * @assert ('','') === false
1800
     * @assert ('a','b') === 'ab'
1801
     */
1802
    public static function create_username($firstname, $lastname)
1803
    {
1804
        if (empty($firstname) && empty($lastname)) {
1805
            return false;
1806
        }
1807
1808
        // The first letter only.
1809
        $firstname = api_substr(
1810
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1811
            0,
1812
            1
1813
        );
1814
        //Looking for a space in the lastname
1815
        $pos = api_strpos($lastname, ' ');
1816
        if ($pos !== false) {
1817
            $lastname = api_substr($lastname, 0, $pos);
1818
        }
1819
1820
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1821
        $username = $firstname.$lastname;
1822
        if (empty($username)) {
1823
            $username = 'user';
1824
        }
1825
1826
        $username = URLify::transliterate($username);
1827
1828
        return strtolower(substr($username, 0, USERNAME_MAX_LENGTH - 3));
1829
    }
1830
1831
    /**
1832
     * Creates a unique username, using:
1833
     * 1. the first name and the last name of a user;
1834
     * 2. an already created username but not checked for uniqueness yet.
1835
     *
1836
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1837
     *                          parameter is treated as username which is to be checked f
1838
     *                          or uniqueness and to be modified when it is necessary.
1839
     * @param string $lastname  the last name of the user
1840
     *
1841
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1842
     *                Note: When the method is called several times with same parameters,
1843
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1844
     *
1845
     * @author Ivan Tcholakov, 2009
1846
     */
1847
    public static function create_unique_username($firstname, $lastname = null)
1848
    {
1849
        if (is_null($lastname)) {
1850
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1851
            // For making this method tolerant of mistakes,
1852
            // let us transliterate and purify the suggested input username anyway.
1853
            // So, instead of the sentence $username = $firstname; we place the following:
1854
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1855
        } else {
1856
            $username = self::create_username($firstname, $lastname);
1857
        }
1858
        if (!self::is_username_available($username)) {
1859
            $i = 2;
1860
            $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1861
            while (!self::is_username_available($temp_username)) {
1862
                $i++;
1863
                $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1864
            }
1865
            $username = $temp_username;
1866
        }
1867
1868
        $username = URLify::transliterate($username);
1869
1870
        return $username;
1871
    }
1872
1873
    /**
1874
     * Modifies a given username accordingly to the specification for valid characters and length.
1875
     *
1876
     * @param $username string          The input username
1877
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1878
     *                     otherwise compliance may be partial. The default value is FALSE.
1879
     *
1880
     * @return string the resulting purified username
1881
     */
1882
    public static function purify_username($username, $strict = false)
1883
    {
1884
        if ($strict) {
1885
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1886
            // into ASCII letters in order they not to be totally removed.
1887
            // 2. Applying the strict purifier.
1888
            // 3. Length limitation.
1889
            if ('true' === api_get_setting('login_is_email')) {
1890
                $return = substr(preg_replace(USERNAME_PURIFIER_MAIL, '', $username), 0, USERNAME_MAX_LENGTH);
1891
            } else {
1892
                $return = substr(preg_replace(USERNAME_PURIFIER, '', $username), 0, USERNAME_MAX_LENGTH);
1893
            }
1894
1895
            return URLify::transliterate($return);
1896
        }
1897
1898
        // 1. Applying the shallow purifier.
1899
        // 2. Length limitation.
1900
        return substr(
1901
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1902
            0,
1903
            USERNAME_MAX_LENGTH
1904
        );
1905
    }
1906
1907
    /**
1908
     * Checks whether the user id exists in the database.
1909
     *
1910
     * @param int $userId User id
1911
     *
1912
     * @return bool True if user id was found, false otherwise
1913
     */
1914
    public static function is_user_id_valid($userId)
1915
    {
1916
        $resultData = Database::select(
1917
            'COUNT(1) AS count',
1918
            Database::get_main_table(TABLE_MAIN_USER),
1919
            [
1920
                'where' => ['id = ?' => (int) $userId],
1921
            ],
1922
            'first'
1923
        );
1924
1925
        if ($resultData === false) {
1926
            return false;
1927
        }
1928
1929
        return $resultData['count'] > 0;
1930
    }
1931
1932
    /**
1933
     * Checks whether a given username matches to the specification strictly.
1934
     * The empty username is assumed here as invalid.
1935
     * Mostly this function is to be used in the user interface built-in validation routines
1936
     * for providing feedback while usernames are enterd manually.
1937
     *
1938
     * @param string $username the input username
1939
     *
1940
     * @return bool returns TRUE if the username is valid, FALSE otherwise
1941
     */
1942
    public static function is_username_valid($username)
1943
    {
1944
        return !empty($username) && $username == self::purify_username($username, true);
1945
    }
1946
1947
    /**
1948
     * Checks whether a username is empty. If the username contains whitespace characters,
1949
     * such as spaces, tabulators, newlines, etc.,
1950
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
1951
     *
1952
     * @param string $username the given username
1953
     *
1954
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1955
     */
1956
    public static function is_username_empty($username)
1957
    {
1958
        return strlen(self::purify_username($username, false)) == 0;
1959
    }
1960
1961
    /**
1962
     * Checks whether a username is too long or not.
1963
     *
1964
     * @param string $username the given username, it should contain only ASCII-letters and digits
1965
     *
1966
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1967
     */
1968
    public static function is_username_too_long($username)
1969
    {
1970
        return strlen($username) > USERNAME_MAX_LENGTH;
1971
    }
1972
1973
    /**
1974
     * Get the users by ID.
1975
     *
1976
     * @param array  $ids    student ids
1977
     * @param string $active
1978
     * @param string $order
1979
     * @param string $limit
1980
     *
1981
     * @return array $result student information
1982
     */
1983
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
1984
    {
1985
        if (empty($ids)) {
1986
            return [];
1987
        }
1988
1989
        $ids = is_array($ids) ? $ids : [$ids];
1990
        $ids = array_map('intval', $ids);
1991
        $ids = implode(',', $ids);
1992
1993
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
1994
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
1995
        if (!is_null($active)) {
1996
            $sql .= ' AND active='.($active ? '1' : '0');
1997
        }
1998
1999
        if (!is_null($order)) {
2000
            $order = Database::escape_string($order);
2001
            $sql .= ' ORDER BY '.$order;
2002
        }
2003
2004
        if (!is_null($limit)) {
2005
            $limit = Database::escape_string($limit);
2006
            $sql .= ' LIMIT '.$limit;
2007
        }
2008
2009
        $rs = Database::query($sql);
2010
        $result = [];
2011
        while ($row = Database::fetch_array($rs)) {
2012
            $result[] = $row;
2013
        }
2014
2015
        return $result;
2016
    }
2017
2018
    /**
2019
     * Get a list of users of which the given conditions match with an = 'cond'.
2020
     *
2021
     * @param array $conditions a list of condition (example : status=>STUDENT)
2022
     * @param array $order_by   a list of fields on which sort
2023
     *
2024
     * @return array an array with all users of the platform
2025
     *
2026
     * @todo security filter order by
2027
     */
2028
    public static function get_user_list(
2029
        $conditions = [],
2030
        $order_by = [],
2031
        $limit_from = false,
2032
        $limit_to = false,
2033
        $idCampus = null
2034
    ) {
2035
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
2036
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2037
        $return_array = [];
2038
        $sql = "SELECT user.* FROM $user_table user ";
2039
2040
        if (api_is_multiple_url_enabled()) {
2041
            if ($idCampus) {
2042
                $urlId = $idCampus;
2043
            } else {
2044
                $urlId = api_get_current_access_url_id();
2045
            }
2046
            $sql .= " INNER JOIN $userUrlTable url_user
2047
                      ON (user.user_id = url_user.user_id)
2048
                      WHERE url_user.access_url_id = $urlId";
2049
        } else {
2050
            $sql .= " WHERE 1=1 ";
2051
        }
2052
2053
        if (count($conditions) > 0) {
2054
            foreach ($conditions as $field => $value) {
2055
                $field = Database::escape_string($field);
2056
                $value = Database::escape_string($value);
2057
                $sql .= " AND $field = '$value'";
2058
            }
2059
        }
2060
2061
        if (count($order_by) > 0) {
2062
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
2063
        }
2064
2065
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
2066
            $limit_from = (int) $limit_from;
2067
            $limit_to = (int) $limit_to;
2068
            $sql .= " LIMIT $limit_from, $limit_to";
2069
        }
2070
        $sql_result = Database::query($sql);
2071
        while ($result = Database::fetch_array($sql_result)) {
2072
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
2073
            $return_array[] = $result;
2074
        }
2075
2076
        return $return_array;
2077
    }
2078
2079
    public static function getUserListExtraConditions(
2080
        $conditions = [],
2081
        $order_by = [],
2082
        $limit_from = false,
2083
        $limit_to = false,
2084
        $idCampus = null,
2085
        $extraConditions = '',
2086
        $getCount = false
2087
    ) {
2088
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
2089
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2090
        $return_array = [];
2091
        $sql = "SELECT user.* FROM $user_table user ";
2092
2093
        if ($getCount) {
2094
            $sql = "SELECT count(user.id) count FROM $user_table user ";
2095
        }
2096
2097
        if (api_is_multiple_url_enabled()) {
2098
            if ($idCampus) {
2099
                $urlId = $idCampus;
2100
            } else {
2101
                $urlId = api_get_current_access_url_id();
2102
            }
2103
            $sql .= " INNER JOIN $userUrlTable url_user
2104
                      ON (user.user_id = url_user.user_id)
2105
                      WHERE url_user.access_url_id = $urlId";
2106
        } else {
2107
            $sql .= " WHERE 1=1 ";
2108
        }
2109
2110
        $sql .= " AND status <> ".ANONYMOUS." ";
2111
2112
        if (count($conditions) > 0) {
2113
            foreach ($conditions as $field => $value) {
2114
                $field = Database::escape_string($field);
2115
                $value = Database::escape_string($value);
2116
                $sql .= " AND $field = '$value'";
2117
            }
2118
        }
2119
2120
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
2121
2122
        if (!empty($order_by) && count($order_by) > 0) {
2123
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
2124
        }
2125
2126
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
2127
            $limit_from = (int) $limit_from;
2128
            $limit_to = (int) $limit_to;
2129
            $sql .= " LIMIT $limit_from, $limit_to";
2130
        }
2131
2132
        $sql_result = Database::query($sql);
2133
2134
        if ($getCount) {
2135
            $result = Database::fetch_array($sql_result);
2136
2137
            return $result['count'];
2138
        }
2139
2140
        while ($result = Database::fetch_array($sql_result)) {
2141
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
2142
            $return_array[] = $result;
2143
        }
2144
2145
        return $return_array;
2146
    }
2147
2148
    /**
2149
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
2150
     *
2151
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
2152
     * @param array  $order_by         a list of fields on which sort
2153
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
2154
     * @param string $condition        Whether we want the filters to be combined by AND or OR
2155
     * @param array  $onlyThisUserList
2156
     *
2157
     * @return array an array with all users of the platform
2158
     *
2159
     * @todo optional course code parameter, optional sorting parameters...
2160
     * @todo security filter order_by
2161
     */
2162
    public static function getUserListLike(
2163
        $conditions = [],
2164
        $order_by = [],
2165
        $simple_like = false,
2166
        $condition = 'AND',
2167
        $onlyThisUserList = []
2168
    ) {
2169
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
2170
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2171
        $return_array = [];
2172
        $sql_query = "SELECT user.id FROM $user_table user ";
2173
2174
        if (api_is_multiple_url_enabled()) {
2175
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
2176
        }
2177
2178
        $sql_query .= ' WHERE 1 = 1 ';
2179
        if (count($conditions) > 0) {
2180
            $temp_conditions = [];
2181
            foreach ($conditions as $field => $value) {
2182
                $field = Database::escape_string($field);
2183
                $value = Database::escape_string($value);
2184
                if ($simple_like) {
2185
                    $temp_conditions[] = $field." LIKE '$value%'";
2186
                } else {
2187
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
2188
                }
2189
            }
2190
            if (!empty($temp_conditions)) {
2191
                $sql_query .= ' AND '.implode(' '.$condition.' ', $temp_conditions);
2192
            }
2193
2194
            if (api_is_multiple_url_enabled()) {
2195
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
2196
            }
2197
        } else {
2198
            if (api_is_multiple_url_enabled()) {
2199
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
2200
            }
2201
        }
2202
2203
        if (!empty($onlyThisUserList)) {
2204
            $onlyThisUserListToString = implode("','", $onlyThisUserList);
2205
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
2206
        }
2207
2208
        if (count($order_by) > 0) {
2209
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
2210
        }
2211
2212
        $sql_result = Database::query($sql_query);
2213
        while ($result = Database::fetch_array($sql_result)) {
2214
            $userInfo = api_get_user_info($result['id']);
2215
            $return_array[] = $userInfo;
2216
        }
2217
2218
        return $return_array;
2219
    }
2220
2221
    /**
2222
     * Get user picture URL or path from user ID (returns an array).
2223
     * The return format is a complete path, enabling recovery of the directory
2224
     * with dirname() or the file with basename(). This also works for the
2225
     * functions dealing with the user's productions, as they are located in
2226
     * the same directory.
2227
     *
2228
     * @param int    $id       User ID
2229
     * @param string $type     Type of path to return (can be 'system', 'web')
2230
     * @param array  $userInfo user information to avoid query the DB
2231
     *                         returns the /main/img/unknown.jpg image set it at true
2232
     *
2233
     * @return array Array of 2 elements: 'dir' and 'file' which contain
2234
     *               the dir and file as the name implies if image does not exist it will
2235
     *               return the unknow image if anonymous parameter is true if not it returns an empty array
2236
     */
2237
    public static function get_user_picture_path_by_id(
2238
        $id,
2239
        $type = 'web',
2240
        $userInfo = []
2241
    ) {
2242
        switch ($type) {
2243
            case 'system': // Base: absolute system path.
2244
                $base = api_get_path(SYS_CODE_PATH);
2245
                break;
2246
            case 'web': // Base: absolute web path.
2247
            default:
2248
                $base = api_get_path(WEB_CODE_PATH);
2249
                break;
2250
        }
2251
2252
        $anonymousPath = [
2253
            'dir' => $base.'img/',
2254
            'file' => 'unknown.jpg',
2255
            'email' => '',
2256
        ];
2257
2258
        if (empty($id) || empty($type)) {
2259
            return $anonymousPath;
2260
        }
2261
2262
        $id = (int) $id;
2263
        if (empty($userInfo)) {
2264
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
2265
            $sql = "SELECT email, picture_uri FROM $user_table
2266
                    WHERE id = ".$id;
2267
            $res = Database::query($sql);
2268
2269
            if (!Database::num_rows($res)) {
2270
                return $anonymousPath;
2271
            }
2272
            $user = Database::fetch_array($res);
2273
            if (empty($user['picture_uri'])) {
2274
                return $anonymousPath;
2275
            }
2276
        } else {
2277
            $user = $userInfo;
2278
        }
2279
2280
        $pictureFilename = trim($user['picture_uri']);
2281
2282
        $dir = self::getUserPathById($id, $type);
2283
2284
        return [
2285
            'dir' => $dir,
2286
            'file' => $pictureFilename,
2287
            'email' => $user['email'],
2288
        ];
2289
    }
2290
2291
    /**
2292
     * *** READ BEFORE REVIEW THIS FUNCTION ***
2293
     * This function is a exact copy from get_user_picture_path_by_id() and it was create it to avoid
2294
     * a recursive calls for get_user_picture_path_by_id() in another functions when you update a user picture
2295
     * in same script, so you can find this function usage in update_user_picture() function.
2296
     *
2297
     * @param int    $id       User ID
2298
     * @param string $type     Type of path to return (can be 'system', 'web')
2299
     * @param array  $userInfo user information to avoid query the DB
2300
     *                         returns the /main/img/unknown.jpg image set it at true
2301
     *
2302
     * @return array Array of 2 elements: 'dir' and 'file' which contain
2303
     *               the dir and file as the name implies if image does not exist it will
2304
     *               return the unknown image if anonymous parameter is true if not it returns an empty array
2305
     */
2306
    public static function getUserPicturePathById($id, $type = 'web', $userInfo = [])
2307
    {
2308
        switch ($type) {
2309
            case 'system': // Base: absolute system path.
2310
                $base = api_get_path(SYS_CODE_PATH);
2311
                break;
2312
            case 'web': // Base: absolute web path.
2313
            default:
2314
                $base = api_get_path(WEB_CODE_PATH);
2315
                break;
2316
        }
2317
2318
        $anonymousPath = [
2319
            'dir' => $base.'img/',
2320
            'file' => 'unknown.jpg',
2321
            'email' => '',
2322
        ];
2323
2324
        if (empty($id) || empty($type)) {
2325
            return $anonymousPath;
2326
        }
2327
2328
        $id = (int) $id;
2329
        if (empty($userInfo)) {
2330
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
2331
            $sql = "SELECT email, picture_uri FROM $user_table WHERE id = $id";
2332
            $res = Database::query($sql);
2333
2334
            if (!Database::num_rows($res)) {
2335
                return $anonymousPath;
2336
            }
2337
            $user = Database::fetch_array($res);
2338
2339
            if (empty($user['picture_uri'])) {
2340
                return $anonymousPath;
2341
            }
2342
        } else {
2343
            $user = $userInfo;
2344
        }
2345
2346
        $pictureFilename = trim($user['picture_uri']);
2347
        $dir = self::getUserPathById($id, $type);
2348
2349
        return [
2350
            'dir' => $dir,
2351
            'file' => $pictureFilename,
2352
            'email' => $user['email'],
2353
        ];
2354
    }
2355
2356
    /**
2357
     * Get user path from user ID (returns an array).
2358
     * The return format is a complete path to a folder ending with "/"
2359
     * In case the first level of subdirectory of users/ does not exist, the
2360
     * function will attempt to create it. Probably not the right place to do it
2361
     * but at least it avoids headaches in many other places.
2362
     *
2363
     * @param int    $id   User ID
2364
     * @param string $type Type of path to return (can be 'system', 'web', 'last')
2365
     *
2366
     * @return string User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
2367
     */
2368
    public static function getUserPathById($id, $type)
2369
    {
2370
        $id = (int) $id;
2371
        if (!$id) {
2372
            return null;
2373
        }
2374
2375
        $userPath = "users/$id/";
2376
        if (api_get_setting('split_users_upload_directory') === 'true') {
2377
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
2378
            // In exceptional cases, on some portals, the intermediate base user
2379
            // directory might not have been created. Make sure it is before
2380
            // going further.
2381
2382
            $rootPath = api_get_path(SYS_UPLOAD_PATH).'users/'.substr((string) $id, 0, 1);
2383
            if (!is_dir($rootPath)) {
2384
                $perm = api_get_permissions_for_new_directories();
2385
                try {
2386
                    mkdir($rootPath, $perm);
2387
                } catch (Exception $e) {
2388
                    error_log($e->getMessage());
2389
                }
2390
            }
2391
        }
2392
        switch ($type) {
2393
            case 'system': // Base: absolute system path.
2394
                $userPath = api_get_path(SYS_UPLOAD_PATH).$userPath;
2395
                break;
2396
            case 'web': // Base: absolute web path.
2397
                $userPath = api_get_path(WEB_UPLOAD_PATH).$userPath;
2398
                break;
2399
            case 'last': // Only the last part starting with users/
2400
                break;
2401
        }
2402
2403
        return $userPath;
2404
    }
2405
2406
    /**
2407
     * Gets the current user image.
2408
     *
2409
     * @param string $user_id
2410
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
2411
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
2412
     * @param bool   $addRandomId
2413
     * @param array  $userInfo    to avoid query the DB
2414
     *
2415
     * @return string
2416
     */
2417
    public static function getUserPicture(
2418
        $user_id,
2419
        $size = USER_IMAGE_SIZE_MEDIUM,
2420
        $addRandomId = true,
2421
        $userInfo = []
2422
    ) {
2423
        // Make sure userInfo is defined. Otherwise, define it!
2424
        if (empty($userInfo) || !is_array($userInfo) || count($userInfo) == 0) {
2425
            if (empty($user_id)) {
2426
                return '';
2427
            } else {
2428
                $userInfo = api_get_user_info($user_id);
2429
            }
2430
        }
2431
2432
        $imageWebPath = self::get_user_picture_path_by_id(
2433
            $user_id,
2434
            'web',
2435
            $userInfo
2436
        );
2437
        $pictureWebFile = $imageWebPath['file'];
2438
        $pictureWebDir = $imageWebPath['dir'];
2439
2440
        $pictureAnonymousSize = '128';
2441
        $gravatarSize = 22;
2442
        $realSizeName = 'small_';
2443
2444
        switch ($size) {
2445
            case USER_IMAGE_SIZE_SMALL:
2446
                $pictureAnonymousSize = '32';
2447
                $realSizeName = 'small_';
2448
                $gravatarSize = 32;
2449
                break;
2450
            case USER_IMAGE_SIZE_MEDIUM:
2451
                $pictureAnonymousSize = '64';
2452
                $realSizeName = 'medium_';
2453
                $gravatarSize = 64;
2454
                break;
2455
            case USER_IMAGE_SIZE_ORIGINAL:
2456
                $pictureAnonymousSize = '128';
2457
                $realSizeName = '';
2458
                $gravatarSize = 128;
2459
                break;
2460
            case USER_IMAGE_SIZE_BIG:
2461
                $pictureAnonymousSize = '128';
2462
                $realSizeName = 'big_';
2463
                $gravatarSize = 128;
2464
                break;
2465
        }
2466
2467
        $gravatarEnabled = api_get_setting('gravatar_enabled');
2468
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
2469
        if ($pictureWebFile == 'unknown.jpg' || empty($pictureWebFile)) {
2470
            if ($gravatarEnabled === 'true') {
2471
                $file = self::getGravatar(
2472
                    $imageWebPath['email'],
2473
                    $gravatarSize,
2474
                    api_get_setting('gravatar_type')
2475
                );
2476
2477
                if ($addRandomId) {
2478
                    $file .= '&rand='.uniqid();
2479
                }
2480
2481
                return $file;
2482
            }
2483
2484
            return $anonymousPath;
2485
        }
2486
2487
        $pictureSysPath = self::get_user_picture_path_by_id($user_id, 'system');
2488
        $file = $pictureSysPath['dir'].$realSizeName.$pictureWebFile;
2489
        $picture = '';
2490
        if (file_exists($file)) {
2491
            $picture = $pictureWebDir.$realSizeName.$pictureWebFile;
2492
        } else {
2493
            $file = $pictureSysPath['dir'].$pictureWebFile;
2494
            if (file_exists($file) && !is_dir($file)) {
2495
                $picture = $pictureWebFile['dir'].$pictureWebFile;
2496
            }
2497
        }
2498
2499
        if (empty($picture)) {
2500
            return $anonymousPath;
2501
        }
2502
2503
        if ($addRandomId) {
2504
            $picture .= '?rand='.uniqid();
2505
        }
2506
2507
        return $picture;
2508
    }
2509
2510
    /**
2511
     * Creates new user photos in various sizes of a user, or deletes user photos.
2512
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2513
     *
2514
     * @param int    $user_id the user internal identification number
2515
     * @param string $file    The common file name for the newly created photos.
2516
     *                        It will be checked and modified for compatibility with the file system.
2517
     *                        If full name is provided, path component is ignored.
2518
     *                        If an empty name is provided, then old user photos are deleted only,
2519
     *
2520
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
2521
     *
2522
     * @param string $source_file    the full system name of the image from which user photos will be created
2523
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
2524
     *
2525
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
2526
     *              When deletion is requested returns empty string.
2527
     *              In case of internal error or negative validation returns FALSE.
2528
     */
2529
    public static function update_user_picture(
2530
        $user_id,
2531
        $file = null,
2532
        $source_file = null,
2533
        $cropParameters = ''
2534
    ) {
2535
        if (empty($user_id)) {
2536
            return false;
2537
        }
2538
        $delete = empty($file);
2539
        if (empty($source_file)) {
2540
            $source_file = $file;
2541
        }
2542
2543
        // User-reserved directory where photos have to be placed.
2544
        $path_info = self::getUserPicturePathById($user_id, 'system');
2545
        $path = $path_info['dir'];
2546
2547
        // If this directory does not exist - we create it.
2548
        if (!file_exists($path)) {
2549
            mkdir($path, api_get_permissions_for_new_directories(), true);
2550
        }
2551
2552
        // The old photos (if any).
2553
        $old_file = $path_info['file'];
2554
2555
        // Let us delete them.
2556
        if ($old_file != 'unknown.jpg') {
2557
            if (KEEP_THE_OLD_IMAGE_AFTER_CHANGE) {
0 ignored issues
show
The constant KEEP_THE_OLD_IMAGE_AFTER_CHANGE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2558
                $prefix = 'saved_'.date('Y_m_d_H_i_s').'_'.uniqid('').'_';
2559
                @rename($path.'small_'.$old_file, $path.$prefix.'small_'.$old_file);
2560
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
2561
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
2562
                @rename($path.$old_file, $path.$prefix.$old_file);
2563
            } else {
2564
                @unlink($path.'small_'.$old_file);
2565
                @unlink($path.'medium_'.$old_file);
2566
                @unlink($path.'big_'.$old_file);
2567
                @unlink($path.$old_file);
2568
            }
2569
        }
2570
2571
        // Exit if only deletion has been requested. Return an empty picture name.
2572
        if ($delete) {
2573
            return '';
2574
        }
2575
2576
        // Validation 2.
2577
        $allowed_types = api_get_supported_image_extensions();
2578
        $file = str_replace('\\', '/', $file);
2579
        $filename = (($pos = strrpos($file, '/')) !== false) ? substr($file, $pos + 1) : $file;
2580
        $extension = strtolower(substr(strrchr($filename, '.'), 1));
2581
        if (!in_array($extension, $allowed_types)) {
2582
            return false;
2583
        }
2584
2585
        // This is the common name for the new photos.
2586
        if (KEEP_THE_NAME_WHEN_CHANGE_IMAGE && $old_file != 'unknown.jpg') {
2587
            $old_extension = strtolower(substr(strrchr($old_file, '.'), 1));
2588
            $filename = in_array($old_extension, $allowed_types) ? substr($old_file, 0, -strlen($old_extension)) : $old_file;
2589
            $filename = (substr($filename, -1) == '.') ? $filename.$extension : $filename.'.'.$extension;
2590
        } else {
2591
            $filename = api_replace_dangerous_char($filename);
2592
            if (PREFIX_IMAGE_FILENAME_WITH_UID) {
0 ignored issues
show
The constant PREFIX_IMAGE_FILENAME_WITH_UID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2593
                $filename = uniqid('').'_'.$filename;
2594
            }
2595
            // We always prefix user photos with user ids, so on setting
2596
            // api_get_setting('split_users_upload_directory') === 'true'
2597
            // the correspondent directories to be found successfully.
2598
            $filename = $user_id.'_'.$filename;
2599
        }
2600
2601
        if (!file_exists($source_file)) {
2602
            return false;
2603
        }
2604
2605
        $mimeContentType = mime_content_type($source_file);
2606
        if (false === strpos($mimeContentType, 'image')) {
2607
            return false;
2608
        }
2609
2610
        //Crop the image to adjust 1:1 ratio
2611
        $image = new Image($source_file);
2612
        $image->crop($cropParameters);
2613
2614
        // Storing the new photos in 4 versions with various sizes.
2615
        $userPath = self::getUserPathById($user_id, 'system');
2616
2617
        // If this path does not exist - we create it.
2618
        if (!file_exists($userPath)) {
2619
            mkdir($userPath, api_get_permissions_for_new_directories(), true);
2620
        }
2621
        $small = new Image($source_file);
2622
        $small->resize(32);
2623
        $small->send_image($userPath.'small_'.$filename);
2624
        $medium = new Image($source_file);
2625
        $medium->resize(85);
2626
        $medium->send_image($userPath.'medium_'.$filename);
2627
        $normal = new Image($source_file);
2628
        $normal->resize(200);
2629
        $normal->send_image($userPath.$filename);
2630
2631
        $big = new Image($source_file); // This is the original picture.
2632
        $big->send_image($userPath.'big_'.$filename);
2633
2634
        $result = $small && $medium && $normal && $big;
2635
2636
        return $result ? $filename : false;
2637
    }
2638
2639
    /**
2640
     * Update User extra field file type into {user_folder}/{$extra_field}.
2641
     *
2642
     * @param int    $user_id     The user internal identification number
2643
     * @param string $extra_field The $extra_field The extra field name
2644
     * @param null   $file        The filename
2645
     * @param null   $source_file The temporal filename
2646
     *
2647
     * @return bool|null return filename if success, but false
2648
     */
2649
    public static function update_user_extra_file(
2650
        $user_id,
2651
        $extra_field = '',
2652
        $file = null,
2653
        $source_file = null
2654
    ) {
2655
        // Add Filter
2656
        $source_file = Security::filter_filename($source_file);
2657
        $file = Security::filter_filename($file);
2658
2659
        if (empty($user_id)) {
2660
            return false;
2661
        }
2662
2663
        if (empty($source_file)) {
2664
            $source_file = $file;
2665
        }
2666
2667
        // User-reserved directory where extra file have to be placed.
2668
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2669
        $path = $path_info['dir'];
2670
        if (!empty($extra_field)) {
2671
            $path .= $extra_field.'/';
2672
        }
2673
        // If this directory does not exist - we create it.
2674
        if (!file_exists($path)) {
2675
            @mkdir($path, api_get_permissions_for_new_directories(), true);
2676
        }
2677
2678
        if (filter_extension($file)) {
2679
            if (@move_uploaded_file($source_file, $path.$file)) {
2680
                if ($extra_field) {
2681
                    return $extra_field.'/'.$file;
2682
                } else {
2683
                    return $file;
2684
                }
2685
            }
2686
        }
2687
2688
        return false; // this should be returned if anything went wrong with the upload
2689
    }
2690
2691
    /**
2692
     * Deletes user photos.
2693
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2694
     *
2695
     * @param int $userId the user internal identification number
2696
     *
2697
     * @return mixed returns empty string on success, FALSE on error
2698
     */
2699
    public static function deleteUserPicture($userId)
2700
    {
2701
        return self::update_user_picture($userId);
2702
    }
2703
2704
    /**
2705
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
2706
     * doesn't have any.
2707
     *
2708
     * If there has been a request to remove a production, the function will return
2709
     * without building the list unless forced to do so by the optional second
2710
     * parameter. This increases performance by avoiding to read through the
2711
     * productions on the filesystem before the removal request has been carried
2712
     * out because they'll have to be re-read afterwards anyway.
2713
     *
2714
     * @param int  $user_id    User id
2715
     * @param bool $force      Optional parameter to force building after a removal request
2716
     * @param bool $showDelete
2717
     *
2718
     * @return string A string containing the XHTML code to display the production list, or FALSE
2719
     */
2720
    public static function build_production_list($user_id, $force = false, $showDelete = false)
2721
    {
2722
        if (!$force && !empty($_POST['remove_production'])) {
2723
            return true; // postpone reading from the filesystem
2724
        }
2725
2726
        $productions = self::get_user_productions($user_id);
2727
2728
        if (empty($productions)) {
2729
            return false;
2730
        }
2731
2732
        $production_dir = self::getUserPathById($user_id, 'web');
2733
        $del_image = Display::returnIconPath('delete.png');
2734
        $add_image = Display::returnIconPath('archive.png');
2735
        $del_text = get_lang('Delete');
2736
        $production_list = '';
2737
        if (count($productions) > 0) {
2738
            $production_list = '<div class="files-production"><ul id="productions">';
2739
            foreach ($productions as $file) {
2740
                $production_list .= '<li>
2741
                    <img src="'.$add_image.'" />
2742
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2743
                        '.htmlentities($file).'
2744
                    </a>';
2745
                if ($showDelete) {
2746
                    $production_list .= '&nbsp;&nbsp;
2747
                        <input
2748
                            style="width:16px;"
2749
                            type="image"
2750
                            name="remove_production['.urlencode($file).']"
2751
                            src="'.$del_image.'"
2752
                            alt="'.$del_text.'"
2753
                            title="'.$del_text.' '.htmlentities($file).'"
2754
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2755
                }
2756
            }
2757
            $production_list .= '</ul></div>';
2758
        }
2759
2760
        return $production_list;
2761
    }
2762
2763
    /**
2764
     * Returns an array with the user's productions.
2765
     *
2766
     * @param int $user_id User id
2767
     *
2768
     * @return array An array containing the user's productions
2769
     */
2770
    public static function get_user_productions($user_id)
2771
    {
2772
        $production_repository = self::getUserPathById($user_id, 'system');
2773
        $productions = [];
2774
2775
        if (is_dir($production_repository)) {
2776
            $handle = opendir($production_repository);
2777
            while ($file = readdir($handle)) {
2778
                if ($file == '.' ||
2779
                    $file == '..' ||
2780
                    $file == '.htaccess' ||
2781
                    is_dir($production_repository.$file)
2782
                ) {
2783
                    // skip current/parent directory and .htaccess
2784
                    continue;
2785
                }
2786
2787
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2788
                    // User's photos should not be listed as productions.
2789
                    continue;
2790
                }
2791
                $productions[] = $file;
2792
            }
2793
        }
2794
2795
        return $productions;
2796
    }
2797
2798
    /**
2799
     * Remove a user production.
2800
     *
2801
     * @param int    $user_id    User id
2802
     * @param string $production The production to remove
2803
     *
2804
     * @return bool
2805
     */
2806
    public static function remove_user_production($user_id, $production)
2807
    {
2808
        $production_path = self::get_user_picture_path_by_id($user_id, 'system');
2809
        $production_file = $production_path['dir'].$production;
2810
        if (is_file($production_file)) {
2811
            unlink($production_file);
2812
2813
            return true;
2814
        }
2815
2816
        return false;
2817
    }
2818
2819
    /**
2820
     * Update an extra field value for a given user.
2821
     *
2822
     * @param int    $userId   User ID
2823
     * @param string $variable Field variable name
2824
     * @param string $value    Field value
2825
     *
2826
     * @return bool true if field updated, false otherwise
2827
     */
2828
    public static function update_extra_field_value($userId, $variable, $value = '')
2829
    {
2830
        $extraFieldValue = new ExtraFieldValue('user');
2831
        $params = [
2832
            'item_id' => $userId,
2833
            'variable' => $variable,
2834
            'value' => $value,
2835
        ];
2836
2837
        return $extraFieldValue->save($params);
2838
    }
2839
2840
    /**
2841
     * Get an array of extra fields with field details (type, default value and options).
2842
     *
2843
     * @param    int    Offset (from which row)
2844
     * @param    int    Number of items
2845
     * @param    int    Column on which sorting is made
2846
     * @param    string    Sorting direction
2847
     * @param    bool    Optional. Whether we get all the fields or just the visible ones
2848
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2849
     *
2850
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2851
     */
2852
    public static function get_extra_fields(
2853
        $from = 0,
2854
        $number_of_items = 0,
2855
        $column = 5,
2856
        $direction = 'ASC',
2857
        $all_visibility = true,
2858
        $field_filter = null
2859
    ) {
2860
        $fields = [];
2861
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2862
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2863
        $columns = [
2864
            'id',
2865
            'variable',
2866
            'field_type',
2867
            'display_text',
2868
            'default_value',
2869
            'field_order',
2870
            'filter',
2871
        ];
2872
        $column = (int) $column;
2873
        $sort_direction = '';
2874
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2875
            $sort_direction = strtoupper($direction);
2876
        }
2877
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2878
        $sqlf = "SELECT * FROM $t_uf WHERE extra_field_type = $extraFieldType ";
2879
        if (!$all_visibility) {
2880
            $sqlf .= " AND visible_to_self = 1 ";
2881
        }
2882
        if (!is_null($field_filter)) {
2883
            $field_filter = (int) $field_filter;
2884
            $sqlf .= " AND filter = $field_filter ";
2885
        }
2886
        $sqlf .= " ORDER BY `".$columns[$column]."` $sort_direction ";
2887
        if ($number_of_items != 0) {
2888
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2889
        }
2890
        $resf = Database::query($sqlf);
2891
        if (Database::num_rows($resf) > 0) {
2892
            while ($rowf = Database::fetch_array($resf)) {
2893
                $fields[$rowf['id']] = [
2894
                    0 => $rowf['id'],
2895
                    1 => $rowf['variable'],
2896
                    2 => $rowf['field_type'],
2897
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2898
                    4 => $rowf['default_value'],
2899
                    5 => $rowf['field_order'],
2900
                    6 => $rowf['visible_to_self'],
2901
                    7 => $rowf['changeable'],
2902
                    8 => $rowf['filter'],
2903
                    9 => [],
2904
                    10 => '<a name="'.$rowf['id'].'"></a>',
2905
                ];
2906
2907
                $sqlo = "SELECT * FROM $t_ufo
2908
                         WHERE field_id = ".$rowf['id']."
2909
                         ORDER BY option_order ASC";
2910
                $reso = Database::query($sqlo);
2911
                if (Database::num_rows($reso) > 0) {
2912
                    while ($rowo = Database::fetch_array($reso)) {
2913
                        $fields[$rowf['id']][9][$rowo['id']] = [
2914
                            0 => $rowo['id'],
2915
                            1 => $rowo['option_value'],
2916
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2917
                            3 => $rowo['option_order'],
2918
                        ];
2919
                    }
2920
                }
2921
            }
2922
        }
2923
2924
        return $fields;
2925
    }
2926
2927
    /**
2928
     * Build a list of extra file already uploaded in $user_folder/{$extra_field}/.
2929
     *
2930
     * @param $user_id
2931
     * @param $extra_field
2932
     * @param bool $force
2933
     * @param bool $showDelete
2934
     *
2935
     * @return bool|string
2936
     */
2937
    public static function build_user_extra_file_list(
2938
        $user_id,
2939
        $extra_field,
2940
        $force = false,
2941
        $showDelete = false
2942
    ) {
2943
        if (!$force && !empty($_POST['remove_'.$extra_field])) {
2944
            return true; // postpone reading from the filesystem
2945
        }
2946
2947
        $extra_files = self::get_user_extra_files($user_id, $extra_field);
2948
        if (empty($extra_files)) {
2949
            return false;
2950
        }
2951
2952
        $path_info = self::get_user_picture_path_by_id($user_id, 'web');
2953
        $path = $path_info['dir'];
2954
        $del_image = Display::returnIconPath('delete.png');
2955
2956
        $del_text = get_lang('Delete');
2957
        $extra_file_list = '';
2958
        if (count($extra_files) > 0) {
2959
            $extra_file_list = '<div class="files-production"><ul id="productions">';
2960
            foreach ($extra_files as $file) {
2961
                $filename = substr($file, strlen($extra_field) + 1);
2962
                $extra_file_list .= '<li>'.Display::return_icon('archive.png').
2963
                    '<a href="'.$path.$extra_field.'/'.urlencode($filename).'" target="_blank">
2964
                        '.htmlentities($filename).
2965
                    '</a> ';
2966
                if ($showDelete) {
2967
                    $extra_file_list .= '<input
2968
                        style="width:16px;"
2969
                        type="image"
2970
                        name="remove_extra_'.$extra_field.'['.urlencode($file).']"
2971
                        src="'.$del_image.'"
2972
                        alt="'.$del_text.'"
2973
                        title="'.$del_text.' '.htmlentities($filename).'"
2974
                        onclick="javascript: return confirmation(\''.htmlentities($filename).'\');" /></li>';
2975
                }
2976
            }
2977
            $extra_file_list .= '</ul></div>';
2978
        }
2979
2980
        return $extra_file_list;
2981
    }
2982
2983
    /**
2984
     * Get valid filenames in $user_folder/{$extra_field}/.
2985
     *
2986
     * @param $user_id
2987
     * @param $extra_field
2988
     * @param bool $full_path
2989
     *
2990
     * @return array
2991
     */
2992
    public static function get_user_extra_files($user_id, $extra_field, $full_path = false)
2993
    {
2994
        if (!$full_path) {
2995
            // Nothing to do
2996
        } else {
2997
            $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2998
            $path = $path_info['dir'];
2999
        }
3000
        $extra_data = self::get_extra_user_data_by_field($user_id, $extra_field);
3001
        $extra_files = $extra_data[$extra_field];
3002
3003
        $files = [];
3004
        if (is_array($extra_files)) {
3005
            foreach ($extra_files as $key => $value) {
3006
                if (!$full_path) {
3007
                    // Relative path from user folder
3008
                    $files[] = $value;
3009
                } else {
3010
                    $files[] = $path.$value;
3011
                }
3012
            }
3013
        } elseif (!empty($extra_files)) {
3014
            if (!$full_path) {
3015
                // Relative path from user folder
3016
                $files[] = $extra_files;
3017
            } else {
3018
                $files[] = $path.$extra_files;
3019
            }
3020
        }
3021
3022
        return $files; // can be an empty array
3023
    }
3024
3025
    /**
3026
     * Remove an {$extra_file} from the user folder $user_folder/{$extra_field}/.
3027
     *
3028
     * @param int    $user_id
3029
     * @param string $extra_field
3030
     * @param string $extra_file
3031
     *
3032
     * @return bool
3033
     */
3034
    public static function remove_user_extra_file($user_id, $extra_field, $extra_file)
3035
    {
3036
        $extra_file = Security::filter_filename($extra_file);
3037
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
3038
        if (strpos($extra_file, $extra_field) !== false) {
3039
            $path_extra_file = $path_info['dir'].$extra_file;
3040
        } else {
3041
            $path_extra_file = $path_info['dir'].$extra_field.'/'.$extra_file;
3042
        }
3043
        if (is_file($path_extra_file)) {
3044
            unlink($path_extra_file);
3045
3046
            return true;
3047
        }
3048
3049
        return false;
3050
    }
3051
3052
    /**
3053
     * Creates a new extra field.
3054
     *
3055
     * @param string $variable    Field's internal variable name
3056
     * @param int    $fieldType   Field's type
3057
     * @param string $displayText Field's language var name
3058
     * @param string $default     Field's default value
3059
     *
3060
     * @return int
3061
     */
3062
    public static function create_extra_field(
3063
        $variable,
3064
        $fieldType,
3065
        $displayText,
3066
        $default
3067
    ) {
3068
        $extraField = new ExtraField('user');
3069
        $params = [
3070
            'variable' => $variable,
3071
            'field_type' => $fieldType,
3072
            'display_text' => $displayText,
3073
            'default_value' => $default,
3074
        ];
3075
3076
        return $extraField->save($params);
3077
    }
3078
3079
    /**
3080
     * Check if a field is available.
3081
     *
3082
     * @param string $variable
3083
     *
3084
     * @return bool
3085
     */
3086
    public static function is_extra_field_available($variable)
3087
    {
3088
        $extraField = new ExtraField('user');
3089
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
3090
3091
        return !empty($data) ? true : false;
3092
    }
3093
3094
    /**
3095
     * Gets user extra fields data.
3096
     *
3097
     * @param    int    User ID
3098
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
3099
     * @param    bool    Whether to return invisible fields as well
3100
     * @param    bool    Whether to split multiple-selection fields or not
3101
     *
3102
     * @return array Array of fields => value for the given user
3103
     */
3104
    public static function get_extra_user_data(
3105
        $user_id,
3106
        $prefix = false,
3107
        $allVisibility = true,
3108
        $splitMultiple = false,
3109
        $fieldFilter = null
3110
    ) {
3111
        $user_id = (int) $user_id;
3112
3113
        if (empty($user_id)) {
3114
            return [];
3115
        }
3116
3117
        $extra_data = [];
3118
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
3119
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3120
        $user_id = (int) $user_id;
3121
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
3122
                FROM $t_uf f
3123
                WHERE
3124
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
3125
                ";
3126
        $filter_cond = '';
3127
3128
        if (!$allVisibility) {
3129
            if (isset($fieldFilter)) {
3130
                $fieldFilter = (int) $fieldFilter;
3131
                $filter_cond .= " AND filter = $fieldFilter ";
3132
            }
3133
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
3134
        } else {
3135
            if (isset($fieldFilter)) {
3136
                $fieldFilter = (int) $fieldFilter;
3137
                $sql .= " AND filter = $fieldFilter ";
3138
            }
3139
        }
3140
3141
        $sql .= ' ORDER BY f.field_order';
3142
3143
        $res = Database::query($sql);
3144
        if (Database::num_rows($res) > 0) {
3145
            while ($row = Database::fetch_array($res)) {
3146
                if ($row['type'] == self::USER_FIELD_TYPE_TAG) {
3147
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
3148
                    $extra_data['extra_'.$row['fvar']] = $tags;
3149
                } else {
3150
                    $sqlu = "SELECT value as fval
3151
                            FROM $t_ufv
3152
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
3153
                    $resu = Database::query($sqlu);
3154
                    // get default value
3155
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
3156
                               WHERE id=".$row['id'];
3157
                    $res_df = Database::query($sql_df);
3158
3159
                    if (Database::num_rows($resu) > 0) {
3160
                        $rowu = Database::fetch_array($resu);
3161
                        $fval = $rowu['fval'];
3162
                        if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
3163
                            $fval = explode(';', $rowu['fval']);
3164
                        }
3165
                    } else {
3166
                        $row_df = Database::fetch_array($res_df);
3167
                        $fval = $row_df['fval_df'];
3168
                    }
3169
                    // We get here (and fill the $extra_data array) even if there
3170
                    // is no user with data (we fill it with default values)
3171
                    if ($prefix) {
3172
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
3173
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
3174
                        } else {
3175
                            $extra_data['extra_'.$row['fvar']] = $fval;
3176
                        }
3177
                    } else {
3178
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
3179
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
3180
                        } else {
3181
                            $extra_data[$row['fvar']] = $fval;
3182
                        }
3183
                    }
3184
                }
3185
            }
3186
        }
3187
3188
        return $extra_data;
3189
    }
3190
3191
    /**
3192
     * Get extra user data by field.
3193
     *
3194
     * @param int    user ID
3195
     * @param string the internal variable name of the field
3196
     *
3197
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
3198
     */
3199
    public static function get_extra_user_data_by_field(
3200
        $user_id,
3201
        $field_variable,
3202
        $prefix = false,
3203
        $all_visibility = true,
3204
        $splitmultiple = false
3205
    ) {
3206
        $user_id = (int) $user_id;
3207
3208
        if (empty($user_id)) {
3209
            return [];
3210
        }
3211
3212
        $extra_data = [];
3213
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
3214
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3215
3216
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
3217
                FROM $t_uf f
3218
                WHERE f.variable = '$field_variable' ";
3219
3220
        if (!$all_visibility) {
3221
            $sql .= " AND f.visible_to_self = 1 ";
3222
        }
3223
3224
        $sql .= " AND extra_field_type = ".EntityExtraField::USER_FIELD_TYPE;
3225
        $sql .= " ORDER BY f.field_order ";
3226
3227
        $res = Database::query($sql);
3228
        if (Database::num_rows($res) > 0) {
3229
            while ($row = Database::fetch_array($res)) {
3230
                $sqlu = "SELECT value as fval FROM $t_ufv v
3231
                         INNER JOIN $t_uf f
3232
                         ON (v.field_id = f.id)
3233
                         WHERE
3234
                            extra_field_type = ".EntityExtraField::USER_FIELD_TYPE." AND
3235
                            field_id = ".$row['id']." AND
3236
                            item_id = ".$user_id;
3237
                $resu = Database::query($sqlu);
3238
                $fval = '';
3239
                if (Database::num_rows($resu) > 0) {
3240
                    $rowu = Database::fetch_array($resu);
3241
                    $fval = $rowu['fval'];
3242
                    if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
3243
                        $fval = explode(';', $rowu['fval']);
3244
                    }
3245
                }
3246
                if ($prefix) {
3247
                    $extra_data['extra_'.$row['fvar']] = $fval;
3248
                } else {
3249
                    $extra_data[$row['fvar']] = $fval;
3250
                }
3251
            }
3252
        }
3253
3254
        return $extra_data;
3255
    }
3256
3257
    /**
3258
     * Get the extra field information for a certain field (the options as well).
3259
     *
3260
     * @param string $variable The name of the field we want to know everything about
3261
     *
3262
     * @return array Array containing all the information about the extra profile field
3263
     *               (first level of array contains field details, then 'options' sub-array contains options details,
3264
     *               as returned by the database)
3265
     *
3266
     * @author Julio Montoya
3267
     *
3268
     * @since v1.8.6
3269
     */
3270
    public static function get_extra_field_information_by_name($variable)
3271
    {
3272
        $extraField = new ExtraField('user');
3273
3274
        return $extraField->get_handler_field_info_by_field_variable($variable);
3275
    }
3276
3277
    /**
3278
     * Get the extra field information for user tag (the options as well).
3279
     *
3280
     * @param int $variable The name of the field we want to know everything about
3281
     *
3282
     * @return array Array containing all the information about the extra profile field
3283
     *               (first level of array contains field details, then 'options' sub-array contains options details,
3284
     *               as returned by the database)
3285
     *
3286
     * @author José Loguercio
3287
     *
3288
     * @since v1.11.0
3289
     */
3290
    public static function get_extra_field_tags_information_by_name($variable)
3291
    {
3292
        $extraField = new ExtraField('user');
3293
3294
        return $extraField->get_handler_field_info_by_tags($variable);
3295
    }
3296
3297
    /**
3298
     * @param string $type
3299
     *
3300
     * @return array
3301
     */
3302
    public static function get_all_extra_field_by_type($type)
3303
    {
3304
        $extraField = new ExtraField('user');
3305
3306
        return $extraField->get_all_extra_field_by_type($type);
3307
    }
3308
3309
    /**
3310
     * Get all the extra field information of a certain field (also the options).
3311
     *
3312
     * @param int $fieldId the ID of the field we want to know everything of
3313
     *
3314
     * @return array $return containing all th information about the extra profile field
3315
     *
3316
     * @author Julio Montoya
3317
     *
3318
     * @deprecated
3319
     * @since v1.8.6
3320
     */
3321
    public static function get_extra_field_information($fieldId)
3322
    {
3323
        $extraField = new ExtraField('user');
3324
3325
        return $extraField->getFieldInfoByFieldId($fieldId);
3326
    }
3327
3328
    /**
3329
     * Get extra user data by value.
3330
     *
3331
     * @param string $variable the internal variable name of the field
3332
     * @param string $value    the internal value of the field
3333
     * @param bool   $useLike
3334
     *
3335
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
3336
     */
3337
    public static function get_extra_user_data_by_value($variable, $value, $useLike = false)
3338
    {
3339
        $extraFieldValue = new ExtraFieldValue('user');
3340
        $extraField = new ExtraField('user');
3341
3342
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
3343
3344
        if (false === $info) {
3345
            return [];
3346
        }
3347
3348
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
3349
            $variable,
3350
            $value,
3351
            false,
3352
            false,
3353
            true,
3354
            $useLike
3355
        );
3356
3357
        $result = [];
3358
        if (!empty($data)) {
3359
            foreach ($data as $item) {
3360
                $result[] = $item['item_id'];
3361
            }
3362
        }
3363
3364
        return $result;
3365
    }
3366
3367
    /**
3368
     * Get extra user data by tags value.
3369
     *
3370
     * @param int    $fieldId the ID of the field we want to know everything of
3371
     * @param string $tag     the tag name for search
3372
     *
3373
     * @return array with extra data info of a user
3374
     *
3375
     * @author José Loguercio
3376
     *
3377
     * @since v1.11.0
3378
     */
3379
    public static function get_extra_user_data_by_tags($fieldId, $tag)
3380
    {
3381
        $extraField = new ExtraField('user');
3382
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
3383
        $array = [];
3384
        foreach ($result as $index => $user) {
3385
            $array[] = $user['user_id'];
3386
        }
3387
3388
        return $array;
3389
    }
3390
3391
    /**
3392
     * Get extra user data by field variable.
3393
     *
3394
     * @param string $variable field variable
3395
     *
3396
     * @return array data
3397
     */
3398
    public static function get_extra_user_data_by_field_variable($variable)
3399
    {
3400
        $extraInfo = self::get_extra_field_information_by_name($variable);
3401
        $field_id = (int) $extraInfo['id'];
3402
3403
        $extraField = new ExtraFieldValue('user');
3404
        $data = $extraField->getValuesByFieldId($field_id);
3405
3406
        if (!empty($data)) {
3407
            foreach ($data as $row) {
3408
                $user_id = $row['item_id'];
3409
                $data[$user_id] = $row;
3410
            }
3411
        }
3412
3413
        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...
3414
    }
3415
3416
    /**
3417
     * Get extra user data tags by field variable.
3418
     *
3419
     * @param string $variable field variable
3420
     *
3421
     * @return array
3422
     */
3423
    public static function get_extra_user_data_for_tags($variable)
3424
    {
3425
        $data = self::get_extra_field_tags_information_by_name($variable);
3426
3427
        return $data;
3428
    }
3429
3430
    /**
3431
     * Gives a list of [session_category][session_id] for the current user.
3432
     *
3433
     * @param int  $user_id
3434
     * @param bool $is_time_over                 whether to fill the first element or not
3435
     *                                           (to give space for courses out of categories)
3436
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
3437
     * @param bool $ignoreTimeLimit              ignore time start/end
3438
     * @param bool $getCount
3439
     *
3440
     * @return array list of statuses [session_category][session_id]
3441
     *
3442
     * @todo ensure multiple access urls are managed correctly
3443
     */
3444
    public static function get_sessions_by_category(
3445
        $user_id,
3446
        $is_time_over = true,
3447
        $ignore_visibility_for_admins = false,
3448
        $ignoreTimeLimit = false,
3449
        $getCount = false
3450
    ) {
3451
        $user_id = (int) $user_id;
3452
3453
        if (empty($user_id)) {
3454
            return [];
3455
        }
3456
3457
        $allowOrder = api_get_configuration_value('session_list_order');
3458
        $position = '';
3459
        if ($allowOrder) {
3460
            $position = ', s.position AS position ';
3461
        }
3462
3463
        // Get the list of sessions per user
3464
        $now = new DateTime('now', new DateTimeZone('UTC'));
3465
3466
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
3467
        // join would not catch session-courses where the user is general
3468
        // session coach but which do not have students nor coaches registered
3469
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
3470
3471
        if (!$getCount) {
3472
            $dqlSelect = " DISTINCT
3473
                s.id,
3474
                s.name,
3475
                s.accessStartDate AS access_start_date,
3476
                s.accessEndDate AS access_end_date,
3477
                s.duration,
3478
                sc.id AS session_category_id,
3479
                sc.name AS session_category_name,
3480
                sc.dateStart AS session_category_date_start,
3481
                sc.dateEnd AS session_category_date_end,
3482
                s.coachAccessStartDate AS coach_access_start_date,
3483
                s.coachAccessEndDate AS coach_access_end_date,
3484
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
3485
                $position
3486
            ";
3487
        }
3488
3489
        $dql = "SELECT $dqlSelect
3490
                FROM ChamiloCoreBundle:Session AS s
3491
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
3492
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.sessionId = s.id
3493
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc ";
3494
3495
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
3496
        // is awfully inefficient for large sets of data (1m25s for 58K
3497
        // sessions, BT#14115) but executing a similar query twice and grouping
3498
        // the results afterwards in PHP takes about 1/1000th of the time
3499
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
3500
        $dqlStudent = $dql.' WHERE scu.user = :user AND url.accessUrlId = :url ';
3501
        $dqlCoach = $dql.' WHERE s.generalCoach = :user AND url.accessUrlId = :url ';
3502
3503
        // Default order
3504
        $order = 'ORDER BY sc.name, s.name';
3505
3506
        // Order by date if showing all sessions
3507
        $showAllSessions = api_get_configuration_value('show_all_sessions_on_my_course_page') === true;
3508
        if ($showAllSessions) {
3509
            $order = 'ORDER BY s.accessStartDate';
3510
        }
3511
3512
        // Order by position
3513
        if ($allowOrder) {
3514
            $order = 'ORDER BY s.position';
3515
        }
3516
3517
        // Order by dates according to settings
3518
        $orderBySettings = api_get_configuration_value('my_courses_session_order');
3519
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
3520
            $field = $orderBySettings['field'];
3521
            $orderSetting = $orderBySettings['order'];
3522
            switch ($field) {
3523
                case 'start_date':
3524
                    $order = " ORDER BY s.accessStartDate $orderSetting";
3525
                    break;
3526
                case 'end_date':
3527
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
3528
                    if ($orderSetting === 'asc') {
3529
                        // Put null values at the end
3530
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
3531
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
3532
                    }
3533
                    break;
3534
                case 'name':
3535
                    $order = " ORDER BY s.name $orderSetting ";
3536
                    break;
3537
            }
3538
        }
3539
3540
        $dqlStudent .= $order;
3541
        $dqlCoach .= $order;
3542
3543
        $accessUrlId = api_get_current_access_url_id();
3544
        $dqlStudent = Database::getManager()
3545
            ->createQuery($dqlStudent)
3546
            ->setParameters(
3547
                ['user' => $user_id, 'url' => $accessUrlId]
3548
            )
3549
        ;
3550
        $dqlCoach = Database::getManager()
3551
            ->createQuery($dqlCoach)
3552
            ->setParameters(
3553
                ['user' => $user_id, 'url' => $accessUrlId]
3554
            )
3555
        ;
3556
3557
        if ($getCount) {
3558
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
3559
        }
3560
3561
        $sessionDataStudent = $dqlStudent->getResult();
3562
        $sessionDataCoach = $dqlCoach->getResult();
3563
3564
        $sessionData = [];
3565
        // First fill $sessionData with student sessions
3566
        if (!empty($sessionDataStudent)) {
3567
            foreach ($sessionDataStudent as $row) {
3568
                $sessionData[$row['id']] = $row;
3569
            }
3570
        }
3571
3572
        // Overwrite session data of the user as a student with session data
3573
        // of the user as a coach.
3574
        // There shouldn't be such duplicate rows, but just in case...
3575
        if (!empty($sessionDataCoach)) {
3576
            foreach ($sessionDataCoach as $row) {
3577
                $sessionData[$row['id']] = $row;
3578
            }
3579
        }
3580
3581
        $collapsable = api_get_configuration_value('allow_user_session_collapsable');
3582
        $extraField = new ExtraFieldValue('session');
3583
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
3584
3585
        if (empty($sessionData)) {
3586
            return [];
3587
        }
3588
3589
        $categories = [];
3590
        foreach ($sessionData as $row) {
3591
            $session_id = $row['id'];
3592
            $coachList = SessionManager::getCoachesBySession($session_id);
3593
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
3594
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
3595
            $courseList = self::get_courses_list_by_session($user_id, $session_id);
3596
3597
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
3598
3599
            // User portal filters:
3600
            if (false === $ignoreTimeLimit) {
3601
                if ($is_time_over) {
3602
                    // History
3603
                    if ($row['duration']) {
3604
                        if ($daysLeft >= 0) {
3605
                            continue;
3606
                        }
3607
                    } else {
3608
                        if (empty($row['access_end_date'])) {
3609
                            continue;
3610
                        } else {
3611
                            if ($row['access_end_date'] > $now) {
3612
                                continue;
3613
                            }
3614
                        }
3615
                    }
3616
                } else {
3617
                    // Current user portal
3618
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
3619
                    $isCoachOfCourse = in_array($user_id, $coachList);
3620
3621
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
3622
                        // Teachers can access the session depending in the access_coach date
3623
                    } else {
3624
                        if ($row['duration']) {
3625
                            if ($daysLeft <= 0) {
3626
                                continue;
3627
                            }
3628
                        } else {
3629
                            if (isset($row['access_end_date']) &&
3630
                                !empty($row['access_end_date'])
3631
                            ) {
3632
                                if ($row['access_end_date'] <= $now) {
3633
                                    continue;
3634
                                }
3635
                            }
3636
                        }
3637
                    }
3638
                }
3639
            }
3640
3641
            $categories[$row['session_category_id']]['session_category'] = [
3642
                'id' => $row['session_category_id'],
3643
                'name' => $row['session_category_name'],
3644
                'date_start' => $categoryStart,
3645
                'date_end' => $categoryEnd,
3646
            ];
3647
3648
            $visibility = api_get_session_visibility(
3649
                $session_id,
3650
                null,
3651
                $ignore_visibility_for_admins
3652
            );
3653
3654
            if ($visibility != SESSION_VISIBLE) {
3655
                // Course Coach session visibility.
3656
                $blockedCourseCount = 0;
3657
                $closedVisibilityList = [
3658
                    COURSE_VISIBILITY_CLOSED,
3659
                    COURSE_VISIBILITY_HIDDEN,
3660
                ];
3661
3662
                foreach ($courseList as $course) {
3663
                    // Checking session visibility
3664
                    $sessionCourseVisibility = api_get_session_visibility(
3665
                        $session_id,
3666
                        $course['real_id'],
3667
                        $ignore_visibility_for_admins
3668
                    );
3669
3670
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
3671
                    if ($courseIsVisible === false || $sessionCourseVisibility == SESSION_INVISIBLE) {
3672
                        $blockedCourseCount++;
3673
                    }
3674
                }
3675
3676
                // If all courses are blocked then no show in the list.
3677
                if ($blockedCourseCount === count($courseList)) {
3678
                    $visibility = SESSION_INVISIBLE;
3679
                } else {
3680
                    $visibility = $sessionCourseVisibility;
3681
                }
3682
            }
3683
3684
            switch ($visibility) {
3685
                case SESSION_VISIBLE_READ_ONLY:
3686
                case SESSION_VISIBLE:
3687
                case SESSION_AVAILABLE:
3688
                    break;
3689
                case SESSION_INVISIBLE:
3690
                    if ($ignore_visibility_for_admins === false) {
3691
                        continue 2;
3692
                    }
3693
            }
3694
3695
            $collapsed = '';
3696
            $collapsedAction = '';
3697
            if ($collapsable) {
3698
                $collapsableData = SessionManager::getCollapsableData(
3699
                    $user_id,
3700
                    $session_id,
3701
                    $extraField,
3702
                    $collapsableLink
3703
                );
3704
                $collapsed = $collapsableData['collapsed'];
3705
                $collapsedAction = $collapsableData['collapsable_link'];
3706
            }
3707
3708
            $categories[$row['session_category_id']]['sessions'][] = [
3709
                'session_name' => $row['name'],
3710
                'session_id' => $row['id'],
3711
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
3712
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
3713
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
3714
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
3715
                'courses' => $courseList,
3716
                'collapsed' => $collapsed,
3717
                'collapsable_link' => $collapsedAction,
3718
                'duration' => $row['duration'],
3719
            ];
3720
        }
3721
3722
        return $categories;
3723
    }
3724
3725
    /**
3726
     * Gives a list of [session_id-course_code] => [status] for the current user.
3727
     *
3728
     * @param int $user_id
3729
     * @param int $sessionLimit
3730
     *
3731
     * @return array list of statuses (session_id-course_code => status)
3732
     */
3733
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
3734
    {
3735
        // Database Table Definitions
3736
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3737
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
3738
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3739
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3740
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3741
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3742
3743
        $user_id = (int) $user_id;
3744
3745
        if (empty($user_id)) {
3746
            return [];
3747
        }
3748
3749
        // We filter the courses from the URL
3750
        $join_access_url = $where_access_url = '';
3751
        if (api_get_multiple_access_url()) {
3752
            $access_url_id = api_get_current_access_url_id();
3753
            if ($access_url_id != -1) {
3754
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3755
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
3756
                $where_access_url = " AND access_url_id = $access_url_id ";
3757
            }
3758
        }
3759
3760
        // Courses in which we subscribed out of any session
3761
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3762
3763
        $sql = "SELECT
3764
                    course.code,
3765
                    course_rel_user.status course_rel_status,
3766
                    course_rel_user.sort sort,
3767
                    course_rel_user.user_course_cat user_course_cat
3768
                 FROM $tbl_course_user course_rel_user
3769
                 LEFT JOIN $tbl_course course
3770
                 ON course.id = course_rel_user.c_id
3771
                 LEFT JOIN $tbl_user_course_category user_course_category
3772
                 ON course_rel_user.user_course_cat = user_course_category.id
3773
                 $join_access_url
3774
                 WHERE
3775
                    course_rel_user.user_id = '".$user_id."' AND
3776
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
3777
                    $where_access_url
3778
                 ORDER BY user_course_category.sort, course_rel_user.sort, course.title ASC";
3779
3780
        $course_list_sql_result = Database::query($sql);
3781
3782
        $personal_course_list = [];
3783
        if (Database::num_rows($course_list_sql_result) > 0) {
3784
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3785
                $course_info = api_get_course_info($result_row['code']);
3786
                $result_row['course_info'] = $course_info;
3787
                $personal_course_list[] = $result_row;
3788
            }
3789
        }
3790
3791
        $coachCourseConditions = '';
3792
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
3793
        if (api_is_allowed_to_create_course()) {
3794
            $sessionListFromCourseCoach = [];
3795
            $sql = " SELECT DISTINCT session_id
3796
                    FROM $tbl_session_course_user
3797
                    WHERE user_id = $user_id AND status = 2 ";
3798
3799
            $result = Database::query($sql);
3800
            if (Database::num_rows($result)) {
3801
                $result = Database::store_result($result);
3802
                foreach ($result as $session) {
3803
                    $sessionListFromCourseCoach[] = $session['session_id'];
3804
                }
3805
            }
3806
            if (!empty($sessionListFromCourseCoach)) {
3807
                $condition = implode("','", $sessionListFromCourseCoach);
3808
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
3809
            }
3810
        }
3811
3812
        // Get the list of sessions where the user is subscribed
3813
        // This is divided into two different queries
3814
        $sessions = [];
3815
        $sessionLimitRestriction = '';
3816
        if (!empty($sessionLimit)) {
3817
            $sessionLimit = (int) $sessionLimit;
3818
            $sessionLimitRestriction = "LIMIT $sessionLimit";
3819
        }
3820
3821
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
3822
                FROM $tbl_session_user su INNER JOIN $tbl_session s
3823
                ON (s.id = su.session_id)
3824
                WHERE (
3825
                    su.user_id = $user_id AND
3826
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
3827
                )
3828
                $coachCourseConditions
3829
                ORDER BY access_start_date, access_end_date, name
3830
                $sessionLimitRestriction
3831
        ";
3832
3833
        $result = Database::query($sql);
3834
        if (Database::num_rows($result) > 0) {
3835
            while ($row = Database::fetch_assoc($result)) {
3836
                $sessions[$row['id']] = $row;
3837
            }
3838
        }
3839
3840
        $sql = "SELECT DISTINCT
3841
                id, name, access_start_date, access_end_date
3842
                FROM $tbl_session s
3843
                WHERE (
3844
                    id_coach = $user_id
3845
                )
3846
                $coachCourseConditions
3847
                ORDER BY access_start_date, access_end_date, name";
3848
3849
        $result = Database::query($sql);
3850
        if (Database::num_rows($result) > 0) {
3851
            while ($row = Database::fetch_assoc($result)) {
3852
                if (empty($sessions[$row['id']])) {
3853
                    $sessions[$row['id']] = $row;
3854
                }
3855
            }
3856
        }
3857
3858
        if (api_is_allowed_to_create_course()) {
3859
            foreach ($sessions as $enreg) {
3860
                $session_id = $enreg['id'];
3861
                $session_visibility = api_get_session_visibility($session_id);
3862
3863
                if ($session_visibility == SESSION_INVISIBLE) {
3864
                    continue;
3865
                }
3866
3867
                // This query is horribly slow when more than a few thousand
3868
                // users and just a few sessions to which they are subscribed
3869
                $sql = "SELECT DISTINCT
3870
                        course.code code,
3871
                        course.title i,
3872
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
3873
                        email, course.course_language l,
3874
                        1 sort,
3875
                        category_code user_course_cat,
3876
                        access_start_date,
3877
                        access_end_date,
3878
                        session.id as session_id,
3879
                        session.name as session_name
3880
                    FROM $tbl_session_course_user as session_course_user
3881
                    INNER JOIN $tbl_course AS course
3882
                        ON course.id = session_course_user.c_id
3883
                    INNER JOIN $tbl_session as session
3884
                        ON session.id = session_course_user.session_id
3885
                    LEFT JOIN $tbl_user as user
3886
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
3887
                    WHERE
3888
                        session_course_user.session_id = $session_id AND (
3889
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
3890
                            OR session.id_coach = $user_id
3891
                        )
3892
                    ORDER BY i";
3893
                $course_list_sql_result = Database::query($sql);
3894
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3895
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3896
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3897
                    $personal_course_list[$key] = $result_row;
3898
                }
3899
            }
3900
        }
3901
3902
        foreach ($sessions as $enreg) {
3903
            $session_id = $enreg['id'];
3904
            $session_visibility = api_get_session_visibility($session_id);
3905
            if ($session_visibility == SESSION_INVISIBLE) {
3906
                continue;
3907
            }
3908
3909
            /* This query is very similar to the above query,
3910
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3911
            $sql = "SELECT DISTINCT
3912
                course.code code,
3913
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
3914
                email,
3915
                course.course_language l,
3916
                1 sort,
3917
                category_code user_course_cat,
3918
                access_start_date,
3919
                access_end_date,
3920
                session.id as session_id,
3921
                session.name as session_name,
3922
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
3923
            FROM $tbl_session_course_user as session_course_user
3924
            INNER JOIN $tbl_course AS course
3925
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
3926
            INNER JOIN $tbl_session as session
3927
            ON session_course_user.session_id = session.id
3928
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
3929
            WHERE session_course_user.user_id = $user_id
3930
            ORDER BY i";
3931
3932
            $course_list_sql_result = Database::query($sql);
3933
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3934
                $result_row['course_info'] = api_get_course_info($result_row['code']);
3935
                $key = $result_row['session_id'].' - '.$result_row['code'];
3936
                if (!isset($personal_course_list[$key])) {
3937
                    $personal_course_list[$key] = $result_row;
3938
                }
3939
            }
3940
        }
3941
3942
        return $personal_course_list;
3943
    }
3944
3945
    /**
3946
     * Gives a list of courses for the given user in the given session.
3947
     *
3948
     * @param int $user_id
3949
     * @param int $session_id
3950
     *
3951
     * @return array list of statuses (session_id-course_code => status)
3952
     */
3953
    public static function get_courses_list_by_session($user_id, $session_id)
3954
    {
3955
        // Database Table Definitions
3956
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3957
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
3958
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3959
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3960
3961
        $user_id = (int) $user_id;
3962
        $session_id = (int) $session_id;
3963
        // We filter the courses from the URL
3964
        $join_access_url = $where_access_url = '';
3965
        if (api_get_multiple_access_url()) {
3966
            $urlId = api_get_current_access_url_id();
3967
            if (-1 != $urlId) {
3968
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3969
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
3970
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
3971
            }
3972
        }
3973
3974
        /* This query is very similar to the query below, but it will check the
3975
        session_rel_course_user table if there are courses registered
3976
        to our user or not */
3977
        $sql = "SELECT DISTINCT
3978
                    c.title,
3979
                    c.visibility,
3980
                    c.id as real_id,
3981
                    c.code as course_code,
3982
                    sc.position,
3983
                    c.unsubscribe
3984
                FROM $tbl_session_course_user as scu
3985
                INNER JOIN $tbl_session_course sc
3986
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3987
                INNER JOIN $tableCourse as c
3988
                ON (scu.c_id = c.id)
3989
                $join_access_url
3990
                WHERE
3991
                    scu.user_id = $user_id AND
3992
                    scu.session_id = $session_id
3993
                    $where_access_url
3994
                ORDER BY sc.position ASC";
3995
3996
        $myCourseList = [];
3997
        $courses = [];
3998
        $result = Database::query($sql);
3999
        if (Database::num_rows($result) > 0) {
4000
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
4001
                $result_row['status'] = 5;
4002
                if (!in_array($result_row['real_id'], $courses)) {
4003
                    $position = $result_row['position'];
4004
                    if (!isset($myCourseList[$position])) {
4005
                        $myCourseList[$position] = $result_row;
4006
                    } else {
4007
                        $myCourseList[] = $result_row;
4008
                    }
4009
                    $courses[] = $result_row['real_id'];
4010
                }
4011
            }
4012
        }
4013
4014
        if (api_is_allowed_to_create_course()) {
4015
            $sql = "SELECT DISTINCT
4016
                        c.title,
4017
                        c.visibility,
4018
                        c.id as real_id,
4019
                        c.code as course_code,
4020
                        sc.position,
4021
                        c.unsubscribe
4022
                    FROM $tbl_session_course_user as scu
4023
                    INNER JOIN $tbl_session as s
4024
                    ON (scu.session_id = s.id)
4025
                    INNER JOIN $tbl_session_course sc
4026
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
4027
                    INNER JOIN $tableCourse as c
4028
                    ON (scu.c_id = c.id)
4029
                    $join_access_url
4030
                    WHERE
4031
                      s.id = $session_id AND
4032
                      (
4033
                        (scu.user_id = $user_id AND scu.status = 2) OR
4034
                        s.id_coach = $user_id
4035
                      )
4036
                    $where_access_url
4037
                    ORDER BY sc.position ASC";
4038
            $result = Database::query($sql);
4039
4040
            if (Database::num_rows($result) > 0) {
4041
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
4042
                    $result_row['status'] = 2;
4043
                    if (!in_array($result_row['real_id'], $courses)) {
4044
                        $position = $result_row['position'];
4045
                        if (!isset($myCourseList[$position])) {
4046
                            $myCourseList[$position] = $result_row;
4047
                        } else {
4048
                            $myCourseList[] = $result_row;
4049
                        }
4050
                        $courses[] = $result_row['real_id'];
4051
                    }
4052
                }
4053
            }
4054
        }
4055
4056
        if (api_is_drh()) {
4057
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
4058
            $sessionList = array_keys($sessionList);
4059
            if (in_array($session_id, $sessionList)) {
4060
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
4061
                if (!empty($courseList)) {
4062
                    foreach ($courseList as $course) {
4063
                        if (!in_array($course['id'], $courses)) {
4064
                            $position = $course['position'];
4065
                            if (!isset($myCourseList[$position])) {
4066
                                $myCourseList[$position] = $course;
4067
                            } else {
4068
                                $myCourseList[] = $course;
4069
                            }
4070
                        }
4071
                    }
4072
                }
4073
            }
4074
        } else {
4075
            //check if user is general coach for this session
4076
            $sessionInfo = api_get_session_info($session_id);
4077
            if ($sessionInfo['id_coach'] == $user_id) {
4078
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
4079
                if (!empty($courseList)) {
4080
                    foreach ($courseList as $course) {
4081
                        if (!in_array($course['id'], $courses)) {
4082
                            $position = $course['position'];
4083
                            if (!isset($myCourseList[$position])) {
4084
                                $myCourseList[$position] = $course;
4085
                            } else {
4086
                                $myCourseList[] = $course;
4087
                            }
4088
                        }
4089
                    }
4090
                }
4091
            }
4092
        }
4093
4094
        if (!empty($myCourseList)) {
4095
            ksort($myCourseList);
4096
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
4097
            if (empty($checkPosition)) {
4098
                // The session course list doesn't have any position,
4099
                // then order the course list by course code.
4100
                $orderByCode = array_column($myCourseList, 'course_code');
4101
                sort($orderByCode, SORT_NATURAL);
4102
                $newCourseList = [];
4103
                foreach ($orderByCode as $code) {
4104
                    foreach ($myCourseList as $course) {
4105
                        if ($code === $course['course_code']) {
4106
                            $newCourseList[] = $course;
4107
                            break;
4108
                        }
4109
                    }
4110
                }
4111
                $myCourseList = $newCourseList;
4112
            }
4113
        }
4114
4115
        return $myCourseList;
4116
    }
4117
4118
    /**
4119
     * Get user id from a username.
4120
     *
4121
     * @param string $username
4122
     *
4123
     * @return int User ID (or false if not found)
4124
     */
4125
    public static function get_user_id_from_username($username)
4126
    {
4127
        if (empty($username)) {
4128
            return false;
4129
        }
4130
        $username = trim($username);
4131
        $username = Database::escape_string($username);
4132
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
4133
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
4134
        $res = Database::query($sql);
4135
4136
        if ($res === false) {
4137
            return false;
4138
        }
4139
        if (Database::num_rows($res) !== 1) {
4140
            return false;
4141
        }
4142
        $row = Database::fetch_array($res);
4143
4144
        return $row['id'];
4145
    }
4146
4147
    /**
4148
     * Get the users files upload from his share_folder.
4149
     *
4150
     * @param string $user_id      User ID
4151
     * @param string $course       course directory
4152
     * @param string $resourceType resource type: images, all
4153
     *
4154
     * @return string
4155
     */
4156
    public static function get_user_upload_files_by_course(
4157
        $user_id,
4158
        $course,
4159
        $resourceType = 'all'
4160
    ) {
4161
        $return = '';
4162
        $user_id = (int) $user_id;
4163
4164
        if (!empty($user_id) && !empty($course)) {
4165
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
4166
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
4167
            $file_list = [];
4168
4169
            if (is_dir($path)) {
4170
                $handle = opendir($path);
4171
                while ($file = readdir($handle)) {
4172
                    if ($file == '.' || $file == '..' || $file == '.htaccess' || is_dir($path.$file)) {
4173
                        continue; // skip current/parent directory and .htaccess
4174
                    }
4175
                    $file_list[] = $file;
4176
                }
4177
                if (count($file_list) > 0) {
4178
                    $return = "<h4>$course</h4>";
4179
                    $return .= '<ul class="thumbnails">';
4180
                }
4181
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
4182
                foreach ($file_list as $file) {
4183
                    if ($resourceType == 'all') {
4184
                        $return .= '<li>
4185
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
4186
                    } elseif ($resourceType == 'images') {
4187
                        //get extension
4188
                        $ext = explode('.', $file);
4189
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
4190
                            $return .= '<li class="span2">
4191
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
4192
                                                <img src="'.$web_path.urlencode($file).'" >
4193
                                            </a>
4194
                                        </li>';
4195
                        }
4196
                    }
4197
                }
4198
                if (count($file_list) > 0) {
4199
                    $return .= '</ul>';
4200
                }
4201
            }
4202
        }
4203
4204
        return $return;
4205
    }
4206
4207
    /**
4208
     * Gets the API key (or keys) and return them into an array.
4209
     *
4210
     * @param int     Optional user id (defaults to the result of api_get_user_id())
4211
     * @param string $api_service
4212
     *
4213
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
4214
     */
4215
    public static function get_api_keys($user_id = null, $api_service = 'dokeos')
4216
    {
4217
        if ($user_id != strval(intval($user_id))) {
4218
            return false;
4219
        }
4220
        if (empty($user_id)) {
4221
            $user_id = api_get_user_id();
4222
        }
4223
        if ($user_id === false) {
4224
            return false;
4225
        }
4226
        $service_name = Database::escape_string($api_service);
4227
        if (is_string($service_name) === false) {
4228
            return false;
4229
        }
4230
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4231
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
4232
        $res = Database::query($sql);
4233
        if ($res === false) {
4234
            return false;
4235
        } //error during query
4236
        $num = Database::num_rows($res);
4237
        if ($num == 0) {
4238
            return false;
4239
        }
4240
        $list = [];
4241
        while ($row = Database::fetch_array($res)) {
4242
            $list[$row['id']] = $row['api_key'];
4243
        }
4244
4245
        return $list;
4246
    }
4247
4248
    /**
4249
     * Adds a new API key to the users' account.
4250
     *
4251
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
4252
     * @param string $api_service
4253
     *
4254
     * @return bool True on success, false on failure
4255
     */
4256
    public static function add_api_key($user_id = null, $api_service = 'dokeos')
4257
    {
4258
        if ($user_id != strval(intval($user_id))) {
4259
            return false;
4260
        }
4261
        if (empty($user_id)) {
4262
            $user_id = api_get_user_id();
4263
        }
4264
        if ($user_id === false) {
4265
            return false;
4266
        }
4267
        $service_name = Database::escape_string($api_service);
4268
        if (is_string($service_name) === false) {
4269
            return false;
4270
        }
4271
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4272
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
4273
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
4274
        $res = Database::query($sql);
4275
        if ($res === false) {
4276
            return false;
4277
        } //error during query
4278
        $num = Database::insert_id();
4279
4280
        return $num == 0 ? false : $num;
4281
    }
4282
4283
    /**
4284
     * Deletes an API key from the user's account.
4285
     *
4286
     * @param   int     API key's internal ID
4287
     *
4288
     * @return bool True on success, false on failure
4289
     */
4290
    public static function delete_api_key($key_id)
4291
    {
4292
        if ($key_id != strval(intval($key_id))) {
4293
            return false;
4294
        }
4295
        if ($key_id === false) {
4296
            return false;
4297
        }
4298
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4299
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
4300
        $res = Database::query($sql);
4301
        if ($res === false) {
4302
            return false;
4303
        } //error during query
4304
        $num = Database::num_rows($res);
4305
        if ($num !== 1) {
4306
            return false;
4307
        }
4308
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
4309
        $res = Database::query($sql);
4310
        if ($res === false) {
4311
            return false;
4312
        } //error during query
4313
4314
        return true;
4315
    }
4316
4317
    /**
4318
     * Regenerate an API key from the user's account.
4319
     *
4320
     * @param   int     user ID (defaults to the results of api_get_user_id())
4321
     * @param   string  API key's internal ID
4322
     *
4323
     * @return int num
4324
     */
4325
    public static function update_api_key($user_id, $api_service)
4326
    {
4327
        if ($user_id != strval(intval($user_id))) {
4328
            return false;
4329
        }
4330
        if ($user_id === false) {
4331
            return false;
4332
        }
4333
        $service_name = Database::escape_string($api_service);
4334
        if (is_string($service_name) === false) {
4335
            return false;
4336
        }
4337
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4338
        $sql = "SELECT id FROM $t_api
4339
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
4340
        $res = Database::query($sql);
4341
        $num = Database::num_rows($res);
4342
        if ($num == 1) {
4343
            $id_key = Database::fetch_array($res, 'ASSOC');
4344
            self::delete_api_key($id_key['id']);
4345
            $num = self::add_api_key($user_id, $api_service);
4346
        } elseif ($num == 0) {
4347
            $num = self::add_api_key($user_id, $api_service);
4348
        }
4349
4350
        return $num;
4351
    }
4352
4353
    /**
4354
     * @param   int     user ID (defaults to the results of api_get_user_id())
4355
     * @param   string    API key's internal ID
4356
     *
4357
     * @return int row ID, or return false if not found
4358
     */
4359
    public static function get_api_key_id($user_id, $api_service)
4360
    {
4361
        if ($user_id != strval(intval($user_id))) {
4362
            return false;
4363
        }
4364
        if ($user_id === false) {
4365
            return false;
4366
        }
4367
        if (empty($api_service)) {
4368
            return false;
4369
        }
4370
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4371
        $api_service = Database::escape_string($api_service);
4372
        $sql = "SELECT id FROM $t_api
4373
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
4374
        $res = Database::query($sql);
4375
        if (Database::num_rows($res) < 1) {
4376
            return false;
4377
        }
4378
        $row = Database::fetch_array($res, 'ASSOC');
4379
4380
        return $row['id'];
4381
    }
4382
4383
    /**
4384
     * Checks if a user_id is platform admin.
4385
     *
4386
     * @param   int user ID
4387
     *
4388
     * @return bool True if is admin, false otherwise
4389
     *
4390
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
4391
     */
4392
    public static function is_admin($user_id)
4393
    {
4394
        $user_id = (int) $user_id;
4395
        if (empty($user_id)) {
4396
            return false;
4397
        }
4398
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
4399
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
4400
        $res = Database::query($sql);
4401
4402
        return Database::num_rows($res) === 1;
4403
    }
4404
4405
    /**
4406
     * Get the total count of users.
4407
     *
4408
     * @param int $status        Status of users to be counted
4409
     * @param int $access_url_id Access URL ID (optional)
4410
     * @param int $active
4411
     *
4412
     * @return mixed Number of users or false on error
4413
     */
4414
    public static function get_number_of_users($status = 0, $access_url_id = 1, $active = null)
4415
    {
4416
        $t_u = Database::get_main_table(TABLE_MAIN_USER);
4417
        $t_a = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4418
4419
        if (api_is_multiple_url_enabled()) {
4420
            $sql = "SELECT count(u.id)
4421
                    FROM $t_u u
4422
                    INNER JOIN $t_a url_user
4423
                    ON (u.id = url_user.user_id)
4424
                    WHERE url_user.access_url_id = $access_url_id
4425
            ";
4426
        } else {
4427
            $sql = "SELECT count(u.id)
4428
                    FROM $t_u u
4429
                    WHERE 1 = 1 ";
4430
        }
4431
4432
        if (is_int($status) && $status > 0) {
4433
            $status = (int) $status;
4434
            $sql .= " AND u.status = $status ";
4435
        }
4436
4437
        if ($active !== null) {
4438
            $active = (int) $active;
4439
            $sql .= " AND u.active = $active ";
4440
        }
4441
4442
        $res = Database::query($sql);
4443
        if (Database::num_rows($res) === 1) {
4444
            return (int) Database::result($res, 0, 0);
4445
        }
4446
4447
        return false;
4448
    }
4449
4450
    /**
4451
     * Gets the tags of a specific field_id
4452
     * USER TAGS.
4453
     *
4454
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
4455
     *
4456
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
4457
     *    Called it "books" for example.
4458
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
4459
     * 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
4460
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
4461
     * 5. Test and enjoy.
4462
     *
4463
     * @param string $tag
4464
     * @param int    $field_id      field_id
4465
     * @param string $return_format how we are going to result value in array or in a string (json)
4466
     * @param $limit
4467
     *
4468
     * @return mixed
4469
     */
4470
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
4471
    {
4472
        // database table definition
4473
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4474
        $field_id = (int) $field_id;
4475
        $limit = (int) $limit;
4476
        $tag = trim(Database::escape_string($tag));
4477
4478
        // all the information of the field
4479
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
4480
                WHERE field_id = $field_id AND tag LIKE '$tag%'
4481
                ORDER BY tag
4482
                LIMIT $limit";
4483
        $result = Database::query($sql);
4484
        $return = [];
4485
        if (Database::num_rows($result) > 0) {
4486
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4487
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
4488
            }
4489
        }
4490
        if ($return_format === 'json') {
4491
            $return = json_encode($return);
4492
        }
4493
4494
        return $return;
4495
    }
4496
4497
    /**
4498
     * @param int $field_id
4499
     * @param int $limit
4500
     *
4501
     * @return array
4502
     */
4503
    public static function get_top_tags($field_id, $limit = 100)
4504
    {
4505
        // database table definition
4506
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4507
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4508
        $field_id = (int) $field_id;
4509
        $limit = (int) $limit;
4510
        // all the information of the field
4511
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
4512
                INNER JOIN $table_user_tag ut
4513
                ON (ut.id = uv.tag_id)
4514
                WHERE field_id = $field_id
4515
                GROUP BY tag_id
4516
                ORDER BY count DESC
4517
                LIMIT $limit";
4518
        $result = Database::query($sql);
4519
        $return = [];
4520
        if (Database::num_rows($result) > 0) {
4521
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4522
                $return[] = $row;
4523
            }
4524
        }
4525
4526
        return $return;
4527
    }
4528
4529
    /**
4530
     * Get user's tags.
4531
     *
4532
     * @param int $user_id
4533
     * @param int $field_id
4534
     *
4535
     * @return array
4536
     */
4537
    public static function get_user_tags($user_id, $field_id)
4538
    {
4539
        // database table definition
4540
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4541
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4542
        $field_id = (int) $field_id;
4543
        $user_id = (int) $user_id;
4544
4545
        // all the information of the field
4546
        $sql = "SELECT ut.id, tag, count
4547
                FROM $table_user_tag ut
4548
                INNER JOIN $table_user_tag_values uv
4549
                ON (uv.tag_id=ut.ID)
4550
                WHERE field_id = $field_id AND user_id = $user_id
4551
                ORDER BY tag";
4552
        $result = Database::query($sql);
4553
        $return = [];
4554
        if (Database::num_rows($result) > 0) {
4555
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4556
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
4557
            }
4558
        }
4559
4560
        return $return;
4561
    }
4562
4563
    /**
4564
     * Get user's tags.
4565
     *
4566
     * @param int  $user_id
4567
     * @param int  $field_id
4568
     * @param bool $show_links show links or not
4569
     *
4570
     * @return string
4571
     */
4572
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
4573
    {
4574
        // database table definition
4575
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4576
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4577
        $field_id = (int) $field_id;
4578
        $user_id = (int) $user_id;
4579
4580
        // all the information of the field
4581
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
4582
                INNER JOIN $table_user_tag_values uv
4583
                ON (uv.tag_id = ut.id)
4584
                WHERE field_id = $field_id AND user_id = $user_id
4585
                ORDER BY tag";
4586
4587
        $result = Database::query($sql);
4588
        $return = [];
4589
        if (Database::num_rows($result) > 0) {
4590
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4591
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
4592
            }
4593
        }
4594
        $user_tags = $return;
4595
        $tag_tmp = [];
4596
        foreach ($user_tags as $tag) {
4597
            if ($show_links) {
4598
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
4599
                    $tag['tag'].
4600
                '</a>';
4601
            } else {
4602
                $tag_tmp[] = $tag['tag'];
4603
            }
4604
        }
4605
4606
        if (is_array($user_tags) && count($user_tags) > 0) {
4607
            return implode(', ', $tag_tmp);
4608
        } else {
4609
            return '';
4610
        }
4611
    }
4612
4613
    /**
4614
     * Get the tag id.
4615
     *
4616
     * @param int $tag
4617
     * @param int $field_id
4618
     *
4619
     * @return int returns 0 if fails otherwise the tag id
4620
     */
4621
    public static function get_tag_id($tag, $field_id)
4622
    {
4623
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4624
        $tag = Database::escape_string($tag);
4625
        $field_id = (int) $field_id;
4626
        //with COLLATE latin1_bin to select query in a case sensitive mode
4627
        $sql = "SELECT id FROM $table_user_tag
4628
                WHERE tag LIKE '$tag' AND field_id = $field_id";
4629
        $result = Database::query($sql);
4630
        if (Database::num_rows($result) > 0) {
4631
            $row = Database::fetch_array($result, 'ASSOC');
4632
4633
            return $row['id'];
4634
        } else {
4635
            return 0;
4636
        }
4637
    }
4638
4639
    /**
4640
     * Get the tag id.
4641
     *
4642
     * @param int $tag_id
4643
     * @param int $field_id
4644
     *
4645
     * @return int 0 if fails otherwise the tag id
4646
     */
4647
    public static function get_tag_id_from_id($tag_id, $field_id)
4648
    {
4649
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4650
        $tag_id = (int) $tag_id;
4651
        $field_id = (int) $field_id;
4652
        $sql = "SELECT id FROM $table_user_tag
4653
                WHERE id = '$tag_id' AND field_id = $field_id";
4654
        $result = Database::query($sql);
4655
        if (Database::num_rows($result) > 0) {
4656
            $row = Database::fetch_array($result, 'ASSOC');
4657
4658
            return $row['id'];
4659
        } else {
4660
            return false;
4661
        }
4662
    }
4663
4664
    /**
4665
     * Adds a user-tag value.
4666
     *
4667
     * @param mixed $tag
4668
     * @param int   $user_id
4669
     * @param int   $field_id field id of the tag
4670
     *
4671
     * @return bool True if the tag was inserted or updated. False otherwise.
4672
     *              The return value doesn't take into account *values* added to the tag.
4673
     *              Only the creation/update of the tag field itself.
4674
     */
4675
    public static function add_tag($tag, $user_id, $field_id)
4676
    {
4677
        // database table definition
4678
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4679
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4680
        $tag = trim(Database::escape_string($tag));
4681
        $user_id = (int) $user_id;
4682
        $field_id = (int) $field_id;
4683
4684
        $tag_id = self::get_tag_id($tag, $field_id);
4685
4686
        /* IMPORTANT
4687
         *  @todo we don't create tags with numbers
4688
         *
4689
         */
4690
        if (is_numeric($tag)) {
4691
            //the form is sending an id this means that the user select it from the list so it MUST exists
4692
            /* $new_tag_id = self::get_tag_id_from_id($tag,$field_id);
4693
              if ($new_tag_id !== false) {
4694
              $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $new_tag_id";
4695
              $result = Database::query($sql);
4696
              $last_insert_id = $new_tag_id;
4697
              } else {
4698
              $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
4699
              $result = Database::query($sql);
4700
              $last_insert_id = Database::insert_id();
4701
              } */
4702
        }
4703
4704
        //this is a new tag
4705
        if ($tag_id == 0) {
4706
            //the tag doesn't exist
4707
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
4708
            Database::query($sql);
4709
            $last_insert_id = Database::insert_id();
4710
        } else {
4711
            //the tag exists we update it
4712
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
4713
            Database::query($sql);
4714
            $last_insert_id = $tag_id;
4715
        }
4716
4717
        if (!empty($last_insert_id) && ($last_insert_id != 0)) {
4718
            //we insert the relationship user-tag
4719
            $sql = "SELECT tag_id FROM $table_user_tag_values
4720
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
4721
            $result = Database::query($sql);
4722
            //if the relationship does not exist we create it
4723
            if (Database::num_rows($result) == 0) {
4724
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
4725
                Database::query($sql);
4726
            }
4727
4728
            return true;
4729
        }
4730
4731
        return false;
4732
    }
4733
4734
    /**
4735
     * Deletes an user tag.
4736
     *
4737
     * @param int $user_id
4738
     * @param int $field_id
4739
     */
4740
    public static function delete_user_tags($user_id, $field_id)
4741
    {
4742
        // database table definition
4743
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4744
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4745
        $user_id = (int) $user_id;
4746
4747
        $tags = self::get_user_tags($user_id, $field_id);
4748
        if (is_array($tags) && count($tags) > 0) {
4749
            foreach ($tags as $key => $tag) {
4750
                if ($tag['count'] > '0') {
4751
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
4752
                    Database::query($sql);
4753
                }
4754
                $sql = "DELETE FROM $table_user_tag_values
4755
                        WHERE user_id = $user_id AND tag_id = $key";
4756
                Database::query($sql);
4757
            }
4758
        }
4759
    }
4760
4761
    /**
4762
     * Process the tag list comes from the UserManager::update_extra_field_value() function.
4763
     *
4764
     * @param array $tags     the tag list that will be added
4765
     * @param int   $user_id
4766
     * @param int   $field_id
4767
     *
4768
     * @return bool
4769
     */
4770
    public static function process_tags($tags, $user_id, $field_id)
4771
    {
4772
        // We loop the tags and add it to the DB
4773
        if (is_array($tags)) {
4774
            foreach ($tags as $tag) {
4775
                self::add_tag($tag, $user_id, $field_id);
4776
            }
4777
        } else {
4778
            self::add_tag($tags, $user_id, $field_id);
4779
        }
4780
4781
        return true;
4782
    }
4783
4784
    /**
4785
     * Returns a list of all administrators.
4786
     *
4787
     * @return array
4788
     */
4789
    public static function get_all_administrators()
4790
    {
4791
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4792
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
4793
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4794
        $access_url_id = api_get_current_access_url_id();
4795
        if (api_get_multiple_access_url()) {
4796
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
4797
                    FROM $tbl_url_rel_user as url
4798
                    INNER JOIN $table_admin as admin
4799
                    ON (admin.user_id=url.user_id)
4800
                    INNER JOIN $table_user u
4801
                    ON (u.id=admin.user_id)
4802
                    WHERE access_url_id ='".$access_url_id."'";
4803
        } else {
4804
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
4805
                    FROM $table_admin as admin
4806
                    INNER JOIN $table_user u
4807
                    ON (u.id=admin.user_id)";
4808
        }
4809
        $result = Database::query($sql);
4810
        $return = [];
4811
        if (Database::num_rows($result) > 0) {
4812
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4813
                $return[$row['user_id']] = $row;
4814
            }
4815
        }
4816
4817
        return $return;
4818
    }
4819
4820
    /**
4821
     * Search an user (tags, first name, last name and email ).
4822
     *
4823
     * @param string $tag
4824
     * @param int    $field_id        field id of the tag
4825
     * @param int    $from            where to start in the query
4826
     * @param int    $number_of_items
4827
     * @param bool   $getCount        get count or not
4828
     *
4829
     * @return array
4830
     */
4831
    public static function get_all_user_tags(
4832
        $tag,
4833
        $field_id = 0,
4834
        $from = 0,
4835
        $number_of_items = 10,
4836
        $getCount = false
4837
    ) {
4838
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
4839
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4840
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4841
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4842
4843
        $field_id = intval($field_id);
4844
        $from = intval($from);
4845
        $number_of_items = intval($number_of_items);
4846
4847
        $where_field = "";
4848
        $where_extra_fields = self::get_search_form_where_extra_fields();
4849
        if ($field_id != 0) {
4850
            $where_field = " field_id = $field_id AND ";
4851
        }
4852
4853
        // all the information of the field
4854
        if ($getCount) {
4855
            $select = "SELECT count(DISTINCT u.id) count";
4856
        } else {
4857
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, picture_uri";
4858
        }
4859
4860
        $sql = " $select
4861
                FROM $user_table u
4862
                INNER JOIN $access_url_rel_user_table url_rel_user
4863
                ON (u.id = url_rel_user.user_id)
4864
                LEFT JOIN $table_user_tag_values uv
4865
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
4866
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
4867
                WHERE
4868
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
4869
                    (
4870
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
4871
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
4872
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
4873
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
4874
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
4875
                     )
4876
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
4877
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
4878
4879
        $keyword_active = true;
4880
        // only active users
4881
        if ($keyword_active) {
4882
            $sql .= " AND u.active='1'";
4883
        }
4884
        // avoid anonymous
4885
        $sql .= " AND u.status <> 6 ";
4886
        $sql .= " ORDER BY username";
4887
        $sql .= " LIMIT $from , $number_of_items";
4888
4889
        $result = Database::query($sql);
4890
        $return = [];
4891
4892
        if (Database::num_rows($result) > 0) {
4893
            if ($getCount) {
4894
                $row = Database::fetch_array($result, 'ASSOC');
4895
4896
                return $row['count'];
4897
            }
4898
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4899
                $return[$row['id']] = $row;
4900
            }
4901
        }
4902
4903
        return $return;
4904
    }
4905
4906
    /**
4907
     * Get extra filterable user fields (only type select).
4908
     *
4909
     * @return array Array of extra fields as [int => ['name' => ..., 'variable' => ..., 'data' => ...]] (
4910
     *               or empty array if no extra field)
4911
     */
4912
    public static function getExtraFilterableFields()
4913
    {
4914
        $extraFieldList = self::get_extra_fields();
4915
        $fields = [];
4916
        if (is_array($extraFieldList)) {
4917
            foreach ($extraFieldList as $extraField) {
4918
                // If is enabled to filter and is a "<select>" field type
4919
                if ($extraField[8] == 1 && $extraField[2] == 4) {
4920
                    $fields[] = [
4921
                        'name' => $extraField[3],
4922
                        'variable' => $extraField[1],
4923
                        'data' => $extraField[9],
4924
                    ];
4925
                }
4926
            }
4927
        }
4928
4929
        return $fields;
4930
    }
4931
4932
    /**
4933
     * Get extra where clauses for finding users based on extra filterable user fields (type select).
4934
     *
4935
     * @return string With AND clauses based on user's ID which have the values to search in extra user fields
4936
     *                (or empty if no extra field exists)
4937
     */
4938
    public static function get_search_form_where_extra_fields()
4939
    {
4940
        $useExtraFields = false;
4941
        $extraFields = self::getExtraFilterableFields();
4942
        $extraFieldResult = [];
4943
        if (is_array($extraFields) && count($extraFields) > 0) {
4944
            foreach ($extraFields as $extraField) {
4945
                $varName = 'field_'.$extraField['variable'];
4946
                if (self::is_extra_field_available($extraField['variable'])) {
4947
                    if (isset($_GET[$varName]) && $_GET[$varName] != '0') {
4948
                        $useExtraFields = true;
4949
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
4950
                            $extraField['variable'],
4951
                            $_GET[$varName]
4952
                        );
4953
                    }
4954
                }
4955
            }
4956
        }
4957
4958
        if ($useExtraFields) {
4959
            $finalResult = [];
4960
            if (count($extraFieldResult) > 1) {
4961
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
4962
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
4963
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
4964
                    }
4965
                }
4966
            } else {
4967
                $finalResult = $extraFieldResult[0];
4968
            }
4969
4970
            if (is_array($finalResult) && count($finalResult) > 0) {
4971
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
4972
            } else {
4973
                //no results
4974
                $whereFilter = " AND u.id  = -1 ";
4975
            }
4976
4977
            return $whereFilter;
4978
        }
4979
4980
        return '';
4981
    }
4982
4983
    /**
4984
     * Show the search form.
4985
     *
4986
     * @param string $query the value of the search box
4987
     *
4988
     * @throws Exception
4989
     *
4990
     * @return string HTML form
4991
     */
4992
    public static function get_search_form($query, $defaultParams = [])
4993
    {
4994
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
4995
        $form = new FormValidator(
4996
            'search_user',
4997
            'get',
4998
            api_get_path(WEB_PATH).'main/social/search.php',
4999
            '',
5000
            [],
5001
            FormValidator::LAYOUT_HORIZONTAL
5002
        );
5003
5004
        $query = Security::remove_XSS($query);
5005
5006
        if (!empty($query)) {
5007
            $form->addHeader(get_lang('Results').' "'.$query.'"');
5008
        }
5009
5010
        $form->addText(
5011
            'q',
5012
            get_lang('UsersGroups'),
5013
            false,
5014
            [
5015
                'id' => 'q',
5016
            ]
5017
        );
5018
        $options = [
5019
            0 => get_lang('Select'),
5020
            1 => get_lang('User'),
5021
            2 => get_lang('Group'),
5022
        ];
5023
        $form->addSelect(
5024
            'search_type',
5025
            get_lang('Type'),
5026
            $options,
5027
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
5028
        );
5029
5030
        // Extra fields
5031
        $extraFields = self::getExtraFilterableFields();
5032
        $defaults = [];
5033
        if (is_array($extraFields) && count($extraFields) > 0) {
5034
            foreach ($extraFields as $extraField) {
5035
                $varName = 'field_'.$extraField['variable'];
5036
                $options = [
5037
                    0 => get_lang('Select'),
5038
                ];
5039
                foreach ($extraField['data'] as $option) {
5040
                    if (isset($_GET[$varName])) {
5041
                        if ($_GET[$varName] == $option[1]) {
5042
                            $defaults[$option[1]] = true;
5043
                        }
5044
                    }
5045
5046
                    $options[$option[1]] = $option[1];
5047
                }
5048
                $form->addSelect($varName, $extraField['name'], $options);
5049
            }
5050
        }
5051
5052
        $defaults['search_type'] = (int) $searchType;
5053
        $defaults['q'] = $query;
5054
5055
        if (!empty($defaultParams)) {
5056
            $defaults = array_merge($defaults, $defaultParams);
5057
        }
5058
        $form->setDefaults($defaults);
5059
        $form->addButtonSearch(get_lang('Search'));
5060
5061
        $js = '<script>
5062
        extra_field_toogle();
5063
        function extra_field_toogle() {
5064
            if (jQuery("select[name=search_type]").val() != "1") {
5065
                jQuery(".extra_field").hide();
5066
            } else {
5067
                jQuery(".extra_field").show();
5068
            }
5069
        }
5070
        </script>';
5071
5072
        return $js.$form->returnForm();
5073
    }
5074
5075
    /**
5076
     * Shows the user menu.
5077
     */
5078
    public static function show_menu()
5079
    {
5080
        echo '<div class="actions">';
5081
        echo '<a href="/main/auth/profile.php">'.
5082
            Display::return_icon('profile.png').' '.get_lang('PersonalData').'</a>';
5083
        echo '<a href="/main/messages/inbox.php">'.
5084
            Display::return_icon('inbox.png').' '.get_lang('Inbox').'</a>';
5085
        echo '<a href="/main/messages/outbox.php">'.
5086
            Display::return_icon('outbox.png').' '.get_lang('Outbox').'</a>';
5087
        echo '<span style="float:right; padding-top:7px;">'.
5088
        '<a href="/main/auth/profile.php?show=1">'.
5089
            Display::return_icon('edit.gif').' '.get_lang('Configuration').'</a>';
5090
        echo '</span>';
5091
        echo '</div>';
5092
    }
5093
5094
    /**
5095
     * Allow to register contact to social network.
5096
     *
5097
     * @param int $friend_id     user friend id
5098
     * @param int $my_user_id    user id
5099
     * @param int $relation_type relation between users see constants definition
5100
     *
5101
     * @return bool
5102
     */
5103
    public static function relate_users($friend_id, $my_user_id, $relation_type)
5104
    {
5105
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5106
5107
        $friend_id = (int) $friend_id;
5108
        $my_user_id = (int) $my_user_id;
5109
        $relation_type = (int) $relation_type;
5110
5111
        $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
5112
                WHERE
5113
                    friend_user_id='.$friend_id.' AND
5114
                    user_id='.$my_user_id.' AND
5115
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
5116
        $result = Database::query($sql);
5117
        $row = Database::fetch_array($result, 'ASSOC');
5118
        $current_date = api_get_utc_datetime();
5119
5120
        if ($row['count'] == 0) {
5121
            $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
5122
                    VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
5123
            Database::query($sql);
5124
5125
            return true;
5126
        }
5127
5128
        $sql = 'SELECT COUNT(*) as count, relation_type  FROM '.$tbl_my_friend.'
5129
                WHERE
5130
                    friend_user_id='.$friend_id.' AND
5131
                    user_id='.$my_user_id.' AND
5132
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
5133
        $result = Database::query($sql);
5134
        $row = Database::fetch_array($result, 'ASSOC');
5135
5136
        if ($row['count'] == 1) {
5137
            //only for the case of a RRHH or a Student BOSS
5138
            if ($row['relation_type'] != $relation_type &&
5139
                ($relation_type == USER_RELATION_TYPE_RRHH || $relation_type == USER_RELATION_TYPE_BOSS)
5140
            ) {
5141
                $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
5142
                        VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
5143
            } else {
5144
                $sql = 'UPDATE '.$tbl_my_friend.' SET relation_type='.$relation_type.'
5145
                        WHERE friend_user_id='.$friend_id.' AND user_id='.$my_user_id;
5146
            }
5147
            Database::query($sql);
5148
5149
            return true;
5150
        }
5151
5152
        return false;
5153
    }
5154
5155
    /**
5156
     * Deletes a contact.
5157
     *
5158
     * @param bool   $friend_id
5159
     * @param bool   $real_removed          true will delete ALL friends relationship
5160
     * @param string $with_status_condition
5161
     *
5162
     * @author isaac flores paz <[email protected]>
5163
     * @author Julio Montoya <[email protected]> Cleaning code
5164
     */
5165
    public static function remove_user_rel_user(
5166
        $friend_id,
5167
        $real_removed = false,
5168
        $with_status_condition = ''
5169
    ) {
5170
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5171
        $tbl_my_message = Database::get_main_table(TABLE_MESSAGE);
5172
        $friend_id = (int) $friend_id;
5173
        $user_id = api_get_user_id();
5174
5175
        if ($real_removed) {
5176
            $extra_condition = '';
5177
            if ($with_status_condition != '') {
5178
                $extra_condition = ' AND relation_type = '.intval($with_status_condition);
5179
            }
5180
            $sql = 'DELETE FROM '.$tbl_my_friend.'
5181
                    WHERE
5182
                        relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
5183
                        friend_user_id='.$friend_id.' '.$extra_condition;
5184
            Database::query($sql);
5185
            $sql = 'DELETE FROM '.$tbl_my_friend.'
5186
                   WHERE
5187
                    relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
5188
                    user_id='.$friend_id.' '.$extra_condition;
5189
            Database::query($sql);
5190
        } else {
5191
            $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
5192
                    WHERE
5193
                        user_id='.$user_id.' AND
5194
                        relation_type NOT IN('.USER_RELATION_TYPE_DELETED.', '.USER_RELATION_TYPE_RRHH.') AND
5195
                        friend_user_id='.$friend_id;
5196
            $result = Database::query($sql);
5197
            $row = Database::fetch_array($result, 'ASSOC');
5198
            if ($row['count'] == 1) {
5199
                //Delete user rel user
5200
                $sql_i = 'UPDATE '.$tbl_my_friend.' SET relation_type='.USER_RELATION_TYPE_DELETED.'
5201
                          WHERE user_id='.$user_id.' AND friend_user_id='.$friend_id;
5202
5203
                $sql_j = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
5204
                          WHERE
5205
                                user_receiver_id='.$user_id.' AND
5206
                                user_sender_id='.$friend_id.' AND update_date="0000-00-00 00:00:00" ';
5207
                // Delete user
5208
                $sql_ij = 'UPDATE '.$tbl_my_friend.'  SET relation_type='.USER_RELATION_TYPE_DELETED.'
5209
                           WHERE user_id='.$friend_id.' AND friend_user_id='.$user_id;
5210
                $sql_ji = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
5211
                           WHERE
5212
                                user_receiver_id='.$friend_id.' AND
5213
                                user_sender_id='.$user_id.' AND
5214
                                update_date="0000-00-00 00:00:00" ';
5215
                Database::query($sql_i);
5216
                Database::query($sql_j);
5217
                Database::query($sql_ij);
5218
                Database::query($sql_ji);
5219
            }
5220
        }
5221
5222
        // Delete accepted invitations
5223
        $sql = "DELETE FROM $tbl_my_message
5224
                WHERE
5225
                    msg_status = ".MESSAGE_STATUS_INVITATION_ACCEPTED." AND
5226
                    (
5227
                        user_receiver_id = $user_id AND
5228
                        user_sender_id = $friend_id
5229
                    ) OR
5230
                    (
5231
                        user_sender_id = $user_id AND
5232
                        user_receiver_id = $friend_id
5233
                    )
5234
        ";
5235
        Database::query($sql);
5236
    }
5237
5238
    /**
5239
     * @param int $userId
5240
     *
5241
     * @return array
5242
     */
5243
    public static function getDrhListFromUser($userId)
5244
    {
5245
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
5246
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5247
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5248
        $userId = (int) $userId;
5249
5250
        $orderBy = null;
5251
        if (api_is_western_name_order()) {
5252
            $orderBy .= ' ORDER BY firstname, lastname ';
5253
        } else {
5254
            $orderBy .= ' ORDER BY lastname, firstname ';
5255
        }
5256
5257
        $sql = "SELECT u.id, username, u.firstname, u.lastname
5258
                FROM $tblUser u
5259
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
5260
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
5261
                WHERE
5262
                    access_url_id = ".api_get_current_access_url_id()." AND
5263
                    uru.user_id = '$userId' AND
5264
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
5265
                $orderBy
5266
                ";
5267
        $result = Database::query($sql);
5268
5269
        return Database::store_result($result);
5270
    }
5271
5272
    /**
5273
     * get users followed by human resource manager.
5274
     *
5275
     * @param int    $userId
5276
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
5277
     * @param bool   $getOnlyUserId
5278
     * @param bool   $getSql
5279
     * @param bool   $getCount
5280
     * @param int    $from
5281
     * @param int    $numberItems
5282
     * @param int    $column
5283
     * @param string $direction
5284
     * @param int    $active
5285
     * @param string $lastConnectionDate
5286
     *
5287
     * @return array users
5288
     */
5289
    public static function get_users_followed_by_drh(
5290
        $userId,
5291
        $userStatus = 0,
5292
        $getOnlyUserId = false,
5293
        $getSql = false,
5294
        $getCount = false,
5295
        $from = null,
5296
        $numberItems = null,
5297
        $column = null,
5298
        $direction = null,
5299
        $active = null,
5300
        $lastConnectionDate = null
5301
    ) {
5302
        return self::getUsersFollowedByUser(
5303
            $userId,
5304
            $userStatus,
5305
            $getOnlyUserId,
5306
            $getSql,
5307
            $getCount,
5308
            $from,
5309
            $numberItems,
5310
            $column,
5311
            $direction,
5312
            $active,
5313
            $lastConnectionDate,
5314
            DRH
5315
        );
5316
    }
5317
5318
    /**
5319
     * Get users followed by human resource manager.
5320
     *
5321
     * @param int    $userId
5322
     * @param int    $userStatus             Filter users by status (STUDENT, COURSEMANAGER, etc)
5323
     * @param bool   $getOnlyUserId
5324
     * @param bool   $getSql
5325
     * @param bool   $getCount
5326
     * @param int    $from
5327
     * @param int    $numberItems
5328
     * @param int    $column
5329
     * @param string $direction
5330
     * @param int    $active
5331
     * @param string $lastConnectionDate
5332
     * @param int    $status                 the function is called by who? COURSEMANAGER, DRH?
5333
     * @param string $keyword
5334
     * @param bool   $checkSessionVisibility
5335
     *
5336
     * @return mixed Users list (array) or the SQL query if $getSQL was set to true
5337
     */
5338
    public static function getUsersFollowedByUser(
5339
        $userId,
5340
        $userStatus = null,
5341
        $getOnlyUserId = false,
5342
        $getSql = false,
5343
        $getCount = false,
5344
        $from = null,
5345
        $numberItems = null,
5346
        $column = null,
5347
        $direction = null,
5348
        $active = null,
5349
        $lastConnectionDate = null,
5350
        $status = null,
5351
        $keyword = null,
5352
        $checkSessionVisibility = false
5353
    ) {
5354
        // Database Table Definitions
5355
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
5356
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5357
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5358
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
5359
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5360
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5361
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
5362
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
5363
5364
        $userId = (int) $userId;
5365
        $limitCondition = '';
5366
5367
        if (isset($from) && isset($numberItems)) {
5368
            $from = (int) $from;
5369
            $numberItems = (int) $numberItems;
5370
            $limitCondition = "LIMIT $from, $numberItems";
5371
        }
5372
5373
        $column = Database::escape_string($column);
5374
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
5375
5376
        $userConditions = '';
5377
        if (!empty($userStatus)) {
5378
            $userConditions .= ' AND u.status = '.intval($userStatus);
5379
        }
5380
5381
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
5382
        if ($getOnlyUserId) {
5383
            $select = " SELECT DISTINCT u.id user_id";
5384
        }
5385
5386
        $masterSelect = "SELECT DISTINCT * FROM ";
5387
5388
        if ($getCount) {
5389
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
5390
            $select = " SELECT DISTINCT(u.id) user_id";
5391
        }
5392
5393
        if (!is_null($active)) {
5394
            $active = intval($active);
5395
            $userConditions .= " AND u.active = $active ";
5396
        }
5397
5398
        if (!empty($keyword)) {
5399
            $keyword = trim(Database::escape_string($keyword));
5400
            $keywordParts = array_filter(explode(' ', $keyword));
5401
            $extraKeyword = '';
5402
            if (!empty($keywordParts)) {
5403
                $keywordPartsFixed = Database::escape_string(implode('%', $keywordParts));
5404
                if (!empty($keywordPartsFixed)) {
5405
                    $extraKeyword .= " OR
5406
                        CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keywordPartsFixed%' OR
5407
                        CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keywordPartsFixed%' ";
5408
                }
5409
            }
5410
5411
            $userConditions .= " AND (
5412
                u.username LIKE '%$keyword%' OR
5413
                u.firstname LIKE '%$keyword%' OR
5414
                u.lastname LIKE '%$keyword%' OR
5415
                u.official_code LIKE '%$keyword%' OR
5416
                u.email LIKE '%$keyword%' OR
5417
                CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keyword%' OR
5418
                CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keyword%'
5419
                $extraKeyword
5420
            )";
5421
        }
5422
5423
        if (!empty($lastConnectionDate)) {
5424
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
5425
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
5426
        }
5427
5428
        $sessionConditionsCoach = null;
5429
        $dateCondition = '';
5430
        $drhConditions = null;
5431
        $teacherSelect = null;
5432
5433
        $urlId = api_get_current_access_url_id();
5434
5435
        switch ($status) {
5436
            case DRH:
5437
                $drhConditions .= " AND
5438
                    friend_user_id = '$userId' AND
5439
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
5440
                ";
5441
                break;
5442
            case COURSEMANAGER:
5443
                $drhConditions .= " AND
5444
                    friend_user_id = '$userId' AND
5445
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
5446
                ";
5447
5448
                $sessionConditionsCoach .= " AND
5449
                    (s.id_coach = '$userId')
5450
                ";
5451
5452
                $sessionConditionsTeacher = " AND
5453
                    (scu.status = 2 AND scu.user_id = '$userId')
5454
                ";
5455
5456
                if ($checkSessionVisibility) {
5457
                    $today = api_strtotime('now', 'UTC');
5458
                    $today = date('Y-m-d', $today);
5459
                    $dateCondition = "
5460
                        AND
5461
                        (
5462
                            (s.access_start_date <= '$today' AND '$today' <= s.access_end_date) OR
5463
                            (s.access_start_date IS NULL AND s.access_end_date IS NULL) OR
5464
                            (s.access_start_date <= '$today' AND s.access_end_date IS NULL) OR
5465
                            ('$today' <= s.access_end_date AND s.access_start_date IS NULL)
5466
                        )
5467
					";
5468
                }
5469
5470
                // Use $tbl_session_rel_course_rel_user instead of $tbl_session_rel_user
5471
                /*
5472
                INNER JOIN $tbl_session_rel_user sru
5473
                ON (sru.user_id = u.id)
5474
                INNER JOIN $tbl_session_rel_course_rel_user scu
5475
                ON (scu.user_id = u.id AND scu.c_id IS NOT NULL AND visibility = 1)*/
5476
                $teacherSelect =
5477
                "UNION ALL (
5478
                        $select
5479
                        FROM $tbl_user u
5480
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
5481
                        WHERE
5482
                            (
5483
                                sru.session_id IN (
5484
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
5485
                                    $tbl_session_rel_access_url session_rel_access_rel_user
5486
                                    ON session_rel_access_rel_user.session_id = s.id
5487
                                    WHERE access_url_id = ".$urlId."
5488
                                    $sessionConditionsCoach
5489
                                ) OR sru.session_id IN (
5490
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
5491
                                    INNER JOIN $tbl_session_rel_access_url url
5492
                                    ON (url.session_id = s.id)
5493
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
5494
                                    ON (scu.session_id = s.id)
5495
                                    WHERE access_url_id = ".$urlId."
5496
                                    $sessionConditionsTeacher
5497
                                    $dateCondition
5498
                                )
5499
                            )
5500
                            $userConditions
5501
                    )
5502
                    UNION ALL(
5503
                        $select
5504
                        FROM $tbl_user u
5505
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
5506
                        WHERE cu.c_id IN (
5507
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
5508
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
5509
                        )
5510
                        $userConditions
5511
                    )"
5512
                ;
5513
                break;
5514
            case STUDENT_BOSS:
5515
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
5516
                break;
5517
            case HRM_REQUEST:
5518
                $drhConditions .= " AND
5519
                    friend_user_id = '$userId' AND
5520
                    relation_type = '".USER_RELATION_TYPE_HRM_REQUEST."'
5521
                ";
5522
                break;
5523
        }
5524
5525
        $join = null;
5526
        $sql = " $masterSelect
5527
                (
5528
                    (
5529
                        $select
5530
                        FROM $tbl_user u
5531
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
5532
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
5533
                        $join
5534
                        WHERE
5535
                            access_url_id = ".$urlId."
5536
                            $drhConditions
5537
                            $userConditions
5538
                    )
5539
                    $teacherSelect
5540
                ) as t1";
5541
5542
        if ($getSql) {
5543
            return $sql;
5544
        }
5545
5546
        if ($getCount) {
5547
            $result = Database::query($sql);
5548
            $row = Database::fetch_array($result);
5549
5550
            return $row['count'];
5551
        }
5552
5553
        $orderBy = null;
5554
        if ($getOnlyUserId == false) {
5555
            if (api_is_western_name_order()) {
5556
                $orderBy .= " ORDER BY firstname, lastname ";
5557
            } else {
5558
                $orderBy .= " ORDER BY lastname, firstname ";
5559
            }
5560
5561
            if (!empty($column) && !empty($direction)) {
5562
                // Fixing order due the UNIONs
5563
                $column = str_replace('u.', '', $column);
5564
                $orderBy = " ORDER BY `$column` $direction ";
5565
            }
5566
        }
5567
5568
        $sql .= $orderBy;
5569
        $sql .= $limitCondition;
5570
5571
        $result = Database::query($sql);
5572
        $users = [];
5573
        if (Database::num_rows($result) > 0) {
5574
            while ($row = Database::fetch_array($result)) {
5575
                $users[$row['user_id']] = $row;
5576
            }
5577
        }
5578
5579
        return $users;
5580
    }
5581
5582
    /**
5583
     * Subscribes users to human resource manager (Dashboard feature).
5584
     *
5585
     * @param int   $hr_dept_id
5586
     * @param array $users_id
5587
     * @param bool  $deleteOtherAssignedUsers
5588
     *
5589
     * @return int
5590
     */
5591
    public static function subscribeUsersToHRManager(
5592
        $hr_dept_id,
5593
        $users_id,
5594
        $deleteOtherAssignedUsers = true
5595
    ) {
5596
        return self::subscribeUsersToUser(
5597
            $hr_dept_id,
5598
            $users_id,
5599
            USER_RELATION_TYPE_RRHH,
5600
            false,
5601
            $deleteOtherAssignedUsers
5602
        );
5603
    }
5604
5605
    /**
5606
     * Register request to assign users to HRM.
5607
     *
5608
     * @param int   $hrmId   The HRM ID
5609
     * @param array $usersId The users IDs
5610
     *
5611
     * @return int
5612
     */
5613
    public static function requestUsersToHRManager($hrmId, $usersId)
5614
    {
5615
        return self::subscribeUsersToUser(
5616
            $hrmId,
5617
            $usersId,
5618
            USER_RELATION_TYPE_HRM_REQUEST,
5619
            false,
5620
            false
5621
        );
5622
    }
5623
5624
    /**
5625
     * Remove the requests for assign a user to a HRM.
5626
     *
5627
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
5628
     */
5629
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
5630
    {
5631
        $users = implode(', ', $usersId);
5632
        Database::getManager()
5633
            ->createQuery('
5634
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
5635
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
5636
            ')
5637
            ->execute(['hrm_id' => $hrmId, 'relation_type' => USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
5638
    }
5639
5640
    /**
5641
     * Add subscribed users to a user by relation type.
5642
     *
5643
     * @param int    $userId                   The user id
5644
     * @param array  $subscribedUsersId        The id of subscribed users
5645
     * @param string $relationType             The relation type
5646
     * @param bool   $deleteUsersBeforeInsert
5647
     * @param bool   $deleteOtherAssignedUsers
5648
     *
5649
     * @return int
5650
     */
5651
    public static function subscribeUsersToUser(
5652
        $userId,
5653
        $subscribedUsersId,
5654
        $relationType,
5655
        $deleteUsersBeforeInsert = false,
5656
        $deleteOtherAssignedUsers = true
5657
    ) {
5658
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5659
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5660
5661
        $userId = (int) $userId;
5662
        $relationType = (int) $relationType;
5663
        $affectedRows = 0;
5664
5665
        if ($deleteOtherAssignedUsers) {
5666
            if (api_get_multiple_access_url()) {
5667
                // Deleting assigned users to hrm_id
5668
                $sql = "SELECT s.user_id
5669
                        FROM $userRelUserTable s
5670
                        INNER JOIN $userRelAccessUrlTable a
5671
                        ON (a.user_id = s.user_id)
5672
                        WHERE
5673
                            friend_user_id = $userId AND
5674
                            relation_type = $relationType AND
5675
                            access_url_id = ".api_get_current_access_url_id();
5676
            } else {
5677
                $sql = "SELECT user_id
5678
                        FROM $userRelUserTable
5679
                        WHERE
5680
                            friend_user_id = $userId AND
5681
                            relation_type = $relationType";
5682
            }
5683
            $result = Database::query($sql);
5684
5685
            if (Database::num_rows($result) > 0) {
5686
                while ($row = Database::fetch_array($result)) {
5687
                    $sql = "DELETE FROM $userRelUserTable
5688
                            WHERE
5689
                                user_id = {$row['user_id']} AND
5690
                                friend_user_id = $userId AND
5691
                                relation_type = $relationType";
5692
                    Database::query($sql);
5693
                }
5694
            }
5695
        }
5696
5697
        if ($deleteUsersBeforeInsert) {
5698
            $sql = "DELETE FROM $userRelUserTable
5699
                    WHERE
5700
                        user_id = $userId AND
5701
                        relation_type = $relationType";
5702
            Database::query($sql);
5703
        }
5704
5705
        // Inserting new user list
5706
        if (is_array($subscribedUsersId)) {
5707
            foreach ($subscribedUsersId as $subscribedUserId) {
5708
                $subscribedUserId = (int) $subscribedUserId;
5709
                $sql = "SELECT id
5710
                        FROM $userRelUserTable
5711
                        WHERE
5712
                            user_id = $subscribedUserId AND
5713
                            friend_user_id = $userId AND
5714
                            relation_type = $relationType";
5715
5716
                $result = Database::query($sql);
5717
                $num = Database::num_rows($result);
5718
                if ($num === 0) {
5719
                    $date = api_get_utc_datetime();
5720
                    $sql = "INSERT INTO $userRelUserTable (user_id, friend_user_id, relation_type, last_edit)
5721
                            VALUES ($subscribedUserId, $userId, $relationType, '$date')";
5722
                    $result = Database::query($sql);
5723
                    $affectedRows += Database::affected_rows($result);
5724
                }
5725
            }
5726
        }
5727
5728
        return $affectedRows;
5729
    }
5730
5731
    /**
5732
     * This function check if an user is followed by human resources manager.
5733
     *
5734
     * @param int $user_id
5735
     * @param int $hr_dept_id Human resources manager
5736
     *
5737
     * @return bool
5738
     */
5739
    public static function is_user_followed_by_drh($user_id, $hr_dept_id)
5740
    {
5741
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5742
        $user_id = (int) $user_id;
5743
        $hr_dept_id = (int) $hr_dept_id;
5744
        $result = false;
5745
5746
        $sql = "SELECT user_id FROM $tbl_user_rel_user
5747
                WHERE
5748
                    user_id = $user_id AND
5749
                    friend_user_id = $hr_dept_id AND
5750
                    relation_type = ".USER_RELATION_TYPE_RRHH;
5751
        $rs = Database::query($sql);
5752
        if (Database::num_rows($rs) > 0) {
5753
            $result = true;
5754
        }
5755
5756
        return $result;
5757
    }
5758
5759
    /**
5760
     * Return the user id of teacher or session administrator.
5761
     *
5762
     * @return int|bool The user id, or false if the session ID was negative
5763
     */
5764
    public static function get_user_id_of_course_admin_or_session_admin(array $courseInfo)
5765
    {
5766
        $session = api_get_session_id();
5767
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5768
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5769
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5770
5771
        if (empty($courseInfo)) {
5772
            return false;
5773
        }
5774
5775
        $courseId = $courseInfo['real_id'];
5776
5777
        if (empty($session)) {
5778
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
5779
                    INNER JOIN '.$table_course_user.' ru
5780
                    ON ru.user_id = u.id
5781
                    WHERE
5782
                        ru.status = '.COURSEMANAGER.' AND
5783
                        ru.c_id = "'.$courseId.'" ';
5784
        } else {
5785
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
5786
                    INNER JOIN '.$table_session_course_user.' sru
5787
                    ON sru.user_id=u.id
5788
                    WHERE
5789
                        sru.c_id="'.$courseId.'" AND
5790
                        sru.status = '.SessionEntity::COACH;
5791
        }
5792
5793
        $rs = Database::query($sql);
5794
        $num_rows = Database::num_rows($rs);
5795
5796
        if (0 === $num_rows) {
5797
            return false;
5798
        }
5799
5800
        if (1 === $num_rows) {
5801
            $row = Database::fetch_array($rs);
5802
5803
            return (int) $row['uid'];
5804
        }
5805
5806
        $my_num_rows = $num_rows;
5807
        $my_user_id = Database::result($rs, $my_num_rows - 1, 'uid');
5808
5809
        return (int) $my_user_id;
5810
    }
5811
5812
    /**
5813
     * Determines if a user is a gradebook certified.
5814
     *
5815
     * @param int $cat_id  The category id of gradebook
5816
     * @param int $user_id The user id
5817
     *
5818
     * @return bool
5819
     */
5820
    public static function is_user_certified($cat_id, $user_id)
5821
    {
5822
        $cat_id = (int) $cat_id;
5823
        $user_id = (int) $user_id;
5824
5825
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
5826
        $sql = 'SELECT path_certificate
5827
                FROM '.$table.'
5828
                WHERE
5829
                    cat_id = "'.$cat_id.'" AND
5830
                    user_id = "'.$user_id.'"';
5831
        $rs = Database::query($sql);
5832
        $row = Database::fetch_array($rs);
5833
5834
        if ($row['path_certificate'] == '' || is_null($row['path_certificate'])) {
5835
            return false;
5836
        }
5837
5838
        return true;
5839
    }
5840
5841
    /**
5842
     * Gets the info about a gradebook certificate for a user by course.
5843
     *
5844
     * @param string $course_code The course code
5845
     * @param int    $session_id
5846
     * @param int    $user_id     The user id
5847
     *
5848
     * @return array if there is not information return false
5849
     */
5850
    public static function get_info_gradebook_certificate($course_code, $session_id, $user_id)
5851
    {
5852
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
5853
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
5854
        $session_id = (int) $session_id;
5855
        $user_id = (int) $user_id;
5856
5857
        if (empty($session_id)) {
5858
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
5859
        } else {
5860
            $session_condition = " AND session_id = $session_id";
5861
        }
5862
5863
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.'
5864
                WHERE cat_id = (
5865
                    SELECT id FROM '.$tbl_grade_category.'
5866
                    WHERE
5867
                        course_code = "'.Database::escape_string($course_code).'" '.$session_condition.'
5868
                    LIMIT 1
5869
                ) AND user_id='.$user_id;
5870
5871
        $rs = Database::query($sql);
5872
        if (Database::num_rows($rs) > 0) {
5873
            $row = Database::fetch_array($rs, 'ASSOC');
5874
            $score = $row['score_certificate'];
5875
            $category_id = $row['cat_id'];
5876
            $cat = Category::load($category_id);
5877
            $displayscore = ScoreDisplay::instance();
5878
            if (isset($cat) && $displayscore->is_custom()) {
5879
                $grade = $displayscore->display_score(
5880
                    [$score, $cat[0]->get_weight()],
5881
                    SCORE_DIV_PERCENT_WITH_CUSTOM
5882
                );
5883
            } else {
5884
                $grade = $displayscore->display_score(
5885
                    [$score, $cat[0]->get_weight()]
5886
                );
5887
            }
5888
            $row['grade'] = $grade;
5889
5890
            return $row;
5891
        }
5892
5893
        return false;
5894
    }
5895
5896
    /**
5897
     * This function check if the user is a coach inside session course.
5898
     *
5899
     * @param int $user_id    User id
5900
     * @param int $courseId
5901
     * @param int $session_id
5902
     *
5903
     * @return bool True if the user is a coach
5904
     */
5905
    public static function is_session_course_coach($user_id, $courseId, $session_id)
5906
    {
5907
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5908
        // Protect data
5909
        $user_id = intval($user_id);
5910
        $courseId = intval($courseId);
5911
        $session_id = intval($session_id);
5912
        $result = false;
5913
5914
        $sql = "SELECT session_id FROM $table
5915
                WHERE
5916
                  session_id = $session_id AND
5917
                  c_id = $courseId AND
5918
                  user_id = $user_id AND
5919
                  status = 2 ";
5920
        $res = Database::query($sql);
5921
5922
        if (Database::num_rows($res) > 0) {
5923
            $result = true;
5924
        }
5925
5926
        return $result;
5927
    }
5928
5929
    /**
5930
     * This function returns an icon path that represents the favicon of the website of which the url given.
5931
     * Defaults to the current Chamilo favicon.
5932
     *
5933
     * @param string $url1 URL of website where to look for favicon.ico
5934
     * @param string $url2 Optional second URL of website where to look for favicon.ico
5935
     *
5936
     * @return string Path of icon to load
5937
     */
5938
    public static function get_favicon_from_url($url1, $url2 = null)
5939
    {
5940
        $icon_link = '';
5941
        $url = $url1;
5942
        if (empty($url1)) {
5943
            $url = $url2;
5944
            if (empty($url)) {
5945
                $url = api_get_access_url(api_get_current_access_url_id());
5946
                $url = $url[0];
5947
            }
5948
        }
5949
        if (!empty($url)) {
5950
            $pieces = parse_url($url);
5951
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
5952
        }
5953
5954
        return $icon_link;
5955
    }
5956
5957
    public static function addUserAsAdmin(User $user)
5958
    {
5959
        if ($user) {
5960
            $userId = $user->getId();
5961
            if (!self::is_admin($userId)) {
5962
                $table = Database::get_main_table(TABLE_MAIN_ADMIN);
5963
                $sql = "INSERT INTO $table SET user_id = $userId";
5964
                Database::query($sql);
5965
            }
5966
5967
            $user->addRole('ROLE_SUPER_ADMIN');
5968
            self::getManager()->updateUser($user, true);
5969
        }
5970
    }
5971
5972
    public static function removeUserAdmin(User $user)
5973
    {
5974
        $userId = (int) $user->getId();
5975
        if (self::is_admin($userId)) {
5976
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
5977
            $sql = "DELETE FROM $table WHERE user_id = $userId";
5978
            Database::query($sql);
5979
            $user->removeRole('ROLE_SUPER_ADMIN');
5980
            self::getManager()->updateUser($user, true);
5981
        }
5982
    }
5983
5984
    /**
5985
     * @param string $from
5986
     * @param string $to
5987
     */
5988
    public static function update_all_user_languages($from, $to)
5989
    {
5990
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5991
        $from = Database::escape_string($from);
5992
        $to = Database::escape_string($to);
5993
5994
        if (!empty($to) && !empty($from)) {
5995
            $sql = "UPDATE $table_user SET language = '$to'
5996
                    WHERE language = '$from'";
5997
            Database::query($sql);
5998
        }
5999
    }
6000
6001
    /**
6002
     * Subscribe boss to students.
6003
     *
6004
     * @param int   $bossId                   The boss id
6005
     * @param array $usersId                  The users array
6006
     * @param bool  $deleteOtherAssignedUsers
6007
     *
6008
     * @return int Affected rows
6009
     */
6010
    public static function subscribeBossToUsers($bossId, $usersId, $deleteOtherAssignedUsers = true)
6011
    {
6012
        return self::subscribeUsersToUser(
6013
            $bossId,
6014
            $usersId,
6015
            USER_RELATION_TYPE_BOSS,
6016
            false,
6017
            $deleteOtherAssignedUsers
6018
        );
6019
    }
6020
6021
    /**
6022
     * @param int $userId
6023
     *
6024
     * @return bool
6025
     */
6026
    public static function removeAllBossFromStudent($userId)
6027
    {
6028
        $userId = (int) $userId;
6029
6030
        if (empty($userId)) {
6031
            return false;
6032
        }
6033
6034
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6035
        $sql = "DELETE FROM $userRelUserTable
6036
                WHERE user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
6037
        Database::query($sql);
6038
6039
        return true;
6040
    }
6041
6042
    /**
6043
     * Subscribe boss to students, if $bossList is empty then the boss list will be empty too.
6044
     *
6045
     * @param int   $studentId
6046
     * @param array $bossList
6047
     * @param bool  $sendNotification
6048
     *
6049
     * @return mixed Affected rows or false on failure
6050
     */
6051
    public static function subscribeUserToBossList(
6052
        $studentId,
6053
        $bossList,
6054
        $sendNotification = false
6055
    ) {
6056
        $inserted = 0;
6057
        if (!empty($bossList)) {
6058
            sort($bossList);
6059
            $studentId = (int) $studentId;
6060
            $studentInfo = api_get_user_info($studentId);
6061
6062
            if (empty($studentInfo)) {
6063
                return false;
6064
            }
6065
6066
            $previousBossList = self::getStudentBossList($studentId);
6067
            $previousBossList = !empty($previousBossList) ? array_column($previousBossList, 'boss_id') : [];
6068
            sort($previousBossList);
6069
6070
            // Boss list is the same, nothing changed.
6071
            if ($bossList == $previousBossList) {
6072
                return false;
6073
            }
6074
6075
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6076
            self::removeAllBossFromStudent($studentId);
6077
6078
            foreach ($bossList as $bossId) {
6079
                $bossId = (int) $bossId;
6080
                $bossInfo = api_get_user_info($bossId);
6081
6082
                if (empty($bossInfo)) {
6083
                    continue;
6084
                }
6085
6086
                $bossLanguage = $bossInfo['language'];
6087
6088
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
6089
                        VALUES ($studentId, $bossId, ".USER_RELATION_TYPE_BOSS.")";
6090
                $insertId = Database::query($sql);
6091
6092
                if ($insertId) {
6093
                    if ($sendNotification) {
6094
                        $name = $studentInfo['complete_name'];
6095
                        $url = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?student='.$studentId;
6096
                        $url = Display::url($url, $url);
6097
                        $subject = sprintf(
6098
                            get_lang('UserXHasBeenAssignedToBoss', false, $bossLanguage),
6099
                            $name
6100
                        );
6101
                        $message = sprintf(
6102
                            get_lang('UserXHasBeenAssignedToBossWithUrlX', false, $bossLanguage),
6103
                            $name,
6104
                            $url
6105
                        );
6106
                        MessageManager::send_message_simple(
6107
                            $bossId,
6108
                            $subject,
6109
                            $message
6110
                        );
6111
                    }
6112
                    $inserted++;
6113
                }
6114
            }
6115
        } else {
6116
            self::removeAllBossFromStudent($studentId);
6117
        }
6118
6119
        return $inserted;
6120
    }
6121
6122
    /**
6123
     * Get users followed by student boss.
6124
     *
6125
     * @param int    $userId
6126
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
6127
     * @param bool   $getOnlyUserId
6128
     * @param bool   $getSql
6129
     * @param bool   $getCount
6130
     * @param int    $from
6131
     * @param int    $numberItems
6132
     * @param int    $column
6133
     * @param string $direction
6134
     * @param int    $active
6135
     * @param string $lastConnectionDate
6136
     *
6137
     * @return array users
6138
     */
6139
    public static function getUsersFollowedByStudentBoss(
6140
        $userId,
6141
        $userStatus = 0,
6142
        $getOnlyUserId = false,
6143
        $getSql = false,
6144
        $getCount = false,
6145
        $from = null,
6146
        $numberItems = null,
6147
        $column = null,
6148
        $direction = null,
6149
        $active = null,
6150
        $lastConnectionDate = null
6151
    ) {
6152
        return self::getUsersFollowedByUser(
6153
            $userId,
6154
            $userStatus,
6155
            $getOnlyUserId,
6156
            $getSql,
6157
            $getCount,
6158
            $from,
6159
            $numberItems,
6160
            $column,
6161
            $direction,
6162
            $active,
6163
            $lastConnectionDate,
6164
            STUDENT_BOSS
6165
        );
6166
    }
6167
6168
    /**
6169
     * @return array
6170
     */
6171
    public static function getOfficialCodeGrouped()
6172
    {
6173
        $user = Database::get_main_table(TABLE_MAIN_USER);
6174
        $sql = "SELECT DISTINCT official_code
6175
                FROM $user
6176
                GROUP BY official_code";
6177
        $result = Database::query($sql);
6178
        $values = Database::store_result($result, 'ASSOC');
6179
        $result = [];
6180
        foreach ($values as $value) {
6181
            $result[$value['official_code']] = $value['official_code'];
6182
        }
6183
6184
        return $result;
6185
    }
6186
6187
    /**
6188
     * @param string $officialCode
6189
     *
6190
     * @return array
6191
     */
6192
    public static function getUsersByOfficialCode($officialCode)
6193
    {
6194
        $user = Database::get_main_table(TABLE_MAIN_USER);
6195
        $officialCode = Database::escape_string($officialCode);
6196
6197
        $sql = "SELECT DISTINCT id
6198
                FROM $user
6199
                WHERE official_code = '$officialCode'
6200
                ";
6201
        $result = Database::query($sql);
6202
6203
        $users = [];
6204
        while ($row = Database::fetch_array($result)) {
6205
            $users[] = $row['id'];
6206
        }
6207
6208
        return $users;
6209
    }
6210
6211
    /**
6212
     * Calc the expended time (in seconds) by a user in a course.
6213
     *
6214
     * @param int    $userId    The user id
6215
     * @param int    $courseId  The course id
6216
     * @param int    $sessionId Optional. The session id
6217
     * @param string $from      Optional. From date
6218
     * @param string $until     Optional. Until date
6219
     *
6220
     * @return int The time
6221
     */
6222
    public static function getTimeSpentInCourses(
6223
        $userId,
6224
        $courseId,
6225
        $sessionId = 0,
6226
        $from = '',
6227
        $until = ''
6228
    ) {
6229
        $userId = (int) $userId;
6230
        $sessionId = (int) $sessionId;
6231
6232
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6233
        $whereConditions = [
6234
            'user_id = ? ' => $userId,
6235
            'AND c_id = ? ' => $courseId,
6236
            'AND session_id = ? ' => $sessionId,
6237
        ];
6238
6239
        if (!empty($from) && !empty($until)) {
6240
            $whereConditions["AND (login_course_date >= '?' "] = $from;
6241
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
6242
        }
6243
6244
        $trackResult = Database::select(
6245
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
6246
            $trackCourseAccessTable,
6247
            [
6248
                'where' => $whereConditions,
6249
            ],
6250
            'first'
6251
        );
6252
6253
        if ($trackResult != false) {
6254
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
6255
        }
6256
6257
        return 0;
6258
    }
6259
6260
    /**
6261
     * Get the boss user ID from a followed user id.
6262
     *
6263
     * @param $userId
6264
     *
6265
     * @return bool
6266
     */
6267
    public static function getFirstStudentBoss($userId)
6268
    {
6269
        $userId = (int) $userId;
6270
        if ($userId > 0) {
6271
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6272
            $row = Database::select(
6273
                'DISTINCT friend_user_id AS boss_id',
6274
                $userRelTable,
6275
                [
6276
                    'where' => [
6277
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
6278
                            $userId,
6279
                            USER_RELATION_TYPE_BOSS,
6280
                        ],
6281
                    ],
6282
                ]
6283
            );
6284
            if (!empty($row)) {
6285
                return $row[0]['boss_id'];
6286
            }
6287
        }
6288
6289
        return false;
6290
    }
6291
6292
    /**
6293
     * Get the boss user ID from a followed user id.
6294
     *
6295
     * @param int $userId student id
6296
     *
6297
     * @return array
6298
     */
6299
    public static function getStudentBossList($userId)
6300
    {
6301
        $userId = (int) $userId;
6302
6303
        if ($userId > 0) {
6304
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6305
6306
            return Database::select(
6307
                'DISTINCT friend_user_id AS boss_id',
6308
                $userRelTable,
6309
                [
6310
                    'where' => [
6311
                        'user_id = ? AND relation_type = ? ' => [
6312
                            $userId,
6313
                            USER_RELATION_TYPE_BOSS,
6314
                        ],
6315
                    ],
6316
                ]
6317
            );
6318
        }
6319
6320
        return [];
6321
    }
6322
6323
    /**
6324
     * @param int $bossId
6325
     * @param int $studentId
6326
     *
6327
     * @return bool
6328
     */
6329
    public static function userIsBossOfStudent($bossId, $studentId)
6330
    {
6331
        $result = false;
6332
        $bossList = self::getStudentBossList($studentId);
6333
        if (!empty($bossList)) {
6334
            $bossList = array_column($bossList, 'boss_id');
6335
            if (in_array($bossId, $bossList)) {
6336
                $result = true;
6337
            }
6338
        }
6339
6340
        return $result;
6341
    }
6342
6343
    /**
6344
     * Displays the name of the user and makes the link to the user profile.
6345
     *
6346
     * @param array $userInfo
6347
     *
6348
     * @return string
6349
     */
6350
    public static function getUserProfileLink($userInfo)
6351
    {
6352
        if (isset($userInfo) && isset($userInfo['user_id'])) {
6353
            return Display::url(
6354
                $userInfo['complete_name_with_username'],
6355
                $userInfo['profile_url']
6356
            );
6357
        }
6358
6359
        return get_lang('Anonymous');
6360
    }
6361
6362
    /**
6363
     * Get users whose name matches $firstname and $lastname.
6364
     *
6365
     * @param string $firstname Firstname to search
6366
     * @param string $lastname  Lastname to search
6367
     *
6368
     * @return array The user list
6369
     */
6370
    public static function getUsersByName($firstname, $lastname)
6371
    {
6372
        $firstname = Database::escape_string($firstname);
6373
        $lastname = Database::escape_string($lastname);
6374
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
6375
6376
        $sql = <<<SQL
6377
            SELECT id, username, lastname, firstname
6378
            FROM $userTable
6379
            WHERE
6380
                firstname LIKE '$firstname%' AND
6381
                lastname LIKE '$lastname%'
6382
SQL;
6383
        $result = Database::query($sql);
6384
        $users = [];
6385
        while ($resultData = Database::fetch_object($result)) {
6386
            $users[] = $resultData;
6387
        }
6388
6389
        return $users;
6390
    }
6391
6392
    /**
6393
     * @param int $optionSelected
6394
     *
6395
     * @return string
6396
     */
6397
    public static function getUserSubscriptionTab($optionSelected = 1)
6398
    {
6399
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
6400
        if (($allowAdmin === 'true' && api_is_allowed_to_edit()) ||
6401
            api_is_platform_admin()
6402
        ) {
6403
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
6404
6405
            $headers = [
6406
                [
6407
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
6408
                    'content' => get_lang('Students'),
6409
                ],
6410
                [
6411
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
6412
                    'content' => get_lang('Teachers'),
6413
                ],
6414
                /*[
6415
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
6416
                    'content' => get_lang('Students'),
6417
                ],
6418
                [
6419
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
6420
                    'content' => get_lang('Teachers'),
6421
                ],*/
6422
                [
6423
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
6424
                    'content' => get_lang('Groups'),
6425
                ],
6426
                [
6427
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
6428
                    'content' => get_lang('Classes'),
6429
                ],
6430
            ];
6431
6432
            return Display::tabsOnlyLink($headers, $optionSelected);
6433
        }
6434
6435
        return '';
6436
    }
6437
6438
    /**
6439
     * Make sure this function is protected because it does NOT check password!
6440
     *
6441
     * This function defines globals.
6442
     *
6443
     * @param int  $userId
6444
     * @param bool $checkIfUserCanLoginAs
6445
     *
6446
     * @return bool
6447
     *
6448
     * @author Evie Embrechts
6449
     * @author Yannick Warnier <[email protected]>
6450
     */
6451
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
6452
    {
6453
        $userId = (int) $userId;
6454
        $userInfo = api_get_user_info($userId);
6455
6456
        // Check if the user is allowed to 'login_as'
6457
        $canLoginAs = true;
6458
        if ($checkIfUserCanLoginAs) {
6459
            $canLoginAs = api_can_login_as($userId);
6460
        }
6461
6462
        if (!$canLoginAs || empty($userInfo)) {
6463
            return false;
6464
        }
6465
6466
        if ($userId) {
6467
            $logInfo = [
6468
                'tool' => 'logout',
6469
                'tool_id' => 0,
6470
                'tool_id_detail' => 0,
6471
                'action' => '',
6472
                'info' => 'Change user (login as)',
6473
            ];
6474
            Event::registerLog($logInfo);
6475
6476
            // Logout the current user
6477
            self::loginDelete(api_get_user_id());
6478
6479
            Session::erase('_user');
6480
            Session::erase('is_platformAdmin');
6481
            Session::erase('is_allowedCreateCourse');
6482
            Session::erase('_uid');
6483
6484
            // Cleaning session variables
6485
            $_user['firstName'] = $userInfo['firstname'];
6486
            $_user['lastName'] = $userInfo['lastname'];
6487
            $_user['mail'] = $userInfo['email'];
6488
            $_user['official_code'] = $userInfo['official_code'];
6489
            $_user['picture_uri'] = $userInfo['picture_uri'];
6490
            $_user['user_id'] = $userId;
6491
            $_user['id'] = $userId;
6492
            $_user['status'] = $userInfo['status'];
6493
6494
            // Filling session variables with new data
6495
            Session::write('_uid', $userId);
6496
            Session::write('_user', $userInfo);
6497
            Session::write('is_platformAdmin', (bool) self::is_admin($userId));
6498
            Session::write('is_allowedCreateCourse', $userInfo['status'] == 1);
6499
            // will be useful later to know if the user is actually an admin or not (example reporting)
6500
            Session::write('login_as', true);
6501
            $logInfo = [
6502
                'tool' => 'login',
6503
                'tool_id' => 0,
6504
                'tool_id_detail' => 0,
6505
                'info' => $userId,
6506
            ];
6507
            Event::registerLog($logInfo);
6508
6509
            return true;
6510
        }
6511
6512
        return false;
6513
    }
6514
6515
    /**
6516
     * Remove all login records from the track_e_online stats table,
6517
     * for the given user ID.
6518
     *
6519
     * @param int $userId User ID
6520
     */
6521
    public static function loginDelete($userId)
6522
    {
6523
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6524
        $userId = (int) $userId;
6525
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
6526
        Database::query($query);
6527
    }
6528
6529
    /**
6530
     * Login as first admin user registered in the platform.
6531
     *
6532
     * @return array
6533
     */
6534
    public static function logInAsFirstAdmin()
6535
    {
6536
        $adminList = self::get_all_administrators();
6537
6538
        if (!empty($adminList)) {
6539
            $userInfo = current($adminList);
6540
            if (!empty($userInfo)) {
6541
                $result = self::loginAsUser($userInfo['user_id'], false);
6542
                if ($result && api_is_platform_admin()) {
6543
                    return api_get_user_info();
6544
                }
6545
            }
6546
        }
6547
6548
        return [];
6549
    }
6550
6551
    /**
6552
     * Check if user is teacher of a student based in their courses.
6553
     *
6554
     * @param $teacherId
6555
     * @param $studentId
6556
     *
6557
     * @return array
6558
     */
6559
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
6560
    {
6561
        $courses = CourseManager::getCoursesFollowedByUser(
6562
            $teacherId,
6563
            COURSEMANAGER
6564
        );
6565
        if (empty($courses)) {
6566
            return false;
6567
        }
6568
6569
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
6570
        if (empty($coursesFromUser)) {
6571
            return false;
6572
        }
6573
6574
        $coursesCodeList = array_column($courses, 'code');
6575
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
6576
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
6577
        $commonCourses = array_filter($commonCourses);
6578
6579
        if (!empty($commonCourses)) {
6580
            return $commonCourses;
6581
        }
6582
6583
        return [];
6584
    }
6585
6586
    /**
6587
     * @param int $teacherId
6588
     * @param int $studentId
6589
     *
6590
     * @return bool
6591
     */
6592
    public static function isTeacherOfStudent($teacherId, $studentId)
6593
    {
6594
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
6595
            $teacherId,
6596
            $studentId
6597
        );
6598
6599
        if (!empty($courses)) {
6600
            return true;
6601
        }
6602
6603
        return false;
6604
    }
6605
6606
    /**
6607
     * Send user confirmation mail.
6608
     *
6609
     * @throws Exception
6610
     */
6611
    public static function sendUserConfirmationMail(User $user)
6612
    {
6613
        $uniqueId = api_get_unique_id();
6614
        $user->setConfirmationToken($uniqueId);
6615
6616
        Database::getManager()->persist($user);
6617
        Database::getManager()->flush();
6618
6619
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
6620
6621
        // Check if the user was originally set for an automated subscription to a course or session
6622
        $courseCodeToRedirect = Session::read('course_redirect');
6623
        $sessionToRedirect = Session::read('session_redirect');
6624
        if (!empty($courseCodeToRedirect)) {
6625
            $url .= '&c='.$courseCodeToRedirect;
6626
        }
6627
        if (!empty($sessionToRedirect)) {
6628
            $url .= '&s='.$sessionToRedirect;
6629
        }
6630
        $mailSubject = get_lang('RegistrationConfirmation');
6631
        $mailBody = get_lang('RegistrationConfirmationEmailMessage')
6632
            .PHP_EOL
6633
            .Display::url($url, $url);
6634
6635
        api_mail_html(
6636
            self::formatUserFullName($user),
6637
            $user->getEmail(),
6638
            $mailSubject,
6639
            $mailBody
6640
        );
6641
        Display::addFlash(Display::return_message(get_lang('CheckYourEmailAndFollowInstructions')));
6642
    }
6643
6644
    /**
6645
     * Anonymize a user. Replace personal info by anonymous info.
6646
     *
6647
     * @param int  $userId   User id
6648
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
6649
     *
6650
     * @throws \Exception
6651
     *
6652
     * @return bool
6653
     * @assert (0) === false
6654
     */
6655
    public static function anonymize($userId, $deleteIP = true)
6656
    {
6657
        global $debug;
6658
6659
        $userId = (int) $userId;
6660
6661
        if (empty($userId)) {
6662
            return false;
6663
        }
6664
6665
        $em = Database::getManager();
6666
        $user = api_get_user_entity($userId);
6667
        $uniqueId = uniqid('anon', true);
6668
        $user
6669
            ->setFirstname($uniqueId)
6670
            ->setLastname($uniqueId)
6671
            ->setBiography('')
6672
            ->setAddress('')
6673
            ->setCurriculumItems(null)
6674
            ->setDateOfBirth(null)
6675
            ->setCompetences('')
6676
            ->setDiplomas('')
6677
            ->setOpenarea('')
6678
            ->setTeach('')
6679
            ->setProductions(null)
6680
            ->setOpenid('')
6681
            ->setEmailCanonical($uniqueId.'@example.com')
6682
            ->setEmail($uniqueId.'@example.com')
6683
            ->setUsername($uniqueId)
6684
            ->setUsernameCanonical($uniqueId)
6685
            ->setPhone('')
6686
            ->setOfficialCode('')
6687
        ;
6688
6689
        self::deleteUserPicture($userId);
6690
        self::cleanUserRequestsOfRemoval($userId);
6691
6692
        // The IP address is a border-case personal data, as it does
6693
        // not directly allow for personal identification (it is not
6694
        // a completely safe value in most countries - the IP could
6695
        // be used by neighbours and crackers)
6696
        if ($deleteIP) {
6697
            $substitute = '127.0.0.1';
6698
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
6699
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
6700
            $res = Database::query($sql);
6701
            if ($res === false && $debug > 0) {
6702
                error_log("Could not anonymize IP address for user $userId ($sql)");
6703
            }
6704
6705
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6706
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6707
            $res = Database::query($sql);
6708
            if ($res === false && $debug > 0) {
6709
                error_log("Could not anonymize IP address for user $userId ($sql)");
6710
            }
6711
6712
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6713
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE exe_user_id = $userId";
6714
            $res = Database::query($sql);
6715
            if ($res === false && $debug > 0) {
6716
                error_log("Could not anonymize IP address for user $userId ($sql)");
6717
            }
6718
6719
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6720
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6721
            $res = Database::query($sql);
6722
            if ($res === false && $debug > 0) {
6723
                error_log("Could not anonymize IP address for user $userId ($sql)");
6724
            }
6725
6726
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6727
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6728
            $res = Database::query($sql);
6729
            if ($res === false && $debug > 0) {
6730
                error_log("Could not anonymize IP address for user $userId ($sql)");
6731
            }
6732
6733
            $table = Database::get_course_table(TABLE_WIKI);
6734
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6735
            $res = Database::query($sql);
6736
            if ($res === false && $debug > 0) {
6737
                error_log("Could not anonymize IP address for user $userId ($sql)");
6738
            }
6739
6740
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
6741
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
6742
            $res = Database::query($sql);
6743
            if ($res === false && $debug > 0) {
6744
                error_log("Could not anonymize IP address for user $userId ($sql)");
6745
            }
6746
6747
            $table = Database::get_course_table(TABLE_WIKI);
6748
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6749
            $res = Database::query($sql);
6750
            if ($res === false && $debug > 0) {
6751
                error_log("Could not anonymize IP address for user $userId ($sql)");
6752
            }
6753
        }
6754
        $em->persist($user);
6755
        $em->flush($user);
6756
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
6757
6758
        return true;
6759
    }
6760
6761
    /**
6762
     * @param int $userId
6763
     *
6764
     * @throws Exception
6765
     *
6766
     * @return string
6767
     */
6768
    public static function anonymizeUserWithVerification($userId)
6769
    {
6770
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
6771
6772
        $message = '';
6773
        if (api_is_platform_admin() ||
6774
            ($allowDelete && api_is_session_admin())
6775
        ) {
6776
            $userToUpdateInfo = api_get_user_info($userId);
6777
            $currentUserId = api_get_user_id();
6778
6779
            if ($userToUpdateInfo &&
6780
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
6781
            ) {
6782
                if ($userId != $currentUserId &&
6783
                    self::anonymize($userId)
6784
                ) {
6785
                    $message = Display::return_message(
6786
                        sprintf(get_lang('UserXAnonymized'), $userToUpdateInfo['complete_name_with_username']),
6787
                        'confirmation'
6788
                    );
6789
                } else {
6790
                    $message = Display::return_message(
6791
                        sprintf(get_lang('CannotAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
6792
                        'error'
6793
                    );
6794
                }
6795
            } else {
6796
                $message = Display::return_message(
6797
                    sprintf(get_lang('NoPermissionToAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
6798
                    'error'
6799
                );
6800
            }
6801
        }
6802
6803
        return $message;
6804
    }
6805
6806
    /**
6807
     * @param int $userId
6808
     *
6809
     * @throws Exception
6810
     *
6811
     * @return string
6812
     */
6813
    public static function deleteUserWithVerification($userId)
6814
    {
6815
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
6816
        $message = Display::return_message(get_lang('CannotDeleteUser'), 'error');
6817
        $userToUpdateInfo = api_get_user_info($userId);
6818
6819
        // User must exist.
6820
        if (empty($userToUpdateInfo)) {
6821
            return $message;
6822
        }
6823
6824
        $currentUserId = api_get_user_id();
6825
6826
        // Cannot delete myself.
6827
        if ($userId == $currentUserId) {
6828
            return $message;
6829
        }
6830
6831
        if (api_is_platform_admin() ||
6832
            ($allowDelete && api_is_session_admin())
6833
        ) {
6834
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
6835
                if (self::delete_user($userId)) {
6836
                    $message = Display::return_message(
6837
                        get_lang('UserDeleted').': '.$userToUpdateInfo['complete_name_with_username'],
6838
                        'confirmation'
6839
                    );
6840
                } else {
6841
                    $message = Display::return_message(get_lang('CannotDeleteUserBecauseOwnsCourse'), 'error');
6842
                }
6843
            }
6844
        }
6845
6846
        return $message;
6847
    }
6848
6849
    /**
6850
     * @return array
6851
     */
6852
    public static function createDataPrivacyExtraFields()
6853
    {
6854
        self::create_extra_field(
6855
            'request_for_legal_agreement_consent_removal_justification',
6856
            1, //text
6857
            'Request for legal agreement consent removal justification	',
6858
            ''
6859
        );
6860
6861
        self::create_extra_field(
6862
            'request_for_delete_account_justification',
6863
            1, //text
6864
            'Request for delete account justification',
6865
            ''
6866
        );
6867
6868
        $extraFieldId = self::create_extra_field(
6869
            'request_for_legal_agreement_consent_removal',
6870
            1, //text
6871
            'Request for legal agreement consent removal',
6872
            ''
6873
        );
6874
6875
        $extraFieldIdDeleteAccount = self::create_extra_field(
6876
            'request_for_delete_account',
6877
            1, //text
6878
            'Request for delete user account',
6879
            ''
6880
        );
6881
6882
        return [
6883
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
6884
            'delete_legal' => $extraFieldId,
6885
        ];
6886
    }
6887
6888
    /**
6889
     * @param int $userId
6890
     */
6891
    public static function cleanUserRequestsOfRemoval($userId)
6892
    {
6893
        $userId = (int) $userId;
6894
6895
        $extraFieldValue = new ExtraFieldValue('user');
6896
        $extraFieldsToDelete = [
6897
            'legal_accept',
6898
            'request_for_legal_agreement_consent_removal',
6899
            'request_for_legal_agreement_consent_removal_justification',
6900
            'request_for_delete_account_justification', // just in case delete also this
6901
            'request_for_delete_account',
6902
        ];
6903
6904
        foreach ($extraFieldsToDelete as $variable) {
6905
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
6906
                $userId,
6907
                $variable
6908
            );
6909
            if ($value && isset($value['id'])) {
6910
                $extraFieldValue->delete($value['id']);
6911
            }
6912
        }
6913
    }
6914
6915
    /**
6916
     * @param int $searchYear
6917
     *
6918
     * @throws Exception
6919
     *
6920
     * @return array
6921
     */
6922
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
6923
    {
6924
        $timezone = new DateTimeZone(api_get_timezone());
6925
6926
        $sessions = [];
6927
        if (DRH == $userInfo['status']) {
6928
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
6929
        } elseif (api_is_platform_admin(true)) {
6930
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
6931
        } else {
6932
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
6933
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
6934
6935
            foreach ($sessionsByCategory as $sessionsInCategory) {
6936
                $sessions = array_merge($sessions, $sessionsInCategory);
6937
            }
6938
        }
6939
6940
        $sessions = array_map(
6941
            function ($sessionInfo) {
6942
                if (!isset($sessionInfo['session_id'])) {
6943
                    $sessionInfo['session_id'] = $sessionInfo['id'];
6944
                }
6945
                if (!isset($sessionInfo['session_name'])) {
6946
                    $sessionInfo['session_name'] = $sessionInfo['name'];
6947
                }
6948
6949
                return $sessionInfo;
6950
            },
6951
            $sessions
6952
        );
6953
6954
        $calendarSessions = [];
6955
6956
        foreach ($sessions as $sessionInfo) {
6957
            if (!empty($sessionInfo['duration'])) {
6958
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
6959
                    $sessionInfo['session_id'],
6960
                    $userInfo['id']
6961
                );
6962
6963
                if (empty($courseAccess)) {
6964
                    continue;
6965
                }
6966
6967
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
6968
                $lastAccessDate = clone $firstAcessDate;
6969
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
6970
6971
                $firstAccessYear = (int) $firstAcessDate->format('Y');
6972
                $lastAccessYear = (int) $lastAccessDate->format('Y');
6973
6974
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
6975
                    $calendarSessions[$sessionInfo['session_id']] = [
6976
                        'name' => $sessionInfo['session_name'],
6977
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
6978
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
6979
                    ];
6980
                }
6981
6982
                continue;
6983
            }
6984
6985
            $accessStartDate = !empty($sessionInfo['access_start_date'])
6986
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6987
                : null;
6988
            $accessEndDate = !empty($sessionInfo['access_end_date'])
6989
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6990
                : null;
6991
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
6992
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
6993
6994
            $isValid = false;
6995
6996
            if ($accessStartYear && $accessEndYear) {
6997
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
6998
                    $isValid = true;
6999
                }
7000
            }
7001
7002
            if ($accessStartYear && !$accessEndYear) {
7003
                if ($accessStartYear == $searchYear) {
7004
                    $isValid = true;
7005
                }
7006
            }
7007
7008
            if (!$accessStartYear && $accessEndYear) {
7009
                if ($accessEndYear == $searchYear) {
7010
                    $isValid = true;
7011
                }
7012
            }
7013
7014
            if ($isValid) {
7015
                $calendarSessions[$sessionInfo['session_id']] = [
7016
                    'name' => $sessionInfo['session_name'],
7017
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
7018
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
7019
                ];
7020
            }
7021
        }
7022
7023
        return $calendarSessions;
7024
    }
7025
7026
    /**
7027
     * Get sessions info for planification calendar.
7028
     *
7029
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
7030
     * @param int   $searchYear
7031
     *
7032
     * @throws Exception
7033
     *
7034
     * @return array
7035
     */
7036
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
7037
    {
7038
        $timezone = new DateTimeZone(api_get_timezone());
7039
        $calendar = [];
7040
7041
        foreach ($sessionsList as $sessionId => $sessionInfo) {
7042
            $startDate = $sessionInfo['access_start_date']
7043
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
7044
                : null;
7045
            $endDate = $sessionInfo['access_end_date']
7046
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
7047
                : null;
7048
7049
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
7050
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
7051
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
7052
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
7053
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
7054
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
7055
7056
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
7057
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
7058
7059
            $calendar[] = [
7060
                'id' => $sessionId,
7061
                'name' => $sessionInfo['name'],
7062
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
7063
                'start_in_last_year' => $startYear < $searchYear,
7064
                'end_in_next_year' => $endYear > $searchYear,
7065
                'no_start' => !$startWeek,
7066
                'no_end' => !$endWeek,
7067
                'start' => $start,
7068
                'duration' => $duration > 0 ? $duration : 1,
7069
            ];
7070
        }
7071
7072
        usort(
7073
            $calendar,
7074
            function ($sA, $sB) {
7075
                if ($sA['start'] == $sB['start']) {
7076
                    return 0;
7077
                }
7078
7079
                if ($sA['start'] < $sB['start']) {
7080
                    return -1;
7081
                }
7082
7083
                return 1;
7084
            }
7085
        );
7086
7087
        return $calendar;
7088
    }
7089
7090
    /**
7091
     * Return the user's full name. Optionally with the username.
7092
     *
7093
     * @param bool $includeUsername Optional. By default username is not included.
7094
     *
7095
     * @return string
7096
     */
7097
    public static function formatUserFullName(User $user, $includeUsername = false)
7098
    {
7099
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
7100
7101
        if ($includeUsername && api_get_configuration_value('hide_username_with_complete_name') !== true) {
7102
            $username = $user->getUsername();
7103
7104
            return "$fullName ($username)";
7105
        }
7106
7107
        return $fullName;
7108
    }
7109
7110
    /**
7111
     * @param int $userId
7112
     *
7113
     * @return array
7114
     */
7115
    public static function getUserCareers($userId)
7116
    {
7117
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7118
        $tableCareer = Database::get_main_table(TABLE_CAREER);
7119
        $userId = (int) $userId;
7120
7121
        $sql = "SELECT c.id, c.name
7122
                FROM $table uc
7123
                INNER JOIN $tableCareer c
7124
                ON uc.career_id = c.id
7125
                WHERE user_id = $userId
7126
                ORDER BY uc.created_at
7127
                ";
7128
        $result = Database::query($sql);
7129
7130
        return Database::store_result($result, 'ASSOC');
7131
    }
7132
7133
    /**
7134
     * @param int $userId
7135
     * @param int $careerId
7136
     */
7137
    public static function addUserCareer($userId, $careerId)
7138
    {
7139
        if (!api_get_configuration_value('allow_career_users')) {
7140
            return false;
7141
        }
7142
7143
        if (self::userHasCareer($userId, $careerId) === false) {
7144
            $params = [
7145
                'user_id' => $userId,
7146
                'career_id' => $careerId,
7147
                'created_at' => api_get_utc_datetime(),
7148
                'updated_at' => api_get_utc_datetime(),
7149
            ];
7150
            $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7151
            Database::insert($table, $params);
7152
        }
7153
7154
        return true;
7155
    }
7156
7157
    /**
7158
     * @param int   $userCareerId
7159
     * @param array $data
7160
     *
7161
     * @return bool
7162
     */
7163
    public static function updateUserCareer($userCareerId, $data)
7164
    {
7165
        if (!api_get_configuration_value('allow_career_users')) {
7166
            return false;
7167
        }
7168
7169
        $params = ['extra_data' => $data, 'updated_at' => api_get_utc_datetime()];
7170
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7171
        Database::update(
7172
            $table,
7173
            $params,
7174
            ['id = ?' => (int) $userCareerId]
7175
        );
7176
7177
        return true;
7178
    }
7179
7180
    /**
7181
     * @param int $userId
7182
     * @param int $careerId
7183
     *
7184
     * @return array
7185
     */
7186
    public static function getUserCareer($userId, $careerId)
7187
    {
7188
        $userId = (int) $userId;
7189
        $careerId = (int) $careerId;
7190
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7191
7192
        $sql = "SELECT * FROM $table WHERE user_id = $userId AND career_id = $careerId";
7193
        $result = Database::query($sql);
7194
7195
        return Database::fetch_array($result, 'ASSOC');
7196
    }
7197
7198
    /**
7199
     * @param int $userId
7200
     * @param int $careerId
7201
     *
7202
     * @return bool
7203
     */
7204
    public static function userHasCareer($userId, $careerId)
7205
    {
7206
        $userId = (int) $userId;
7207
        $careerId = (int) $careerId;
7208
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7209
7210
        $sql = "SELECT id FROM $table WHERE user_id = $userId AND career_id = $careerId";
7211
        $result = Database::query($sql);
7212
7213
        return Database::num_rows($result) > 0;
7214
    }
7215
7216
    /**
7217
     * @param int $userId
7218
     *
7219
     * @throws Exception
7220
     */
7221
    public static function deleteUserFiles($userId)
7222
    {
7223
        $path = self::getUserPathById($userId, 'system');
7224
7225
        $fs = new Filesystem();
7226
        $fs->remove($path);
7227
    }
7228
7229
    public static function redirectToResetPassword($userId)
7230
    {
7231
        if (!api_get_configuration_value('force_renew_password_at_first_login')) {
7232
            return;
7233
        }
7234
7235
        $askPassword = self::get_extra_user_data_by_field(
7236
            $userId,
7237
            'ask_new_password'
7238
        );
7239
7240
        if (!empty($askPassword) && isset($askPassword['ask_new_password']) &&
7241
            1 === (int) $askPassword['ask_new_password']
7242
        ) {
7243
            $uniqueId = api_get_unique_id();
7244
            $userObj = api_get_user_entity($userId);
7245
7246
            $userObj->setConfirmationToken($uniqueId);
7247
            $userObj->setPasswordRequestedAt(new \DateTime());
7248
7249
            Database::getManager()->persist($userObj);
7250
            Database::getManager()->flush();
7251
7252
            $url = api_get_path(WEB_CODE_PATH).'auth/reset.php?token='.$uniqueId;
7253
            api_location($url);
7254
        }
7255
    }
7256
7257
    /**
7258
     * @return EncoderFactory
7259
     */
7260
    private static function getEncoderFactory()
7261
    {
7262
        $encryption = self::getPasswordEncryption();
7263
        $encoders = [
7264
            'Chamilo\\UserBundle\\Entity\\User' => new \Chamilo\UserBundle\Security\Encoder($encryption),
7265
        ];
7266
7267
        return new EncoderFactory($encoders);
7268
    }
7269
7270
    /**
7271
     * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface
7272
     */
7273
    private static function getEncoder(User $user)
7274
    {
7275
        $encoderFactory = self::getEncoderFactory();
7276
7277
        return $encoderFactory->getEncoder($user);
7278
    }
7279
7280
    /**
7281
     * Disables or enables a user.
7282
     *
7283
     * @param int $user_id
7284
     * @param int $active  Enable or disable
7285
     *
7286
     * @return bool True on success, false on failure
7287
     * @assert (-1,0) === false
7288
     * @assert (1,1) === true
7289
     */
7290
    private static function change_active_state($user_id, $active)
7291
    {
7292
        $user_id = (int) $user_id;
7293
        $active = (int) $active;
7294
7295
        if (empty($user_id)) {
7296
            return false;
7297
        }
7298
7299
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
7300
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
7301
        $r = Database::query($sql);
7302
        $ev = LOG_USER_DISABLE;
7303
        if ($active == 1) {
7304
            $ev = LOG_USER_ENABLE;
7305
        }
7306
        if ($r !== false) {
7307
            Event::addEvent($ev, LOG_USER_ID, $user_id);
7308
        }
7309
7310
        return $r;
7311
    }
7312
7313
    /**
7314
     * Get either a Gravatar URL or complete image tag for a specified email address.
7315
     *
7316
     * @param string $email The email address
7317
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
7318
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
7319
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
7320
     * @param bool   $img   True to return a complete IMG tag False for just the URL
7321
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
7322
     *
7323
     * @return string containing either just a URL or a complete image tag
7324
     * @source http://gravatar.com/site/implement/images/php/
7325
     */
7326
    private static function getGravatar(
7327
        $email,
7328
        $s = 80,
7329
        $d = 'mm',
7330
        $r = 'g',
7331
        $img = false,
7332
        $atts = []
7333
    ) {
7334
        $url = 'http://www.gravatar.com/avatar/';
7335
        if (!empty($_SERVER['HTTPS'])) {
7336
            $url = 'https://secure.gravatar.com/avatar/';
7337
        }
7338
        $url .= md5(strtolower(trim($email)));
7339
        $url .= "?s=$s&d=$d&r=$r";
7340
        if ($img) {
7341
            $url = '<img src="'.$url.'"';
7342
            foreach ($atts as $key => $val) {
7343
                $url .= ' '.$key.'="'.$val.'"';
7344
            }
7345
            $url .= ' />';
7346
        }
7347
7348
        return $url;
7349
    }
7350
}
7351