Passed
Pull Request — 1.11.x (#4321)
by Angel Fernando Quiroz
08:37
created

UserManager::create_extra_field()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 7
nc 1
nop 4
dl 0
loc 15
rs 10
c 0
b 0
f 0
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;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, SkillRelUser. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
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
     * Detects and returns the type of encryption of the given encrypted
131
     * password.
132
     *
133
     * @param string $encoded The encrypted password
134
     * @param string $salt    The user salt, if any
135
     */
136
    public static function detectPasswordEncryption(string $encoded, string $salt): bool
137
    {
138
        $encryption = false;
139
140
        $length = strlen($encoded);
141
142
        $pattern = '/^\$2y\$04\$[A-Za-z0-9\.\/]{53}$/';
143
144
        if ($length == 60 && preg_match($pattern, $encoded)) {
145
            $encryption = 'bcrypt';
146
        } elseif ($length == 32 && ctype_xdigit($encoded)) {
147
            $encryption = 'md5';
148
        } elseif ($length == 40 && ctype_xdigit($encoded)) {
149
            $encryption = 'sha1';
150
        } else {
151
            $start = strpos($encoded, '{');
152
            if ($start !== false && substr($encoded, -1, 1) == '}') {
153
                if (substr($encoded, $start + 1, -1) == $salt) {
154
                    $encryption = 'none';
155
                }
156
            }
157
        }
158
159
        return $encryption;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $encryption could return the type string which is incompatible with the type-hinted return boolean. Consider adding an additional type-check to rule them out.
Loading history...
160
    }
161
162
    /**
163
     * Checks if the password is correct for this user.
164
     * If the password_conversion setting is true, also update the password
165
     * in the database to a new encryption method.
166
     *
167
     * @param string $encoded Encrypted password
168
     * @param string $raw     Clear password given through login form
169
     * @param string $salt    User salt, if any
170
     * @param int    $userId  The user's internal ID
171
     */
172
    public static function checkPassword(string $encoded, string $raw, string $salt, int $userId): bool
173
    {
174
        $result = false;
175
176
        if (true === api_get_configuration_value('password_conversion')) {
177
            $detectedEncryption = self::detectPasswordEncryption($encoded, $salt);
178
            if (self::getPasswordEncryption() != $detectedEncryption) {
179
                $encoder = new \Chamilo\UserBundle\Security\Encoder($detectedEncryption);
180
                $result = $encoder->isPasswordValid($encoded, $raw, $salt);
181
                if ($result) {
182
                    self::updatePassword($userId, $raw);
183
                }
184
            }
185
        } else {
186
            return self::isPasswordValid($encoded, $raw, $salt);
187
        }
188
189
        return $result;
190
    }
191
192
    /**
193
     * Encrypt the password using the current encoder.
194
     *
195
     * @param string $raw The clear password
196
     */
197
    public static function encryptPassword(string $raw, User $user): string
198
    {
199
        $encoder = self::getEncoder($user);
200
201
        return $encoder->encodePassword(
202
            $raw,
203
            $user->getSalt()
204
        );
205
    }
206
207
    /**
208
     * Update the password of the given user to the given (in-clear) password.
209
     *
210
     * @param int    $userId   Internal user ID
211
     * @param string $password Password in clear
212
     */
213
    public static function updatePassword(int $userId, string $password): void
214
    {
215
        $repository = self::getRepository();
216
        /** @var User $user */
217
        $user = $repository->find($userId);
218
        $userManager = self::getManager();
219
        $user->setPlainPassword($password);
220
        $userManager->updateUser($user, true);
221
        Event::addEvent(LOG_USER_PASSWORD_UPDATE, LOG_USER_ID, $userId);
0 ignored issues
show
Bug introduced by
The method addEvent() does not exist on Event. ( Ignorable by Annotation )

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

221
        Event::/** @scrutinizer ignore-call */ 
222
               addEvent(LOG_USER_PASSWORD_UPDATE, LOG_USER_ID, $userId);

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

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

Loading history...
222
    }
223
224
    /**
225
     * Updates user expiration date.
226
     *
227
     * @param int    $userId
228
     * @param string $expirationDate
229
     */
230
    public static function updateExpirationDate($userId, $expirationDate)
231
    {
232
        $repository = self::getRepository();
233
        /** @var User $user */
234
        $user = $repository->find($userId);
235
        $userManager = self::getManager();
236
        $expirationDate = api_get_utc_datetime($expirationDate, false, true);
237
        $user->setExpirationDate($expirationDate);
238
        $userManager->updateUser($user, true);
239
    }
240
241
    /**
242
     * Creates a new user for the platform.
243
     *
244
     * @author Hugues Peeters <[email protected]>,
245
     * @author Roan Embrechts <[email protected]>
246
     *
247
     * @param string        $firstName
248
     * @param string        $lastName
249
     * @param int           $status                  (1 for course tutor, 5 for student, 6 for anonymous)
250
     * @param string        $email
251
     * @param string        $loginName
252
     * @param string        $password
253
     * @param string        $official_code           Any official code (optional)
254
     * @param string        $language                User language    (optional)
255
     * @param string        $phone                   Phone number    (optional)
256
     * @param string        $picture_uri             Picture URI        (optional)
257
     * @param string        $authSource              Authentication source (defaults to 'platform', dependind on constant)
258
     * @param string        $expirationDate          Account expiration date (optional, defaults to null)
259
     * @param int           $active                  Whether the account is enabled or disabled by default
260
     * @param int           $hr_dept_id              The department of HR in which the user is registered (defaults to 0)
261
     * @param array         $extra                   Extra fields (prefix labels with "extra_")
262
     * @param string        $encrypt_method          Used if password is given encrypted. Set to an empty string by default
263
     * @param bool          $send_mail
264
     * @param bool          $isAdmin
265
     * @param string        $address
266
     * @param bool          $sendEmailToAllAdmins
267
     * @param FormValidator $form
268
     * @param int           $creatorId
269
     * @param array         $emailTemplate
270
     * @param string        $redirectToURLAfterLogin
271
     *
272
     * @return mixed new user id - if the new user creation succeeds, false otherwise
273
     * @desc The function tries to retrieve user id from the session.
274
     * If it exists, the current user id is the creator id. If a problem arises,
275
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
276
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
277
     */
278
    public static function create_user(
279
        $firstName,
280
        $lastName,
281
        $status,
282
        $email,
283
        $loginName,
284
        $password,
285
        $official_code = '',
286
        $language = '',
287
        $phone = '',
288
        $picture_uri = '',
289
        $authSource = PLATFORM_AUTH_SOURCE,
290
        $expirationDate = null,
291
        $active = 1,
292
        $hr_dept_id = 0,
293
        $extra = [],
294
        $encrypt_method = '',
295
        $send_mail = false,
296
        $isAdmin = false,
297
        $address = '',
298
        $sendEmailToAllAdmins = false,
299
        $form = null,
300
        $creatorId = 0,
301
        $emailTemplate = [],
302
        $redirectToURLAfterLogin = ''
303
    ) {
304
        $creatorId = empty($creatorId) ? api_get_user_id() : 0;
305
        $creatorInfo = api_get_user_info($creatorId);
306
        $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
307
308
        $hook = HookCreateUser::create();
309
        if (!empty($hook)) {
310
            $hook->notifyCreateUser(HOOK_EVENT_TYPE_PRE);
311
        }
312
313
        if ('true' === api_get_setting('registration', 'email')) {
314
            // Force email validation.
315
            if (false === api_valid_email($email)) {
316
                Display::addFlash(
317
                   Display::return_message(get_lang('PleaseEnterValidEmail').' - '.$email, 'warning')
318
               );
319
320
                return false;
321
            }
322
        } else {
323
            // Allow empty email. If email is set, check if is valid.
324
            if (!empty($email) && false === api_valid_email($email)) {
325
                Display::addFlash(
326
                    Display::return_message(get_lang('PleaseEnterValidEmail').' - '.$email, 'warning')
327
                );
328
329
                return false;
330
            }
331
        }
332
333
        if ('true' === api_get_setting('login_is_email')) {
334
            if (false === api_valid_email($loginName)) {
335
                Display::addFlash(
336
                    Display::return_message(get_lang('PleaseEnterValidEmail').' - '.$loginName, 'warning')
337
                );
338
339
                return false;
340
            }
341
        } else {
342
            if (false === self::is_username_valid($loginName)) {
343
                Display::addFlash(
344
                    Display::return_message(get_lang('UsernameWrong').' - '.$loginName, 'warning')
345
                );
346
347
                return false;
348
            }
349
        }
350
351
        // First check wether the login already exists
352
        if (!self::is_username_available($loginName)) {
353
            Display::addFlash(
354
                Display::return_message(get_lang('LoginAlreadyTaken').' - '.$loginName, 'warning')
355
            );
356
357
            return false;
358
        }
359
360
        global $_configuration;
361
        $original_password = $password;
362
363
        $access_url_id = 1;
364
        if (api_get_multiple_access_url()) {
365
            $access_url_id = api_get_current_access_url_id();
366
        } else {
367
            // In some cases, the first access_url ID might be different from 1
368
            // for example when using a DB cluster or hacking the DB manually.
369
            // In this case, we want the first row, not necessarily "1".
370
            $dbm = Database::getManager();
371
            /** @var AccessUrlRepository $accessUrlRepository */
372
            $accessUrlRepository = $dbm->getRepository('ChamiloCoreBundle:AccessUrl');
373
            $accessUrl = $accessUrlRepository->getFirstId();
374
            if (!empty($accessUrl[0]) && !empty($accessUrl[0][1])) {
375
                $access_url_id = $accessUrl[0][1];
376
            }
377
        }
378
379
        if (isset($_configuration[$access_url_id]) &&
380
            is_array($_configuration[$access_url_id]) &&
381
            isset($_configuration[$access_url_id]['hosting_limit_users']) &&
382
            $_configuration[$access_url_id]['hosting_limit_users'] > 0) {
383
            $num = self::get_number_of_users(null, $access_url_id);
384
            if ($num >= $_configuration[$access_url_id]['hosting_limit_users']) {
385
                api_warn_hosting_contact('hosting_limit_users');
386
                Display::addFlash(
387
                    Display::return_message(
388
                        get_lang('PortalUsersLimitReached'),
389
                        'warning'
390
                    )
391
                );
392
393
                return false;
394
            }
395
        }
396
397
        if ($status === 1 &&
398
            isset($_configuration[$access_url_id]) &&
399
            is_array($_configuration[$access_url_id]) &&
400
            isset($_configuration[$access_url_id]['hosting_limit_teachers']) &&
401
            $_configuration[$access_url_id]['hosting_limit_teachers'] > 0
402
        ) {
403
            $num = self::get_number_of_users(1, $access_url_id);
404
            if ($num >= $_configuration[$access_url_id]['hosting_limit_teachers']) {
405
                Display::addFlash(
406
                    Display::return_message(
407
                        get_lang('PortalTeachersLimitReached'),
408
                        'warning'
409
                    )
410
                );
411
                api_warn_hosting_contact('hosting_limit_teachers');
412
413
                return false;
414
            }
415
        }
416
417
        if (empty($password)) {
418
            if ($authSource === PLATFORM_AUTH_SOURCE) {
419
                Display::addFlash(
420
                    Display::return_message(
421
                        get_lang('ThisFieldIsRequired').': '.get_lang(
422
                            'Password'
423
                        ),
424
                        'warning'
425
                    )
426
                );
427
428
                return false;
429
            }
430
431
            // We use the authSource as password.
432
            // The real validation will be by processed by the auth
433
            // source not Chamilo
434
            $password = $authSource;
435
        }
436
437
        // database table definition
438
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
439
440
        // Checking the user language
441
        $languages = api_get_languages();
442
        $language = strtolower($language);
443
444
        if (isset($languages['folder'])) {
445
            if (!in_array($language, $languages['folder'])) {
446
                $language = api_get_setting('platformLanguage');
447
            }
448
        }
449
450
        $currentDate = api_get_utc_datetime();
451
        $now = new DateTime();
452
453
        if (empty($expirationDate) || $expirationDate == '0000-00-00 00:00:00') {
454
            // Default expiration date
455
            // if there is a default duration of a valid account then
456
            // we have to change the expiration_date accordingly
457
            // Accept 0000-00-00 00:00:00 as a null value to avoid issues with
458
            // third party code using this method with the previous (pre-1.10)
459
            // value of 0000...
460
            if (api_get_setting('account_valid_duration') != '') {
461
                $expirationDate = new DateTime($currentDate);
462
                $days = (int) api_get_setting('account_valid_duration');
463
                $expirationDate->modify('+'.$days.' day');
464
            }
465
        } else {
466
            $expirationDate = api_get_utc_datetime($expirationDate);
467
            $expirationDate = new \DateTime($expirationDate, new DateTimeZone('UTC'));
468
        }
469
470
        $userManager = self::getManager();
471
472
        /** @var User $user */
473
        $user = $userManager->createUser();
474
475
        $user
476
            ->setLastname($lastName)
477
            ->setFirstname($firstName)
478
            ->setUsername($loginName)
479
            ->setStatus($status)
480
            ->setPlainPassword($password)
481
            ->setEmail($email)
482
            ->setOfficialCode($official_code)
483
            ->setPictureUri($picture_uri)
484
            ->setCreatorId($creatorId)
485
            ->setAuthSource($authSource)
486
            ->setPhone($phone)
487
            ->setAddress($address)
488
            ->setLanguage($language)
489
            ->setRegistrationDate($now)
490
            ->setHrDeptId($hr_dept_id)
491
            ->setActive($active)
492
            ->setEnabled($active)
493
        ;
494
495
        if (!empty($expirationDate)) {
496
            $user->setExpirationDate($expirationDate);
497
        }
498
499
        $userManager->updateUser($user);
500
        $userId = $user->getId();
501
502
        if (!empty($userId)) {
503
            $return = $userId;
504
            $sql = "UPDATE $table_user SET user_id = $return WHERE id = $return";
505
            Database::query($sql);
506
507
            if ($isAdmin) {
508
                self::addUserAsAdmin($user);
509
            }
510
511
            if (api_get_multiple_access_url()) {
512
                UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
513
            } else {
514
                //we are adding by default the access_url_user table with access_url_id = 1
515
                UrlManager::add_user_to_url($userId, 1);
516
            }
517
518
            $extra['item_id'] = $userId;
519
520
            if (is_array($extra) && count($extra) > 0) {
521
                $userFieldValue = new ExtraFieldValue('user');
522
                // Force saving of extra fields (otherwise, if the current
523
                // user is not admin, fields not visible to the user - most
524
                // of them - are just ignored)
525
                $userFieldValue->saveFieldValues(
526
                    $extra,
527
                    true,
528
                    null,
529
                    null,
530
                    null,
531
                    true
532
                );
533
            } else {
534
                // Create notify settings by default
535
                self::update_extra_field_value(
536
                    $userId,
537
                    'mail_notify_invitation',
538
                    '1'
539
                );
540
                self::update_extra_field_value(
541
                    $userId,
542
                    'mail_notify_message',
543
                    '1'
544
                );
545
                self::update_extra_field_value(
546
                    $userId,
547
                    'mail_notify_group_message',
548
                    '1'
549
                );
550
            }
551
552
            self::update_extra_field_value(
553
                $userId,
554
                'already_logged_in',
555
                'false'
556
            );
557
558
            if (!empty($redirectToURLAfterLogin) && api_get_configuration_value('plugin_redirection_enabled')) {
559
                RedirectionPlugin::insert($userId, $redirectToURLAfterLogin);
560
            }
561
562
            if (!empty($email) && $send_mail) {
563
                $recipient_name = api_get_person_name(
564
                    $firstName,
565
                    $lastName,
566
                    null,
567
                    PERSON_NAME_EMAIL_ADDRESS
568
                );
569
                $tplSubject = new Template(
570
                    null,
571
                    false,
572
                    false,
573
                    false,
574
                    false,
575
                    false
576
                );
577
                $layoutSubject = $tplSubject->get_template('mail/subject_registration_platform.tpl');
578
                $emailSubject = $tplSubject->fetch($layoutSubject);
579
                $sender_name = api_get_person_name(
580
                    api_get_setting('administratorName'),
581
                    api_get_setting('administratorSurname'),
582
                    null,
583
                    PERSON_NAME_EMAIL_ADDRESS
584
                );
585
                $email_admin = api_get_setting('emailAdministrator');
586
587
                $url = api_get_path(WEB_PATH);
588
                if (api_is_multiple_url_enabled()) {
589
                    $access_url_id = api_get_current_access_url_id();
590
                    if ($access_url_id != -1) {
591
                        $urlInfo = api_get_access_url($access_url_id);
592
                        if ($urlInfo) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $urlInfo of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
593
                            $url = $urlInfo['url'];
594
                        }
595
                    }
596
                }
597
598
                $tplContent = new Template(
599
                    null,
600
                    false,
601
                    false,
602
                    false,
603
                    false,
604
                    false
605
                );
606
                // variables for the default template
607
                $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
608
                $tplContent->assign('login_name', $loginName);
609
                $tplContent->assign('original_password', stripslashes($original_password));
610
                $tplContent->assign('mailWebPath', $url);
611
                $tplContent->assign('new_user', $user);
612
                // Adding this variable but not used in default template, used for task BT19518 with a customized template
613
                $tplContent->assign('status_type', $status);
614
615
                $layoutContent = $tplContent->get_template('mail/content_registration_platform.tpl');
616
                $emailBody = $tplContent->fetch($layoutContent);
617
618
                $userInfo = api_get_user_info($userId);
619
                $mailTemplateManager = new MailTemplateManager();
620
621
                /* MANAGE EVENT WITH MAIL */
622
                if (EventsMail::check_if_using_class('user_registration')) {
623
                    $values["about_user"] = $return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$values was never initialized. Although not strictly required by PHP, it is generally a good practice to add $values = array(); before regardless.
Loading history...
624
                    $values["password"] = $original_password;
625
                    $values["send_to"] = [$return];
626
                    $values["prior_lang"] = null;
627
                    EventsDispatcher::events('user_registration', $values);
628
                } else {
629
                    $phoneNumber = isset($extra['mobile_phone_number']) ? $extra['mobile_phone_number'] : null;
630
                    $additionalParameters = [
631
                        'smsType' => SmsPlugin::WELCOME_LOGIN_PASSWORD,
632
                        'userId' => $return,
633
                        'mobilePhoneNumber' => $phoneNumber,
634
                        'password' => $original_password,
635
                    ];
636
637
                    $emailBodyTemplate = '';
638
                    if (!empty($emailTemplate)) {
639
                        if (isset($emailTemplate['content_registration_platform.tpl']) &&
640
                            !empty($emailTemplate['content_registration_platform.tpl'])
641
                        ) {
642
                            $emailBodyTemplate = $mailTemplateManager->parseTemplate(
643
                                $emailTemplate['content_registration_platform.tpl'],
644
                                $userInfo
645
                            );
646
                        }
647
                    }
648
649
                    $twoEmail = api_get_configuration_value('send_two_inscription_confirmation_mail');
650
                    if ($twoEmail === true) {
651
                        $layoutContent = $tplContent->get_template('mail/new_user_first_email_confirmation.tpl');
652
                        $emailBody = $tplContent->fetch($layoutContent);
653
654
                        if (!empty($emailTemplate) &&
655
                            isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
656
                            !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
657
                        ) {
658
                            $emailBody = $mailTemplateManager->parseTemplate(
659
                                $emailTemplate['new_user_first_email_confirmation.tpl'],
660
                                $userInfo
661
                            );
662
                        }
663
664
                        api_mail_html(
665
                            $recipient_name,
666
                            $email,
667
                            $emailSubject,
668
                            $emailBody,
669
                            $sender_name,
670
                            $email_admin,
671
                            null,
672
                            null,
673
                            null,
674
                            $additionalParameters,
675
                            $creatorEmail
676
                        );
677
678
                        $layoutContent = $tplContent->get_template('mail/new_user_second_email_confirmation.tpl');
679
                        $emailBody = $tplContent->fetch($layoutContent);
680
681
                        if (!empty($emailTemplate) &&
682
                            isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
683
                            !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
684
                        ) {
685
                            $emailBody = $mailTemplateManager->parseTemplate(
686
                                $emailTemplate['new_user_second_email_confirmation.tpl'],
687
                                $userInfo
688
                            );
689
                        }
690
691
                        api_mail_html(
692
                            $recipient_name,
693
                            $email,
694
                            $emailSubject,
695
                            $emailBody,
696
                            $sender_name,
697
                            $email_admin,
698
                            null,
699
                            null,
700
                            null,
701
                            $additionalParameters,
702
                            $creatorEmail
703
                        );
704
                    } else {
705
                        if (!empty($emailBodyTemplate)) {
706
                            $emailBody = $emailBodyTemplate;
707
                        }
708
                        $sendToInbox = api_get_configuration_value('send_inscription_msg_to_inbox');
709
                        if ($sendToInbox) {
710
                            $adminList = self::get_all_administrators();
711
                            $senderId = 1;
712
                            if (!empty($adminList)) {
713
                                $adminInfo = current($adminList);
714
                                $senderId = $adminInfo['user_id'];
715
                            }
716
717
                            MessageManager::send_message_simple(
718
                                $userId,
719
                                $emailSubject,
720
                                $emailBody,
721
                                $senderId
722
                            );
723
                        } else {
724
                            api_mail_html(
725
                                $recipient_name,
726
                                $email,
727
                                $emailSubject,
728
                                $emailBody,
729
                                $sender_name,
730
                                $email_admin,
731
                                null,
732
                                null,
733
                                null,
734
                                $additionalParameters,
735
                                $creatorEmail
736
                            );
737
                        }
738
                    }
739
740
                    $notification = api_get_configuration_value('send_notification_when_user_added');
741
                    if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
742
                        foreach ($notification['admins'] as $adminId) {
743
                            $emailSubjectToAdmin = get_lang('UserAdded').': '.api_get_person_name($firstName, $lastName);
744
                            MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
745
                        }
746
                    }
747
                }
748
749
                if ($sendEmailToAllAdmins) {
750
                    $adminList = self::get_all_administrators();
751
752
                    $tplContent = new Template(
753
                        null,
754
                        false,
755
                        false,
756
                        false,
757
                        false,
758
                        false
759
                    );
760
                    // variables for the default template
761
                    $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
762
                    $tplContent->assign('user_added', $user);
763
                    $renderer = FormValidator::getDefaultRenderer();
764
                    // Form template
765
                    $elementTemplate = ' {label}: {element} <br />';
766
                    $renderer->setElementTemplate($elementTemplate);
767
                    /** @var FormValidator $form */
768
                    $form->freeze(null, $elementTemplate);
769
                    $form->removeElement('submit');
770
                    $formData = $form->returnForm();
771
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
772
                    $tplContent->assign('link', Display::url($url, $url));
773
                    $tplContent->assign('form', $formData);
774
775
                    $layoutContent = $tplContent->get_template('mail/content_registration_platform_to_admin.tpl');
776
                    $emailBody = $tplContent->fetch($layoutContent);
777
778
                    if (!empty($emailTemplate) &&
779
                        isset($emailTemplate['content_registration_platform_to_admin.tpl']) &&
780
                        !empty($emailTemplate['content_registration_platform_to_admin.tpl'])
781
                    ) {
782
                        $emailBody = $mailTemplateManager->parseTemplate(
783
                            $emailTemplate['content_registration_platform_to_admin.tpl'],
784
                            $userInfo
785
                        );
786
                    }
787
788
                    $subject = get_lang('UserAdded');
789
                    foreach ($adminList as $adminId => $data) {
790
                        MessageManager::send_message_simple(
791
                            $adminId,
792
                            $subject,
793
                            $emailBody,
794
                            $userId
795
                        );
796
                    }
797
                }
798
                /* ENDS MANAGE EVENT WITH MAIL */
799
            }
800
801
            if (!empty($hook)) {
802
                $hook->setEventData([
803
                    'return' => $userId,
804
                    'originalPassword' => $original_password,
805
                ]);
806
                $hook->notifyCreateUser(HOOK_EVENT_TYPE_POST);
807
            }
808
            Event::addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId);
809
        } else {
810
            Display::addFlash(
811
                Display::return_message(get_lang('ErrorContactPlatformAdmin'))
812
            );
813
814
            return false;
815
        }
816
817
        return $return;
818
    }
819
820
    /**
821
     * Ensure the CAS-authenticated user exists in the database.
822
     *
823
     * @param $casUser string the CAS user identifier
824
     *
825
     * @throws Exception if more than one user share the same CAS user identifier
826
     *
827
     * @return string|bool the recognised user login name or false if not found
828
     */
829
    public static function casUserLoginName($casUser)
830
    {
831
        $loginName = false;
832
833
        // look inside the casUser extra field
834
        if (UserManager::is_extra_field_available('cas_user')) {
835
            $valueModel = new ExtraFieldValue('user');
836
            $itemList = $valueModel->get_item_id_from_field_variable_and_field_value(
837
                'cas_user',
838
                $casUser,
839
                false,
840
                false,
841
                true
842
            );
843
            if (false !== $itemList) {
844
                // at least one user has $casUser in the 'cas_user' extra field
845
                // we attempt to load each candidate user because there might be deleted ones
846
                // (extra field values of a deleted user might remain)
847
                foreach ($itemList as $item) {
848
                    $userId = intval($item['item_id']);
849
                    $user = UserManager::getRepository()->find($userId);
850
                    if (!is_null($user)) {
851
                        if (false === $loginName) {
852
                            $loginName = $user->getUsername();
853
                        } else {
854
                            throw new Exception(get_lang('MoreThanOneUserMatched'));
855
                        }
856
                    }
857
                }
858
            }
859
        }
860
861
        if (false === $loginName) {
862
            // no matching 'cas_user' extra field value, or no such extra field
863
            // falling back to the old behaviour: $casUser must be the login name
864
            $userId = UserManager::get_user_id_from_username($casUser);
865
            if (false !== $userId) {
866
                $loginName = $casUser;
867
            }
868
        }
869
870
        return $loginName;
871
    }
872
873
    /**
874
     * Checks the availability of extra field 'cas_user'
875
     * and creates it if missing.
876
     *
877
     * @throws Exception on failure
878
     */
879
    public static function ensureCASUserExtraFieldExists()
880
    {
881
        if (!self::is_extra_field_available('cas_user')) {
882
            $extraField = new ExtraField('user');
883
            if (false === $extraField->save(
884
                    [
885
                        'variable' => 'cas_user',
886
                        'field_type' => ExtraField::FIELD_TYPE_TEXT,
887
                        'display_text' => get_lang('CAS User Identifier'),
888
                        'visible_to_self' => true,
889
                        'filter' => true,
890
                    ]
891
                )) {
892
                throw new Exception(get_lang('FailedToCreateExtraFieldCasUser'));
893
            }
894
895
            $rules = api_get_configuration_value('cas_user_map');
896
            if (!empty($rules) && isset($rules['extra'])) {
897
                foreach ($rules['extra'] as $extra) {
898
                    $extraField->save(
899
                        [
900
                            'variable' => $extra,
901
                            'field_type' => ExtraField::FIELD_TYPE_TEXT,
902
                            'display_text' => $extra,
903
                            'visible_to_self' => false,
904
                            'filter' => false,
905
                        ]
906
                    );
907
                }
908
            }
909
        }
910
    }
911
912
    /**
913
     * Create a CAS-authenticated user from scratch, from its CAS user identifier, with temporary default values.
914
     *
915
     * @param string $casUser the CAS user identifier
916
     *
917
     * @throws Exception on error
918
     *
919
     * @return string the login name of the new user
920
     */
921
    public static function createCASAuthenticatedUserFromScratch($casUser)
922
    {
923
        self::ensureCASUserExtraFieldExists();
924
925
        $loginName = 'cas_user_'.$casUser;
926
        $defaultValue = get_lang('EditInProfile');
927
        $defaultEmailValue = get_lang('EditInProfile');
928
        require_once __DIR__.'/../../auth/external_login/functions.inc.php';
929
        if ('true' === api_get_setting('login_is_email')) {
930
            $defaultEmailValue = $casUser;
931
        }
932
        $userId = external_add_user(
933
            [
934
                'username' => $loginName,
935
                'auth_source' => CAS_AUTH_SOURCE,
936
                'firstname' => $defaultValue,
937
                'lastname' => $defaultValue,
938
                'email' => $defaultEmailValue,
939
            ]
940
        );
941
        if (false === $userId) {
942
            throw new Exception(get_lang('FailedUserCreation'));
943
        }
944
        // Not checking function update_extra_field_value return value because not reliable
945
        self::update_extra_field_value($userId, 'cas_user', $casUser);
946
947
        return $loginName;
948
    }
949
950
    public static function updateCasUser($_user)
951
    {
952
        $rules = api_get_configuration_value('cas_user_map');
953
954
        if (empty($_user)) {
955
            return false;
956
        }
957
958
        if (!empty($rules)) {
959
            $userEntity = api_get_user_entity($_user['id']);
960
            $attributes = phpCAS::getAttributes();
961
            if (isset($rules['fields'])) {
962
                $isAdmin = false;
963
                foreach ($rules['fields'] as $field => $attributeName) {
964
                    if (!isset($attributes[$attributeName])) {
965
                        continue;
966
                    }
967
                    $value = $attributes[$attributeName];
968
                    // Check replace.
969
                    if (isset($rules['replace'][$attributeName])) {
970
                        $value = $rules['replace'][$attributeName][$value];
971
                    }
972
973
                    switch ($field) {
974
                        case 'email':
975
                            $userEntity->setEmail($value);
976
                            break;
977
                        case 'firstname':
978
                            $userEntity->setFirstname($value);
979
                            break;
980
                        case 'lastname':
981
                            $userEntity->setLastname($value);
982
                            break;
983
                        case 'active':
984
                            $userEntity->setActive('false' === $value);
985
                            break;
986
                        case 'status':
987
                            if (PLATFORM_ADMIN === (int) $value) {
988
                                $value = COURSEMANAGER;
989
                                $isAdmin = true;
990
                            }
991
                            $userEntity->setStatus($value);
992
                            break;
993
                    }
994
995
                    Database::getManager()->persist($userEntity);
996
                    Database::getManager()->flush();
997
998
                    if ($isAdmin) {
999
                        self::addUserAsAdmin($userEntity);
1000
                    }
1001
                }
1002
            }
1003
1004
            if (isset($rules['extra'])) {
1005
                foreach ($rules['extra'] as $variable) {
1006
                    if (isset($attributes[$variable])) {
1007
                        self::update_extra_field_value(
1008
                            $_user['id'],
1009
                            $variable,
1010
                            $attributes[$variable]
1011
                        );
1012
                    }
1013
                }
1014
            }
1015
        }
1016
    }
1017
1018
    /**
1019
     * Create a CAS-authenticated user from LDAP, from its CAS user identifier.
1020
     *
1021
     * @param $casUser
1022
     *
1023
     * @throws Exception
1024
     *
1025
     * @return string login name of the new user
1026
     */
1027
    public static function createCASAuthenticatedUserFromLDAP($casUser)
1028
    {
1029
        self::ensureCASUserExtraFieldExists();
1030
1031
        require_once __DIR__.'/../../auth/external_login/ldap.inc.php';
1032
        $login = extldapCasUserLogin($casUser);
1033
        if (false !== $login) {
1034
            $ldapUser = extldap_authenticate($login, 'nopass', true);
1035
            if (false !== $ldapUser) {
1036
                require_once __DIR__.'/../../auth/external_login/functions.inc.php';
1037
                $user = extldap_get_chamilo_user($ldapUser);
1038
                $user['username'] = $login;
1039
                $user['auth_source'] = CAS_AUTH_SOURCE;
1040
                $userId = external_add_user($user);
1041
                if (false !== $userId) {
1042
                    // Not checking function update_extra_field_value return value because not reliable
1043
                    self::update_extra_field_value($userId, 'cas_user', $casUser);
1044
1045
                    return $login;
1046
                } else {
1047
                    throw new Exception('Could not create the new user '.$login);
1048
                }
1049
            } else {
1050
                throw new Exception('Could not load the new user from LDAP using its login '.$login);
0 ignored issues
show
Bug introduced by
Are you sure $login of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

1050
                throw new Exception('Could not load the new user from LDAP using its login './** @scrutinizer ignore-type */ $login);
Loading history...
1051
            }
1052
        } else {
1053
            throw new Exception('Could not find the new user from LDAP using its cas user identifier '.$casUser);
1054
        }
1055
    }
1056
1057
    /**
1058
     * updates user record in database from its LDAP record
1059
     * copies relevant LDAP attribute values : firstname, lastname and email.
1060
     *
1061
     * @param $login string the user login name
1062
     *
1063
     * @throws Exception when the user login name is not found in the LDAP or in the database
1064
     */
1065
    public static function updateUserFromLDAP($login)
1066
    {
1067
        require_once __DIR__.'/../../auth/external_login/ldap.inc.php';
1068
1069
        $ldapUser = extldap_authenticate($login, 'nopass', true);
1070
        if (false === $ldapUser) {
1071
            throw new Exception(get_lang('NoSuchUserInLDAP'));
1072
        }
1073
1074
        $user = extldap_get_chamilo_user($ldapUser);
1075
        $userInfo = api_get_user_info_from_username($login);
1076
        if (false === $userInfo) {
1077
            throw new Exception(get_lang('NoSuchUserInInternalDatabase'));
1078
        }
1079
1080
        $userId = UserManager::update_user(
1081
            $userInfo['user_id'],
1082
            $user["firstname"],
1083
            $user["lastname"],
1084
            $login,
1085
            null,
1086
            $userInfo['auth_source'],
1087
            $user["email"],
1088
            $userInfo['status'],
1089
            $userInfo['official_code'],
1090
            $userInfo['phone'],
1091
            $userInfo['picture_uri'],
1092
            $userInfo['expiration_date'],
1093
            $userInfo['active'],
1094
            $userInfo['creator_id'],
1095
            $userInfo['hr_dept_id'],
1096
            null,
1097
            $userInfo['language']
1098
        );
1099
        if (false === $userId) {
1100
            throw new Exception(get_lang('CouldNotUpdateUser'));
1101
        }
1102
    }
1103
1104
    /**
1105
     * Can user be deleted? This function checks whether there's a course
1106
     * in which the given user is the
1107
     * only course administrator. If that is the case, the user can't be
1108
     * deleted because the course would remain without a course admin.
1109
     *
1110
     * @param int $user_id The user id
1111
     *
1112
     * @return bool true if user can be deleted
1113
     *
1114
     * @assert (null) === false
1115
     * @assert (-1) === false
1116
     * @assert ('abc') === false
1117
     */
1118
    public static function canDeleteUser($user_id)
1119
    {
1120
        $deny = api_get_configuration_value('deny_delete_users');
1121
1122
        if ($deny) {
1123
            return false;
1124
        }
1125
1126
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1127
        $user_id = (int) $user_id;
1128
1129
        if (empty($user_id)) {
1130
            return false;
1131
        }
1132
1133
        $res = Database::query(
1134
            "SELECT c_id FROM $table_course_user WHERE status = 1 AND user_id = $user_id"
1135
        );
1136
        while ($course = Database::fetch_assoc($res)) {
1137
            $sql = Database::query(
1138
                "SELECT COUNT(id) number FROM $table_course_user WHERE status = 1 AND c_id = {$course['c_id']}"
1139
            );
1140
            $res2 = Database::fetch_assoc($sql);
1141
1142
            if ($res2['number'] == 1) {
1143
                return false;
1144
            }
1145
        }
1146
1147
        return true;
1148
    }
1149
1150
    /**
1151
     * Delete a user from the platform, and all its belongings. This is a
1152
     * very dangerous function that should only be accessible by
1153
     * super-admins. Other roles should only be able to disable a user,
1154
     * which removes access to the platform but doesn't delete anything.
1155
     *
1156
     * @param int The ID of th user to be deleted
1157
     *
1158
     * @throws Exception
1159
     *
1160
     * @return bool true if user is successfully deleted, false otherwise
1161
     * @assert (null) === false
1162
     * @assert ('abc') === false
1163
     */
1164
    public static function delete_user($user_id)
1165
    {
1166
        $user_id = (int) $user_id;
1167
1168
        if (empty($user_id)) {
1169
            return false;
1170
        }
1171
1172
        if (!self::canDeleteUser($user_id)) {
1173
            return false;
1174
        }
1175
1176
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1177
        $usergroup_rel_user = Database::get_main_table(TABLE_USERGROUP_REL_USER);
1178
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1179
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
1180
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
1181
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
1182
        $table_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
1183
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1184
        $table_group = Database::get_course_table(TABLE_GROUP_USER);
1185
        $table_work = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
1186
1187
        // Unsubscribe the user from all groups in all his courses
1188
        $sql = "SELECT c.id
1189
                FROM $table_course c
1190
                INNER JOIN $table_course_user cu
1191
                ON (c.id = cu.c_id)
1192
                WHERE
1193
                    cu.user_id = '".$user_id."' AND
1194
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
1195
                ";
1196
1197
        $res = Database::query($sql);
1198
        while ($course = Database::fetch_object($res)) {
1199
            $sql = "DELETE FROM $table_group
1200
                    WHERE c_id = {$course->id} AND user_id = $user_id";
1201
            Database::query($sql);
1202
        }
1203
1204
        // Unsubscribe user from usergroup_rel_user
1205
        $sql = "DELETE FROM $usergroup_rel_user WHERE user_id = '".$user_id."'";
1206
        Database::query($sql);
1207
1208
        // Unsubscribe user from all courses
1209
        $sql = "DELETE FROM $table_course_user WHERE user_id = '".$user_id."'";
1210
        Database::query($sql);
1211
1212
        // Unsubscribe user from all courses in sessions
1213
        $sql = "DELETE FROM $table_session_course_user WHERE user_id = '".$user_id."'";
1214
        Database::query($sql);
1215
1216
        // If the user was added as a id_coach then set the current admin as coach see BT#
1217
        $currentUserId = api_get_user_id();
1218
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
1219
                WHERE id_coach = '".$user_id."'";
1220
        Database::query($sql);
1221
1222
        $sql = "UPDATE $table_session SET session_admin_id = $currentUserId
1223
                WHERE session_admin_id = '".$user_id."'";
1224
        Database::query($sql);
1225
1226
        // Unsubscribe user from all sessions
1227
        $sql = "DELETE FROM $table_session_user
1228
                WHERE user_id = '".$user_id."'";
1229
        Database::query($sql);
1230
1231
        if (api_get_configuration_value('plugin_redirection_enabled')) {
1232
            RedirectionPlugin::deleteUserRedirection($user_id);
1233
        }
1234
1235
        $user_info = api_get_user_info($user_id);
1236
1237
        try {
1238
            self::deleteUserFiles($user_id);
1239
        } catch (Exception $exception) {
1240
            error_log('Delete user exception: '.$exception->getMessage());
1241
        }
1242
1243
        // Delete the personal course categories
1244
        $course_cat_table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
1245
        $sql = "DELETE FROM $course_cat_table WHERE user_id = '".$user_id."'";
1246
        Database::query($sql);
1247
1248
        // Delete user from the admin table
1249
        $sql = "DELETE FROM $table_admin WHERE user_id = '".$user_id."'";
1250
        Database::query($sql);
1251
1252
        // Delete the personal agenda-items from this user
1253
        $agenda_table = Database::get_main_table(TABLE_PERSONAL_AGENDA);
1254
        $sql = "DELETE FROM $agenda_table WHERE user = '".$user_id."'";
1255
        Database::query($sql);
1256
1257
        $gradebook_results_table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_RESULT);
1258
        $sql = 'DELETE FROM '.$gradebook_results_table.' WHERE user_id = '.$user_id;
1259
        Database::query($sql);
1260
1261
        $extraFieldValue = new ExtraFieldValue('user');
1262
        $extraFieldValue->deleteValuesByItem($user_id);
1263
1264
        UrlManager::deleteUserFromAllUrls($user_id);
1265
1266
        if (api_get_setting('allow_social_tool') === 'true') {
1267
            $userGroup = new UserGroup();
1268
            //Delete user from portal groups
1269
            $group_list = $userGroup->get_groups_by_user($user_id);
1270
            if (!empty($group_list)) {
1271
                foreach ($group_list as $group_id => $data) {
1272
                    $userGroup->delete_user_rel_group($user_id, $group_id);
1273
                }
1274
            }
1275
1276
            // Delete user from friend lists
1277
            SocialManager::remove_user_rel_user($user_id, true);
1278
        }
1279
1280
        // Removing survey invitation
1281
        SurveyManager::delete_all_survey_invitations_by_user($user_id);
1282
1283
        // Delete students works
1284
        $sql = "DELETE FROM $table_work WHERE user_id = $user_id AND c_id <> 0";
1285
        Database::query($sql);
1286
1287
        $sql = "UPDATE c_item_property SET to_user_id = NULL
1288
                WHERE to_user_id = '".$user_id."'";
1289
        Database::query($sql);
1290
1291
        $sql = "UPDATE c_item_property SET insert_user_id = NULL
1292
                WHERE insert_user_id = '".$user_id."'";
1293
        Database::query($sql);
1294
1295
        $sql = "UPDATE c_item_property SET lastedit_user_id = NULL
1296
                WHERE lastedit_user_id = '".$user_id."'";
1297
        Database::query($sql);
1298
1299
        // Skills
1300
        $em = Database::getManager();
1301
1302
        $criteria = ['user' => $user_id];
1303
        $skills = $em->getRepository('ChamiloCoreBundle:SkillRelUser')->findBy($criteria);
1304
        if ($skills) {
1305
            /** @var SkillRelUser $skill */
1306
            foreach ($skills as $skill) {
1307
                $comments = $skill->getComments();
1308
                if ($comments) {
1309
                    /** @var SkillRelUserComment $comment */
1310
                    foreach ($comments as $comment) {
1311
                        $em->remove($comment);
1312
                    }
1313
                }
1314
                $em->remove($skill);
1315
            }
1316
            $em->flush();
1317
        }
1318
1319
        // ExtraFieldSavedSearch
1320
        $criteria = ['user' => $user_id];
1321
        $searchList = $em->getRepository('ChamiloCoreBundle:ExtraFieldSavedSearch')->findBy($criteria);
1322
        if ($searchList) {
1323
            foreach ($searchList as $search) {
1324
                $em->remove($search);
1325
            }
1326
            $em->flush();
1327
        }
1328
1329
        $connection = Database::getManager()->getConnection();
1330
        $tableExists = $connection->getSchemaManager()->tablesExist(['plugin_bbb_room']);
1331
        if ($tableExists) {
1332
            // Delete user from database
1333
            $sql = "DELETE FROM plugin_bbb_room WHERE participant_id = $user_id";
1334
            Database::query($sql);
1335
        }
1336
1337
        // Delete user/ticket relationships :(
1338
        $tableExists = $connection->getSchemaManager()->tablesExist(['ticket_ticket']);
1339
        if ($tableExists) {
1340
            TicketManager::deleteUserFromTicketSystem($user_id);
1341
        }
1342
1343
        $tableExists = $connection->getSchemaManager()->tablesExist(['c_lp_category_user']);
1344
        if ($tableExists) {
1345
            $sql = "DELETE FROM c_lp_category_user WHERE user_id = $user_id";
1346
            Database::query($sql);
1347
        }
1348
1349
        $app_plugin = new AppPlugin();
1350
        $app_plugin->performActionsWhenDeletingItem('user', $user_id);
1351
1352
        // Delete user from database
1353
        $sql = "DELETE FROM $table_user WHERE id = '".$user_id."'";
1354
        Database::query($sql);
1355
1356
        // Add event to system log
1357
        $user_id_manager = api_get_user_id();
1358
1359
        Event::addEvent(
1360
            LOG_USER_DELETE,
1361
            LOG_USER_ID,
1362
            $user_id,
1363
            api_get_utc_datetime(),
1364
            $user_id_manager
1365
        );
1366
1367
        Event::addEvent(
1368
            LOG_USER_DELETE,
1369
            LOG_USER_OBJECT,
1370
            $user_info,
1371
            api_get_utc_datetime(),
1372
            $user_id_manager
1373
        );
1374
        $cacheAvailable = api_get_configuration_value('apc');
1375
        if ($cacheAvailable === true) {
1376
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1377
            if (apcu_exists($apcVar)) {
1378
                apcu_delete($apcVar);
1379
            }
1380
        }
1381
1382
        return true;
1383
    }
1384
1385
    /**
1386
     * Deletes users completely. Can be called either as:
1387
     * - UserManager::delete_users(1, 2, 3); or
1388
     * - UserManager::delete_users(array(1, 2, 3));.
1389
     *
1390
     * @param array|int $ids
1391
     *
1392
     * @return bool True if at least one user was successfuly deleted. False otherwise.
1393
     *
1394
     * @author Laurent Opprecht
1395
     *
1396
     * @uses \UserManager::delete_user() to actually delete each user
1397
     * @assert (null) === false
1398
     * @assert (-1) === false
1399
     * @assert (array(-1)) === false
1400
     */
1401
    public static function delete_users($ids = [])
1402
    {
1403
        $result = false;
1404
        $ids = is_array($ids) ? $ids : func_get_args();
1405
        if (!is_array($ids) || count($ids) == 0) {
1406
            return false;
1407
        }
1408
        $ids = array_map('intval', $ids);
1409
        foreach ($ids as $id) {
1410
            if (empty($id) || $id < 1) {
1411
                continue;
1412
            }
1413
            $deleted = self::delete_user($id);
1414
            $result = $deleted || $result;
1415
        }
1416
1417
        return $result;
1418
    }
1419
1420
    /**
1421
     * Disable users. Can be called either as:
1422
     * - UserManager::deactivate_users(1, 2, 3);
1423
     * - UserManager::deactivate_users(array(1, 2, 3));.
1424
     *
1425
     * @param array|int $ids
1426
     *
1427
     * @return bool
1428
     *
1429
     * @author Laurent Opprecht
1430
     * @assert (null) === false
1431
     * @assert (array(-1)) === false
1432
     */
1433
    public static function deactivate_users($ids = [])
1434
    {
1435
        if (empty($ids)) {
1436
            return false;
1437
        }
1438
1439
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1440
1441
        $ids = is_array($ids) ? $ids : func_get_args();
1442
        $ids = array_map('intval', $ids);
1443
        $ids = implode(',', $ids);
1444
1445
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
1446
        $r = Database::query($sql);
1447
        if ($r !== false) {
1448
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
1449
1450
            return true;
1451
        }
1452
1453
        return false;
1454
    }
1455
1456
    /**
1457
     * Enable users. Can be called either as:
1458
     * - UserManager::activate_users(1, 2, 3);
1459
     * - UserManager::activate_users(array(1, 2, 3));.
1460
     *
1461
     * @param array|int IDs of the users to enable
1462
     *
1463
     * @return bool
1464
     *
1465
     * @author Laurent Opprecht
1466
     * @assert (null) === false
1467
     * @assert (array(-1)) === false
1468
     */
1469
    public static function activate_users($ids = [])
1470
    {
1471
        if (empty($ids)) {
1472
            return false;
1473
        }
1474
1475
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1476
1477
        $ids = is_array($ids) ? $ids : func_get_args();
1478
        $ids = array_map('intval', $ids);
1479
        $ids = implode(',', $ids);
1480
1481
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
1482
        $r = Database::query($sql);
1483
        if ($r !== false) {
1484
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
1485
1486
            return true;
1487
        }
1488
1489
        return false;
1490
    }
1491
1492
    /**
1493
     * Update user information with new openid.
1494
     *
1495
     * @param int    $user_id
1496
     * @param string $openid
1497
     *
1498
     * @return bool true if the user information was updated
1499
     * @assert (false,'') === false
1500
     * @assert (-1,'') === false
1501
     */
1502
    public static function update_openid($user_id, $openid)
1503
    {
1504
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1505
        if ($user_id != strval(intval($user_id))) {
1506
            return false;
1507
        }
1508
        if ($user_id === false) {
1509
            return false;
1510
        }
1511
        $sql = "UPDATE $table_user SET
1512
                openid='".Database::escape_string($openid)."'";
1513
        $sql .= " WHERE id= $user_id";
1514
1515
        if (Database::query($sql) !== false) {
1516
            return true;
1517
        }
1518
1519
        return false;
1520
    }
1521
1522
    /**
1523
     * Update user information with all the parameters passed to this function.
1524
     *
1525
     * @param int    $user_id         The ID of the user to be updated
1526
     * @param string $firstname       The user's firstname
1527
     * @param string $lastname        The user's lastname
1528
     * @param string $username        The user's username (login)
1529
     * @param string $password        The user's password
1530
     * @param string $auth_source     The authentication source (default: "platform")
1531
     * @param string $email           The user's e-mail address
1532
     * @param int    $status          The user's status
1533
     * @param string $official_code   The user's official code (usually just an internal institutional code)
1534
     * @param string $phone           The user's phone number
1535
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
1536
     * @param string $expiration_date The date at which this user will be automatically disabled
1537
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
1538
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
1539
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
1540
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
1541
     * @param string $language        The language to which the user account will be set
1542
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
1543
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
1544
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
1545
     * @param string $address
1546
     * @param array  $emailTemplate
1547
     *
1548
     * @return bool|int False on error, or the user ID if the user information was updated
1549
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
1550
     */
1551
    public static function update_user(
1552
        $user_id,
1553
        $firstname,
1554
        $lastname,
1555
        $username,
1556
        $password,
1557
        $auth_source,
1558
        $email,
1559
        $status,
1560
        $official_code,
1561
        $phone,
1562
        $picture_uri,
1563
        $expiration_date,
1564
        $active,
1565
        $creator_id = null,
1566
        $hr_dept_id = 0,
1567
        $extra = null,
1568
        $language = 'english',
1569
        $encrypt_method = '',
1570
        $send_email = false,
1571
        $reset_password = 0,
1572
        $address = null,
1573
        $emailTemplate = []
1574
    ) {
1575
        $hook = HookUpdateUser::create();
1576
        if (!empty($hook)) {
1577
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_PRE);
1578
        }
1579
        $original_password = $password;
1580
        $user_id = (int) $user_id;
1581
        $creator_id = (int) $creator_id;
1582
1583
        if (empty($user_id)) {
1584
            return false;
1585
        }
1586
1587
        $userManager = self::getManager();
1588
        /** @var User $user */
1589
        $user = self::getRepository()->find($user_id);
1590
1591
        if (empty($user)) {
1592
            return false;
1593
        }
1594
1595
        if ($reset_password == 0) {
1596
            $password = null;
1597
            $auth_source = $user->getAuthSource();
1598
        } elseif ($reset_password == 1) {
1599
            $original_password = $password = api_generate_password();
1600
            $auth_source = PLATFORM_AUTH_SOURCE;
1601
        } elseif ($reset_password == 2) {
1602
            //$password = $password;
1603
            $auth_source = PLATFORM_AUTH_SOURCE;
1604
        } elseif ($reset_password == 3) {
1605
            //$password = $password;
1606
            //$auth_source = $auth_source;
1607
        }
1608
1609
        // Checking the user language
1610
        $languages = api_get_languages();
1611
        if (!in_array($language, $languages['folder'])) {
1612
            $language = api_get_setting('platformLanguage');
1613
        }
1614
1615
        $change_active = 0;
1616
        $isUserActive = $user->getActive();
1617
        if ($isUserActive != $active) {
1618
            $change_active = 1;
1619
        }
1620
1621
        $originalUsername = $user->getUsername();
1622
1623
        // If username is different from original then check if it exists.
1624
        if ($originalUsername !== $username) {
1625
            $available = self::is_username_available($username);
1626
            if ($available === false) {
1627
                return false;
1628
            }
1629
        }
1630
1631
        if (!empty($expiration_date)) {
1632
            $expiration_date = api_get_utc_datetime($expiration_date);
1633
            $expiration_date = new \DateTime(
1634
                $expiration_date,
1635
                new DateTimeZone('UTC')
1636
            );
1637
        }
1638
1639
        $user
1640
            ->setLastname($lastname)
1641
            ->setFirstname($firstname)
1642
            ->setUsername($username)
1643
            ->setStatus($status)
1644
            ->setAuthSource($auth_source)
1645
            ->setLanguage($language)
1646
            ->setEmail($email)
1647
            ->setOfficialCode($official_code)
1648
            ->setPhone($phone)
1649
            ->setAddress($address)
1650
            ->setPictureUri($picture_uri)
1651
            ->setExpirationDate($expiration_date)
1652
            ->setActive($active)
1653
            ->setEnabled($active)
1654
            ->setHrDeptId($hr_dept_id)
1655
        ;
1656
1657
        if (!is_null($password)) {
1658
            $user->setPlainPassword($password);
1659
            Event::addEvent(LOG_USER_PASSWORD_UPDATE, LOG_USER_ID, $user_id);
1660
        }
1661
1662
        $userManager->updateUser($user, true);
1663
        Event::addEvent(LOG_USER_UPDATE, LOG_USER_ID, $user_id);
1664
1665
        if ($change_active == 1) {
1666
            if ($active == 1) {
1667
                $event_title = LOG_USER_ENABLE;
1668
            } else {
1669
                $event_title = LOG_USER_DISABLE;
1670
            }
1671
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
1672
        }
1673
1674
        if (is_array($extra) && count($extra) > 0) {
1675
            $res = true;
1676
            foreach ($extra as $fname => $fvalue) {
1677
                $res = $res && self::update_extra_field_value(
1678
                    $user_id,
1679
                    $fname,
1680
                    $fvalue
1681
                );
1682
            }
1683
        }
1684
1685
        if (!empty($email) && $send_email) {
1686
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
1687
            $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('YourReg').' '.api_get_setting('siteName');
1688
            $sender_name = api_get_person_name(
1689
                api_get_setting('administratorName'),
1690
                api_get_setting('administratorSurname'),
1691
                null,
1692
                PERSON_NAME_EMAIL_ADDRESS
1693
            );
1694
            $email_admin = api_get_setting('emailAdministrator');
1695
            $url = api_get_path(WEB_PATH);
1696
            if (api_is_multiple_url_enabled()) {
1697
                $access_url_id = api_get_current_access_url_id();
1698
                if ($access_url_id != -1) {
1699
                    $url = api_get_access_url($access_url_id);
1700
                    $url = $url['url'];
1701
                }
1702
            }
1703
1704
            $tplContent = new Template(
1705
                null,
1706
                false,
1707
                false,
1708
                false,
1709
                false,
1710
                false
1711
            );
1712
            // variables for the default template
1713
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
1714
            $tplContent->assign('login_name', $username);
1715
1716
            $originalPassword = '';
1717
            if ($reset_password > 0) {
1718
                $originalPassword = stripslashes($original_password);
1719
            }
1720
            $tplContent->assign('original_password', $originalPassword);
1721
            $tplContent->assign('portal_url', $url);
1722
            // Adding this variable but not used in default template, used for task BT19518 with a customized template
1723
            $tplContent->assign('status_type', $status);
1724
1725
            $layoutContent = $tplContent->get_template('mail/user_edit_content.tpl');
1726
            $emailBody = $tplContent->fetch($layoutContent);
1727
1728
            $mailTemplateManager = new MailTemplateManager();
1729
1730
            if (!empty($emailTemplate) &&
1731
                isset($emailTemplate['user_edit_content.tpl']) &&
1732
                !empty($emailTemplate['user_edit_content.tpl'])
1733
            ) {
1734
                $userInfo = api_get_user_info($user_id);
1735
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], $userInfo);
1736
            }
1737
1738
            $creatorInfo = api_get_user_info($creator_id);
1739
            $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
1740
1741
            api_mail_html(
1742
                $recipient_name,
1743
                $email,
1744
                $emailsubject,
1745
                $emailBody,
1746
                $sender_name,
1747
                $email_admin,
1748
                null,
1749
                null,
1750
                null,
1751
                null,
1752
                $creatorEmail
1753
            );
1754
        }
1755
1756
        if (!empty($hook)) {
1757
            $hook->setEventData(['user' => $user]);
1758
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_POST);
1759
        }
1760
1761
        $cacheAvailable = api_get_configuration_value('apc');
1762
        if ($cacheAvailable === true) {
1763
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1764
            if (apcu_exists($apcVar)) {
1765
                apcu_delete($apcVar);
1766
            }
1767
        }
1768
1769
        return $user->getId();
1770
    }
1771
1772
    /**
1773
     * Disables a user.
1774
     *
1775
     * @param int User id
1776
     *
1777
     * @return bool
1778
     *
1779
     * @uses \UserManager::change_active_state() to actually disable the user
1780
     * @assert (0) === false
1781
     */
1782
    public static function disable($user_id)
1783
    {
1784
        if (empty($user_id)) {
1785
            return false;
1786
        }
1787
        self::change_active_state($user_id, 0);
1788
1789
        return true;
1790
    }
1791
1792
    /**
1793
     * Enable a user.
1794
     *
1795
     * @param int User id
1796
     *
1797
     * @return bool
1798
     *
1799
     * @uses \UserManager::change_active_state() to actually disable the user
1800
     * @assert (0) === false
1801
     */
1802
    public static function enable($user_id)
1803
    {
1804
        if (empty($user_id)) {
1805
            return false;
1806
        }
1807
        self::change_active_state($user_id, 1);
1808
1809
        return true;
1810
    }
1811
1812
    /**
1813
     * Returns the user's id based on the original id and field name in
1814
     * the extra fields. Returns 0 if no user was found. This function is
1815
     * mostly useful in the context of a web services-based sinchronization.
1816
     *
1817
     * @param string Original user id
1818
     * @param string Original field name
1819
     *
1820
     * @return int User id
1821
     * @assert ('0','---') === 0
1822
     */
1823
    public static function get_user_id_from_original_id(
1824
        $original_user_id_value,
1825
        $original_user_id_name
1826
    ) {
1827
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1828
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1829
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1830
1831
        $original_user_id_name = Database::escape_string($original_user_id_name);
1832
        $original_user_id_value = Database::escape_string($original_user_id_value);
1833
1834
        $sql = "SELECT item_id as user_id
1835
                FROM $t_uf uf
1836
                INNER JOIN $t_ufv ufv
1837
                ON ufv.field_id = uf.id
1838
                WHERE
1839
                    variable = '$original_user_id_name' AND
1840
                    value = '$original_user_id_value' AND
1841
                    extra_field_type = $extraFieldType
1842
                ";
1843
        $res = Database::query($sql);
1844
        $row = Database::fetch_object($res);
1845
        if ($row) {
1846
            return $row->user_id;
1847
        }
1848
1849
        return 0;
1850
    }
1851
1852
    /**
1853
     * Check if a username is available.
1854
     *
1855
     * @param string $username the wanted username
1856
     *
1857
     * @return bool true if the wanted username is available
1858
     * @assert ('') === false
1859
     * @assert ('xyzxyzxyz') === true
1860
     */
1861
    public static function is_username_available($username)
1862
    {
1863
        if (empty($username)) {
1864
            return false;
1865
        }
1866
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1867
        $sql = "SELECT username FROM $table_user
1868
                WHERE username = '".Database::escape_string($username)."'";
1869
        $res = Database::query($sql);
1870
1871
        return Database::num_rows($res) == 0;
1872
    }
1873
1874
    /**
1875
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1876
     *
1877
     * @param string $firstname the first name of the user
1878
     * @param string $lastname  the last name of the user
1879
     *
1880
     * @return string suggests a username that contains only ASCII-letters and digits,
1881
     *                without check for uniqueness within the system
1882
     *
1883
     * @author Julio Montoya Armas
1884
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1885
     * @assert ('','') === false
1886
     * @assert ('a','b') === 'ab'
1887
     */
1888
    public static function create_username($firstname, $lastname)
1889
    {
1890
        if (empty($firstname) && empty($lastname)) {
1891
            return false;
1892
        }
1893
1894
        // The first letter only.
1895
        $firstname = api_substr(
1896
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1897
            0,
1898
            1
1899
        );
1900
        //Looking for a space in the lastname
1901
        $pos = api_strpos($lastname, ' ');
1902
        if ($pos !== false) {
1903
            $lastname = api_substr($lastname, 0, $pos);
1904
        }
1905
1906
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1907
        $username = $firstname.$lastname;
1908
        if (empty($username)) {
1909
            $username = 'user';
1910
        }
1911
1912
        $username = URLify::transliterate($username);
1913
1914
        return strtolower(substr($username, 0, USERNAME_MAX_LENGTH - 3));
1915
    }
1916
1917
    /**
1918
     * Creates a unique username, using:
1919
     * 1. the first name and the last name of a user;
1920
     * 2. an already created username but not checked for uniqueness yet.
1921
     *
1922
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1923
     *                          parameter is treated as username which is to be checked f
1924
     *                          or uniqueness and to be modified when it is necessary.
1925
     * @param string $lastname  the last name of the user
1926
     *
1927
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1928
     *                Note: When the method is called several times with same parameters,
1929
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1930
     *
1931
     * @author Ivan Tcholakov, 2009
1932
     */
1933
    public static function create_unique_username($firstname, $lastname = null)
1934
    {
1935
        if (is_null($lastname)) {
1936
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1937
            // For making this method tolerant of mistakes,
1938
            // let us transliterate and purify the suggested input username anyway.
1939
            // So, instead of the sentence $username = $firstname; we place the following:
1940
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1941
        } else {
1942
            $username = self::create_username($firstname, $lastname);
1943
        }
1944
        if (!self::is_username_available($username)) {
1945
            $i = 2;
1946
            $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1947
            while (!self::is_username_available($temp_username)) {
1948
                $i++;
1949
                $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1950
            }
1951
            $username = $temp_username;
1952
        }
1953
1954
        $username = URLify::transliterate($username);
1955
1956
        return $username;
1957
    }
1958
1959
    /**
1960
     * Modifies a given username accordingly to the specification for valid characters and length.
1961
     *
1962
     * @param $username string          The input username
1963
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1964
     *                     otherwise compliance may be partial. The default value is FALSE.
1965
     *
1966
     * @return string the resulting purified username
1967
     */
1968
    public static function purify_username($username, $strict = false)
1969
    {
1970
        if ($strict) {
1971
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1972
            // into ASCII letters in order they not to be totally removed.
1973
            // 2. Applying the strict purifier.
1974
            // 3. Length limitation.
1975
            if ('true' === api_get_setting('login_is_email')) {
1976
                $return = substr(preg_replace(USERNAME_PURIFIER_MAIL, '', $username), 0, USERNAME_MAX_LENGTH);
1977
            } else {
1978
                $return = substr(preg_replace(USERNAME_PURIFIER, '', $username), 0, USERNAME_MAX_LENGTH);
1979
            }
1980
1981
            return URLify::transliterate($return);
1982
        }
1983
1984
        // 1. Applying the shallow purifier.
1985
        // 2. Length limitation.
1986
        return substr(
1987
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1988
            0,
1989
            USERNAME_MAX_LENGTH
1990
        );
1991
    }
1992
1993
    /**
1994
     * Checks whether the user id exists in the database.
1995
     *
1996
     * @param int $userId User id
1997
     *
1998
     * @return bool True if user id was found, false otherwise
1999
     */
2000
    public static function is_user_id_valid($userId)
2001
    {
2002
        $resultData = Database::select(
2003
            'COUNT(1) AS count',
2004
            Database::get_main_table(TABLE_MAIN_USER),
2005
            [
2006
                'where' => ['id = ?' => (int) $userId],
2007
            ],
2008
            'first'
2009
        );
2010
2011
        if ($resultData === false) {
2012
            return false;
2013
        }
2014
2015
        return $resultData['count'] > 0;
2016
    }
2017
2018
    /**
2019
     * Checks whether a given username matches to the specification strictly.
2020
     * The empty username is assumed here as invalid.
2021
     * Mostly this function is to be used in the user interface built-in validation routines
2022
     * for providing feedback while usernames are enterd manually.
2023
     *
2024
     * @param string $username the input username
2025
     *
2026
     * @return bool returns TRUE if the username is valid, FALSE otherwise
2027
     */
2028
    public static function is_username_valid($username)
2029
    {
2030
        return !empty($username) && $username == self::purify_username($username, true);
2031
    }
2032
2033
    /**
2034
     * Checks whether a username is empty. If the username contains whitespace characters,
2035
     * such as spaces, tabulators, newlines, etc.,
2036
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
2037
     *
2038
     * @param string $username the given username
2039
     *
2040
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
2041
     */
2042
    public static function is_username_empty($username)
2043
    {
2044
        return strlen(self::purify_username($username, false)) == 0;
2045
    }
2046
2047
    /**
2048
     * Checks whether a username is too long or not.
2049
     *
2050
     * @param string $username the given username, it should contain only ASCII-letters and digits
2051
     *
2052
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
2053
     */
2054
    public static function is_username_too_long($username)
2055
    {
2056
        return strlen($username) > USERNAME_MAX_LENGTH;
2057
    }
2058
2059
    /**
2060
     * Get the users by ID.
2061
     *
2062
     * @param array  $ids    student ids
2063
     * @param string $active
2064
     * @param string $order
2065
     * @param string $limit
2066
     *
2067
     * @return array $result student information
2068
     */
2069
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
2070
    {
2071
        if (empty($ids)) {
2072
            return [];
2073
        }
2074
2075
        $ids = is_array($ids) ? $ids : [$ids];
2076
        $ids = array_map('intval', $ids);
2077
        $ids = implode(',', $ids);
2078
2079
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2080
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
2081
        if (!is_null($active)) {
2082
            $sql .= ' AND active='.($active ? '1' : '0');
2083
        }
2084
2085
        if (!is_null($order)) {
2086
            $order = Database::escape_string($order);
2087
            $sql .= ' ORDER BY '.$order;
2088
        }
2089
2090
        if (!is_null($limit)) {
2091
            $limit = Database::escape_string($limit);
2092
            $sql .= ' LIMIT '.$limit;
2093
        }
2094
2095
        $rs = Database::query($sql);
2096
        $result = [];
2097
        while ($row = Database::fetch_array($rs)) {
2098
            $result[] = $row;
2099
        }
2100
2101
        return $result;
2102
    }
2103
2104
    /**
2105
     * Get a list of users of which the given conditions match with an = 'cond'.
2106
     *
2107
     * @param array $conditions a list of condition (example : status=>STUDENT)
2108
     * @param array $order_by   a list of fields on which sort
2109
     *
2110
     * @return array an array with all users of the platform
2111
     *
2112
     * @todo security filter order by
2113
     */
2114
    public static function get_user_list(
2115
        $conditions = [],
2116
        $order_by = [],
2117
        $limit_from = false,
2118
        $limit_to = false,
2119
        $idCampus = null
2120
    ) {
2121
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
2122
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2123
        $return_array = [];
2124
        $sql = "SELECT user.* FROM $user_table user ";
2125
2126
        if (api_is_multiple_url_enabled()) {
2127
            if ($idCampus) {
2128
                $urlId = $idCampus;
2129
            } else {
2130
                $urlId = api_get_current_access_url_id();
2131
            }
2132
            $sql .= " INNER JOIN $userUrlTable url_user
2133
                      ON (user.user_id = url_user.user_id)
2134
                      WHERE url_user.access_url_id = $urlId";
2135
        } else {
2136
            $sql .= " WHERE 1=1 ";
2137
        }
2138
2139
        if (count($conditions) > 0) {
2140
            foreach ($conditions as $field => $value) {
2141
                $field = Database::escape_string($field);
2142
                $value = Database::escape_string($value);
2143
                $sql .= " AND $field = '$value'";
2144
            }
2145
        }
2146
2147
        if (count($order_by) > 0) {
2148
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
2149
        }
2150
2151
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
2152
            $limit_from = (int) $limit_from;
2153
            $limit_to = (int) $limit_to;
2154
            $sql .= " LIMIT $limit_from, $limit_to";
2155
        }
2156
        $sql_result = Database::query($sql);
2157
        while ($result = Database::fetch_array($sql_result)) {
2158
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
2159
            $return_array[] = $result;
2160
        }
2161
2162
        return $return_array;
2163
    }
2164
2165
    public static function getUserListExtraConditions(
2166
        $conditions = [],
2167
        $order_by = [],
2168
        $limit_from = false,
2169
        $limit_to = false,
2170
        $idCampus = null,
2171
        $extraConditions = '',
2172
        $getCount = false
2173
    ) {
2174
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
2175
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2176
        $return_array = [];
2177
        $sql = "SELECT user.* FROM $user_table user ";
2178
2179
        if ($getCount) {
2180
            $sql = "SELECT count(user.id) count FROM $user_table user ";
2181
        }
2182
2183
        if (api_is_multiple_url_enabled()) {
2184
            if ($idCampus) {
2185
                $urlId = $idCampus;
2186
            } else {
2187
                $urlId = api_get_current_access_url_id();
2188
            }
2189
            $sql .= " INNER JOIN $userUrlTable url_user
2190
                      ON (user.user_id = url_user.user_id)
2191
                      WHERE url_user.access_url_id = $urlId";
2192
        } else {
2193
            $sql .= " WHERE 1=1 ";
2194
        }
2195
2196
        $sql .= " AND status <> ".ANONYMOUS." ";
2197
2198
        if (count($conditions) > 0) {
2199
            foreach ($conditions as $field => $value) {
2200
                $field = Database::escape_string($field);
2201
                $value = Database::escape_string($value);
2202
                $sql .= " AND $field = '$value'";
2203
            }
2204
        }
2205
2206
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
2207
2208
        if (!empty($order_by) && count($order_by) > 0) {
2209
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
2210
        }
2211
2212
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
2213
            $limit_from = (int) $limit_from;
2214
            $limit_to = (int) $limit_to;
2215
            $sql .= " LIMIT $limit_from, $limit_to";
2216
        }
2217
2218
        $sql_result = Database::query($sql);
2219
2220
        if ($getCount) {
2221
            $result = Database::fetch_array($sql_result);
2222
2223
            return $result['count'];
2224
        }
2225
2226
        while ($result = Database::fetch_array($sql_result)) {
2227
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
2228
            $return_array[] = $result;
2229
        }
2230
2231
        return $return_array;
2232
    }
2233
2234
    /**
2235
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
2236
     *
2237
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
2238
     * @param array  $order_by         a list of fields on which sort
2239
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
2240
     * @param string $condition        Whether we want the filters to be combined by AND or OR
2241
     * @param array  $onlyThisUserList
2242
     *
2243
     * @return array an array with all users of the platform
2244
     *
2245
     * @todo optional course code parameter, optional sorting parameters...
2246
     * @todo security filter order_by
2247
     */
2248
    public static function getUserListLike(
2249
        $conditions = [],
2250
        $order_by = [],
2251
        $simple_like = false,
2252
        $condition = 'AND',
2253
        $onlyThisUserList = []
2254
    ) {
2255
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
2256
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2257
        $return_array = [];
2258
        $sql_query = "SELECT user.id FROM $user_table user ";
2259
2260
        if (api_is_multiple_url_enabled()) {
2261
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
2262
        }
2263
2264
        $sql_query .= ' WHERE 1 = 1 ';
2265
        if (count($conditions) > 0) {
2266
            $temp_conditions = [];
2267
            foreach ($conditions as $field => $value) {
2268
                $field = Database::escape_string($field);
2269
                $value = Database::escape_string($value);
2270
                if ($simple_like) {
2271
                    $temp_conditions[] = $field." LIKE '$value%'";
2272
                } else {
2273
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
2274
                }
2275
            }
2276
            if (!empty($temp_conditions)) {
2277
                $sql_query .= ' AND '.implode(' '.$condition.' ', $temp_conditions);
2278
            }
2279
2280
            if (api_is_multiple_url_enabled()) {
2281
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
2282
            }
2283
        } else {
2284
            if (api_is_multiple_url_enabled()) {
2285
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
2286
            }
2287
        }
2288
2289
        if (!empty($onlyThisUserList)) {
2290
            $onlyThisUserListToString = implode("','", $onlyThisUserList);
2291
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
2292
        }
2293
2294
        if (count($order_by) > 0) {
2295
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
2296
        }
2297
2298
        $sql_result = Database::query($sql_query);
2299
        while ($result = Database::fetch_array($sql_result)) {
2300
            $userInfo = api_get_user_info($result['id']);
2301
            $return_array[] = $userInfo;
2302
        }
2303
2304
        return $return_array;
2305
    }
2306
2307
    /**
2308
     * Get user picture URL or path from user ID (returns an array).
2309
     * The return format is a complete path, enabling recovery of the directory
2310
     * with dirname() or the file with basename(). This also works for the
2311
     * functions dealing with the user's productions, as they are located in
2312
     * the same directory.
2313
     *
2314
     * @param int    $id       User ID
2315
     * @param string $type     Type of path to return (can be 'system', 'web')
2316
     * @param array  $userInfo user information to avoid query the DB
2317
     *                         returns the /main/img/unknown.jpg image set it at true
2318
     *
2319
     * @return array Array of 2 elements: 'dir' and 'file' which contain
2320
     *               the dir and file as the name implies if image does not exist it will
2321
     *               return the unknow image if anonymous parameter is true if not it returns an empty array
2322
     */
2323
    public static function get_user_picture_path_by_id(
2324
        $id,
2325
        $type = 'web',
2326
        $userInfo = []
2327
    ) {
2328
        switch ($type) {
2329
            case 'system': // Base: absolute system path.
2330
                $base = api_get_path(SYS_CODE_PATH);
2331
                break;
2332
            case 'web': // Base: absolute web path.
2333
            default:
2334
                $base = api_get_path(WEB_CODE_PATH);
2335
                break;
2336
        }
2337
2338
        $anonymousPath = [
2339
            'dir' => $base.'img/',
2340
            'file' => 'unknown.jpg',
2341
            'email' => '',
2342
        ];
2343
2344
        if (empty($id) || empty($type)) {
2345
            return $anonymousPath;
2346
        }
2347
2348
        $id = (int) $id;
2349
        if (empty($userInfo)) {
2350
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
2351
            $sql = "SELECT email, picture_uri FROM $user_table
2352
                    WHERE id = ".$id;
2353
            $res = Database::query($sql);
2354
2355
            if (!Database::num_rows($res)) {
2356
                return $anonymousPath;
2357
            }
2358
            $user = Database::fetch_array($res);
2359
            if (empty($user['picture_uri'])) {
2360
                return $anonymousPath;
2361
            }
2362
        } else {
2363
            $user = $userInfo;
2364
        }
2365
2366
        $pictureFilename = trim($user['picture_uri']);
2367
2368
        $dir = self::getUserPathById($id, $type);
2369
2370
        return [
2371
            'dir' => $dir,
2372
            'file' => $pictureFilename,
2373
            'email' => $user['email'],
2374
        ];
2375
    }
2376
2377
    /**
2378
     * *** READ BEFORE REVIEW THIS FUNCTION ***
2379
     * This function is a exact copy from get_user_picture_path_by_id() and it was create it to avoid
2380
     * a recursive calls for get_user_picture_path_by_id() in another functions when you update a user picture
2381
     * in same script, so you can find this function usage in update_user_picture() function.
2382
     *
2383
     * @param int    $id       User ID
2384
     * @param string $type     Type of path to return (can be 'system', 'web')
2385
     * @param array  $userInfo user information to avoid query the DB
2386
     *                         returns the /main/img/unknown.jpg image set it at true
2387
     *
2388
     * @return array Array of 2 elements: 'dir' and 'file' which contain
2389
     *               the dir and file as the name implies if image does not exist it will
2390
     *               return the unknown image if anonymous parameter is true if not it returns an empty array
2391
     */
2392
    public static function getUserPicturePathById($id, $type = 'web', $userInfo = [])
2393
    {
2394
        switch ($type) {
2395
            case 'system': // Base: absolute system path.
2396
                $base = api_get_path(SYS_CODE_PATH);
2397
                break;
2398
            case 'web': // Base: absolute web path.
2399
            default:
2400
                $base = api_get_path(WEB_CODE_PATH);
2401
                break;
2402
        }
2403
2404
        $anonymousPath = [
2405
            'dir' => $base.'img/',
2406
            'file' => 'unknown.jpg',
2407
            'email' => '',
2408
        ];
2409
2410
        if (empty($id) || empty($type)) {
2411
            return $anonymousPath;
2412
        }
2413
2414
        $id = (int) $id;
2415
        if (empty($userInfo)) {
2416
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
2417
            $sql = "SELECT email, picture_uri FROM $user_table WHERE id = $id";
2418
            $res = Database::query($sql);
2419
2420
            if (!Database::num_rows($res)) {
2421
                return $anonymousPath;
2422
            }
2423
            $user = Database::fetch_array($res);
2424
2425
            if (empty($user['picture_uri'])) {
2426
                return $anonymousPath;
2427
            }
2428
        } else {
2429
            $user = $userInfo;
2430
        }
2431
2432
        $pictureFilename = trim($user['picture_uri']);
2433
        $dir = self::getUserPathById($id, $type);
2434
2435
        return [
2436
            'dir' => $dir,
2437
            'file' => $pictureFilename,
2438
            'email' => $user['email'],
2439
        ];
2440
    }
2441
2442
    /**
2443
     * Get user path from user ID (returns an array).
2444
     * The return format is a complete path to a folder ending with "/"
2445
     * In case the first level of subdirectory of users/ does not exist, the
2446
     * function will attempt to create it. Probably not the right place to do it
2447
     * but at least it avoids headaches in many other places.
2448
     *
2449
     * @param int    $id   User ID
2450
     * @param string $type Type of path to return (can be 'system', 'web', 'last')
2451
     *
2452
     * @return string User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
2453
     */
2454
    public static function getUserPathById($id, $type)
2455
    {
2456
        $id = (int) $id;
2457
        if (!$id) {
2458
            return null;
2459
        }
2460
2461
        $userPath = "users/$id/";
2462
        if (api_get_setting('split_users_upload_directory') === 'true') {
2463
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
2464
            // In exceptional cases, on some portals, the intermediate base user
2465
            // directory might not have been created. Make sure it is before
2466
            // going further.
2467
2468
            $rootPath = api_get_path(SYS_UPLOAD_PATH).'users/'.substr((string) $id, 0, 1);
2469
            if (!is_dir($rootPath)) {
2470
                $perm = api_get_permissions_for_new_directories();
2471
                try {
2472
                    mkdir($rootPath, $perm);
2473
                } catch (Exception $e) {
2474
                    error_log($e->getMessage());
2475
                }
2476
            }
2477
        }
2478
        switch ($type) {
2479
            case 'system': // Base: absolute system path.
2480
                $userPath = api_get_path(SYS_UPLOAD_PATH).$userPath;
2481
                break;
2482
            case 'web': // Base: absolute web path.
2483
                $userPath = api_get_path(WEB_UPLOAD_PATH).$userPath;
2484
                break;
2485
            case 'last': // Only the last part starting with users/
2486
                break;
2487
        }
2488
2489
        return $userPath;
2490
    }
2491
2492
    /**
2493
     * Gets the current user image.
2494
     *
2495
     * @param string $user_id
2496
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
2497
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
2498
     * @param bool   $addRandomId
2499
     * @param array  $userInfo    to avoid query the DB
2500
     *
2501
     * @return string
2502
     */
2503
    public static function getUserPicture(
2504
        $user_id,
2505
        $size = USER_IMAGE_SIZE_MEDIUM,
2506
        $addRandomId = true,
2507
        $userInfo = []
2508
    ) {
2509
        // Make sure userInfo is defined. Otherwise, define it!
2510
        if (empty($userInfo) || !is_array($userInfo) || count($userInfo) == 0) {
2511
            if (empty($user_id)) {
2512
                return '';
2513
            } else {
2514
                $userInfo = api_get_user_info($user_id);
2515
            }
2516
        }
2517
2518
        $imageWebPath = self::get_user_picture_path_by_id(
2519
            $user_id,
2520
            'web',
2521
            $userInfo
2522
        );
2523
        $pictureWebFile = $imageWebPath['file'];
2524
        $pictureWebDir = $imageWebPath['dir'];
2525
2526
        $pictureAnonymousSize = '128';
2527
        $gravatarSize = 22;
2528
        $realSizeName = 'small_';
2529
2530
        switch ($size) {
2531
            case USER_IMAGE_SIZE_SMALL:
2532
                $pictureAnonymousSize = '32';
2533
                $realSizeName = 'small_';
2534
                $gravatarSize = 32;
2535
                break;
2536
            case USER_IMAGE_SIZE_MEDIUM:
2537
                $pictureAnonymousSize = '64';
2538
                $realSizeName = 'medium_';
2539
                $gravatarSize = 64;
2540
                break;
2541
            case USER_IMAGE_SIZE_ORIGINAL:
2542
                $pictureAnonymousSize = '128';
2543
                $realSizeName = '';
2544
                $gravatarSize = 128;
2545
                break;
2546
            case USER_IMAGE_SIZE_BIG:
2547
                $pictureAnonymousSize = '128';
2548
                $realSizeName = 'big_';
2549
                $gravatarSize = 128;
2550
                break;
2551
        }
2552
2553
        $gravatarEnabled = api_get_setting('gravatar_enabled');
2554
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
2555
        if ($pictureWebFile == 'unknown.jpg' || empty($pictureWebFile)) {
2556
            if ($gravatarEnabled === 'true') {
2557
                $file = self::getGravatar(
2558
                    $imageWebPath['email'],
2559
                    $gravatarSize,
2560
                    api_get_setting('gravatar_type')
2561
                );
2562
2563
                if ($addRandomId) {
2564
                    $file .= '&rand='.uniqid();
2565
                }
2566
2567
                return $file;
2568
            }
2569
2570
            return $anonymousPath;
2571
        }
2572
2573
        $pictureSysPath = self::get_user_picture_path_by_id($user_id, 'system');
2574
        $file = $pictureSysPath['dir'].$realSizeName.$pictureWebFile;
2575
        $picture = '';
2576
        if (file_exists($file)) {
2577
            $picture = $pictureWebDir.$realSizeName.$pictureWebFile;
2578
        } else {
2579
            $file = $pictureSysPath['dir'].$pictureWebFile;
2580
            if (file_exists($file) && !is_dir($file)) {
2581
                $picture = $pictureWebFile['dir'].$pictureWebFile;
2582
            }
2583
        }
2584
2585
        if (empty($picture)) {
2586
            return $anonymousPath;
2587
        }
2588
2589
        if ($addRandomId) {
2590
            $picture .= '?rand='.uniqid();
2591
        }
2592
2593
        return $picture;
2594
    }
2595
2596
    /**
2597
     * Creates new user photos in various sizes of a user, or deletes user photos.
2598
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2599
     *
2600
     * @param int    $user_id the user internal identification number
2601
     * @param string $file    The common file name for the newly created photos.
2602
     *                        It will be checked and modified for compatibility with the file system.
2603
     *                        If full name is provided, path component is ignored.
2604
     *                        If an empty name is provided, then old user photos are deleted only,
2605
     *
2606
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
2607
     *
2608
     * @param string $source_file    the full system name of the image from which user photos will be created
2609
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
2610
     *
2611
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
2612
     *              When deletion is requested returns empty string.
2613
     *              In case of internal error or negative validation returns FALSE.
2614
     */
2615
    public static function update_user_picture(
2616
        $user_id,
2617
        $file = null,
2618
        $source_file = null,
2619
        $cropParameters = ''
2620
    ) {
2621
        if (empty($user_id)) {
2622
            return false;
2623
        }
2624
        $delete = empty($file);
2625
        if (empty($source_file)) {
2626
            $source_file = $file;
2627
        }
2628
2629
        // User-reserved directory where photos have to be placed.
2630
        $path_info = self::getUserPicturePathById($user_id, 'system');
2631
        $path = $path_info['dir'];
2632
2633
        // If this directory does not exist - we create it.
2634
        if (!file_exists($path)) {
2635
            mkdir($path, api_get_permissions_for_new_directories(), true);
2636
        }
2637
2638
        // The old photos (if any).
2639
        $old_file = $path_info['file'];
2640
2641
        // Let us delete them.
2642
        if ($old_file != 'unknown.jpg') {
2643
            if (KEEP_THE_OLD_IMAGE_AFTER_CHANGE) {
0 ignored issues
show
Bug introduced by
The constant KEEP_THE_OLD_IMAGE_AFTER_CHANGE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2644
                $prefix = 'saved_'.date('Y_m_d_H_i_s').'_'.uniqid('').'_';
2645
                @rename($path.'small_'.$old_file, $path.$prefix.'small_'.$old_file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

2645
                /** @scrutinizer ignore-unhandled */ @rename($path.'small_'.$old_file, $path.$prefix.'small_'.$old_file);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2646
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
2647
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
2648
                @rename($path.$old_file, $path.$prefix.$old_file);
2649
            } else {
2650
                @unlink($path.'small_'.$old_file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

2650
                /** @scrutinizer ignore-unhandled */ @unlink($path.'small_'.$old_file);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2651
                @unlink($path.'medium_'.$old_file);
2652
                @unlink($path.'big_'.$old_file);
2653
                @unlink($path.$old_file);
2654
            }
2655
        }
2656
2657
        // Exit if only deletion has been requested. Return an empty picture name.
2658
        if ($delete) {
2659
            return '';
2660
        }
2661
2662
        // Validation 2.
2663
        $allowed_types = api_get_supported_image_extensions();
2664
        $file = str_replace('\\', '/', $file);
2665
        $filename = (($pos = strrpos($file, '/')) !== false) ? substr($file, $pos + 1) : $file;
2666
        $extension = strtolower(substr(strrchr($filename, '.'), 1));
2667
        if (!in_array($extension, $allowed_types)) {
2668
            return false;
2669
        }
2670
2671
        // This is the common name for the new photos.
2672
        if (KEEP_THE_NAME_WHEN_CHANGE_IMAGE && $old_file != 'unknown.jpg') {
0 ignored issues
show
Bug introduced by
The constant KEEP_THE_NAME_WHEN_CHANGE_IMAGE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2673
            $old_extension = strtolower(substr(strrchr($old_file, '.'), 1));
2674
            $filename = in_array($old_extension, $allowed_types) ? substr($old_file, 0, -strlen($old_extension)) : $old_file;
2675
            $filename = (substr($filename, -1) == '.') ? $filename.$extension : $filename.'.'.$extension;
2676
        } else {
2677
            $filename = api_replace_dangerous_char($filename);
2678
            if (PREFIX_IMAGE_FILENAME_WITH_UID) {
0 ignored issues
show
Bug introduced by
The constant PREFIX_IMAGE_FILENAME_WITH_UID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2679
                $filename = uniqid('').'_'.$filename;
2680
            }
2681
            // We always prefix user photos with user ids, so on setting
2682
            // api_get_setting('split_users_upload_directory') === 'true'
2683
            // the correspondent directories to be found successfully.
2684
            $filename = $user_id.'_'.$filename;
2685
        }
2686
2687
        if (!file_exists($source_file)) {
2688
            return false;
2689
        }
2690
2691
        $mimeContentType = mime_content_type($source_file);
2692
        if (false === strpos($mimeContentType, 'image')) {
2693
            return false;
2694
        }
2695
2696
        //Crop the image to adjust 1:1 ratio
2697
        $image = new Image($source_file);
2698
        $image->crop($cropParameters);
2699
2700
        // Storing the new photos in 4 versions with various sizes.
2701
        $userPath = self::getUserPathById($user_id, 'system');
2702
2703
        // If this path does not exist - we create it.
2704
        if (!file_exists($userPath)) {
2705
            mkdir($userPath, api_get_permissions_for_new_directories(), true);
2706
        }
2707
        $small = new Image($source_file);
2708
        $small->resize(32);
2709
        $small->send_image($userPath.'small_'.$filename);
2710
        $medium = new Image($source_file);
2711
        $medium->resize(85);
2712
        $medium->send_image($userPath.'medium_'.$filename);
2713
        $normal = new Image($source_file);
2714
        $normal->resize(200);
2715
        $normal->send_image($userPath.$filename);
2716
2717
        $big = new Image($source_file); // This is the original picture.
2718
        $big->send_image($userPath.'big_'.$filename);
2719
2720
        $result = $small && $medium && $normal && $big;
0 ignored issues
show
introduced by
$normal is of type Image, thus it always evaluated to true.
Loading history...
introduced by
$big is of type Image, thus it always evaluated to true.
Loading history...
introduced by
$medium is of type Image, thus it always evaluated to true.
Loading history...
2721
2722
        return $result ? $filename : false;
2723
    }
2724
2725
    /**
2726
     * Update User extra field file type into {user_folder}/{$extra_field}.
2727
     *
2728
     * @param int    $user_id     The user internal identification number
2729
     * @param string $extra_field The $extra_field The extra field name
2730
     * @param null   $file        The filename
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $file is correct as it would always require null to be passed?
Loading history...
2731
     * @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...
2732
     *
2733
     * @return bool|null return filename if success, but false
2734
     */
2735
    public static function update_user_extra_file(
2736
        $user_id,
2737
        $extra_field = '',
2738
        $file = null,
2739
        $source_file = null
2740
    ) {
2741
        // Add Filter
2742
        $source_file = Security::filter_filename($source_file);
2743
        $file = Security::filter_filename($file);
2744
2745
        if (empty($user_id)) {
2746
            return false;
2747
        }
2748
2749
        if (empty($source_file)) {
2750
            $source_file = $file;
2751
        }
2752
2753
        // User-reserved directory where extra file have to be placed.
2754
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2755
        $path = $path_info['dir'];
2756
        if (!empty($extra_field)) {
2757
            $path .= $extra_field.'/';
2758
        }
2759
        // If this directory does not exist - we create it.
2760
        if (!file_exists($path)) {
2761
            @mkdir($path, api_get_permissions_for_new_directories(), true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

2761
            /** @scrutinizer ignore-unhandled */ @mkdir($path, api_get_permissions_for_new_directories(), true);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2762
        }
2763
2764
        if (filter_extension($file)) {
2765
            if (@move_uploaded_file($source_file, $path.$file)) {
2766
                if ($extra_field) {
2767
                    return $extra_field.'/'.$file;
2768
                } else {
2769
                    return $file;
2770
                }
2771
            }
2772
        }
2773
2774
        return false; // this should be returned if anything went wrong with the upload
2775
    }
2776
2777
    /**
2778
     * Deletes user photos.
2779
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2780
     *
2781
     * @param int $userId the user internal identification number
2782
     *
2783
     * @return mixed returns empty string on success, FALSE on error
2784
     */
2785
    public static function deleteUserPicture($userId)
2786
    {
2787
        return self::update_user_picture($userId);
2788
    }
2789
2790
    /**
2791
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
2792
     * doesn't have any.
2793
     *
2794
     * If there has been a request to remove a production, the function will return
2795
     * without building the list unless forced to do so by the optional second
2796
     * parameter. This increases performance by avoiding to read through the
2797
     * productions on the filesystem before the removal request has been carried
2798
     * out because they'll have to be re-read afterwards anyway.
2799
     *
2800
     * @param int  $user_id    User id
2801
     * @param bool $force      Optional parameter to force building after a removal request
2802
     * @param bool $showDelete
2803
     *
2804
     * @return string A string containing the XHTML code to display the production list, or FALSE
2805
     */
2806
    public static function build_production_list($user_id, $force = false, $showDelete = false)
2807
    {
2808
        if (!$force && !empty($_POST['remove_production'])) {
2809
            return true; // postpone reading from the filesystem
2810
        }
2811
2812
        $productions = self::get_user_productions($user_id);
2813
2814
        if (empty($productions)) {
2815
            return false;
2816
        }
2817
2818
        $production_dir = self::getUserPathById($user_id, 'web');
2819
        $del_image = Display::returnIconPath('delete.png');
2820
        $add_image = Display::returnIconPath('archive.png');
2821
        $del_text = get_lang('Delete');
2822
        $production_list = '';
2823
        if (count($productions) > 0) {
2824
            $production_list = '<div class="files-production"><ul id="productions">';
2825
            foreach ($productions as $file) {
2826
                $production_list .= '<li>
2827
                    <img src="'.$add_image.'" />
2828
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2829
                        '.htmlentities($file).'
2830
                    </a>';
2831
                if ($showDelete) {
2832
                    $production_list .= '&nbsp;&nbsp;
2833
                        <input
2834
                            style="width:16px;"
2835
                            type="image"
2836
                            name="remove_production['.urlencode($file).']"
2837
                            src="'.$del_image.'"
2838
                            alt="'.$del_text.'"
2839
                            title="'.$del_text.' '.htmlentities($file).'"
2840
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2841
                }
2842
            }
2843
            $production_list .= '</ul></div>';
2844
        }
2845
2846
        return $production_list;
2847
    }
2848
2849
    /**
2850
     * Returns an array with the user's productions.
2851
     *
2852
     * @param int $user_id User id
2853
     *
2854
     * @return array An array containing the user's productions
2855
     */
2856
    public static function get_user_productions($user_id)
2857
    {
2858
        $production_repository = self::getUserPathById($user_id, 'system');
2859
        $productions = [];
2860
2861
        if (is_dir($production_repository)) {
2862
            $handle = opendir($production_repository);
2863
            while ($file = readdir($handle)) {
2864
                if ($file == '.' ||
2865
                    $file == '..' ||
2866
                    $file == '.htaccess' ||
2867
                    is_dir($production_repository.$file)
2868
                ) {
2869
                    // skip current/parent directory and .htaccess
2870
                    continue;
2871
                }
2872
2873
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2874
                    // User's photos should not be listed as productions.
2875
                    continue;
2876
                }
2877
                $productions[] = $file;
2878
            }
2879
        }
2880
2881
        return $productions;
2882
    }
2883
2884
    /**
2885
     * Remove a user production.
2886
     *
2887
     * @param int    $user_id    User id
2888
     * @param string $production The production to remove
2889
     *
2890
     * @return bool
2891
     */
2892
    public static function remove_user_production($user_id, $production)
2893
    {
2894
        $production_path = self::get_user_picture_path_by_id($user_id, 'system');
2895
        $production_file = $production_path['dir'].$production;
2896
        if (is_file($production_file)) {
2897
            unlink($production_file);
2898
2899
            return true;
2900
        }
2901
2902
        return false;
2903
    }
2904
2905
    /**
2906
     * Update an extra field value for a given user.
2907
     *
2908
     * @param int    $userId   User ID
2909
     * @param string $variable Field variable name
2910
     * @param string $value    Field value
2911
     *
2912
     * @return bool true if field updated, false otherwise
2913
     */
2914
    public static function update_extra_field_value($userId, $variable, $value = '')
2915
    {
2916
        $extraFieldValue = new ExtraFieldValue('user');
2917
        $params = [
2918
            'item_id' => $userId,
2919
            'variable' => $variable,
2920
            'value' => $value,
2921
        ];
2922
2923
        return $extraFieldValue->save($params);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraFieldValue->save($params) also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
2924
    }
2925
2926
    /**
2927
     * Get an array of extra fields with field details (type, default value and options).
2928
     *
2929
     * @param    int    Offset (from which row)
2930
     * @param    int    Number of items
2931
     * @param    int    Column on which sorting is made
2932
     * @param    string    Sorting direction
2933
     * @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...
2934
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2935
     *
2936
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2937
     */
2938
    public static function get_extra_fields(
2939
        $from = 0,
2940
        $number_of_items = 0,
2941
        $column = 5,
2942
        $direction = 'ASC',
2943
        $all_visibility = true,
2944
        $field_filter = null
2945
    ) {
2946
        $fields = [];
2947
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2948
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2949
        $columns = [
2950
            'id',
2951
            'variable',
2952
            'field_type',
2953
            'display_text',
2954
            'default_value',
2955
            'field_order',
2956
            'filter',
2957
        ];
2958
        $column = (int) $column;
2959
        $sort_direction = '';
2960
        if (!empty($direction)) {
2961
            if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2962
                $sort_direction = strtoupper($direction);
2963
            }
2964
        }
2965
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2966
        $sqlf = "SELECT * FROM $t_uf WHERE extra_field_type = $extraFieldType ";
2967
        if (!$all_visibility) {
2968
            $sqlf .= " AND visible_to_self = 1 ";
2969
        }
2970
        if (!is_null($field_filter)) {
2971
            $field_filter = (int) $field_filter;
2972
            $sqlf .= " AND filter = $field_filter ";
2973
        }
2974
        $sqlf .= " ORDER BY `".$columns[$column]."` $sort_direction ";
2975
        if ($number_of_items != 0) {
2976
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2977
        }
2978
        $resf = Database::query($sqlf);
2979
        if (Database::num_rows($resf) > 0) {
2980
            while ($rowf = Database::fetch_array($resf)) {
2981
                $fields[$rowf['id']] = [
2982
                    0 => $rowf['id'],
2983
                    1 => $rowf['variable'],
2984
                    2 => $rowf['field_type'],
2985
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2986
                    4 => $rowf['default_value'],
2987
                    5 => $rowf['field_order'],
2988
                    6 => $rowf['visible_to_self'],
2989
                    7 => $rowf['changeable'],
2990
                    8 => $rowf['filter'],
2991
                    9 => [],
2992
                    10 => '<a name="'.$rowf['id'].'"></a>',
2993
                ];
2994
2995
                $sqlo = "SELECT * FROM $t_ufo
2996
                         WHERE field_id = ".$rowf['id']."
2997
                         ORDER BY option_order ASC";
2998
                $reso = Database::query($sqlo);
2999
                if (Database::num_rows($reso) > 0) {
3000
                    while ($rowo = Database::fetch_array($reso)) {
3001
                        $fields[$rowf['id']][9][$rowo['id']] = [
3002
                            0 => $rowo['id'],
3003
                            1 => $rowo['option_value'],
3004
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
3005
                            3 => $rowo['option_order'],
3006
                        ];
3007
                    }
3008
                }
3009
            }
3010
        }
3011
3012
        return $fields;
3013
    }
3014
3015
    /**
3016
     * Build a list of extra file already uploaded in $user_folder/{$extra_field}/.
3017
     *
3018
     * @param $user_id
3019
     * @param $extra_field
3020
     * @param bool $force
3021
     * @param bool $showDelete
3022
     *
3023
     * @return bool|string
3024
     */
3025
    public static function build_user_extra_file_list(
3026
        $user_id,
3027
        $extra_field,
3028
        $force = false,
3029
        $showDelete = false
3030
    ) {
3031
        if (!$force && !empty($_POST['remove_'.$extra_field])) {
3032
            return true; // postpone reading from the filesystem
3033
        }
3034
3035
        $extra_files = self::get_user_extra_files($user_id, $extra_field);
3036
        if (empty($extra_files)) {
3037
            return false;
3038
        }
3039
3040
        $path_info = self::get_user_picture_path_by_id($user_id, 'web');
3041
        $path = $path_info['dir'];
3042
        $del_image = Display::returnIconPath('delete.png');
3043
3044
        $del_text = get_lang('Delete');
3045
        $extra_file_list = '';
3046
        if (count($extra_files) > 0) {
3047
            $extra_file_list = '<div class="files-production"><ul id="productions">';
3048
            foreach ($extra_files as $file) {
3049
                $filename = substr($file, strlen($extra_field) + 1);
3050
                $extra_file_list .= '<li>'.Display::return_icon('archive.png').
3051
                    '<a href="'.$path.$extra_field.'/'.urlencode($filename).'" target="_blank">
3052
                        '.htmlentities($filename).
3053
                    '</a> ';
3054
                if ($showDelete) {
3055
                    $extra_file_list .= '<input
3056
                        style="width:16px;"
3057
                        type="image"
3058
                        name="remove_extra_'.$extra_field.'['.urlencode($file).']"
3059
                        src="'.$del_image.'"
3060
                        alt="'.$del_text.'"
3061
                        title="'.$del_text.' '.htmlentities($filename).'"
3062
                        onclick="javascript: return confirmation(\''.htmlentities($filename).'\');" /></li>';
3063
                }
3064
            }
3065
            $extra_file_list .= '</ul></div>';
3066
        }
3067
3068
        return $extra_file_list;
3069
    }
3070
3071
    /**
3072
     * Get valid filenames in $user_folder/{$extra_field}/.
3073
     *
3074
     * @param $user_id
3075
     * @param $extra_field
3076
     * @param bool $full_path
3077
     *
3078
     * @return array
3079
     */
3080
    public static function get_user_extra_files($user_id, $extra_field, $full_path = false)
3081
    {
3082
        if (!$full_path) {
3083
            // Nothing to do
3084
        } else {
3085
            $path_info = self::get_user_picture_path_by_id($user_id, 'system');
3086
            $path = $path_info['dir'];
3087
        }
3088
        $extra_data = self::get_extra_user_data_by_field($user_id, $extra_field);
3089
        $extra_files = $extra_data[$extra_field];
3090
3091
        $files = [];
3092
        if (is_array($extra_files)) {
3093
            foreach ($extra_files as $key => $value) {
3094
                if (!$full_path) {
3095
                    // Relative path from user folder
3096
                    $files[] = $value;
3097
                } else {
3098
                    $files[] = $path.$value;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $path does not seem to be defined for all execution paths leading up to this point.
Loading history...
3099
                }
3100
            }
3101
        } elseif (!empty($extra_files)) {
3102
            if (!$full_path) {
3103
                // Relative path from user folder
3104
                $files[] = $extra_files;
3105
            } else {
3106
                $files[] = $path.$extra_files;
3107
            }
3108
        }
3109
3110
        return $files; // can be an empty array
3111
    }
3112
3113
    /**
3114
     * Remove an {$extra_file} from the user folder $user_folder/{$extra_field}/.
3115
     *
3116
     * @param int    $user_id
3117
     * @param string $extra_field
3118
     * @param string $extra_file
3119
     *
3120
     * @return bool
3121
     */
3122
    public static function remove_user_extra_file($user_id, $extra_field, $extra_file)
3123
    {
3124
        $extra_file = Security::filter_filename($extra_file);
3125
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
3126
        if (strpos($extra_file, $extra_field) !== false) {
3127
            $path_extra_file = $path_info['dir'].$extra_file;
3128
        } else {
3129
            $path_extra_file = $path_info['dir'].$extra_field.'/'.$extra_file;
3130
        }
3131
        if (is_file($path_extra_file)) {
3132
            unlink($path_extra_file);
3133
3134
            return true;
3135
        }
3136
3137
        return false;
3138
    }
3139
3140
    /**
3141
     * Creates a new extra field.
3142
     *
3143
     * @param string $variable    Field's internal variable name
3144
     * @param int    $fieldType   Field's type
3145
     * @param string $displayText Field's language var name
3146
     * @param string $default     Field's default value
3147
     *
3148
     * @return int
3149
     */
3150
    public static function create_extra_field(
3151
        $variable,
3152
        $fieldType,
3153
        $displayText,
3154
        $default
3155
    ) {
3156
        $extraField = new ExtraField('user');
3157
        $params = [
3158
            'variable' => $variable,
3159
            'field_type' => $fieldType,
3160
            'display_text' => $displayText,
3161
            'default_value' => $default,
3162
        ];
3163
3164
        return $extraField->save($params);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraField->save($params) also could return the type boolean which is incompatible with the documented return type integer.
Loading history...
3165
    }
3166
3167
    /**
3168
     * Check if a field is available.
3169
     *
3170
     * @param string $variable
3171
     *
3172
     * @return bool
3173
     */
3174
    public static function is_extra_field_available($variable)
3175
    {
3176
        $extraField = new ExtraField('user');
3177
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
3178
3179
        return !empty($data) ? true : false;
3180
    }
3181
3182
    /**
3183
     * Gets user extra fields data.
3184
     *
3185
     * @param    int    User ID
3186
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
3187
     * @param    bool    Whether to return invisible fields as well
3188
     * @param    bool    Whether to split multiple-selection fields or not
3189
     *
3190
     * @return array Array of fields => value for the given user
3191
     */
3192
    public static function get_extra_user_data(
3193
        $user_id,
3194
        $prefix = false,
3195
        $allVisibility = true,
3196
        $splitMultiple = false,
3197
        $fieldFilter = null
3198
    ) {
3199
        $user_id = (int) $user_id;
3200
3201
        if (empty($user_id)) {
3202
            return [];
3203
        }
3204
3205
        $extra_data = [];
3206
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
3207
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3208
        $user_id = (int) $user_id;
3209
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
3210
                FROM $t_uf f
3211
                WHERE
3212
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
3213
                ";
3214
        $filter_cond = '';
3215
3216
        if (!$allVisibility) {
3217
            if (isset($fieldFilter)) {
3218
                $fieldFilter = (int) $fieldFilter;
3219
                $filter_cond .= " AND filter = $fieldFilter ";
3220
            }
3221
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
3222
        } else {
3223
            if (isset($fieldFilter)) {
3224
                $fieldFilter = (int) $fieldFilter;
3225
                $sql .= " AND filter = $fieldFilter ";
3226
            }
3227
        }
3228
3229
        $sql .= ' ORDER BY f.field_order';
3230
3231
        $res = Database::query($sql);
3232
        if (Database::num_rows($res) > 0) {
3233
            while ($row = Database::fetch_array($res)) {
3234
                if ($row['type'] == self::USER_FIELD_TYPE_TAG) {
3235
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
3236
                    $extra_data['extra_'.$row['fvar']] = $tags;
3237
                } else {
3238
                    $sqlu = "SELECT value as fval
3239
                            FROM $t_ufv
3240
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
3241
                    $resu = Database::query($sqlu);
3242
                    // get default value
3243
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
3244
                               WHERE id=".$row['id'];
3245
                    $res_df = Database::query($sql_df);
3246
3247
                    if (Database::num_rows($resu) > 0) {
3248
                        $rowu = Database::fetch_array($resu);
3249
                        $fval = $rowu['fval'];
3250
                        if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
3251
                            $fval = explode(';', $rowu['fval']);
3252
                        }
3253
                    } else {
3254
                        $row_df = Database::fetch_array($res_df);
3255
                        $fval = $row_df['fval_df'];
3256
                    }
3257
                    // We get here (and fill the $extra_data array) even if there
3258
                    // is no user with data (we fill it with default values)
3259
                    if ($prefix) {
3260
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
3261
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
3262
                        } else {
3263
                            $extra_data['extra_'.$row['fvar']] = $fval;
3264
                        }
3265
                    } else {
3266
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
3267
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
3268
                        } else {
3269
                            $extra_data[$row['fvar']] = $fval;
3270
                        }
3271
                    }
3272
                }
3273
            }
3274
        }
3275
3276
        return $extra_data;
3277
    }
3278
3279
    /**
3280
     * Get extra user data by field.
3281
     *
3282
     * @param int    user ID
3283
     * @param string the internal variable name of the field
3284
     *
3285
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
3286
     */
3287
    public static function get_extra_user_data_by_field(
3288
        $user_id,
3289
        $field_variable,
3290
        $prefix = false,
3291
        $all_visibility = true,
3292
        $splitmultiple = false
3293
    ) {
3294
        $user_id = (int) $user_id;
3295
3296
        if (empty($user_id)) {
3297
            return [];
3298
        }
3299
3300
        $extra_data = [];
3301
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
3302
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3303
3304
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
3305
                FROM $t_uf f
3306
                WHERE f.variable = '$field_variable' ";
3307
3308
        if (!$all_visibility) {
3309
            $sql .= " AND f.visible_to_self = 1 ";
3310
        }
3311
3312
        $sql .= " AND extra_field_type = ".EntityExtraField::USER_FIELD_TYPE;
3313
        $sql .= " ORDER BY f.field_order ";
3314
3315
        $res = Database::query($sql);
3316
        if (Database::num_rows($res) > 0) {
3317
            while ($row = Database::fetch_array($res)) {
3318
                $sqlu = "SELECT value as fval FROM $t_ufv v
3319
                         INNER JOIN $t_uf f
3320
                         ON (v.field_id = f.id)
3321
                         WHERE
3322
                            extra_field_type = ".EntityExtraField::USER_FIELD_TYPE." AND
3323
                            field_id = ".$row['id']." AND
3324
                            item_id = ".$user_id;
3325
                $resu = Database::query($sqlu);
3326
                $fval = '';
3327
                if (Database::num_rows($resu) > 0) {
3328
                    $rowu = Database::fetch_array($resu);
3329
                    $fval = $rowu['fval'];
3330
                    if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
3331
                        $fval = explode(';', $rowu['fval']);
3332
                    }
3333
                }
3334
                if ($prefix) {
3335
                    $extra_data['extra_'.$row['fvar']] = $fval;
3336
                } else {
3337
                    $extra_data[$row['fvar']] = $fval;
3338
                }
3339
            }
3340
        }
3341
3342
        return $extra_data;
3343
    }
3344
3345
    /**
3346
     * Get the extra field information for a certain field (the options as well).
3347
     *
3348
     * @param string $variable The name of the field we want to know everything about
3349
     *
3350
     * @return array Array containing all the information about the extra profile field
3351
     *               (first level of array contains field details, then 'options' sub-array contains options details,
3352
     *               as returned by the database)
3353
     *
3354
     * @author Julio Montoya
3355
     *
3356
     * @since v1.8.6
3357
     */
3358
    public static function get_extra_field_information_by_name($variable)
3359
    {
3360
        $extraField = new ExtraField('user');
3361
3362
        return $extraField->get_handler_field_info_by_field_variable($variable);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraField->get_...eld_variable($variable) also could return the type boolean which is incompatible with the documented return type array.
Loading history...
3363
    }
3364
3365
    /**
3366
     * Get the extra field information for user tag (the options as well).
3367
     *
3368
     * @param int $variable The name of the field we want to know everything about
3369
     *
3370
     * @return array Array containing all the information about the extra profile field
3371
     *               (first level of array contains field details, then 'options' sub-array contains options details,
3372
     *               as returned by the database)
3373
     *
3374
     * @author José Loguercio
3375
     *
3376
     * @since v1.11.0
3377
     */
3378
    public static function get_extra_field_tags_information_by_name($variable)
3379
    {
3380
        $extraField = new ExtraField('user');
3381
3382
        return $extraField->get_handler_field_info_by_tags($variable);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraField->get_...info_by_tags($variable) also could return the type boolean which is incompatible with the documented return type array.
Loading history...
3383
    }
3384
3385
    /**
3386
     * @param string $type
3387
     *
3388
     * @return array
3389
     */
3390
    public static function get_all_extra_field_by_type($type)
3391
    {
3392
        $extraField = new ExtraField('user');
3393
3394
        return $extraField->get_all_extra_field_by_type($type);
3395
    }
3396
3397
    /**
3398
     * Get all the extra field information of a certain field (also the options).
3399
     *
3400
     * @param int $fieldId the ID of the field we want to know everything of
3401
     *
3402
     * @return array $return containing all th information about the extra profile field
3403
     *
3404
     * @author Julio Montoya
3405
     *
3406
     * @deprecated
3407
     * @since v1.8.6
3408
     */
3409
    public static function get_extra_field_information($fieldId)
3410
    {
3411
        $extraField = new ExtraField('user');
3412
3413
        return $extraField->getFieldInfoByFieldId($fieldId);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraField->getF...InfoByFieldId($fieldId) also could return the type boolean which is incompatible with the documented return type array.
Loading history...
3414
    }
3415
3416
    /**
3417
     * Get extra user data by value.
3418
     *
3419
     * @param string $variable the internal variable name of the field
3420
     * @param string $value    the internal value of the field
3421
     * @param bool   $useLike
3422
     *
3423
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
3424
     */
3425
    public static function get_extra_user_data_by_value($variable, $value, $useLike = false)
3426
    {
3427
        $extraFieldValue = new ExtraFieldValue('user');
3428
        $extraField = new ExtraField('user');
3429
3430
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
3431
3432
        if (false === $info) {
3433
            return [];
3434
        }
3435
3436
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
3437
            $variable,
3438
            $value,
3439
            false,
3440
            false,
3441
            true,
3442
            $useLike
3443
        );
3444
3445
        $result = [];
3446
        if (!empty($data)) {
3447
            foreach ($data as $item) {
3448
                $result[] = $item['item_id'];
3449
            }
3450
        }
3451
3452
        return $result;
3453
    }
3454
3455
    /**
3456
     * Get extra user data by tags value.
3457
     *
3458
     * @param int    $fieldId the ID of the field we want to know everything of
3459
     * @param string $tag     the tag name for search
3460
     *
3461
     * @return array with extra data info of a user
3462
     *
3463
     * @author José Loguercio
3464
     *
3465
     * @since v1.11.0
3466
     */
3467
    public static function get_extra_user_data_by_tags($fieldId, $tag)
3468
    {
3469
        $extraField = new ExtraField('user');
3470
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
3471
        $array = [];
3472
        foreach ($result as $index => $user) {
3473
            $array[] = $user['user_id'];
3474
        }
3475
3476
        return $array;
3477
    }
3478
3479
    /**
3480
     * Get extra user data by field variable.
3481
     *
3482
     * @param string $variable field variable
3483
     *
3484
     * @return array data
3485
     */
3486
    public static function get_extra_user_data_by_field_variable($variable)
3487
    {
3488
        $extraInfo = self::get_extra_field_information_by_name($variable);
3489
        $field_id = (int) $extraInfo['id'];
3490
3491
        $extraField = new ExtraFieldValue('user');
3492
        $data = $extraField->getValuesByFieldId($field_id);
3493
3494
        if (!empty($data)) {
3495
            foreach ($data as $row) {
3496
                $user_id = $row['item_id'];
3497
                $data[$user_id] = $row;
3498
            }
3499
        }
3500
3501
        return $data;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $data could also return false which is incompatible with the documented return type array. Did you maybe forget to handle an error condition?

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

Loading history...
3502
    }
3503
3504
    /**
3505
     * Get extra user data tags by field variable.
3506
     *
3507
     * @param string $variable field variable
3508
     *
3509
     * @return array
3510
     */
3511
    public static function get_extra_user_data_for_tags($variable)
3512
    {
3513
        $data = self::get_extra_field_tags_information_by_name($variable);
3514
3515
        return $data;
3516
    }
3517
3518
    /**
3519
     * Gives a list of [session_category][session_id] for the current user.
3520
     *
3521
     * @param int  $user_id
3522
     * @param bool $is_time_over                 whether to fill the first element or not
3523
     *                                           (to give space for courses out of categories)
3524
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
3525
     * @param bool $ignoreTimeLimit              ignore time start/end
3526
     * @param bool $getCount
3527
     *
3528
     * @return array list of statuses [session_category][session_id]
3529
     *
3530
     * @todo ensure multiple access urls are managed correctly
3531
     */
3532
    public static function get_sessions_by_category(
3533
        $user_id,
3534
        $is_time_over = true,
3535
        $ignore_visibility_for_admins = false,
3536
        $ignoreTimeLimit = false,
3537
        $getCount = false
3538
    ) {
3539
        $user_id = (int) $user_id;
3540
3541
        if (empty($user_id)) {
3542
            return [];
3543
        }
3544
3545
        $allowOrder = api_get_configuration_value('session_list_order');
3546
        $position = '';
3547
        if ($allowOrder) {
3548
            $position = ', s.position AS position ';
3549
        }
3550
3551
        // Get the list of sessions per user
3552
        $now = new DateTime('now', new DateTimeZone('UTC'));
3553
3554
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
3555
        // join would not catch session-courses where the user is general
3556
        // session coach but which do not have students nor coaches registered
3557
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
3558
3559
        if (!$getCount) {
3560
            $dqlSelect = " DISTINCT
3561
                s.id,
3562
                s.name,
3563
                s.accessStartDate AS access_start_date,
3564
                s.accessEndDate AS access_end_date,
3565
                s.duration,
3566
                sc.id AS session_category_id,
3567
                sc.name AS session_category_name,
3568
                sc.dateStart AS session_category_date_start,
3569
                sc.dateEnd AS session_category_date_end,
3570
                s.coachAccessStartDate AS coach_access_start_date,
3571
                s.coachAccessEndDate AS coach_access_end_date,
3572
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
3573
                $position
3574
            ";
3575
        }
3576
3577
        $dql = "SELECT $dqlSelect
3578
                FROM ChamiloCoreBundle:Session AS s
3579
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
3580
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.sessionId = s.id
3581
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc ";
3582
3583
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
3584
        // is awfully inefficient for large sets of data (1m25s for 58K
3585
        // sessions, BT#14115) but executing a similar query twice and grouping
3586
        // the results afterwards in PHP takes about 1/1000th of the time
3587
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
3588
        $dqlStudent = $dql.' WHERE scu.user = :user AND url.accessUrlId = :url ';
3589
        $dqlCoach = $dql.' WHERE s.generalCoach = :user AND url.accessUrlId = :url ';
3590
3591
        // Default order
3592
        $order = 'ORDER BY sc.name, s.name';
3593
3594
        // Order by date if showing all sessions
3595
        $showAllSessions = api_get_configuration_value('show_all_sessions_on_my_course_page') === true;
3596
        if ($showAllSessions) {
3597
            $order = 'ORDER BY s.accessStartDate';
3598
        }
3599
3600
        // Order by position
3601
        if ($allowOrder) {
3602
            $order = 'ORDER BY s.position';
3603
        }
3604
3605
        // Order by dates according to settings
3606
        $orderBySettings = api_get_configuration_value('my_courses_session_order');
3607
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
3608
            $field = $orderBySettings['field'];
3609
            $orderSetting = $orderBySettings['order'];
3610
            switch ($field) {
3611
                case 'start_date':
3612
                    $order = " ORDER BY s.accessStartDate $orderSetting";
3613
                    break;
3614
                case 'end_date':
3615
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
3616
                    if ($orderSetting === 'asc') {
3617
                        // Put null values at the end
3618
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
3619
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
3620
                    }
3621
                    break;
3622
                case 'name':
3623
                    $order = " ORDER BY s.name $orderSetting ";
3624
                    break;
3625
            }
3626
        }
3627
3628
        $dqlStudent .= $order;
3629
        $dqlCoach .= $order;
3630
3631
        $accessUrlId = api_get_current_access_url_id();
3632
        $dqlStudent = Database::getManager()
3633
            ->createQuery($dqlStudent)
3634
            ->setParameters(
3635
                ['user' => $user_id, 'url' => $accessUrlId]
3636
            )
3637
        ;
3638
        $dqlCoach = Database::getManager()
3639
            ->createQuery($dqlCoach)
3640
            ->setParameters(
3641
                ['user' => $user_id, 'url' => $accessUrlId]
3642
            )
3643
        ;
3644
3645
        if ($getCount) {
3646
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
3647
        }
3648
3649
        $sessionDataStudent = $dqlStudent->getResult();
3650
        $sessionDataCoach = $dqlCoach->getResult();
3651
3652
        $sessionData = [];
3653
        // First fill $sessionData with student sessions
3654
        if (!empty($sessionDataStudent)) {
3655
            foreach ($sessionDataStudent as $row) {
3656
                $sessionData[$row['id']] = $row;
3657
            }
3658
        }
3659
3660
        // Overwrite session data of the user as a student with session data
3661
        // of the user as a coach.
3662
        // There shouldn't be such duplicate rows, but just in case...
3663
        if (!empty($sessionDataCoach)) {
3664
            foreach ($sessionDataCoach as $row) {
3665
                $sessionData[$row['id']] = $row;
3666
            }
3667
        }
3668
3669
        $collapsable = api_get_configuration_value('allow_user_session_collapsable');
3670
        $extraField = new ExtraFieldValue('session');
3671
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
3672
3673
        if (empty($sessionData)) {
3674
            return [];
3675
        }
3676
3677
        $categories = [];
3678
        foreach ($sessionData as $row) {
3679
            $session_id = $row['id'];
3680
            $coachList = SessionManager::getCoachesBySession($session_id);
3681
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
3682
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
3683
            $courseList = self::get_courses_list_by_session($user_id, $session_id);
3684
3685
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
3686
3687
            // User portal filters:
3688
            if (false === $ignoreTimeLimit) {
3689
                if ($is_time_over) {
3690
                    // History
3691
                    if ($row['duration']) {
3692
                        if ($daysLeft >= 0) {
3693
                            continue;
3694
                        }
3695
                    } else {
3696
                        if (empty($row['access_end_date'])) {
3697
                            continue;
3698
                        } else {
3699
                            if ($row['access_end_date'] > $now) {
3700
                                continue;
3701
                            }
3702
                        }
3703
                    }
3704
                } else {
3705
                    // Current user portal
3706
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
3707
                    $isCoachOfCourse = in_array($user_id, $coachList);
3708
3709
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
3710
                        // Teachers can access the session depending in the access_coach date
3711
                    } else {
3712
                        if ($row['duration']) {
3713
                            if ($daysLeft <= 0) {
3714
                                continue;
3715
                            }
3716
                        } else {
3717
                            if (isset($row['access_end_date']) &&
3718
                                !empty($row['access_end_date'])
3719
                            ) {
3720
                                if ($row['access_end_date'] <= $now) {
3721
                                    continue;
3722
                                }
3723
                            }
3724
                        }
3725
                    }
3726
                }
3727
            }
3728
3729
            $categories[$row['session_category_id']]['session_category'] = [
3730
                'id' => $row['session_category_id'],
3731
                'name' => $row['session_category_name'],
3732
                'date_start' => $categoryStart,
3733
                'date_end' => $categoryEnd,
3734
            ];
3735
3736
            $visibility = api_get_session_visibility(
3737
                $session_id,
3738
                null,
3739
                $ignore_visibility_for_admins
3740
            );
3741
3742
            if ($visibility != SESSION_VISIBLE) {
3743
                // Course Coach session visibility.
3744
                $blockedCourseCount = 0;
3745
                $closedVisibilityList = [
3746
                    COURSE_VISIBILITY_CLOSED,
3747
                    COURSE_VISIBILITY_HIDDEN,
3748
                ];
3749
3750
                foreach ($courseList as $course) {
3751
                    // Checking session visibility
3752
                    $sessionCourseVisibility = api_get_session_visibility(
3753
                        $session_id,
3754
                        $course['real_id'],
3755
                        $ignore_visibility_for_admins
3756
                    );
3757
3758
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
3759
                    if ($courseIsVisible === false || $sessionCourseVisibility == SESSION_INVISIBLE) {
3760
                        $blockedCourseCount++;
3761
                    }
3762
                }
3763
3764
                // If all courses are blocked then no show in the list.
3765
                if ($blockedCourseCount === count($courseList)) {
3766
                    $visibility = SESSION_INVISIBLE;
3767
                } else {
3768
                    $visibility = $sessionCourseVisibility;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sessionCourseVisibility does not seem to be defined for all execution paths leading up to this point.
Loading history...
3769
                }
3770
            }
3771
3772
            switch ($visibility) {
3773
                case SESSION_VISIBLE_READ_ONLY:
3774
                case SESSION_VISIBLE:
3775
                case SESSION_AVAILABLE:
3776
                    break;
3777
                case SESSION_INVISIBLE:
3778
                    if ($ignore_visibility_for_admins === false) {
3779
                        continue 2;
3780
                    }
3781
            }
3782
3783
            $collapsed = '';
3784
            $collapsedAction = '';
3785
            if ($collapsable) {
3786
                $collapsableData = SessionManager::getCollapsableData(
3787
                    $user_id,
3788
                    $session_id,
3789
                    $extraField,
3790
                    $collapsableLink
3791
                );
3792
                $collapsed = $collapsableData['collapsed'];
3793
                $collapsedAction = $collapsableData['collapsable_link'];
3794
            }
3795
3796
            $categories[$row['session_category_id']]['sessions'][] = [
3797
                'session_name' => $row['name'],
3798
                'session_id' => $row['id'],
3799
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
3800
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
3801
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
3802
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
3803
                'courses' => $courseList,
3804
                'collapsed' => $collapsed,
3805
                'collapsable_link' => $collapsedAction,
3806
                'duration' => $row['duration'],
3807
            ];
3808
        }
3809
3810
        return $categories;
3811
    }
3812
3813
    /**
3814
     * Gives a list of [session_id-course_code] => [status] for the current user.
3815
     *
3816
     * @param int $user_id
3817
     * @param int $sessionLimit
3818
     *
3819
     * @return array list of statuses (session_id-course_code => status)
3820
     */
3821
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
3822
    {
3823
        // Database Table Definitions
3824
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3825
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
3826
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3827
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3828
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3829
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3830
3831
        $user_id = (int) $user_id;
3832
3833
        if (empty($user_id)) {
3834
            return [];
3835
        }
3836
3837
        // We filter the courses from the URL
3838
        $join_access_url = $where_access_url = '';
3839
        if (api_get_multiple_access_url()) {
3840
            $access_url_id = api_get_current_access_url_id();
3841
            if ($access_url_id != -1) {
3842
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3843
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
3844
                $where_access_url = " AND access_url_id = $access_url_id ";
3845
            }
3846
        }
3847
3848
        // Courses in which we subscribed out of any session
3849
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3850
3851
        $sql = "SELECT
3852
                    course.code,
3853
                    course_rel_user.status course_rel_status,
3854
                    course_rel_user.sort sort,
3855
                    course_rel_user.user_course_cat user_course_cat
3856
                 FROM $tbl_course_user course_rel_user
3857
                 LEFT JOIN $tbl_course course
3858
                 ON course.id = course_rel_user.c_id
3859
                 LEFT JOIN $tbl_user_course_category user_course_category
3860
                 ON course_rel_user.user_course_cat = user_course_category.id
3861
                 $join_access_url
3862
                 WHERE
3863
                    course_rel_user.user_id = '".$user_id."' AND
3864
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
3865
                    $where_access_url
3866
                 ORDER BY user_course_category.sort, course_rel_user.sort, course.title ASC";
3867
3868
        $course_list_sql_result = Database::query($sql);
3869
3870
        $personal_course_list = [];
3871
        if (Database::num_rows($course_list_sql_result) > 0) {
3872
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3873
                $course_info = api_get_course_info($result_row['code']);
3874
                $result_row['course_info'] = $course_info;
3875
                $personal_course_list[] = $result_row;
3876
            }
3877
        }
3878
3879
        $coachCourseConditions = '';
3880
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
3881
        if (api_is_allowed_to_create_course()) {
3882
            $sessionListFromCourseCoach = [];
3883
            $sql = " SELECT DISTINCT session_id
3884
                    FROM $tbl_session_course_user
3885
                    WHERE user_id = $user_id AND status = 2 ";
3886
3887
            $result = Database::query($sql);
3888
            if (Database::num_rows($result)) {
3889
                $result = Database::store_result($result);
3890
                foreach ($result as $session) {
3891
                    $sessionListFromCourseCoach[] = $session['session_id'];
3892
                }
3893
            }
3894
            if (!empty($sessionListFromCourseCoach)) {
3895
                $condition = implode("','", $sessionListFromCourseCoach);
3896
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
3897
            }
3898
        }
3899
3900
        // Get the list of sessions where the user is subscribed
3901
        // This is divided into two different queries
3902
        $sessions = [];
3903
        $sessionLimitRestriction = '';
3904
        if (!empty($sessionLimit)) {
3905
            $sessionLimit = (int) $sessionLimit;
3906
            $sessionLimitRestriction = "LIMIT $sessionLimit";
3907
        }
3908
3909
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
3910
                FROM $tbl_session_user su INNER JOIN $tbl_session s
3911
                ON (s.id = su.session_id)
3912
                WHERE (
3913
                    su.user_id = $user_id AND
3914
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
3915
                )
3916
                $coachCourseConditions
3917
                ORDER BY access_start_date, access_end_date, name
3918
                $sessionLimitRestriction
3919
        ";
3920
3921
        $result = Database::query($sql);
3922
        if (Database::num_rows($result) > 0) {
3923
            while ($row = Database::fetch_assoc($result)) {
3924
                $sessions[$row['id']] = $row;
3925
            }
3926
        }
3927
3928
        $sql = "SELECT DISTINCT
3929
                id, name, access_start_date, access_end_date
3930
                FROM $tbl_session s
3931
                WHERE (
3932
                    id_coach = $user_id
3933
                )
3934
                $coachCourseConditions
3935
                ORDER BY access_start_date, access_end_date, name";
3936
3937
        $result = Database::query($sql);
3938
        if (Database::num_rows($result) > 0) {
3939
            while ($row = Database::fetch_assoc($result)) {
3940
                if (empty($sessions[$row['id']])) {
3941
                    $sessions[$row['id']] = $row;
3942
                }
3943
            }
3944
        }
3945
3946
        if (api_is_allowed_to_create_course()) {
3947
            foreach ($sessions as $enreg) {
3948
                $session_id = $enreg['id'];
3949
                $session_visibility = api_get_session_visibility($session_id);
3950
3951
                if ($session_visibility == SESSION_INVISIBLE) {
3952
                    continue;
3953
                }
3954
3955
                // This query is horribly slow when more than a few thousand
3956
                // users and just a few sessions to which they are subscribed
3957
                $sql = "SELECT DISTINCT
3958
                        course.code code,
3959
                        course.title i,
3960
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
3961
                        email, course.course_language l,
3962
                        1 sort,
3963
                        category_code user_course_cat,
3964
                        access_start_date,
3965
                        access_end_date,
3966
                        session.id as session_id,
3967
                        session.name as session_name
3968
                    FROM $tbl_session_course_user as session_course_user
3969
                    INNER JOIN $tbl_course AS course
3970
                        ON course.id = session_course_user.c_id
3971
                    INNER JOIN $tbl_session as session
3972
                        ON session.id = session_course_user.session_id
3973
                    LEFT JOIN $tbl_user as user
3974
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
3975
                    WHERE
3976
                        session_course_user.session_id = $session_id AND (
3977
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
3978
                            OR session.id_coach = $user_id
3979
                        )
3980
                    ORDER BY i";
3981
                $course_list_sql_result = Database::query($sql);
3982
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3983
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3984
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3985
                    $personal_course_list[$key] = $result_row;
3986
                }
3987
            }
3988
        }
3989
3990
        foreach ($sessions as $enreg) {
3991
            $session_id = $enreg['id'];
3992
            $session_visibility = api_get_session_visibility($session_id);
3993
            if ($session_visibility == SESSION_INVISIBLE) {
3994
                continue;
3995
            }
3996
3997
            /* This query is very similar to the above query,
3998
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3999
            $sql = "SELECT DISTINCT
4000
                course.code code,
4001
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
4002
                email,
4003
                course.course_language l,
4004
                1 sort,
4005
                category_code user_course_cat,
4006
                access_start_date,
4007
                access_end_date,
4008
                session.id as session_id,
4009
                session.name as session_name,
4010
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
4011
            FROM $tbl_session_course_user as session_course_user
4012
            INNER JOIN $tbl_course AS course
4013
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
4014
            INNER JOIN $tbl_session as session
4015
            ON session_course_user.session_id = session.id
4016
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
4017
            WHERE session_course_user.user_id = $user_id
4018
            ORDER BY i";
4019
4020
            $course_list_sql_result = Database::query($sql);
4021
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
4022
                $result_row['course_info'] = api_get_course_info($result_row['code']);
4023
                $key = $result_row['session_id'].' - '.$result_row['code'];
4024
                if (!isset($personal_course_list[$key])) {
4025
                    $personal_course_list[$key] = $result_row;
4026
                }
4027
            }
4028
        }
4029
4030
        return $personal_course_list;
4031
    }
4032
4033
    /**
4034
     * Gives a list of courses for the given user in the given session.
4035
     *
4036
     * @param int $user_id
4037
     * @param int $session_id
4038
     *
4039
     * @return array list of statuses (session_id-course_code => status)
4040
     */
4041
    public static function get_courses_list_by_session($user_id, $session_id)
4042
    {
4043
        // Database Table Definitions
4044
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4045
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
4046
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4047
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4048
4049
        $user_id = (int) $user_id;
4050
        $session_id = (int) $session_id;
4051
        // We filter the courses from the URL
4052
        $join_access_url = $where_access_url = '';
4053
        if (api_get_multiple_access_url()) {
4054
            $urlId = api_get_current_access_url_id();
4055
            if (-1 != $urlId) {
4056
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4057
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
4058
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
4059
            }
4060
        }
4061
4062
        $exlearnerCondition = "";
4063
        if (false !== api_get_configuration_value('user_edition_extra_field_to_check')) {
4064
            $exlearnerCondition = " AND scu.status NOT IN(".COURSE_EXLEARNER.")";
4065
        }
4066
4067
        /* This query is very similar to the query below, but it will check the
4068
        session_rel_course_user table if there are courses registered
4069
        to our user or not */
4070
        $sql = "SELECT DISTINCT
4071
                    c.title,
4072
                    c.visibility,
4073
                    c.id as real_id,
4074
                    c.code as course_code,
4075
                    sc.position,
4076
                    c.unsubscribe
4077
                FROM $tbl_session_course_user as scu
4078
                INNER JOIN $tbl_session_course sc
4079
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
4080
                INNER JOIN $tableCourse as c
4081
                ON (scu.c_id = c.id)
4082
                $join_access_url
4083
                WHERE
4084
                    scu.user_id = $user_id AND
4085
                    scu.session_id = $session_id
4086
                    $where_access_url
4087
                    $exlearnerCondition
4088
                ORDER BY sc.position ASC";
4089
4090
        $myCourseList = [];
4091
        $courses = [];
4092
        $result = Database::query($sql);
4093
        if (Database::num_rows($result) > 0) {
4094
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
4095
                $result_row['status'] = 5;
4096
                if (!in_array($result_row['real_id'], $courses)) {
4097
                    $position = $result_row['position'];
4098
                    if (!isset($myCourseList[$position])) {
4099
                        $myCourseList[$position] = $result_row;
4100
                    } else {
4101
                        $myCourseList[] = $result_row;
4102
                    }
4103
                    $courses[] = $result_row['real_id'];
4104
                }
4105
            }
4106
        }
4107
4108
        if (api_is_allowed_to_create_course()) {
4109
            $sql = "SELECT DISTINCT
4110
                        c.title,
4111
                        c.visibility,
4112
                        c.id as real_id,
4113
                        c.code as course_code,
4114
                        sc.position,
4115
                        c.unsubscribe
4116
                    FROM $tbl_session_course_user as scu
4117
                    INNER JOIN $tbl_session as s
4118
                    ON (scu.session_id = s.id)
4119
                    INNER JOIN $tbl_session_course sc
4120
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
4121
                    INNER JOIN $tableCourse as c
4122
                    ON (scu.c_id = c.id)
4123
                    $join_access_url
4124
                    WHERE
4125
                      s.id = $session_id AND
4126
                      (
4127
                        (scu.user_id = $user_id AND scu.status = 2) OR
4128
                        s.id_coach = $user_id
4129
                      )
4130
                    $where_access_url
4131
                    $exlearnerCondition
4132
                    ORDER BY sc.position ASC";
4133
            $result = Database::query($sql);
4134
4135
            if (Database::num_rows($result) > 0) {
4136
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
4137
                    $result_row['status'] = 2;
4138
                    if (!in_array($result_row['real_id'], $courses)) {
4139
                        $position = $result_row['position'];
4140
                        if (!isset($myCourseList[$position])) {
4141
                            $myCourseList[$position] = $result_row;
4142
                        } else {
4143
                            $myCourseList[] = $result_row;
4144
                        }
4145
                        $courses[] = $result_row['real_id'];
4146
                    }
4147
                }
4148
            }
4149
        }
4150
4151
        if (api_is_drh()) {
4152
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
4153
            $sessionList = array_keys($sessionList);
4154
            if (in_array($session_id, $sessionList)) {
4155
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
4156
                if (!empty($courseList)) {
4157
                    foreach ($courseList as $course) {
4158
                        if (!in_array($course['id'], $courses)) {
4159
                            $position = $course['position'];
4160
                            if (!isset($myCourseList[$position])) {
4161
                                $myCourseList[$position] = $course;
4162
                            } else {
4163
                                $myCourseList[] = $course;
4164
                            }
4165
                        }
4166
                    }
4167
                }
4168
            }
4169
        } else {
4170
            //check if user is general coach for this session
4171
            $sessionInfo = api_get_session_info($session_id);
4172
            if ($sessionInfo['id_coach'] == $user_id) {
4173
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
4174
                if (!empty($courseList)) {
4175
                    foreach ($courseList as $course) {
4176
                        if (!in_array($course['id'], $courses)) {
4177
                            $position = $course['position'];
4178
                            if (!isset($myCourseList[$position])) {
4179
                                $myCourseList[$position] = $course;
4180
                            } else {
4181
                                $myCourseList[] = $course;
4182
                            }
4183
                        }
4184
                    }
4185
                }
4186
            }
4187
        }
4188
4189
        if (!empty($myCourseList)) {
4190
            ksort($myCourseList);
4191
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
4192
            if (empty($checkPosition)) {
4193
                // The session course list doesn't have any position,
4194
                // then order the course list by course code.
4195
                $orderByCode = array_column($myCourseList, 'course_code');
4196
                sort($orderByCode, SORT_NATURAL);
4197
                $newCourseList = [];
4198
                foreach ($orderByCode as $code) {
4199
                    foreach ($myCourseList as $course) {
4200
                        if ($code === $course['course_code']) {
4201
                            $newCourseList[] = $course;
4202
                            break;
4203
                        }
4204
                    }
4205
                }
4206
                $myCourseList = $newCourseList;
4207
            }
4208
        }
4209
4210
        return $myCourseList;
4211
    }
4212
4213
    /**
4214
     * Get user id from a username.
4215
     *
4216
     * @param string $username
4217
     *
4218
     * @return int User ID (or false if not found)
4219
     */
4220
    public static function get_user_id_from_username($username)
4221
    {
4222
        if (empty($username)) {
4223
            return false;
4224
        }
4225
        $username = trim($username);
4226
        $username = Database::escape_string($username);
4227
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
4228
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
4229
        $res = Database::query($sql);
4230
4231
        if ($res === false) {
4232
            return false;
4233
        }
4234
        if (Database::num_rows($res) !== 1) {
4235
            return false;
4236
        }
4237
        $row = Database::fetch_array($res);
4238
4239
        return $row['id'];
4240
    }
4241
4242
    /**
4243
     * Get the users files upload from his share_folder.
4244
     *
4245
     * @param string $user_id      User ID
4246
     * @param string $course       course directory
4247
     * @param string $resourceType resource type: images, all
4248
     *
4249
     * @return string
4250
     */
4251
    public static function get_user_upload_files_by_course(
4252
        $user_id,
4253
        $course,
4254
        $resourceType = 'all'
4255
    ) {
4256
        $return = '';
4257
        $user_id = (int) $user_id;
4258
4259
        if (!empty($user_id) && !empty($course)) {
4260
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
4261
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
4262
            $file_list = [];
4263
4264
            if (is_dir($path)) {
4265
                $handle = opendir($path);
4266
                while ($file = readdir($handle)) {
4267
                    if ($file == '.' || $file == '..' || $file == '.htaccess' || is_dir($path.$file)) {
4268
                        continue; // skip current/parent directory and .htaccess
4269
                    }
4270
                    $file_list[] = $file;
4271
                }
4272
                if (count($file_list) > 0) {
4273
                    $return = "<h4>$course</h4>";
4274
                    $return .= '<ul class="thumbnails">';
4275
                }
4276
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
4277
                foreach ($file_list as $file) {
4278
                    if ($resourceType == 'all') {
4279
                        $return .= '<li>
4280
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
4281
                    } elseif ($resourceType == 'images') {
4282
                        //get extension
4283
                        $ext = explode('.', $file);
4284
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
4285
                            $return .= '<li class="span2">
4286
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
4287
                                                <img src="'.$web_path.urlencode($file).'" >
4288
                                            </a>
4289
                                        </li>';
4290
                        }
4291
                    }
4292
                }
4293
                if (count($file_list) > 0) {
4294
                    $return .= '</ul>';
4295
                }
4296
            }
4297
        }
4298
4299
        return $return;
4300
    }
4301
4302
    /**
4303
     * Gets the API key (or keys) and return them into an array.
4304
     *
4305
     * @param int     Optional user id (defaults to the result of api_get_user_id())
4306
     * @param string $api_service
4307
     *
4308
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
4309
     */
4310
    public static function get_api_keys($user_id = null, $api_service = 'dokeos')
4311
    {
4312
        if ($user_id != strval(intval($user_id))) {
4313
            return false;
4314
        }
4315
        if (empty($user_id)) {
4316
            $user_id = api_get_user_id();
4317
        }
4318
        if ($user_id === false) {
4319
            return false;
4320
        }
4321
        $service_name = Database::escape_string($api_service);
4322
        if (is_string($service_name) === false) {
4323
            return false;
4324
        }
4325
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4326
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
4327
        $res = Database::query($sql);
4328
        if ($res === false) {
4329
            return false;
4330
        } //error during query
4331
        $num = Database::num_rows($res);
4332
        if ($num == 0) {
4333
            return false;
4334
        }
4335
        $list = [];
4336
        while ($row = Database::fetch_array($res)) {
4337
            $list[$row['id']] = $row['api_key'];
4338
        }
4339
4340
        return $list;
4341
    }
4342
4343
    /**
4344
     * Adds a new API key to the users' account.
4345
     *
4346
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
4347
     * @param string $api_service
4348
     *
4349
     * @return bool True on success, false on failure
4350
     */
4351
    public static function add_api_key($user_id = null, $api_service = 'dokeos')
4352
    {
4353
        if ($user_id != strval(intval($user_id))) {
4354
            return false;
4355
        }
4356
        if (empty($user_id)) {
4357
            $user_id = api_get_user_id();
4358
        }
4359
        if ($user_id === false) {
4360
            return false;
4361
        }
4362
        $service_name = Database::escape_string($api_service);
4363
        if (is_string($service_name) === false) {
4364
            return false;
4365
        }
4366
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4367
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
4368
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
4369
        $res = Database::query($sql);
4370
        if ($res === false) {
4371
            return false;
4372
        } //error during query
4373
        $num = Database::insert_id();
4374
4375
        return $num == 0 ? false : $num;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $num == 0 ? false : $num also could return the type string which is incompatible with the documented return type boolean.
Loading history...
4376
    }
4377
4378
    /**
4379
     * Deletes an API key from the user's account.
4380
     *
4381
     * @param   int     API key's internal ID
4382
     *
4383
     * @return bool True on success, false on failure
4384
     */
4385
    public static function delete_api_key($key_id)
4386
    {
4387
        if ($key_id != strval(intval($key_id))) {
4388
            return false;
4389
        }
4390
        if ($key_id === false) {
4391
            return false;
4392
        }
4393
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4394
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
4395
        $res = Database::query($sql);
4396
        if ($res === false) {
4397
            return false;
4398
        } //error during query
4399
        $num = Database::num_rows($res);
4400
        if ($num !== 1) {
4401
            return false;
4402
        }
4403
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
4404
        $res = Database::query($sql);
4405
        if ($res === false) {
4406
            return false;
4407
        } //error during query
4408
4409
        return true;
4410
    }
4411
4412
    /**
4413
     * Regenerate an API key from the user's account.
4414
     *
4415
     * @param   int     user ID (defaults to the results of api_get_user_id())
4416
     * @param   string  API key's internal ID
4417
     *
4418
     * @return int num
4419
     */
4420
    public static function update_api_key($user_id, $api_service)
4421
    {
4422
        if ($user_id != strval(intval($user_id))) {
4423
            return false;
4424
        }
4425
        if ($user_id === false) {
4426
            return false;
4427
        }
4428
        $service_name = Database::escape_string($api_service);
4429
        if (is_string($service_name) === false) {
4430
            return false;
4431
        }
4432
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4433
        $sql = "SELECT id FROM $t_api
4434
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
4435
        $res = Database::query($sql);
4436
        $num = Database::num_rows($res);
4437
        if ($num == 1) {
4438
            $id_key = Database::fetch_array($res, 'ASSOC');
4439
            self::delete_api_key($id_key['id']);
4440
            $num = self::add_api_key($user_id, $api_service);
4441
        } elseif ($num == 0) {
4442
            $num = self::add_api_key($user_id, $api_service);
4443
        }
4444
4445
        return $num;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $num also could return the type boolean which is incompatible with the documented return type integer.
Loading history...
4446
    }
4447
4448
    /**
4449
     * @param   int     user ID (defaults to the results of api_get_user_id())
4450
     * @param   string    API key's internal ID
4451
     *
4452
     * @return int row ID, or return false if not found
4453
     */
4454
    public static function get_api_key_id($user_id, $api_service)
4455
    {
4456
        if ($user_id != strval(intval($user_id))) {
4457
            return false;
4458
        }
4459
        if ($user_id === false) {
4460
            return false;
4461
        }
4462
        if (empty($api_service)) {
4463
            return false;
4464
        }
4465
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4466
        $api_service = Database::escape_string($api_service);
4467
        $sql = "SELECT id FROM $t_api
4468
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
4469
        $res = Database::query($sql);
4470
        if (Database::num_rows($res) < 1) {
4471
            return false;
4472
        }
4473
        $row = Database::fetch_array($res, 'ASSOC');
4474
4475
        return $row['id'];
4476
    }
4477
4478
    /**
4479
     * Checks if a user_id is platform admin.
4480
     *
4481
     * @param   int user ID
4482
     *
4483
     * @return bool True if is admin, false otherwise
4484
     *
4485
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
4486
     */
4487
    public static function is_admin($user_id)
4488
    {
4489
        $user_id = (int) $user_id;
4490
        if (empty($user_id)) {
4491
            return false;
4492
        }
4493
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
4494
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
4495
        $res = Database::query($sql);
4496
4497
        return Database::num_rows($res) === 1;
4498
    }
4499
4500
    /**
4501
     * Get the total count of users.
4502
     *
4503
     * @param int $status        Status of users to be counted
4504
     * @param int $access_url_id Access URL ID (optional)
4505
     * @param int $active
4506
     *
4507
     * @return mixed Number of users or false on error
4508
     */
4509
    public static function get_number_of_users($status = 0, $access_url_id = 1, $active = null)
4510
    {
4511
        $t_u = Database::get_main_table(TABLE_MAIN_USER);
4512
        $t_a = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4513
4514
        if (api_is_multiple_url_enabled()) {
4515
            $sql = "SELECT count(u.id)
4516
                    FROM $t_u u
4517
                    INNER JOIN $t_a url_user
4518
                    ON (u.id = url_user.user_id)
4519
                    WHERE url_user.access_url_id = $access_url_id
4520
            ";
4521
        } else {
4522
            $sql = "SELECT count(u.id)
4523
                    FROM $t_u u
4524
                    WHERE 1 = 1 ";
4525
        }
4526
4527
        if (is_int($status) && $status > 0) {
4528
            $status = (int) $status;
4529
            $sql .= " AND u.status = $status ";
4530
        }
4531
4532
        if ($active !== null) {
4533
            $active = (int) $active;
4534
            $sql .= " AND u.active = $active ";
4535
        }
4536
4537
        $res = Database::query($sql);
4538
        if (Database::num_rows($res) === 1) {
4539
            return (int) Database::result($res, 0, 0);
4540
        }
4541
4542
        return false;
4543
    }
4544
4545
    /**
4546
     * Gets the tags of a specific field_id
4547
     * USER TAGS.
4548
     *
4549
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
4550
     *
4551
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
4552
     *    Called it "books" for example.
4553
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
4554
     * 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
4555
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
4556
     * 5. Test and enjoy.
4557
     *
4558
     * @param string $tag
4559
     * @param int    $field_id      field_id
4560
     * @param string $return_format how we are going to result value in array or in a string (json)
4561
     * @param $limit
4562
     *
4563
     * @return mixed
4564
     */
4565
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
4566
    {
4567
        // database table definition
4568
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4569
        $field_id = (int) $field_id;
4570
        $limit = (int) $limit;
4571
        $tag = trim(Database::escape_string($tag));
4572
4573
        // all the information of the field
4574
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
4575
                WHERE field_id = $field_id AND tag LIKE '$tag%'
4576
                ORDER BY tag
4577
                LIMIT $limit";
4578
        $result = Database::query($sql);
4579
        $return = [];
4580
        if (Database::num_rows($result) > 0) {
4581
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4582
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
4583
            }
4584
        }
4585
        if ($return_format === 'json') {
4586
            $return = json_encode($return);
4587
        }
4588
4589
        return $return;
4590
    }
4591
4592
    /**
4593
     * @param int $field_id
4594
     * @param int $limit
4595
     *
4596
     * @return array
4597
     */
4598
    public static function get_top_tags($field_id, $limit = 100)
4599
    {
4600
        // database table definition
4601
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4602
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4603
        $field_id = (int) $field_id;
4604
        $limit = (int) $limit;
4605
        // all the information of the field
4606
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
4607
                INNER JOIN $table_user_tag ut
4608
                ON (ut.id = uv.tag_id)
4609
                WHERE field_id = $field_id
4610
                GROUP BY tag_id
4611
                ORDER BY count DESC
4612
                LIMIT $limit";
4613
        $result = Database::query($sql);
4614
        $return = [];
4615
        if (Database::num_rows($result) > 0) {
4616
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4617
                $return[] = $row;
4618
            }
4619
        }
4620
4621
        return $return;
4622
    }
4623
4624
    /**
4625
     * Get user's tags.
4626
     *
4627
     * @param int $user_id
4628
     * @param int $field_id
4629
     *
4630
     * @return array
4631
     */
4632
    public static function get_user_tags($user_id, $field_id)
4633
    {
4634
        // database table definition
4635
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4636
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4637
        $field_id = (int) $field_id;
4638
        $user_id = (int) $user_id;
4639
4640
        // all the information of the field
4641
        $sql = "SELECT ut.id, tag, count
4642
                FROM $table_user_tag ut
4643
                INNER JOIN $table_user_tag_values uv
4644
                ON (uv.tag_id=ut.ID)
4645
                WHERE field_id = $field_id AND user_id = $user_id
4646
                ORDER BY tag";
4647
        $result = Database::query($sql);
4648
        $return = [];
4649
        if (Database::num_rows($result) > 0) {
4650
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4651
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
4652
            }
4653
        }
4654
4655
        return $return;
4656
    }
4657
4658
    /**
4659
     * Get user's tags.
4660
     *
4661
     * @param int  $user_id
4662
     * @param int  $field_id
4663
     * @param bool $show_links show links or not
4664
     *
4665
     * @return string
4666
     */
4667
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
4668
    {
4669
        // database table definition
4670
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4671
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4672
        $field_id = (int) $field_id;
4673
        $user_id = (int) $user_id;
4674
4675
        // all the information of the field
4676
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
4677
                INNER JOIN $table_user_tag_values uv
4678
                ON (uv.tag_id = ut.id)
4679
                WHERE field_id = $field_id AND user_id = $user_id
4680
                ORDER BY tag";
4681
4682
        $result = Database::query($sql);
4683
        $return = [];
4684
        if (Database::num_rows($result) > 0) {
4685
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4686
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
4687
            }
4688
        }
4689
        $user_tags = $return;
4690
        $tag_tmp = [];
4691
        foreach ($user_tags as $tag) {
4692
            if ($show_links) {
4693
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
4694
                    $tag['tag'].
4695
                '</a>';
4696
            } else {
4697
                $tag_tmp[] = $tag['tag'];
4698
            }
4699
        }
4700
4701
        if (is_array($user_tags) && count($user_tags) > 0) {
4702
            return implode(', ', $tag_tmp);
4703
        } else {
4704
            return '';
4705
        }
4706
    }
4707
4708
    /**
4709
     * Get the tag id.
4710
     *
4711
     * @param int $tag
4712
     * @param int $field_id
4713
     *
4714
     * @return int returns 0 if fails otherwise the tag id
4715
     */
4716
    public static function get_tag_id($tag, $field_id)
4717
    {
4718
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4719
        $tag = Database::escape_string($tag);
4720
        $field_id = (int) $field_id;
4721
        //with COLLATE latin1_bin to select query in a case sensitive mode
4722
        $sql = "SELECT id FROM $table_user_tag
4723
                WHERE tag LIKE '$tag' AND field_id = $field_id";
4724
        $result = Database::query($sql);
4725
        if (Database::num_rows($result) > 0) {
4726
            $row = Database::fetch_array($result, 'ASSOC');
4727
4728
            return $row['id'];
4729
        } else {
4730
            return 0;
4731
        }
4732
    }
4733
4734
    /**
4735
     * Get the tag id.
4736
     *
4737
     * @param int $tag_id
4738
     * @param int $field_id
4739
     *
4740
     * @return int 0 if fails otherwise the tag id
4741
     */
4742
    public static function get_tag_id_from_id($tag_id, $field_id)
4743
    {
4744
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4745
        $tag_id = (int) $tag_id;
4746
        $field_id = (int) $field_id;
4747
        $sql = "SELECT id FROM $table_user_tag
4748
                WHERE id = '$tag_id' AND field_id = $field_id";
4749
        $result = Database::query($sql);
4750
        if (Database::num_rows($result) > 0) {
4751
            $row = Database::fetch_array($result, 'ASSOC');
4752
4753
            return $row['id'];
4754
        } else {
4755
            return false;
4756
        }
4757
    }
4758
4759
    /**
4760
     * Adds a user-tag value.
4761
     *
4762
     * @param mixed $tag
4763
     * @param int   $user_id
4764
     * @param int   $field_id field id of the tag
4765
     *
4766
     * @return bool True if the tag was inserted or updated. False otherwise.
4767
     *              The return value doesn't take into account *values* added to the tag.
4768
     *              Only the creation/update of the tag field itself.
4769
     */
4770
    public static function add_tag($tag, $user_id, $field_id)
4771
    {
4772
        // database table definition
4773
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4774
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4775
        $tag = trim(Database::escape_string($tag));
4776
        $user_id = (int) $user_id;
4777
        $field_id = (int) $field_id;
4778
4779
        $tag_id = self::get_tag_id($tag, $field_id);
4780
4781
        /* IMPORTANT
4782
         *  @todo we don't create tags with numbers
4783
         *
4784
         */
4785
        if (is_numeric($tag)) {
4786
            //the form is sending an id this means that the user select it from the list so it MUST exists
4787
            /* $new_tag_id = self::get_tag_id_from_id($tag,$field_id);
4788
              if ($new_tag_id !== false) {
4789
              $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $new_tag_id";
4790
              $result = Database::query($sql);
4791
              $last_insert_id = $new_tag_id;
4792
              } else {
4793
              $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
4794
              $result = Database::query($sql);
4795
              $last_insert_id = Database::insert_id();
4796
              } */
4797
        }
4798
4799
        //this is a new tag
4800
        if ($tag_id == 0) {
4801
            //the tag doesn't exist
4802
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
4803
            Database::query($sql);
4804
            $last_insert_id = Database::insert_id();
4805
        } else {
4806
            //the tag exists we update it
4807
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
4808
            Database::query($sql);
4809
            $last_insert_id = $tag_id;
4810
        }
4811
4812
        if (!empty($last_insert_id) && ($last_insert_id != 0)) {
4813
            //we insert the relationship user-tag
4814
            $sql = "SELECT tag_id FROM $table_user_tag_values
4815
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
4816
            $result = Database::query($sql);
4817
            //if the relationship does not exist we create it
4818
            if (Database::num_rows($result) == 0) {
4819
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
4820
                Database::query($sql);
4821
            }
4822
4823
            return true;
4824
        }
4825
4826
        return false;
4827
    }
4828
4829
    /**
4830
     * Deletes an user tag.
4831
     *
4832
     * @param int $user_id
4833
     * @param int $field_id
4834
     */
4835
    public static function delete_user_tags($user_id, $field_id)
4836
    {
4837
        // database table definition
4838
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4839
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4840
        $user_id = (int) $user_id;
4841
4842
        $tags = self::get_user_tags($user_id, $field_id);
4843
        if (is_array($tags) && count($tags) > 0) {
4844
            foreach ($tags as $key => $tag) {
4845
                if ($tag['count'] > '0') {
4846
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
4847
                    Database::query($sql);
4848
                }
4849
                $sql = "DELETE FROM $table_user_tag_values
4850
                        WHERE user_id = $user_id AND tag_id = $key";
4851
                Database::query($sql);
4852
            }
4853
        }
4854
    }
4855
4856
    /**
4857
     * Process the tag list comes from the UserManager::update_extra_field_value() function.
4858
     *
4859
     * @param array $tags     the tag list that will be added
4860
     * @param int   $user_id
4861
     * @param int   $field_id
4862
     *
4863
     * @return bool
4864
     */
4865
    public static function process_tags($tags, $user_id, $field_id)
4866
    {
4867
        // We loop the tags and add it to the DB
4868
        if (is_array($tags)) {
4869
            foreach ($tags as $tag) {
4870
                self::add_tag($tag, $user_id, $field_id);
4871
            }
4872
        } else {
4873
            self::add_tag($tags, $user_id, $field_id);
4874
        }
4875
4876
        return true;
4877
    }
4878
4879
    /**
4880
     * Returns a list of all administrators.
4881
     *
4882
     * @return array
4883
     */
4884
    public static function get_all_administrators()
4885
    {
4886
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4887
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
4888
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4889
        $access_url_id = api_get_current_access_url_id();
4890
        if (api_get_multiple_access_url()) {
4891
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
4892
                    FROM $tbl_url_rel_user as url
4893
                    INNER JOIN $table_admin as admin
4894
                    ON (admin.user_id=url.user_id)
4895
                    INNER JOIN $table_user u
4896
                    ON (u.id=admin.user_id)
4897
                    WHERE access_url_id ='".$access_url_id."'";
4898
        } else {
4899
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
4900
                    FROM $table_admin as admin
4901
                    INNER JOIN $table_user u
4902
                    ON (u.id=admin.user_id)";
4903
        }
4904
        $result = Database::query($sql);
4905
        $return = [];
4906
        if (Database::num_rows($result) > 0) {
4907
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4908
                $return[$row['user_id']] = $row;
4909
            }
4910
        }
4911
4912
        return $return;
4913
    }
4914
4915
    /**
4916
     * Search an user (tags, first name, last name and email ).
4917
     *
4918
     * @param string $tag
4919
     * @param int    $field_id        field id of the tag
4920
     * @param int    $from            where to start in the query
4921
     * @param int    $number_of_items
4922
     * @param bool   $getCount        get count or not
4923
     *
4924
     * @return array
4925
     */
4926
    public static function get_all_user_tags(
4927
        $tag,
4928
        $field_id = 0,
4929
        $from = 0,
4930
        $number_of_items = 10,
4931
        $getCount = false
4932
    ) {
4933
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
4934
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4935
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4936
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4937
4938
        $field_id = intval($field_id);
4939
        $from = intval($from);
4940
        $number_of_items = intval($number_of_items);
4941
4942
        $where_field = "";
4943
        $where_extra_fields = self::get_search_form_where_extra_fields();
4944
        if ($field_id != 0) {
4945
            $where_field = " field_id = $field_id AND ";
4946
        }
4947
4948
        // all the information of the field
4949
        if ($getCount) {
4950
            $select = "SELECT count(DISTINCT u.id) count";
4951
        } else {
4952
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, picture_uri";
4953
        }
4954
4955
        $sql = " $select
4956
                FROM $user_table u
4957
                INNER JOIN $access_url_rel_user_table url_rel_user
4958
                ON (u.id = url_rel_user.user_id)
4959
                LEFT JOIN $table_user_tag_values uv
4960
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
4961
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
4962
                WHERE
4963
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
4964
                    (
4965
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
4966
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
4967
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
4968
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
4969
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
4970
                     )
4971
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
4972
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
4973
4974
        $keyword_active = true;
4975
        // only active users
4976
        if ($keyword_active) {
4977
            $sql .= " AND u.active='1'";
4978
        }
4979
        // avoid anonymous
4980
        $sql .= " AND u.status <> 6 ";
4981
        $sql .= " ORDER BY username";
4982
        $sql .= " LIMIT $from , $number_of_items";
4983
4984
        $result = Database::query($sql);
4985
        $return = [];
4986
4987
        if (Database::num_rows($result) > 0) {
4988
            if ($getCount) {
4989
                $row = Database::fetch_array($result, 'ASSOC');
4990
4991
                return $row['count'];
4992
            }
4993
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4994
                $return[$row['id']] = $row;
4995
            }
4996
        }
4997
4998
        return $return;
4999
    }
5000
5001
    /**
5002
     * Get extra filterable user fields (only type select).
5003
     *
5004
     * @return array Array of extra fields as [int => ['name' => ..., 'variable' => ..., 'data' => ...]] (
5005
     *               or empty array if no extra field)
5006
     */
5007
    public static function getExtraFilterableFields()
5008
    {
5009
        $extraFieldList = self::get_extra_fields();
5010
        $fields = [];
5011
        if (is_array($extraFieldList)) {
5012
            foreach ($extraFieldList as $extraField) {
5013
                // If is enabled to filter and is a "<select>" field type
5014
                if ($extraField[8] == 1 && $extraField[2] == 4) {
5015
                    $fields[] = [
5016
                        'name' => $extraField[3],
5017
                        'variable' => $extraField[1],
5018
                        'data' => $extraField[9],
5019
                    ];
5020
                }
5021
            }
5022
        }
5023
5024
        return $fields;
5025
    }
5026
5027
    /**
5028
     * Get extra where clauses for finding users based on extra filterable user fields (type select).
5029
     *
5030
     * @return string With AND clauses based on user's ID which have the values to search in extra user fields
5031
     *                (or empty if no extra field exists)
5032
     */
5033
    public static function get_search_form_where_extra_fields()
5034
    {
5035
        $useExtraFields = false;
5036
        $extraFields = self::getExtraFilterableFields();
5037
        $extraFieldResult = [];
5038
        if (is_array($extraFields) && count($extraFields) > 0) {
5039
            foreach ($extraFields as $extraField) {
5040
                $varName = 'field_'.$extraField['variable'];
5041
                if (self::is_extra_field_available($extraField['variable'])) {
5042
                    if (isset($_GET[$varName]) && $_GET[$varName] != '0') {
5043
                        $useExtraFields = true;
5044
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
5045
                            $extraField['variable'],
5046
                            $_GET[$varName]
5047
                        );
5048
                    }
5049
                }
5050
            }
5051
        }
5052
5053
        if ($useExtraFields) {
5054
            $finalResult = [];
5055
            if (count($extraFieldResult) > 1) {
5056
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
5057
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
5058
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
5059
                    }
5060
                }
5061
            } else {
5062
                $finalResult = $extraFieldResult[0];
5063
            }
5064
5065
            if (is_array($finalResult) && count($finalResult) > 0) {
5066
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
5067
            } else {
5068
                //no results
5069
                $whereFilter = " AND u.id  = -1 ";
5070
            }
5071
5072
            return $whereFilter;
5073
        }
5074
5075
        return '';
5076
    }
5077
5078
    /**
5079
     * Show the search form.
5080
     *
5081
     * @param string $query the value of the search box
5082
     *
5083
     * @throws Exception
5084
     *
5085
     * @return string HTML form
5086
     */
5087
    public static function get_search_form($query, $defaultParams = [])
5088
    {
5089
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
5090
        $form = new FormValidator(
5091
            'search_user',
5092
            'get',
5093
            api_get_path(WEB_PATH).'main/social/search.php',
5094
            '',
5095
            [],
5096
            FormValidator::LAYOUT_HORIZONTAL
5097
        );
5098
5099
        $query = Security::remove_XSS($query);
5100
5101
        if (!empty($query)) {
5102
            $form->addHeader(get_lang('Results').' "'.$query.'"');
5103
        }
5104
5105
        $form->addText(
5106
            'q',
5107
            get_lang('UsersGroups'),
5108
            false,
5109
            [
5110
                'id' => 'q',
5111
            ]
5112
        );
5113
        $options = [
5114
            0 => get_lang('Select'),
5115
            1 => get_lang('User'),
5116
            2 => get_lang('Group'),
5117
        ];
5118
        $form->addSelect(
5119
            'search_type',
5120
            get_lang('Type'),
5121
            $options,
5122
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
5123
        );
5124
5125
        // Extra fields
5126
        $extraFields = self::getExtraFilterableFields();
5127
        $defaults = [];
5128
        if (is_array($extraFields) && count($extraFields) > 0) {
5129
            foreach ($extraFields as $extraField) {
5130
                $varName = 'field_'.$extraField['variable'];
5131
                $options = [
5132
                    0 => get_lang('Select'),
5133
                ];
5134
                foreach ($extraField['data'] as $option) {
5135
                    if (isset($_GET[$varName])) {
5136
                        if ($_GET[$varName] == $option[1]) {
5137
                            $defaults[$option[1]] = true;
5138
                        }
5139
                    }
5140
5141
                    $options[$option[1]] = $option[1];
5142
                }
5143
                $form->addSelect($varName, $extraField['name'], $options);
5144
            }
5145
        }
5146
5147
        $defaults['search_type'] = (int) $searchType;
5148
        $defaults['q'] = $query;
5149
5150
        if (!empty($defaultParams)) {
5151
            $defaults = array_merge($defaults, $defaultParams);
5152
        }
5153
        $form->setDefaults($defaults);
5154
        $form->addButtonSearch(get_lang('Search'));
5155
5156
        $js = '<script>
5157
        extra_field_toogle();
5158
        function extra_field_toogle() {
5159
            if (jQuery("select[name=search_type]").val() != "1") {
5160
                jQuery(".extra_field").hide();
5161
            } else {
5162
                jQuery(".extra_field").show();
5163
            }
5164
        }
5165
        </script>';
5166
5167
        return $js.$form->returnForm();
5168
    }
5169
5170
    /**
5171
     * Shows the user menu.
5172
     */
5173
    public static function show_menu()
5174
    {
5175
        echo '<div class="actions">';
5176
        echo '<a href="/main/auth/profile.php">'.
5177
            Display::return_icon('profile.png').' '.get_lang('PersonalData').'</a>';
5178
        echo '<a href="/main/messages/inbox.php">'.
5179
            Display::return_icon('inbox.png').' '.get_lang('Inbox').'</a>';
5180
        echo '<a href="/main/messages/outbox.php">'.
5181
            Display::return_icon('outbox.png').' '.get_lang('Outbox').'</a>';
5182
        echo '<span style="float:right; padding-top:7px;">'.
5183
        '<a href="/main/auth/profile.php?show=1">'.
5184
            Display::return_icon('edit.gif').' '.get_lang('Configuration').'</a>';
5185
        echo '</span>';
5186
        echo '</div>';
5187
    }
5188
5189
    /**
5190
     * Allow to register contact to social network.
5191
     *
5192
     * @param int $friend_id     user friend id
5193
     * @param int $my_user_id    user id
5194
     * @param int $relation_type relation between users see constants definition
5195
     *
5196
     * @return bool
5197
     */
5198
    public static function relate_users($friend_id, $my_user_id, $relation_type)
5199
    {
5200
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5201
5202
        $friend_id = (int) $friend_id;
5203
        $my_user_id = (int) $my_user_id;
5204
        $relation_type = (int) $relation_type;
5205
5206
        $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
5207
                WHERE
5208
                    friend_user_id='.$friend_id.' AND
5209
                    user_id='.$my_user_id.' AND
5210
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
5211
        $result = Database::query($sql);
5212
        $row = Database::fetch_array($result, 'ASSOC');
5213
        $current_date = api_get_utc_datetime();
5214
5215
        if ($row['count'] == 0) {
5216
            $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
5217
                    VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
5218
            Database::query($sql);
5219
5220
            return true;
5221
        }
5222
5223
        $sql = 'SELECT COUNT(*) as count, relation_type  FROM '.$tbl_my_friend.'
5224
                WHERE
5225
                    friend_user_id='.$friend_id.' AND
5226
                    user_id='.$my_user_id.' AND
5227
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
5228
        $result = Database::query($sql);
5229
        $row = Database::fetch_array($result, 'ASSOC');
5230
5231
        if ($row['count'] == 1) {
5232
            //only for the case of a RRHH or a Student BOSS
5233
            if ($row['relation_type'] != $relation_type &&
5234
                ($relation_type == USER_RELATION_TYPE_RRHH || $relation_type == USER_RELATION_TYPE_BOSS)
5235
            ) {
5236
                $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
5237
                        VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
5238
            } else {
5239
                $sql = 'UPDATE '.$tbl_my_friend.' SET relation_type='.$relation_type.'
5240
                        WHERE friend_user_id='.$friend_id.' AND user_id='.$my_user_id;
5241
            }
5242
            Database::query($sql);
5243
5244
            return true;
5245
        }
5246
5247
        return false;
5248
    }
5249
5250
    /**
5251
     * Deletes a contact.
5252
     *
5253
     * @param bool   $friend_id
5254
     * @param bool   $real_removed          true will delete ALL friends relationship
5255
     * @param string $with_status_condition
5256
     *
5257
     * @author isaac flores paz <[email protected]>
5258
     * @author Julio Montoya <[email protected]> Cleaning code
5259
     */
5260
    public static function remove_user_rel_user(
5261
        $friend_id,
5262
        $real_removed = false,
5263
        $with_status_condition = ''
5264
    ) {
5265
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5266
        $tbl_my_message = Database::get_main_table(TABLE_MESSAGE);
5267
        $friend_id = (int) $friend_id;
5268
        $user_id = api_get_user_id();
5269
5270
        if ($real_removed) {
5271
            $extra_condition = '';
5272
            if ($with_status_condition != '') {
5273
                $extra_condition = ' AND relation_type = '.intval($with_status_condition);
5274
            }
5275
            $sql = 'DELETE FROM '.$tbl_my_friend.'
5276
                    WHERE
5277
                        relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
5278
                        friend_user_id='.$friend_id.' '.$extra_condition;
5279
            Database::query($sql);
5280
            $sql = 'DELETE FROM '.$tbl_my_friend.'
5281
                   WHERE
5282
                    relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
5283
                    user_id='.$friend_id.' '.$extra_condition;
5284
            Database::query($sql);
5285
        } else {
5286
            $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
5287
                    WHERE
5288
                        user_id='.$user_id.' AND
5289
                        relation_type NOT IN('.USER_RELATION_TYPE_DELETED.', '.USER_RELATION_TYPE_RRHH.') AND
5290
                        friend_user_id='.$friend_id;
5291
            $result = Database::query($sql);
5292
            $row = Database::fetch_array($result, 'ASSOC');
5293
            if ($row['count'] == 1) {
5294
                //Delete user rel user
5295
                $sql_i = 'UPDATE '.$tbl_my_friend.' SET relation_type='.USER_RELATION_TYPE_DELETED.'
5296
                          WHERE user_id='.$user_id.' AND friend_user_id='.$friend_id;
5297
5298
                $sql_j = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
5299
                          WHERE
5300
                                user_receiver_id='.$user_id.' AND
5301
                                user_sender_id='.$friend_id.' AND update_date="0000-00-00 00:00:00" ';
5302
                // Delete user
5303
                $sql_ij = 'UPDATE '.$tbl_my_friend.'  SET relation_type='.USER_RELATION_TYPE_DELETED.'
5304
                           WHERE user_id='.$friend_id.' AND friend_user_id='.$user_id;
5305
                $sql_ji = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
5306
                           WHERE
5307
                                user_receiver_id='.$friend_id.' AND
5308
                                user_sender_id='.$user_id.' AND
5309
                                update_date="0000-00-00 00:00:00" ';
5310
                Database::query($sql_i);
5311
                Database::query($sql_j);
5312
                Database::query($sql_ij);
5313
                Database::query($sql_ji);
5314
            }
5315
        }
5316
5317
        // Delete accepted invitations
5318
        $sql = "DELETE FROM $tbl_my_message
5319
                WHERE
5320
                    msg_status = ".MESSAGE_STATUS_INVITATION_ACCEPTED." AND
5321
                    (
5322
                        user_receiver_id = $user_id AND
5323
                        user_sender_id = $friend_id
5324
                    ) OR
5325
                    (
5326
                        user_sender_id = $user_id AND
5327
                        user_receiver_id = $friend_id
5328
                    )
5329
        ";
5330
        Database::query($sql);
5331
    }
5332
5333
    /**
5334
     * @param int $userId
5335
     *
5336
     * @return array
5337
     */
5338
    public static function getDrhListFromUser($userId)
5339
    {
5340
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
5341
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5342
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5343
        $userId = (int) $userId;
5344
5345
        $orderBy = null;
5346
        if (api_is_western_name_order()) {
5347
            $orderBy .= ' ORDER BY firstname, lastname ';
5348
        } else {
5349
            $orderBy .= ' ORDER BY lastname, firstname ';
5350
        }
5351
5352
        $sql = "SELECT u.id, username, u.firstname, u.lastname
5353
                FROM $tblUser u
5354
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
5355
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
5356
                WHERE
5357
                    access_url_id = ".api_get_current_access_url_id()." AND
5358
                    uru.user_id = '$userId' AND
5359
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
5360
                $orderBy
5361
                ";
5362
        $result = Database::query($sql);
5363
5364
        return Database::store_result($result);
5365
    }
5366
5367
    /**
5368
     * get users followed by human resource manager.
5369
     *
5370
     * @param int    $userId
5371
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
5372
     * @param bool   $getOnlyUserId
5373
     * @param bool   $getSql
5374
     * @param bool   $getCount
5375
     * @param int    $from
5376
     * @param int    $numberItems
5377
     * @param int    $column
5378
     * @param string $direction
5379
     * @param int    $active
5380
     * @param string $lastConnectionDate
5381
     *
5382
     * @return array users
5383
     */
5384
    public static function get_users_followed_by_drh(
5385
        $userId,
5386
        $userStatus = 0,
5387
        $getOnlyUserId = false,
5388
        $getSql = false,
5389
        $getCount = false,
5390
        $from = null,
5391
        $numberItems = null,
5392
        $column = null,
5393
        $direction = null,
5394
        $active = null,
5395
        $lastConnectionDate = null
5396
    ) {
5397
        return self::getUsersFollowedByUser(
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::getUsersFol...astConnectionDate, DRH) also could return the type string which is incompatible with the documented return type array.
Loading history...
5398
            $userId,
5399
            $userStatus,
5400
            $getOnlyUserId,
5401
            $getSql,
5402
            $getCount,
5403
            $from,
5404
            $numberItems,
5405
            $column,
5406
            $direction,
5407
            $active,
5408
            $lastConnectionDate,
5409
            DRH
5410
        );
5411
    }
5412
5413
    /**
5414
     * Get users followed by human resource manager.
5415
     *
5416
     * @param int    $userId
5417
     * @param int    $userStatus             Filter users by status (STUDENT, COURSEMANAGER, etc)
5418
     * @param bool   $getOnlyUserId
5419
     * @param bool   $getSql
5420
     * @param bool   $getCount
5421
     * @param int    $from
5422
     * @param int    $numberItems
5423
     * @param int    $column
5424
     * @param string $direction
5425
     * @param int    $active
5426
     * @param string $lastConnectionDate
5427
     * @param int    $status                 the function is called by who? COURSEMANAGER, DRH?
5428
     * @param string $keyword
5429
     * @param bool   $checkSessionVisibility
5430
     *
5431
     * @return mixed Users list (array) or the SQL query if $getSQL was set to true
5432
     */
5433
    public static function getUsersFollowedByUser(
5434
        $userId,
5435
        $userStatus = null,
5436
        $getOnlyUserId = false,
5437
        $getSql = false,
5438
        $getCount = false,
5439
        $from = null,
5440
        $numberItems = null,
5441
        $column = null,
5442
        $direction = null,
5443
        $active = null,
5444
        $lastConnectionDate = null,
5445
        $status = null,
5446
        $keyword = null,
5447
        $checkSessionVisibility = false
5448
    ) {
5449
        // Database Table Definitions
5450
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
5451
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5452
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5453
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
5454
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5455
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5456
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
5457
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
5458
5459
        $userId = (int) $userId;
5460
        $limitCondition = '';
5461
5462
        if (isset($from) && isset($numberItems)) {
5463
            $from = (int) $from;
5464
            $numberItems = (int) $numberItems;
5465
            $limitCondition = "LIMIT $from, $numberItems";
5466
        }
5467
5468
        $column = Database::escape_string($column);
5469
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
5470
5471
        $userConditions = '';
5472
        if (!empty($userStatus)) {
5473
            $userConditions .= ' AND u.status = '.intval($userStatus);
5474
        }
5475
5476
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
5477
        if ($getOnlyUserId) {
5478
            $select = " SELECT DISTINCT u.id user_id";
5479
        }
5480
5481
        $masterSelect = "SELECT DISTINCT * FROM ";
5482
5483
        if ($getCount) {
5484
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
5485
            $select = " SELECT DISTINCT(u.id) user_id";
5486
        }
5487
5488
        if (!is_null($active)) {
5489
            $active = intval($active);
5490
            $userConditions .= " AND u.active = $active ";
5491
        }
5492
5493
        if (!empty($keyword)) {
5494
            $keyword = trim(Database::escape_string($keyword));
5495
            $keywordParts = array_filter(explode(' ', $keyword));
5496
            $extraKeyword = '';
5497
            if (!empty($keywordParts)) {
5498
                $keywordPartsFixed = Database::escape_string(implode('%', $keywordParts));
5499
                if (!empty($keywordPartsFixed)) {
5500
                    $extraKeyword .= " OR
5501
                        CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keywordPartsFixed%' OR
5502
                        CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keywordPartsFixed%' ";
5503
                }
5504
            }
5505
5506
            $userConditions .= " AND (
5507
                u.username LIKE '%$keyword%' OR
5508
                u.firstname LIKE '%$keyword%' OR
5509
                u.lastname LIKE '%$keyword%' OR
5510
                u.official_code LIKE '%$keyword%' OR
5511
                u.email LIKE '%$keyword%' OR
5512
                CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keyword%' OR
5513
                CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keyword%'
5514
                $extraKeyword
5515
            )";
5516
        }
5517
5518
        if (!empty($lastConnectionDate)) {
5519
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
5520
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
5521
        }
5522
5523
        $sessionConditionsCoach = null;
5524
        $dateCondition = '';
5525
        $drhConditions = null;
5526
        $teacherSelect = null;
5527
5528
        $urlId = api_get_current_access_url_id();
5529
5530
        switch ($status) {
5531
            case DRH:
5532
                $drhConditions .= " AND
5533
                    friend_user_id = '$userId' AND
5534
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
5535
                ";
5536
                break;
5537
            case COURSEMANAGER:
5538
                $drhConditions .= " AND
5539
                    friend_user_id = '$userId' AND
5540
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
5541
                ";
5542
5543
                $sessionConditionsCoach .= " AND
5544
                    (s.id_coach = '$userId')
5545
                ";
5546
5547
                $sessionConditionsTeacher = " AND
5548
                    (scu.status = 2 AND scu.user_id = '$userId')
5549
                ";
5550
5551
                if ($checkSessionVisibility) {
5552
                    $today = api_strtotime('now', 'UTC');
5553
                    $today = date('Y-m-d', $today);
5554
                    $dateCondition = "
5555
                        AND
5556
                        (
5557
                            (s.access_start_date <= '$today' AND '$today' <= s.access_end_date) OR
5558
                            (s.access_start_date IS NULL AND s.access_end_date IS NULL) OR
5559
                            (s.access_start_date <= '$today' AND s.access_end_date IS NULL) OR
5560
                            ('$today' <= s.access_end_date AND s.access_start_date IS NULL)
5561
                        )
5562
					";
5563
                }
5564
5565
                // Use $tbl_session_rel_course_rel_user instead of $tbl_session_rel_user
5566
                /*
5567
                INNER JOIN $tbl_session_rel_user sru
5568
                ON (sru.user_id = u.id)
5569
                INNER JOIN $tbl_session_rel_course_rel_user scu
5570
                ON (scu.user_id = u.id AND scu.c_id IS NOT NULL AND visibility = 1)*/
5571
                $teacherSelect =
5572
                "UNION ALL (
5573
                        $select
5574
                        FROM $tbl_user u
5575
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
5576
                        WHERE
5577
                            (
5578
                                sru.session_id IN (
5579
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
5580
                                    $tbl_session_rel_access_url session_rel_access_rel_user
5581
                                    ON session_rel_access_rel_user.session_id = s.id
5582
                                    WHERE access_url_id = ".$urlId."
5583
                                    $sessionConditionsCoach
5584
                                ) OR sru.session_id IN (
5585
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
5586
                                    INNER JOIN $tbl_session_rel_access_url url
5587
                                    ON (url.session_id = s.id)
5588
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
5589
                                    ON (scu.session_id = s.id)
5590
                                    WHERE access_url_id = ".$urlId."
5591
                                    $sessionConditionsTeacher
5592
                                    $dateCondition
5593
                                )
5594
                            )
5595
                            $userConditions
5596
                    )
5597
                    UNION ALL(
5598
                        $select
5599
                        FROM $tbl_user u
5600
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
5601
                        WHERE cu.c_id IN (
5602
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
5603
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
5604
                        )
5605
                        $userConditions
5606
                    )"
5607
                ;
5608
                break;
5609
            case STUDENT_BOSS:
5610
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
5611
                break;
5612
            case HRM_REQUEST:
5613
                $drhConditions .= " AND
5614
                    friend_user_id = '$userId' AND
5615
                    relation_type = '".USER_RELATION_TYPE_HRM_REQUEST."'
5616
                ";
5617
                break;
5618
        }
5619
5620
        $join = null;
5621
        $sql = " $masterSelect
5622
                (
5623
                    (
5624
                        $select
5625
                        FROM $tbl_user u
5626
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
5627
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
5628
                        $join
5629
                        WHERE
5630
                            access_url_id = ".$urlId."
5631
                            $drhConditions
5632
                            $userConditions
5633
                    )
5634
                    $teacherSelect
5635
                ) as t1";
5636
5637
        if ($getSql) {
5638
            return $sql;
5639
        }
5640
5641
        if ($getCount) {
5642
            $result = Database::query($sql);
5643
            $row = Database::fetch_array($result);
5644
5645
            return $row['count'];
5646
        }
5647
5648
        $orderBy = null;
5649
        if ($getOnlyUserId == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
5650
            if (api_is_western_name_order()) {
5651
                $orderBy .= " ORDER BY firstname, lastname ";
5652
            } else {
5653
                $orderBy .= " ORDER BY lastname, firstname ";
5654
            }
5655
5656
            if (!empty($column) && !empty($direction)) {
5657
                // Fixing order due the UNIONs
5658
                $column = str_replace('u.', '', $column);
5659
                $column = trim($column);
5660
                $orderBy = " ORDER BY `$column` $direction ";
5661
            }
5662
        }
5663
5664
        $sql .= $orderBy;
5665
        $sql .= $limitCondition;
5666
5667
        $result = Database::query($sql);
5668
        $users = [];
5669
        if (Database::num_rows($result) > 0) {
5670
            while ($row = Database::fetch_array($result)) {
5671
                $users[$row['user_id']] = $row;
5672
            }
5673
        }
5674
5675
        return $users;
5676
    }
5677
5678
    /**
5679
     * Subscribes users to human resource manager (Dashboard feature).
5680
     *
5681
     * @param int   $hr_dept_id
5682
     * @param array $users_id
5683
     * @param bool  $deleteOtherAssignedUsers
5684
     *
5685
     * @return int
5686
     */
5687
    public static function subscribeUsersToHRManager(
5688
        $hr_dept_id,
5689
        $users_id,
5690
        $deleteOtherAssignedUsers = true
5691
    ) {
5692
        return self::subscribeUsersToUser(
5693
            $hr_dept_id,
5694
            $users_id,
5695
            USER_RELATION_TYPE_RRHH,
5696
            false,
5697
            $deleteOtherAssignedUsers
5698
        );
5699
    }
5700
5701
    /**
5702
     * Register request to assign users to HRM.
5703
     *
5704
     * @param int   $hrmId   The HRM ID
5705
     * @param array $usersId The users IDs
5706
     *
5707
     * @return int
5708
     */
5709
    public static function requestUsersToHRManager($hrmId, $usersId)
5710
    {
5711
        return self::subscribeUsersToUser(
5712
            $hrmId,
5713
            $usersId,
5714
            USER_RELATION_TYPE_HRM_REQUEST,
5715
            false,
5716
            false
5717
        );
5718
    }
5719
5720
    /**
5721
     * Remove the requests for assign a user to a HRM.
5722
     *
5723
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
5724
     */
5725
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
5726
    {
5727
        $users = implode(', ', $usersId);
5728
        Database::getManager()
5729
            ->createQuery('
5730
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
5731
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
5732
            ')
5733
            ->execute(['hrm_id' => $hrmId, 'relation_type' => USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
5734
    }
5735
5736
    /**
5737
     * Add subscribed users to a user by relation type.
5738
     *
5739
     * @param int    $userId                   The user id
5740
     * @param array  $subscribedUsersId        The id of subscribed users
5741
     * @param string $relationType             The relation type
5742
     * @param bool   $deleteUsersBeforeInsert
5743
     * @param bool   $deleteOtherAssignedUsers
5744
     *
5745
     * @return int
5746
     */
5747
    public static function subscribeUsersToUser(
5748
        $userId,
5749
        $subscribedUsersId,
5750
        $relationType,
5751
        $deleteUsersBeforeInsert = false,
5752
        $deleteOtherAssignedUsers = true
5753
    ) {
5754
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5755
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5756
5757
        $userId = (int) $userId;
5758
        $relationType = (int) $relationType;
5759
        $affectedRows = 0;
5760
5761
        if ($deleteOtherAssignedUsers) {
5762
            if (api_get_multiple_access_url()) {
5763
                // Deleting assigned users to hrm_id
5764
                $sql = "SELECT s.user_id
5765
                        FROM $userRelUserTable s
5766
                        INNER JOIN $userRelAccessUrlTable a
5767
                        ON (a.user_id = s.user_id)
5768
                        WHERE
5769
                            friend_user_id = $userId AND
5770
                            relation_type = $relationType AND
5771
                            access_url_id = ".api_get_current_access_url_id();
5772
            } else {
5773
                $sql = "SELECT user_id
5774
                        FROM $userRelUserTable
5775
                        WHERE
5776
                            friend_user_id = $userId AND
5777
                            relation_type = $relationType";
5778
            }
5779
            $result = Database::query($sql);
5780
5781
            if (Database::num_rows($result) > 0) {
5782
                while ($row = Database::fetch_array($result)) {
5783
                    $sql = "DELETE FROM $userRelUserTable
5784
                            WHERE
5785
                                user_id = {$row['user_id']} AND
5786
                                friend_user_id = $userId AND
5787
                                relation_type = $relationType";
5788
                    Database::query($sql);
5789
                }
5790
            }
5791
        }
5792
5793
        if ($deleteUsersBeforeInsert) {
5794
            $sql = "DELETE FROM $userRelUserTable
5795
                    WHERE
5796
                        user_id = $userId AND
5797
                        relation_type = $relationType";
5798
            Database::query($sql);
5799
        }
5800
5801
        // Inserting new user list
5802
        if (is_array($subscribedUsersId)) {
5803
            foreach ($subscribedUsersId as $subscribedUserId) {
5804
                $subscribedUserId = (int) $subscribedUserId;
5805
                $sql = "SELECT id
5806
                        FROM $userRelUserTable
5807
                        WHERE
5808
                            user_id = $subscribedUserId AND
5809
                            friend_user_id = $userId AND
5810
                            relation_type = $relationType";
5811
5812
                $result = Database::query($sql);
5813
                $num = Database::num_rows($result);
5814
                if ($num === 0) {
5815
                    $date = api_get_utc_datetime();
5816
                    $sql = "INSERT INTO $userRelUserTable (user_id, friend_user_id, relation_type, last_edit)
5817
                            VALUES ($subscribedUserId, $userId, $relationType, '$date')";
5818
                    $result = Database::query($sql);
5819
                    $affectedRows += Database::affected_rows($result);
5820
                }
5821
            }
5822
        }
5823
5824
        return $affectedRows;
5825
    }
5826
5827
    /**
5828
     * This function check if an user is followed by human resources manager.
5829
     *
5830
     * @param int $user_id
5831
     * @param int $hr_dept_id Human resources manager
5832
     *
5833
     * @return bool
5834
     */
5835
    public static function is_user_followed_by_drh($user_id, $hr_dept_id)
5836
    {
5837
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5838
        $user_id = (int) $user_id;
5839
        $hr_dept_id = (int) $hr_dept_id;
5840
        $result = false;
5841
5842
        $sql = "SELECT user_id FROM $tbl_user_rel_user
5843
                WHERE
5844
                    user_id = $user_id AND
5845
                    friend_user_id = $hr_dept_id AND
5846
                    relation_type = ".USER_RELATION_TYPE_RRHH;
5847
        $rs = Database::query($sql);
5848
        if (Database::num_rows($rs) > 0) {
5849
            $result = true;
5850
        }
5851
5852
        return $result;
5853
    }
5854
5855
    /**
5856
     * Return the user id of teacher or session administrator.
5857
     *
5858
     * @return int|bool The user id, or false if the session ID was negative
5859
     */
5860
    public static function get_user_id_of_course_admin_or_session_admin(array $courseInfo)
5861
    {
5862
        $session = api_get_session_id();
5863
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5864
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5865
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5866
5867
        if (empty($courseInfo)) {
5868
            return false;
5869
        }
5870
5871
        $courseId = $courseInfo['real_id'];
5872
5873
        if (empty($session)) {
5874
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
5875
                    INNER JOIN '.$table_course_user.' ru
5876
                    ON ru.user_id = u.id
5877
                    WHERE
5878
                        ru.status = '.COURSEMANAGER.' AND
5879
                        ru.c_id = "'.$courseId.'" ';
5880
        } else {
5881
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
5882
                    INNER JOIN '.$table_session_course_user.' sru
5883
                    ON sru.user_id=u.id
5884
                    WHERE
5885
                        sru.c_id="'.$courseId.'" AND
5886
                        sru.session_id="'.$session.'" AND
5887
                        sru.status = '.SessionEntity::COACH;
5888
        }
5889
5890
        $rs = Database::query($sql);
5891
        $num_rows = Database::num_rows($rs);
5892
5893
        if (0 === $num_rows) {
5894
            return false;
5895
        }
5896
5897
        if (1 === $num_rows) {
5898
            $row = Database::fetch_array($rs);
5899
5900
            return (int) $row['uid'];
5901
        }
5902
5903
        $my_num_rows = $num_rows;
5904
        $my_user_id = Database::result($rs, $my_num_rows - 1, 'uid');
5905
5906
        return (int) $my_user_id;
5907
    }
5908
5909
    /**
5910
     * Determines if a user is a gradebook certified.
5911
     *
5912
     * @param int $cat_id  The category id of gradebook
5913
     * @param int $user_id The user id
5914
     *
5915
     * @return bool
5916
     */
5917
    public static function is_user_certified($cat_id, $user_id)
5918
    {
5919
        $cat_id = (int) $cat_id;
5920
        $user_id = (int) $user_id;
5921
5922
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
5923
        $sql = 'SELECT path_certificate
5924
                FROM '.$table.'
5925
                WHERE
5926
                    cat_id = "'.$cat_id.'" AND
5927
                    user_id = "'.$user_id.'"';
5928
        $rs = Database::query($sql);
5929
        $row = Database::fetch_array($rs);
5930
5931
        if ($row['path_certificate'] == '' || is_null($row['path_certificate'])) {
5932
            return false;
5933
        }
5934
5935
        return true;
5936
    }
5937
5938
    /**
5939
     * Gets the info about a gradebook certificate for a user by course.
5940
     *
5941
     * @param string $course_code The course code
5942
     * @param int    $session_id
5943
     * @param int    $user_id     The user id
5944
     * @param string $startDate   date string
5945
     * @param string $endDate     date string
5946
     *
5947
     * @return array if there is not information return false
5948
     */
5949
    public static function get_info_gradebook_certificate(
5950
        $course_code,
5951
        $session_id,
5952
        $user_id,
5953
        $startDate = null,
5954
        $endDate = null
5955
    ) {
5956
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
5957
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
5958
        $session_id = (int) $session_id;
5959
        $user_id = (int) $user_id;
5960
5961
        if (empty($session_id)) {
5962
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
5963
        } else {
5964
            $session_condition = " AND session_id = $session_id";
5965
        }
5966
5967
        $dateConditions = "";
5968
        if (!empty($startDate)) {
5969
            $startDate = api_get_utc_datetime($startDate, false, true);
5970
            $dateConditions .= " AND created_at >= '".$startDate->format('Y-m-d 00:00:00')."' ";
5971
        }
5972
        if (!empty($endDate)) {
5973
            $endDate = api_get_utc_datetime($endDate, false, true);
5974
            $dateConditions .= " AND created_at <= '".$endDate->format('Y-m-d 23:59:59')."' ";
5975
        }
5976
5977
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.'
5978
                WHERE cat_id = (
5979
                    SELECT id FROM '.$tbl_grade_category.'
5980
                    WHERE
5981
                        course_code = "'.Database::escape_string($course_code).'" '.$session_condition.' '.$dateConditions.'
5982
                    LIMIT 1
5983
                ) AND user_id='.$user_id;
5984
5985
        $rs = Database::query($sql);
5986
        if (Database::num_rows($rs) > 0) {
5987
            $row = Database::fetch_array($rs, 'ASSOC');
5988
            $score = $row['score_certificate'];
5989
            $category_id = $row['cat_id'];
5990
            $cat = Category::load($category_id);
5991
            $displayscore = ScoreDisplay::instance();
5992
            if (isset($cat) && $displayscore->is_custom()) {
5993
                $grade = $displayscore->display_score(
5994
                    [$score, $cat[0]->get_weight()],
5995
                    SCORE_DIV_PERCENT_WITH_CUSTOM
5996
                );
5997
            } else {
5998
                $grade = $displayscore->display_score(
5999
                    [$score, $cat[0]->get_weight()]
6000
                );
6001
            }
6002
            $row['grade'] = $grade;
6003
6004
            return $row;
6005
        }
6006
6007
        return false;
6008
    }
6009
6010
    /**
6011
     * This function check if the user is a coach inside session course.
6012
     *
6013
     * @param int $user_id    User id
6014
     * @param int $courseId
6015
     * @param int $session_id
6016
     *
6017
     * @return bool True if the user is a coach
6018
     */
6019
    public static function is_session_course_coach($user_id, $courseId, $session_id)
6020
    {
6021
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
6022
        // Protect data
6023
        $user_id = intval($user_id);
6024
        $courseId = intval($courseId);
6025
        $session_id = intval($session_id);
6026
        $result = false;
6027
6028
        $sql = "SELECT session_id FROM $table
6029
                WHERE
6030
                  session_id = $session_id AND
6031
                  c_id = $courseId AND
6032
                  user_id = $user_id AND
6033
                  status = 2 ";
6034
        $res = Database::query($sql);
6035
6036
        if (Database::num_rows($res) > 0) {
6037
            $result = true;
6038
        }
6039
6040
        return $result;
6041
    }
6042
6043
    /**
6044
     * This function returns an icon path that represents the favicon of the website of which the url given.
6045
     * Defaults to the current Chamilo favicon.
6046
     *
6047
     * @param string $url1 URL of website where to look for favicon.ico
6048
     * @param string $url2 Optional second URL of website where to look for favicon.ico
6049
     *
6050
     * @return string Path of icon to load
6051
     */
6052
    public static function get_favicon_from_url($url1, $url2 = null)
6053
    {
6054
        $icon_link = '';
6055
        $url = $url1;
6056
        if (empty($url1)) {
6057
            $url = $url2;
6058
            if (empty($url)) {
6059
                $url = api_get_access_url(api_get_current_access_url_id());
6060
                $url = $url[0];
6061
            }
6062
        }
6063
        if (!empty($url)) {
6064
            $pieces = parse_url($url);
6065
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
6066
        }
6067
6068
        return $icon_link;
6069
    }
6070
6071
    public static function addUserAsAdmin(User $user)
6072
    {
6073
        if ($user) {
0 ignored issues
show
introduced by
$user is of type Chamilo\UserBundle\Entity\User, thus it always evaluated to true.
Loading history...
6074
            $userId = $user->getId();
6075
            if (!self::is_admin($userId)) {
6076
                $table = Database::get_main_table(TABLE_MAIN_ADMIN);
6077
                $sql = "INSERT INTO $table SET user_id = $userId";
6078
                Database::query($sql);
6079
            }
6080
6081
            $user->addRole('ROLE_SUPER_ADMIN');
6082
            self::getManager()->updateUser($user, true);
6083
        }
6084
    }
6085
6086
    public static function removeUserAdmin(User $user)
6087
    {
6088
        $userId = (int) $user->getId();
6089
        if (self::is_admin($userId)) {
6090
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
6091
            $sql = "DELETE FROM $table WHERE user_id = $userId";
6092
            Database::query($sql);
6093
            $user->removeRole('ROLE_SUPER_ADMIN');
6094
            self::getManager()->updateUser($user, true);
6095
        }
6096
    }
6097
6098
    /**
6099
     * @param string $from
6100
     * @param string $to
6101
     */
6102
    public static function update_all_user_languages($from, $to)
6103
    {
6104
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6105
        $from = Database::escape_string($from);
6106
        $to = Database::escape_string($to);
6107
6108
        if (!empty($to) && !empty($from)) {
6109
            $sql = "UPDATE $table_user SET language = '$to'
6110
                    WHERE language = '$from'";
6111
            Database::query($sql);
6112
        }
6113
    }
6114
6115
    /**
6116
     * Subscribe boss to students.
6117
     *
6118
     * @param int   $bossId                   The boss id
6119
     * @param array $usersId                  The users array
6120
     * @param bool  $deleteOtherAssignedUsers
6121
     *
6122
     * @return int Affected rows
6123
     */
6124
    public static function subscribeBossToUsers($bossId, $usersId, $deleteOtherAssignedUsers = true)
6125
    {
6126
        return self::subscribeUsersToUser(
6127
            $bossId,
6128
            $usersId,
6129
            USER_RELATION_TYPE_BOSS,
6130
            false,
6131
            $deleteOtherAssignedUsers
6132
        );
6133
    }
6134
6135
    /**
6136
     * @param int $userId
6137
     *
6138
     * @return bool
6139
     */
6140
    public static function removeAllBossFromStudent($userId)
6141
    {
6142
        $userId = (int) $userId;
6143
6144
        if (empty($userId)) {
6145
            return false;
6146
        }
6147
6148
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6149
        $sql = "DELETE FROM $userRelUserTable
6150
                WHERE user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
6151
        Database::query($sql);
6152
6153
        return true;
6154
    }
6155
6156
    /**
6157
     * It updates course relation type as EX-LEARNER if project name (extra field from user_edition_extra_field_to_check) is changed.
6158
     *
6159
     * @param $userId
6160
     * @param $extraValue
6161
     *
6162
     * @throws \Doctrine\DBAL\Exception
6163
     */
6164
    public static function updateCourseRelationTypeExLearner($userId, $extraValue)
6165
    {
6166
        if (false !== api_get_configuration_value('user_edition_extra_field_to_check')) {
6167
            $extraToCheck = api_get_configuration_value('user_edition_extra_field_to_check');
6168
6169
            // Get the old user extra value to check
6170
            $userExtra = UserManager::get_extra_user_data_by_field($userId, $extraToCheck);
6171
            if (isset($userExtra[$extraToCheck]) && $userExtra[$extraToCheck] != $extraValue) {
6172
                // it searchs the courses with the user old extravalue
6173
                $extraFieldValues = new ExtraFieldValue('course');
6174
                $extraItems = $extraFieldValues->get_item_id_from_field_variable_and_field_value($extraToCheck, $userExtra[$extraToCheck], false, false, true);
6175
                $coursesTocheck = [];
6176
                if (!empty($extraItems)) {
6177
                    foreach ($extraItems as $items) {
6178
                        $coursesTocheck[] = $items['item_id'];
6179
                    }
6180
                }
6181
6182
                // To check in main course
6183
                if (!empty($coursesTocheck)) {
6184
                    $tblCourseUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
6185
                    foreach ($coursesTocheck as $courseId) {
6186
                        $sql = "SELECT id FROM $tblCourseUser
6187
                                WHERE user_id = $userId AND c_id = $courseId";
6188
                        $rs = Database::query($sql);
6189
                        if (Database::num_rows($rs) > 0) {
6190
                            $id = Database::result($rs, 0, 0);
6191
                            $sql = "UPDATE $tblCourseUser SET relation_type = ".COURSE_EXLEARNER."
6192
                                    WHERE id = $id";
6193
                            Database::query($sql);
6194
                        }
6195
                    }
6196
                }
6197
6198
                // To check in sessions
6199
                if (!empty($coursesTocheck)) {
6200
                    $tblSessionCourseUser = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
6201
                    $tblSessionUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
6202
                    $sessionsToCheck = [];
6203
                    foreach ($coursesTocheck as $courseId) {
6204
                        $sql = "SELECT id, session_id FROM $tblSessionCourseUser
6205
                                WHERE user_id = $userId AND c_id = $courseId";
6206
                        $rs = Database::query($sql);
6207
                        if (Database::num_rows($rs) > 0) {
6208
                            $row = Database::fetch_array($rs);
6209
                            $id = $row['id'];
6210
                            $sessionId = $row['session_id'];
6211
                            $sql = "UPDATE $tblSessionCourseUser SET status = ".COURSE_EXLEARNER."
6212
                                    WHERE id = $id";
6213
                            Database::query($sql);
6214
                            $sessionsToCheck[] = $sessionId;
6215
                        }
6216
                    }
6217
                    // It checks if user is ex-learner in all courses in the session to update the session relation type
6218
                    if (!empty($sessionsToCheck)) {
6219
                        $sessionsToCheck = array_unique($sessionsToCheck);
6220
                        foreach ($sessionsToCheck as $sessionId) {
6221
                            $checkAll = Database::query("SELECT count(id) FROM $tblSessionCourseUser WHERE user_id = $userId AND session_id = $sessionId");
6222
                            $countAll = Database::result($checkAll, 0, 0);
6223
                            $checkExLearner = Database::query("SELECT count(id) FROM $tblSessionCourseUser WHERE status = ".COURSE_EXLEARNER." AND user_id = $userId AND session_id = $sessionId");
6224
                            $countExLearner = Database::result($checkExLearner, 0, 0);
6225
                            if ($countAll > 0 && $countAll == $countExLearner) {
6226
                                $sql = "UPDATE $tblSessionUser SET relation_type = ".COURSE_EXLEARNER."
6227
                                    WHERE user_id = $userId AND session_id = $sessionId";
6228
                                Database::query($sql);
6229
                            }
6230
                        }
6231
                    }
6232
                }
6233
                // To check users inside a class
6234
                $tblUserGroupRelUser = Database::get_main_table(TABLE_USERGROUP_REL_USER);
6235
                $tblUserGroupRelCourse = Database::get_main_table(TABLE_USERGROUP_REL_COURSE);
6236
                $tblUserGroupRelSession = Database::get_main_table(TABLE_USERGROUP_REL_SESSION);
6237
6238
                $rsUser = Database::query("SELECT usergroup_id FROM $tblUserGroupRelUser WHERE user_id = $userId");
6239
                if (Database::num_rows($rsUser) > 0) {
6240
                    while ($rowUser = Database::fetch_array($rsUser)) {
6241
                        $usergroupId = $rowUser['usergroup_id'];
6242
6243
                        // Count courses with exlearners
6244
                        $sqlC1 = "SELECT count(id) FROM $tblUserGroupRelCourse WHERE usergroup_id = $usergroupId";
6245
                        $rsCourses = Database::query($sqlC1);
6246
                        $countGroupCourses = Database::result($rsCourses, 0, 0);
6247
6248
                        $sqlC2 = "SELECT count(cu.id)
6249
                                FROM $tblCourseUser cu
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tblCourseUser does not seem to be defined for all execution paths leading up to this point.
Loading history...
6250
                                INNER JOIN $tblUserGroupRelCourse gc
6251
                                    ON gc.course_id = cu.c_id
6252
                                WHERE
6253
                                    cu.user_id = $userId AND
6254
                                    usergroup_id = $usergroupId AND
6255
                                    relation_type = ".COURSE_EXLEARNER;
6256
                        $rsExCourses = Database::query($sqlC2);
6257
                        $countExCourses = Database::result($rsExCourses, 0, 0);
6258
                        $checkedExCourses = $countGroupCourses > 0 && ($countExCourses == $countGroupCourses);
6259
6260
                        // Count sessions with exlearners
6261
                        $sqlS1 = "SELECT count(id) FROM $tblUserGroupRelSession WHERE usergroup_id = $usergroupId";
6262
                        $rsSessions = Database::query($sqlS1);
6263
                        $countGroupSessions = Database::result($rsSessions, 0, 0);
6264
6265
                        $sqlS2 = "SELECT count(su.id)
6266
                                FROM $tblSessionUser su
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tblSessionUser does not seem to be defined for all execution paths leading up to this point.
Loading history...
6267
                                INNER JOIN $tblUserGroupRelSession gs
6268
                                    ON gs.session_id = su.session_id
6269
                                WHERE
6270
                                    su.user_id = $userId AND
6271
                                    usergroup_id = $usergroupId AND
6272
                                    relation_type = ".COURSE_EXLEARNER;
6273
                        $rsExSessions = Database::query($sqlS2);
6274
                        $countExSessions = Database::result($rsExSessions, 0, 0);
6275
                        $checkedExSessions = $countGroupSessions > 0 && ($countExSessions == $countGroupSessions);
6276
6277
                        // it checks if usergroup user should be set to EXLEARNER
6278
                        $checkedExClassLearner = false;
6279
                        if ($countGroupCourses > 0 && $countGroupSessions == 0) {
6280
                            $checkedExClassLearner = $checkedExCourses;
6281
                        } elseif ($countGroupCourses == 0 && $countGroupSessions > 0) {
6282
                            $checkedExClassLearner = $checkedExSessions;
6283
                        } elseif ($countGroupCourses > 0 && $countGroupSessions > 0) {
6284
                            $checkedExClassLearner = ($checkedExCourses && $checkedExSessions);
6285
                        }
6286
6287
                        if ($checkedExClassLearner) {
6288
                            Database::query("UPDATE $tblUserGroupRelUser SET relation_type = ".COURSE_EXLEARNER." WHERE user_id = $userId AND usergroup_id = $usergroupId");
6289
                        }
6290
                    }
6291
                }
6292
            }
6293
        }
6294
    }
6295
6296
    /**
6297
     * Subscribe boss to students, if $bossList is empty then the boss list will be empty too.
6298
     *
6299
     * @param int   $studentId
6300
     * @param array $bossList
6301
     * @param bool  $sendNotification
6302
     *
6303
     * @return mixed Affected rows or false on failure
6304
     */
6305
    public static function subscribeUserToBossList(
6306
        $studentId,
6307
        $bossList,
6308
        $sendNotification = false
6309
    ) {
6310
        $inserted = 0;
6311
        if (!empty($bossList)) {
6312
            sort($bossList);
6313
            $studentId = (int) $studentId;
6314
            $studentInfo = api_get_user_info($studentId);
6315
6316
            if (empty($studentInfo)) {
6317
                return false;
6318
            }
6319
6320
            $previousBossList = self::getStudentBossList($studentId);
6321
            $previousBossList = !empty($previousBossList) ? array_column($previousBossList, 'boss_id') : [];
6322
            sort($previousBossList);
6323
6324
            // Boss list is the same, nothing changed.
6325
            if ($bossList == $previousBossList) {
6326
                return false;
6327
            }
6328
6329
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6330
            self::removeAllBossFromStudent($studentId);
6331
6332
            foreach ($bossList as $bossId) {
6333
                $bossId = (int) $bossId;
6334
                $bossInfo = api_get_user_info($bossId);
6335
6336
                if (empty($bossInfo)) {
6337
                    continue;
6338
                }
6339
6340
                $bossLanguage = $bossInfo['language'];
6341
6342
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
6343
                        VALUES ($studentId, $bossId, ".USER_RELATION_TYPE_BOSS.")";
6344
                $insertId = Database::query($sql);
6345
6346
                if ($insertId) {
6347
                    if ($sendNotification) {
6348
                        $name = $studentInfo['complete_name'];
6349
                        $url = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?student='.$studentId;
6350
                        $url = Display::url($url, $url);
6351
                        $subject = sprintf(
6352
                            get_lang('UserXHasBeenAssignedToBoss', false, $bossLanguage),
6353
                            $name
6354
                        );
6355
                        $message = sprintf(
6356
                            get_lang('UserXHasBeenAssignedToBossWithUrlX', false, $bossLanguage),
6357
                            $name,
6358
                            $url
6359
                        );
6360
                        MessageManager::send_message_simple(
6361
                            $bossId,
6362
                            $subject,
6363
                            $message
6364
                        );
6365
                    }
6366
                    $inserted++;
6367
                }
6368
            }
6369
        } else {
6370
            self::removeAllBossFromStudent($studentId);
6371
        }
6372
6373
        return $inserted;
6374
    }
6375
6376
    /**
6377
     * Get users followed by student boss.
6378
     *
6379
     * @param int    $userId
6380
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
6381
     * @param bool   $getOnlyUserId
6382
     * @param bool   $getSql
6383
     * @param bool   $getCount
6384
     * @param int    $from
6385
     * @param int    $numberItems
6386
     * @param int    $column
6387
     * @param string $direction
6388
     * @param int    $active
6389
     * @param string $lastConnectionDate
6390
     *
6391
     * @return array users
6392
     */
6393
    public static function getUsersFollowedByStudentBoss(
6394
        $userId,
6395
        $userStatus = 0,
6396
        $getOnlyUserId = false,
6397
        $getSql = false,
6398
        $getCount = false,
6399
        $from = null,
6400
        $numberItems = null,
6401
        $column = null,
6402
        $direction = null,
6403
        $active = null,
6404
        $lastConnectionDate = null
6405
    ) {
6406
        return self::getUsersFollowedByUser(
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::getUsersFol...tionDate, STUDENT_BOSS) also could return the type string which is incompatible with the documented return type array.
Loading history...
6407
            $userId,
6408
            $userStatus,
6409
            $getOnlyUserId,
6410
            $getSql,
6411
            $getCount,
6412
            $from,
6413
            $numberItems,
6414
            $column,
6415
            $direction,
6416
            $active,
6417
            $lastConnectionDate,
6418
            STUDENT_BOSS
6419
        );
6420
    }
6421
6422
    /**
6423
     * @return array
6424
     */
6425
    public static function getOfficialCodeGrouped()
6426
    {
6427
        $user = Database::get_main_table(TABLE_MAIN_USER);
6428
        $sql = "SELECT DISTINCT official_code
6429
                FROM $user
6430
                GROUP BY official_code";
6431
        $result = Database::query($sql);
6432
        $values = Database::store_result($result, 'ASSOC');
6433
        $result = [];
6434
        foreach ($values as $value) {
6435
            $result[$value['official_code']] = $value['official_code'];
6436
        }
6437
6438
        return $result;
6439
    }
6440
6441
    /**
6442
     * @param string $officialCode
6443
     *
6444
     * @return array
6445
     */
6446
    public static function getUsersByOfficialCode($officialCode)
6447
    {
6448
        $user = Database::get_main_table(TABLE_MAIN_USER);
6449
        $officialCode = Database::escape_string($officialCode);
6450
6451
        $sql = "SELECT DISTINCT id
6452
                FROM $user
6453
                WHERE official_code = '$officialCode'
6454
                ";
6455
        $result = Database::query($sql);
6456
6457
        $users = [];
6458
        while ($row = Database::fetch_array($result)) {
6459
            $users[] = $row['id'];
6460
        }
6461
6462
        return $users;
6463
    }
6464
6465
    /**
6466
     * Calc the expended time (in seconds) by a user in a course.
6467
     *
6468
     * @param int    $userId    The user id
6469
     * @param int    $courseId  The course id
6470
     * @param int    $sessionId Optional. The session id
6471
     * @param string $from      Optional. From date
6472
     * @param string $until     Optional. Until date
6473
     *
6474
     * @return int The time
6475
     */
6476
    public static function getTimeSpentInCourses(
6477
        $userId,
6478
        $courseId,
6479
        $sessionId = 0,
6480
        $from = '',
6481
        $until = ''
6482
    ) {
6483
        $userId = (int) $userId;
6484
        $sessionId = (int) $sessionId;
6485
6486
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6487
        $whereConditions = [
6488
            'user_id = ? ' => $userId,
6489
            'AND c_id = ? ' => $courseId,
6490
            'AND session_id = ? ' => $sessionId,
6491
        ];
6492
6493
        if (!empty($from) && !empty($until)) {
6494
            $whereConditions["AND (login_course_date >= '?' "] = $from;
6495
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
6496
        }
6497
6498
        $trackResult = Database::select(
6499
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
6500
            $trackCourseAccessTable,
6501
            [
6502
                'where' => $whereConditions,
6503
            ],
6504
            'first'
6505
        );
6506
6507
        if ($trackResult != false) {
6508
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
6509
        }
6510
6511
        return 0;
6512
    }
6513
6514
    /**
6515
     * Get the boss user ID from a followed user id.
6516
     *
6517
     * @param $userId
6518
     *
6519
     * @return bool
6520
     */
6521
    public static function getFirstStudentBoss($userId)
6522
    {
6523
        $userId = (int) $userId;
6524
        if ($userId > 0) {
6525
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6526
            $row = Database::select(
6527
                'DISTINCT friend_user_id AS boss_id',
6528
                $userRelTable,
6529
                [
6530
                    'where' => [
6531
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
6532
                            $userId,
6533
                            USER_RELATION_TYPE_BOSS,
6534
                        ],
6535
                    ],
6536
                ]
6537
            );
6538
            if (!empty($row)) {
6539
                return $row[0]['boss_id'];
6540
            }
6541
        }
6542
6543
        return false;
6544
    }
6545
6546
    /**
6547
     * Get the boss user ID from a followed user id.
6548
     *
6549
     * @param int $userId student id
6550
     *
6551
     * @return array
6552
     */
6553
    public static function getStudentBossList($userId)
6554
    {
6555
        $userId = (int) $userId;
6556
6557
        if ($userId > 0) {
6558
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6559
6560
            return Database::select(
6561
                'DISTINCT friend_user_id AS boss_id',
6562
                $userRelTable,
6563
                [
6564
                    'where' => [
6565
                        'user_id = ? AND relation_type = ? ' => [
6566
                            $userId,
6567
                            USER_RELATION_TYPE_BOSS,
6568
                        ],
6569
                    ],
6570
                ]
6571
            );
6572
        }
6573
6574
        return [];
6575
    }
6576
6577
    /**
6578
     * @param int $bossId
6579
     * @param int $studentId
6580
     *
6581
     * @return bool
6582
     */
6583
    public static function userIsBossOfStudent($bossId, $studentId)
6584
    {
6585
        $result = false;
6586
        $bossList = self::getStudentBossList($studentId);
6587
        if (!empty($bossList)) {
6588
            $bossList = array_column($bossList, 'boss_id');
6589
            if (in_array($bossId, $bossList)) {
6590
                $result = true;
6591
            }
6592
        }
6593
6594
        return $result;
6595
    }
6596
6597
    /**
6598
     * Displays the name of the user and makes the link to the user profile.
6599
     *
6600
     * @param array $userInfo
6601
     *
6602
     * @return string
6603
     */
6604
    public static function getUserProfileLink($userInfo)
6605
    {
6606
        if (isset($userInfo) && isset($userInfo['user_id'])) {
6607
            return Display::url(
6608
                $userInfo['complete_name_with_username'],
6609
                $userInfo['profile_url']
6610
            );
6611
        }
6612
6613
        return get_lang('Anonymous');
6614
    }
6615
6616
    /**
6617
     * Get users whose name matches $firstname and $lastname.
6618
     *
6619
     * @param string $firstname Firstname to search
6620
     * @param string $lastname  Lastname to search
6621
     *
6622
     * @return array The user list
6623
     */
6624
    public static function getUsersByName($firstname, $lastname)
6625
    {
6626
        $firstname = Database::escape_string($firstname);
6627
        $lastname = Database::escape_string($lastname);
6628
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
6629
6630
        $sql = <<<SQL
6631
            SELECT id, username, lastname, firstname
6632
            FROM $userTable
6633
            WHERE
6634
                firstname LIKE '$firstname%' AND
6635
                lastname LIKE '$lastname%'
6636
SQL;
6637
        $result = Database::query($sql);
6638
        $users = [];
6639
        while ($resultData = Database::fetch_object($result)) {
6640
            $users[] = $resultData;
6641
        }
6642
6643
        return $users;
6644
    }
6645
6646
    /**
6647
     * @param int $optionSelected
6648
     *
6649
     * @return string
6650
     */
6651
    public static function getUserSubscriptionTab($optionSelected = 1)
6652
    {
6653
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
6654
        if (($allowAdmin === 'true' && api_is_allowed_to_edit()) ||
6655
            api_is_platform_admin()
6656
        ) {
6657
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
6658
6659
            $headers = [
6660
                [
6661
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
6662
                    'content' => get_lang('Students'),
6663
                ],
6664
                [
6665
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
6666
                    'content' => get_lang('Teachers'),
6667
                ],
6668
                /*[
6669
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
6670
                    'content' => get_lang('Students'),
6671
                ],
6672
                [
6673
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
6674
                    'content' => get_lang('Teachers'),
6675
                ],*/
6676
                [
6677
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
6678
                    'content' => get_lang('Groups'),
6679
                ],
6680
                'classes' => [
6681
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
6682
                    'content' => get_lang('Classes'),
6683
                ],
6684
            ];
6685
6686
            if (api_get_configuration_value('session_classes_tab_disable')
6687
                && !api_is_platform_admin()
6688
                && api_get_session_id()
6689
            ) {
6690
                unset($headers['classes']);
6691
            }
6692
6693
            return Display::tabsOnlyLink($headers, $optionSelected);
6694
        }
6695
6696
        return '';
6697
    }
6698
6699
    /**
6700
     * Make sure this function is protected because it does NOT check password!
6701
     *
6702
     * This function defines globals.
6703
     *
6704
     * @param int  $userId
6705
     * @param bool $checkIfUserCanLoginAs
6706
     *
6707
     * @return bool
6708
     *
6709
     * @author Evie Embrechts
6710
     * @author Yannick Warnier <[email protected]>
6711
     */
6712
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
6713
    {
6714
        $userId = (int) $userId;
6715
        $userInfo = api_get_user_info($userId);
6716
6717
        // Check if the user is allowed to 'login_as'
6718
        $canLoginAs = true;
6719
        if ($checkIfUserCanLoginAs) {
6720
            $canLoginAs = api_can_login_as($userId);
6721
        }
6722
6723
        if (!$canLoginAs || empty($userInfo)) {
6724
            return false;
6725
        }
6726
6727
        if ($userId) {
6728
            $logInfo = [
6729
                'tool' => 'logout',
6730
                'tool_id' => 0,
6731
                'tool_id_detail' => 0,
6732
                'action' => '',
6733
                'info' => 'Change user (login as)',
6734
            ];
6735
            Event::registerLog($logInfo);
0 ignored issues
show
Bug introduced by
The method registerLog() does not exist on Event. ( Ignorable by Annotation )

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

6735
            Event::/** @scrutinizer ignore-call */ 
6736
                   registerLog($logInfo);

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

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

Loading history...
6736
6737
            // Logout the current user
6738
            self::loginDelete(api_get_user_id());
6739
6740
            Session::erase('_user');
6741
            Session::erase('is_platformAdmin');
6742
            Session::erase('is_allowedCreateCourse');
6743
            Session::erase('_uid');
6744
6745
            // Cleaning session variables
6746
            $_user['firstName'] = $userInfo['firstname'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$_user was never initialized. Although not strictly required by PHP, it is generally a good practice to add $_user = array(); before regardless.
Loading history...
6747
            $_user['lastName'] = $userInfo['lastname'];
6748
            $_user['mail'] = $userInfo['email'];
6749
            $_user['official_code'] = $userInfo['official_code'];
6750
            $_user['picture_uri'] = $userInfo['picture_uri'];
6751
            $_user['user_id'] = $userId;
6752
            $_user['id'] = $userId;
6753
            $_user['status'] = $userInfo['status'];
6754
6755
            // Filling session variables with new data
6756
            Session::write('_uid', $userId);
6757
            Session::write('_user', $userInfo);
6758
            Session::write('is_platformAdmin', (bool) self::is_admin($userId));
6759
            Session::write('is_allowedCreateCourse', $userInfo['status'] == 1);
6760
            // will be useful later to know if the user is actually an admin or not (example reporting)
6761
            Session::write('login_as', true);
6762
            $logInfo = [
6763
                'tool' => 'login',
6764
                'tool_id' => 0,
6765
                'tool_id_detail' => 0,
6766
                'info' => $userId,
6767
            ];
6768
            Event::registerLog($logInfo);
6769
6770
            return true;
6771
        }
6772
6773
        return false;
6774
    }
6775
6776
    /**
6777
     * Remove all login records from the track_e_online stats table,
6778
     * for the given user ID.
6779
     *
6780
     * @param int $userId User ID
6781
     */
6782
    public static function loginDelete($userId)
6783
    {
6784
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6785
        $userId = (int) $userId;
6786
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
6787
        Database::query($query);
6788
    }
6789
6790
    /**
6791
     * Login as first admin user registered in the platform.
6792
     *
6793
     * @return array
6794
     */
6795
    public static function logInAsFirstAdmin()
6796
    {
6797
        $adminList = self::get_all_administrators();
6798
6799
        if (!empty($adminList)) {
6800
            $userInfo = current($adminList);
6801
            if (!empty($userInfo)) {
6802
                $result = self::loginAsUser($userInfo['user_id'], false);
6803
                if ($result && api_is_platform_admin()) {
6804
                    return api_get_user_info();
0 ignored issues
show
Bug Best Practice introduced by
The expression return api_get_user_info() could also return false which is incompatible with the documented return type array. Did you maybe forget to handle an error condition?

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

Loading history...
6805
                }
6806
            }
6807
        }
6808
6809
        return [];
6810
    }
6811
6812
    /**
6813
     * Check if user is teacher of a student based in their courses.
6814
     *
6815
     * @param $teacherId
6816
     * @param $studentId
6817
     *
6818
     * @return array
6819
     */
6820
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
6821
    {
6822
        $courses = CourseManager::getCoursesFollowedByUser(
6823
            $teacherId,
6824
            COURSEMANAGER
6825
        );
6826
        if (empty($courses)) {
6827
            return false;
6828
        }
6829
6830
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
6831
        if (empty($coursesFromUser)) {
6832
            return false;
6833
        }
6834
6835
        $coursesCodeList = array_column($courses, 'code');
6836
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
6837
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
6838
        $commonCourses = array_filter($commonCourses);
6839
6840
        if (!empty($commonCourses)) {
6841
            return $commonCourses;
6842
        }
6843
6844
        return [];
6845
    }
6846
6847
    /**
6848
     * @param int $teacherId
6849
     * @param int $studentId
6850
     *
6851
     * @return bool
6852
     */
6853
    public static function isTeacherOfStudent($teacherId, $studentId)
6854
    {
6855
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
6856
            $teacherId,
6857
            $studentId
6858
        );
6859
6860
        if (!empty($courses)) {
6861
            return true;
6862
        }
6863
6864
        return false;
6865
    }
6866
6867
    /**
6868
     * Send user confirmation mail.
6869
     *
6870
     * @throws Exception
6871
     */
6872
    public static function sendUserConfirmationMail(User $user)
6873
    {
6874
        $uniqueId = api_get_unique_id();
6875
        $user->setConfirmationToken($uniqueId);
6876
6877
        Database::getManager()->persist($user);
6878
        Database::getManager()->flush();
6879
6880
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
6881
6882
        // Check if the user was originally set for an automated subscription to a course or session
6883
        $courseCodeToRedirect = Session::read('course_redirect');
6884
        $sessionToRedirect = Session::read('session_redirect');
6885
        if (!empty($courseCodeToRedirect)) {
6886
            $url .= '&c='.$courseCodeToRedirect;
6887
        }
6888
        if (!empty($sessionToRedirect)) {
6889
            $url .= '&s='.$sessionToRedirect;
6890
        }
6891
        $mailSubject = get_lang('RegistrationConfirmation');
6892
        $mailBody = get_lang('RegistrationConfirmationEmailMessage')
6893
            .PHP_EOL
6894
            .Display::url($url, $url);
6895
6896
        api_mail_html(
6897
            self::formatUserFullName($user),
6898
            $user->getEmail(),
6899
            $mailSubject,
6900
            $mailBody
6901
        );
6902
        Display::addFlash(Display::return_message(get_lang('CheckYourEmailAndFollowInstructions')));
6903
    }
6904
6905
    /**
6906
     * Anonymize a user. Replace personal info by anonymous info.
6907
     *
6908
     * @param int  $userId   User id
6909
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
6910
     *
6911
     * @throws \Exception
6912
     *
6913
     * @return bool
6914
     * @assert (0) === false
6915
     */
6916
    public static function anonymize($userId, $deleteIP = true)
6917
    {
6918
        global $debug;
6919
6920
        $userId = (int) $userId;
6921
6922
        if (empty($userId)) {
6923
            return false;
6924
        }
6925
6926
        $em = Database::getManager();
6927
        $user = api_get_user_entity($userId);
6928
        $uniqueId = uniqid('anon', true);
6929
        $user
6930
            ->setFirstname($uniqueId)
6931
            ->setLastname($uniqueId)
6932
            ->setBiography('')
6933
            ->setAddress('')
6934
            ->setCurriculumItems(null)
6935
            ->setDateOfBirth(null)
6936
            ->setCompetences('')
6937
            ->setDiplomas('')
6938
            ->setOpenarea('')
6939
            ->setTeach('')
6940
            ->setProductions(null)
6941
            ->setOpenid('')
6942
            ->setEmailCanonical($uniqueId.'@example.com')
6943
            ->setEmail($uniqueId.'@example.com')
6944
            ->setUsername($uniqueId)
6945
            ->setUsernameCanonical($uniqueId)
6946
            ->setPhone('')
6947
            ->setOfficialCode('')
6948
        ;
6949
6950
        self::deleteUserPicture($userId);
6951
        self::cleanUserRequestsOfRemoval($userId);
6952
6953
        // The IP address is a border-case personal data, as it does
6954
        // not directly allow for personal identification (it is not
6955
        // a completely safe value in most countries - the IP could
6956
        // be used by neighbours and crackers)
6957
        if ($deleteIP) {
6958
            $substitute = '127.0.0.1';
6959
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
6960
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
6961
            $res = Database::query($sql);
6962
            if ($res === false && $debug > 0) {
6963
                error_log("Could not anonymize IP address for user $userId ($sql)");
6964
            }
6965
6966
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6967
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6968
            $res = Database::query($sql);
6969
            if ($res === false && $debug > 0) {
6970
                error_log("Could not anonymize IP address for user $userId ($sql)");
6971
            }
6972
6973
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6974
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE exe_user_id = $userId";
6975
            $res = Database::query($sql);
6976
            if ($res === false && $debug > 0) {
6977
                error_log("Could not anonymize IP address for user $userId ($sql)");
6978
            }
6979
6980
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6981
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6982
            $res = Database::query($sql);
6983
            if ($res === false && $debug > 0) {
6984
                error_log("Could not anonymize IP address for user $userId ($sql)");
6985
            }
6986
6987
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6988
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6989
            $res = Database::query($sql);
6990
            if ($res === false && $debug > 0) {
6991
                error_log("Could not anonymize IP address for user $userId ($sql)");
6992
            }
6993
6994
            $table = Database::get_course_table(TABLE_WIKI);
6995
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6996
            $res = Database::query($sql);
6997
            if ($res === false && $debug > 0) {
6998
                error_log("Could not anonymize IP address for user $userId ($sql)");
6999
            }
7000
7001
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
7002
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
7003
            $res = Database::query($sql);
7004
            if ($res === false && $debug > 0) {
7005
                error_log("Could not anonymize IP address for user $userId ($sql)");
7006
            }
7007
7008
            $table = Database::get_course_table(TABLE_WIKI);
7009
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
7010
            $res = Database::query($sql);
7011
            if ($res === false && $debug > 0) {
7012
                error_log("Could not anonymize IP address for user $userId ($sql)");
7013
            }
7014
        }
7015
        $em->persist($user);
7016
        $em->flush($user);
7017
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
7018
7019
        return true;
7020
    }
7021
7022
    /**
7023
     * @param int $userId
7024
     *
7025
     * @throws Exception
7026
     *
7027
     * @return string
7028
     */
7029
    public static function anonymizeUserWithVerification($userId)
7030
    {
7031
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
7032
7033
        $message = '';
7034
        if (api_is_platform_admin() ||
7035
            ($allowDelete && api_is_session_admin())
7036
        ) {
7037
            $userToUpdateInfo = api_get_user_info($userId);
7038
            $currentUserId = api_get_user_id();
7039
7040
            if ($userToUpdateInfo &&
7041
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
7042
            ) {
7043
                if ($userId != $currentUserId &&
7044
                    self::anonymize($userId)
7045
                ) {
7046
                    $message = Display::return_message(
7047
                        sprintf(get_lang('UserXAnonymized'), $userToUpdateInfo['complete_name_with_username']),
7048
                        'confirmation'
7049
                    );
7050
                } else {
7051
                    $message = Display::return_message(
7052
                        sprintf(get_lang('CannotAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
7053
                        'error'
7054
                    );
7055
                }
7056
            } else {
7057
                $message = Display::return_message(
7058
                    sprintf(get_lang('NoPermissionToAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
7059
                    'error'
7060
                );
7061
            }
7062
        }
7063
7064
        return $message;
7065
    }
7066
7067
    /**
7068
     * @param int $userId
7069
     *
7070
     * @throws Exception
7071
     *
7072
     * @return string
7073
     */
7074
    public static function deleteUserWithVerification($userId)
7075
    {
7076
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
7077
        $message = Display::return_message(get_lang('CannotDeleteUser'), 'error');
7078
        $userToUpdateInfo = api_get_user_info($userId);
7079
7080
        // User must exist.
7081
        if (empty($userToUpdateInfo)) {
7082
            return $message;
7083
        }
7084
7085
        $currentUserId = api_get_user_id();
7086
7087
        // Cannot delete myself.
7088
        if ($userId == $currentUserId) {
7089
            return $message;
7090
        }
7091
7092
        if (api_is_platform_admin() ||
7093
            ($allowDelete && api_is_session_admin())
7094
        ) {
7095
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
7096
                if (self::delete_user($userId)) {
7097
                    $message = Display::return_message(
7098
                        get_lang('UserDeleted').': '.$userToUpdateInfo['complete_name_with_username'],
7099
                        'confirmation'
7100
                    );
7101
                } else {
7102
                    $message = Display::return_message(get_lang('CannotDeleteUserBecauseOwnsCourse'), 'error');
7103
                }
7104
            }
7105
        }
7106
7107
        return $message;
7108
    }
7109
7110
    /**
7111
     * @return array
7112
     */
7113
    public static function createDataPrivacyExtraFields()
7114
    {
7115
        self::create_extra_field(
7116
            'request_for_legal_agreement_consent_removal_justification',
7117
            1, //text
7118
            'Request for legal agreement consent removal justification	',
7119
            ''
7120
        );
7121
7122
        self::create_extra_field(
7123
            'request_for_delete_account_justification',
7124
            1, //text
7125
            'Request for delete account justification',
7126
            ''
7127
        );
7128
7129
        $extraFieldId = self::create_extra_field(
7130
            'request_for_legal_agreement_consent_removal',
7131
            1, //text
7132
            'Request for legal agreement consent removal',
7133
            ''
7134
        );
7135
7136
        $extraFieldIdDeleteAccount = self::create_extra_field(
7137
            'request_for_delete_account',
7138
            1, //text
7139
            'Request for delete user account',
7140
            ''
7141
        );
7142
7143
        return [
7144
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
7145
            'delete_legal' => $extraFieldId,
7146
        ];
7147
    }
7148
7149
    /**
7150
     * @param int $userId
7151
     */
7152
    public static function cleanUserRequestsOfRemoval($userId)
7153
    {
7154
        $userId = (int) $userId;
7155
7156
        $extraFieldValue = new ExtraFieldValue('user');
7157
        $extraFieldsToDelete = [
7158
            'legal_accept',
7159
            'request_for_legal_agreement_consent_removal',
7160
            'request_for_legal_agreement_consent_removal_justification',
7161
            'request_for_delete_account_justification', // just in case delete also this
7162
            'request_for_delete_account',
7163
        ];
7164
7165
        foreach ($extraFieldsToDelete as $variable) {
7166
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
7167
                $userId,
7168
                $variable
7169
            );
7170
            if ($value && isset($value['id'])) {
7171
                $extraFieldValue->delete($value['id']);
7172
            }
7173
        }
7174
    }
7175
7176
    /**
7177
     * @param int $searchYear
7178
     *
7179
     * @throws Exception
7180
     *
7181
     * @return array
7182
     */
7183
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
7184
    {
7185
        $timezone = new DateTimeZone(api_get_timezone());
7186
7187
        $sessions = [];
7188
        if (DRH == $userInfo['status']) {
7189
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
7190
        } elseif (api_is_platform_admin(true)) {
7191
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
7192
        } else {
7193
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
7194
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
7195
7196
            foreach ($sessionsByCategory as $sessionsInCategory) {
7197
                $sessions = array_merge($sessions, $sessionsInCategory);
7198
            }
7199
        }
7200
7201
        $sessions = array_map(
7202
            function ($sessionInfo) {
7203
                if (!isset($sessionInfo['session_id'])) {
7204
                    $sessionInfo['session_id'] = $sessionInfo['id'];
7205
                }
7206
                if (!isset($sessionInfo['session_name'])) {
7207
                    $sessionInfo['session_name'] = $sessionInfo['name'];
7208
                }
7209
7210
                return $sessionInfo;
7211
            },
7212
            $sessions
7213
        );
7214
7215
        $calendarSessions = [];
7216
7217
        foreach ($sessions as $sessionInfo) {
7218
            if (!empty($sessionInfo['duration'])) {
7219
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
7220
                    $sessionInfo['session_id'],
7221
                    $userInfo['id']
7222
                );
7223
7224
                if (empty($courseAccess)) {
7225
                    continue;
7226
                }
7227
7228
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
7229
                $lastAccessDate = clone $firstAcessDate;
7230
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
7231
7232
                $firstAccessYear = (int) $firstAcessDate->format('Y');
7233
                $lastAccessYear = (int) $lastAccessDate->format('Y');
7234
7235
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
7236
                    $calendarSessions[$sessionInfo['session_id']] = [
7237
                        'name' => $sessionInfo['session_name'],
7238
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
7239
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
7240
                    ];
7241
                }
7242
7243
                continue;
7244
            }
7245
7246
            $accessStartDate = !empty($sessionInfo['access_start_date'])
7247
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
7248
                : null;
7249
            $accessEndDate = !empty($sessionInfo['access_end_date'])
7250
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
7251
                : null;
7252
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
7253
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
7254
7255
            $isValid = false;
7256
7257
            if ($accessStartYear && $accessEndYear) {
7258
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
7259
                    $isValid = true;
7260
                }
7261
            }
7262
7263
            if ($accessStartYear && !$accessEndYear) {
7264
                if ($accessStartYear == $searchYear) {
7265
                    $isValid = true;
7266
                }
7267
            }
7268
7269
            if (!$accessStartYear && $accessEndYear) {
7270
                if ($accessEndYear == $searchYear) {
7271
                    $isValid = true;
7272
                }
7273
            }
7274
7275
            if ($isValid) {
7276
                $calendarSessions[$sessionInfo['session_id']] = [
7277
                    'name' => $sessionInfo['session_name'],
7278
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
7279
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
7280
                ];
7281
            }
7282
        }
7283
7284
        return $calendarSessions;
7285
    }
7286
7287
    /**
7288
     * Get sessions info for planification calendar.
7289
     *
7290
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
7291
     * @param int   $searchYear
7292
     *
7293
     * @throws Exception
7294
     *
7295
     * @return array
7296
     */
7297
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
7298
    {
7299
        $timezone = new DateTimeZone(api_get_timezone());
7300
        $calendar = [];
7301
7302
        foreach ($sessionsList as $sessionId => $sessionInfo) {
7303
            $startDate = $sessionInfo['access_start_date']
7304
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
7305
                : null;
7306
            $endDate = $sessionInfo['access_end_date']
7307
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
7308
                : null;
7309
7310
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
7311
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
7312
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
7313
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
7314
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
7315
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
7316
7317
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
7318
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
7319
7320
            $calendar[] = [
7321
                'id' => $sessionId,
7322
                'name' => $sessionInfo['name'],
7323
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
7324
                'start_in_last_year' => $startYear < $searchYear,
7325
                'end_in_next_year' => $endYear > $searchYear,
7326
                'no_start' => !$startWeek,
7327
                'no_end' => !$endWeek,
7328
                'start' => $start,
7329
                'duration' => $duration > 0 ? $duration : 1,
7330
            ];
7331
        }
7332
7333
        usort(
7334
            $calendar,
7335
            function ($sA, $sB) {
7336
                if ($sA['start'] == $sB['start']) {
7337
                    return 0;
7338
                }
7339
7340
                if ($sA['start'] < $sB['start']) {
7341
                    return -1;
7342
                }
7343
7344
                return 1;
7345
            }
7346
        );
7347
7348
        return $calendar;
7349
    }
7350
7351
    /**
7352
     * Return the user's full name. Optionally with the username.
7353
     *
7354
     * @param bool $includeUsername Optional. By default username is not included.
7355
     *
7356
     * @return string
7357
     */
7358
    public static function formatUserFullName(User $user, $includeUsername = false)
7359
    {
7360
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
7361
7362
        if ($includeUsername && api_get_configuration_value('hide_username_with_complete_name') !== true) {
7363
            $username = $user->getUsername();
7364
7365
            return "$fullName ($username)";
7366
        }
7367
7368
        return $fullName;
7369
    }
7370
7371
    /**
7372
     * @param int $userId
7373
     *
7374
     * @return array
7375
     */
7376
    public static function getUserCareers($userId)
7377
    {
7378
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7379
        $tableCareer = Database::get_main_table(TABLE_CAREER);
7380
        $userId = (int) $userId;
7381
7382
        $sql = "SELECT c.id, c.name
7383
                FROM $table uc
7384
                INNER JOIN $tableCareer c
7385
                ON uc.career_id = c.id
7386
                WHERE user_id = $userId
7387
                ORDER BY uc.created_at
7388
                ";
7389
        $result = Database::query($sql);
7390
7391
        return Database::store_result($result, 'ASSOC');
7392
    }
7393
7394
    /**
7395
     * @param int $userId
7396
     * @param int $careerId
7397
     */
7398
    public static function addUserCareer($userId, $careerId)
7399
    {
7400
        if (!api_get_configuration_value('allow_career_users')) {
7401
            return false;
7402
        }
7403
7404
        if (self::userHasCareer($userId, $careerId) === false) {
7405
            $params = [
7406
                'user_id' => $userId,
7407
                'career_id' => $careerId,
7408
                'created_at' => api_get_utc_datetime(),
7409
                'updated_at' => api_get_utc_datetime(),
7410
            ];
7411
            $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7412
            Database::insert($table, $params);
7413
        }
7414
7415
        return true;
7416
    }
7417
7418
    /**
7419
     * @param int   $userCareerId
7420
     * @param array $data
7421
     *
7422
     * @return bool
7423
     */
7424
    public static function updateUserCareer($userCareerId, $data)
7425
    {
7426
        if (!api_get_configuration_value('allow_career_users')) {
7427
            return false;
7428
        }
7429
7430
        $params = ['extra_data' => $data, 'updated_at' => api_get_utc_datetime()];
7431
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7432
        Database::update(
7433
            $table,
7434
            $params,
7435
            ['id = ?' => (int) $userCareerId]
7436
        );
7437
7438
        return true;
7439
    }
7440
7441
    /**
7442
     * @param int $userId
7443
     * @param int $careerId
7444
     *
7445
     * @return array
7446
     */
7447
    public static function getUserCareer($userId, $careerId)
7448
    {
7449
        $userId = (int) $userId;
7450
        $careerId = (int) $careerId;
7451
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7452
7453
        $sql = "SELECT * FROM $table WHERE user_id = $userId AND career_id = $careerId";
7454
        $result = Database::query($sql);
7455
7456
        return Database::fetch_array($result, 'ASSOC');
7457
    }
7458
7459
    /**
7460
     * @param int $userId
7461
     * @param int $careerId
7462
     *
7463
     * @return bool
7464
     */
7465
    public static function userHasCareer($userId, $careerId)
7466
    {
7467
        $userId = (int) $userId;
7468
        $careerId = (int) $careerId;
7469
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7470
7471
        $sql = "SELECT id FROM $table WHERE user_id = $userId AND career_id = $careerId";
7472
        $result = Database::query($sql);
7473
7474
        return Database::num_rows($result) > 0;
7475
    }
7476
7477
    /**
7478
     * @param int $userId
7479
     *
7480
     * @throws Exception
7481
     */
7482
    public static function deleteUserFiles($userId)
7483
    {
7484
        $path = self::getUserPathById($userId, 'system');
7485
7486
        $fs = new Filesystem();
7487
        $fs->remove($path);
7488
    }
7489
7490
    public static function redirectToResetPassword($userId)
7491
    {
7492
        if (!api_get_configuration_value('force_renew_password_at_first_login')) {
7493
            return;
7494
        }
7495
7496
        $askPassword = self::get_extra_user_data_by_field(
7497
            $userId,
7498
            'ask_new_password'
7499
        );
7500
7501
        if (!empty($askPassword) && isset($askPassword['ask_new_password']) &&
7502
            1 === (int) $askPassword['ask_new_password']
7503
        ) {
7504
            $uniqueId = api_get_unique_id();
7505
            $userObj = api_get_user_entity($userId);
7506
7507
            $userObj->setConfirmationToken($uniqueId);
7508
            $userObj->setPasswordRequestedAt(new \DateTime());
7509
7510
            Database::getManager()->persist($userObj);
7511
            Database::getManager()->flush();
7512
7513
            $url = api_get_path(WEB_CODE_PATH).'auth/reset.php?token='.$uniqueId;
7514
            api_location($url);
7515
        }
7516
    }
7517
7518
    /**
7519
     * It returns the list of user status available.
7520
     *
7521
     * @return array
7522
     */
7523
    public static function getUserStatusList()
7524
    {
7525
        $userStatusConfig = [];
7526
        // it gets the roles to show in creation/edition user
7527
        if (true === api_get_configuration_value('user_status_show_options_enabled')) {
7528
            $userStatusConfig = api_get_configuration_value('user_status_show_option');
7529
        }
7530
        // it gets the roles to show in creation/edition user (only for admins)
7531
        if (true === api_get_configuration_value('user_status_option_only_for_admin_enabled') && api_is_platform_admin()) {
7532
            $userStatusConfig = api_get_configuration_value('user_status_option_show_only_for_admin');
7533
        }
7534
7535
        $status = [];
7536
        if (!empty($userStatusConfig)) {
7537
            $statusLang = api_get_status_langvars();
7538
            foreach ($userStatusConfig as $role => $enabled) {
7539
                if ($enabled) {
7540
                    $constStatus = constant($role);
7541
                    $status[$constStatus] = $statusLang[$constStatus];
7542
                }
7543
            }
7544
        } else {
7545
            $status[COURSEMANAGER] = get_lang('Teacher');
7546
            $status[STUDENT] = get_lang('Learner');
7547
            $status[DRH] = get_lang('Drh');
7548
            $status[SESSIONADMIN] = get_lang('SessionsAdmin');
7549
            $status[STUDENT_BOSS] = get_lang('RoleStudentBoss');
7550
            $status[INVITEE] = get_lang('Invitee');
7551
        }
7552
7553
        return $status;
7554
    }
7555
7556
    /**
7557
     * Get the expiration date by user status from configuration value.
7558
     *
7559
     * @param $status
7560
     *
7561
     * @throws Exception
7562
     *
7563
     * @return array
7564
     */
7565
    public static function getExpirationDateByRole($status)
7566
    {
7567
        $status = (int) $status;
7568
        $nbDaysByRole = api_get_configuration_value('user_number_of_days_for_default_expiration_date_per_role');
7569
        $dates = [];
7570
        if (!empty($nbDaysByRole)) {
7571
            $date = new DateTime();
7572
            foreach ($nbDaysByRole as $strVariable => $nDays) {
7573
                $constStatus = constant($strVariable);
7574
                if ($status == $constStatus) {
7575
                    $duration = "P{$nDays}D";
7576
                    $date->add(new DateInterval($duration));
7577
                    $newExpirationDate = $date->format('Y-m-d H:i');
7578
                    $formatted = api_format_date($newExpirationDate, DATE_TIME_FORMAT_LONG_24H);
7579
                    $dates = ['formatted' => $formatted, 'date' => $newExpirationDate];
7580
                }
7581
            }
7582
        }
7583
7584
        return $dates;
7585
    }
7586
7587
    /**
7588
     * @return EncoderFactory
7589
     */
7590
    private static function getEncoderFactory()
7591
    {
7592
        $encryption = self::getPasswordEncryption();
7593
        $encoders = [
7594
            'Chamilo\\UserBundle\\Entity\\User' => new \Chamilo\UserBundle\Security\Encoder($encryption),
7595
        ];
7596
7597
        return new EncoderFactory($encoders);
7598
    }
7599
7600
    /**
7601
     * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface
7602
     */
7603
    private static function getEncoder(User $user)
7604
    {
7605
        $encoderFactory = self::getEncoderFactory();
7606
7607
        return $encoderFactory->getEncoder($user);
7608
    }
7609
7610
    /**
7611
     * Disables or enables a user.
7612
     *
7613
     * @param int $user_id
7614
     * @param int $active  Enable or disable
7615
     *
7616
     * @return bool True on success, false on failure
7617
     * @assert (-1,0) === false
7618
     * @assert (1,1) === true
7619
     */
7620
    private static function change_active_state($user_id, $active)
7621
    {
7622
        $user_id = (int) $user_id;
7623
        $active = (int) $active;
7624
7625
        if (empty($user_id)) {
7626
            return false;
7627
        }
7628
7629
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
7630
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
7631
        $r = Database::query($sql);
7632
        $ev = LOG_USER_DISABLE;
7633
        if ($active == 1) {
7634
            $ev = LOG_USER_ENABLE;
7635
        }
7636
        if ($r !== false) {
7637
            Event::addEvent($ev, LOG_USER_ID, $user_id);
7638
        }
7639
7640
        return $r;
7641
    }
7642
7643
    /**
7644
     * Get either a Gravatar URL or complete image tag for a specified email address.
7645
     *
7646
     * @param string $email The email address
7647
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
7648
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
7649
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
7650
     * @param bool   $img   True to return a complete IMG tag False for just the URL
7651
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
7652
     *
7653
     * @return string containing either just a URL or a complete image tag
7654
     * @source http://gravatar.com/site/implement/images/php/
7655
     */
7656
    private static function getGravatar(
7657
        $email,
7658
        $s = 80,
7659
        $d = 'mm',
7660
        $r = 'g',
7661
        $img = false,
7662
        $atts = []
7663
    ) {
7664
        $url = 'http://www.gravatar.com/avatar/';
7665
        if (!empty($_SERVER['HTTPS'])) {
7666
            $url = 'https://secure.gravatar.com/avatar/';
7667
        }
7668
        $url .= md5(strtolower(trim($email)));
7669
        $url .= "?s=$s&d=$d&r=$r";
7670
        if ($img) {
7671
            $url = '<img src="'.$url.'"';
7672
            foreach ($atts as $key => $val) {
7673
                $url .= ' '.$key.'="'.$val.'"';
7674
            }
7675
            $url .= ' />';
7676
        }
7677
7678
        return $url;
7679
    }
7680
}
7681