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

main/inc/lib/usermanager.lib.php (2 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);
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) {
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) {
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
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $source_file is correct as it would always require null to be passed?
Loading history...
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
0 ignored issues
show
Documentation Bug introduced by
The doc comment Optional. at position 0 could not be parsed: Unknown type name 'Optional.' at position 0 in Optional..
Loading history...
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;
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