Passed
Push — 1.11.x ( b4db80...296215 )
by Angel Fernando Quiroz
08:39 queued 12s
created

UserManager::getAllowedRolesAsTeacher()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 5
rs 10
cc 1
nc 1
nop 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\CoreBundle\Entity\TrackELoginAttempt;
11
use Chamilo\UserBundle\Entity\User;
12
use Chamilo\UserBundle\Repository\UserRepository;
13
use ChamiloSession as Session;
14
use Symfony\Component\Filesystem\Filesystem;
15
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
16
17
/**
18
 * Class UserManager.
19
 *
20
 * This library provides functions for user management.
21
 * Include/require it in your code to use its functionality.
22
 *
23
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
24
 */
25
class UserManager
26
{
27
    // This constants are deprecated use the constants located in ExtraField
28
    public const USER_FIELD_TYPE_TEXT = 1;
29
    public const USER_FIELD_TYPE_TEXTAREA = 2;
30
    public const USER_FIELD_TYPE_RADIO = 3;
31
    public const USER_FIELD_TYPE_SELECT = 4;
32
    public const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
33
    public const USER_FIELD_TYPE_DATE = 6;
34
    public const USER_FIELD_TYPE_DATETIME = 7;
35
    public const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
36
    public const USER_FIELD_TYPE_DIVIDER = 9;
37
    public const USER_FIELD_TYPE_TAG = 10;
38
    public const USER_FIELD_TYPE_TIMEZONE = 11;
39
    public const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
40
    public const USER_FIELD_TYPE_FILE = 13;
41
    public const USER_FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
42
43
    private static $encryptionMethod;
44
45
    /**
46
     * Constructor.
47
     *
48
     * @assert () === null
49
     */
50
    public function __construct()
51
    {
52
    }
53
54
    /**
55
     * Repository is use to query the DB, selects, etc.
56
     *
57
     * @return UserRepository
58
     */
59
    public static function getRepository()
60
    {
61
        /** @var UserRepository $userRepository */
62
63
        return Database::getManager()->getRepository('ChamiloUserBundle:User');
64
    }
65
66
    /**
67
     * Create/update/delete methods are available in the UserManager
68
     * (based in the Sonata\UserBundle\Entity\UserManager).
69
     *
70
     * @return Chamilo\UserBundle\Entity\Manager\UserManager
71
     */
72
    public static function getManager()
73
    {
74
        static $userManager;
75
76
        if (!isset($userManager)) {
77
            $encoderFactory = self::getEncoderFactory();
78
            $passwordUpdater = new \FOS\UserBundle\Util\PasswordUpdater($encoderFactory);
79
            $canonicalFieldUpdater = new \FOS\UserBundle\Util\CanonicalFieldsUpdater(
80
                new \FOS\UserBundle\Util\Canonicalizer(), new \FOS\UserBundle\Util\Canonicalizer()
81
            );
82
            $userManager = new Chamilo\UserBundle\Entity\Manager\UserManager(
83
                $passwordUpdater,
84
                $canonicalFieldUpdater,
85
                Database::getManager(),
86
                'Chamilo\\UserBundle\\Entity\\User'
87
            );
88
        }
89
90
        return $userManager;
91
    }
92
93
    /**
94
     * @param string $encryptionMethod
95
     */
96
    public static function setPasswordEncryption($encryptionMethod)
97
    {
98
        self::$encryptionMethod = $encryptionMethod;
99
    }
100
101
    /**
102
     * @return bool|mixed
103
     */
104
    public static function getPasswordEncryption()
105
    {
106
        $encryptionMethod = self::$encryptionMethod;
107
        if (empty($encryptionMethod)) {
108
            $encryptionMethod = api_get_configuration_value('password_encryption');
109
        }
110
111
        return $encryptionMethod;
112
    }
113
114
    /**
115
     * Validates the password.
116
     *
117
     * @param $encoded
118
     * @param $raw
119
     * @param $salt
120
     *
121
     * @return bool
122
     */
123
    public static function isPasswordValid($encoded, $raw, $salt)
124
    {
125
        $encoder = new \Chamilo\UserBundle\Security\Encoder(self::getPasswordEncryption());
126
127
        return $encoder->isPasswordValid($encoded, $raw, $salt);
128
    }
129
130
    /**
131
     * Detects and returns the type of encryption of the given encrypted
132
     * password.
133
     *
134
     * @param string $encoded The encrypted password
135
     * @param string $salt    The user salt, if any
136
     */
137
    public static function detectPasswordEncryption(string $encoded, string $salt): string
138
    {
139
        $encryption = false;
140
141
        $length = strlen($encoded);
142
143
        $pattern = '/^\$2y\$04\$[A-Za-z0-9\.\/]{53}$/';
144
145
        if ($length == 60 && preg_match($pattern, $encoded)) {
146
            $encryption = 'bcrypt';
147
        } elseif ($length == 32 && ctype_xdigit($encoded)) {
148
            $encryption = 'md5';
149
        } elseif ($length == 40 && ctype_xdigit($encoded)) {
150
            $encryption = 'sha1';
151
        } else {
152
            $start = strpos($encoded, '{');
153
            if ($start !== false && substr($encoded, -1, 1) == '}') {
154
                if (substr($encoded, $start + 1, -1) == $salt) {
155
                    $encryption = 'none';
156
                }
157
            }
158
        }
159
160
        return $encryption;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $encryption could return the type false which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
161
    }
162
163
    /**
164
     * Checks if the password is correct for this user.
165
     * If the password_conversion setting is true, also update the password
166
     * in the database to a new encryption method.
167
     *
168
     * @param string $encoded Encrypted password
169
     * @param string $raw     Clear password given through login form
170
     * @param string $salt    User salt, if any
171
     * @param int    $userId  The user's internal ID
172
     */
173
    public static function checkPassword(string $encoded, string $raw, string $salt, int $userId): bool
174
    {
175
        $result = false;
176
177
        if (true === api_get_configuration_value('password_conversion')) {
178
            $detectedEncryption = self::detectPasswordEncryption($encoded, $salt);
179
            if (self::getPasswordEncryption() != $detectedEncryption) {
180
                $encoder = new \Chamilo\UserBundle\Security\Encoder($detectedEncryption);
181
                $result = $encoder->isPasswordValid($encoded, $raw, $salt);
182
                if ($result) {
183
                    $raw = $encoder->encodePassword($encoded, $salt);
184
                    self::updatePassword($userId, $raw);
185
                }
186
            } else {
187
                $result = self::isPasswordValid($encoded, $raw, $salt);
188
            }
189
        } else {
190
            $result = self::isPasswordValid($encoded, $raw, $salt);
191
        }
192
193
        return $result;
194
    }
195
196
    /**
197
     * Encrypt the password using the current encoder.
198
     *
199
     * @param string $raw The clear password
200
     */
201
    public static function encryptPassword(string $raw, User $user): string
202
    {
203
        $encoder = self::getEncoder($user);
204
205
        return $encoder->encodePassword(
206
            $raw,
207
            $user->getSalt()
208
        );
209
    }
210
211
    /**
212
     * Update the password of the given user to the given (in-clear) password.
213
     *
214
     * @param int    $userId   Internal user ID
215
     * @param string $password Password in clear
216
     */
217
    public static function updatePassword(int $userId, string $password): void
218
    {
219
        $repository = self::getRepository();
220
        /** @var User $user */
221
        $user = $repository->find($userId);
222
        $userManager = self::getManager();
223
        $user->setPlainPassword($password);
224
        $userManager->updateUser($user, true);
225
        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

225
        Event::/** @scrutinizer ignore-call */ 
226
               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...
226
    }
227
228
    /**
229
     * Updates user expiration date.
230
     *
231
     * @param int    $userId
232
     * @param string $expirationDate
233
     */
234
    public static function updateExpirationDate($userId, $expirationDate)
235
    {
236
        $repository = self::getRepository();
237
        /** @var User $user */
238
        $user = $repository->find($userId);
239
        $userManager = self::getManager();
240
        $expirationDate = api_get_utc_datetime($expirationDate, false, true);
241
        $user->setExpirationDate($expirationDate);
242
        $userManager->updateUser($user, true);
243
    }
244
245
    /**
246
     * Creates a new user for the platform.
247
     *
248
     * @author Hugues Peeters <[email protected]>,
249
     * @author Roan Embrechts <[email protected]>
250
     *
251
     * @param string        $firstName
252
     * @param string        $lastName
253
     * @param int           $status                  (1 for course tutor, 5 for student, 6 for anonymous)
254
     * @param string        $email
255
     * @param string        $loginName
256
     * @param string        $password
257
     * @param string        $official_code           Any official code (optional)
258
     * @param string        $language                User language    (optional)
259
     * @param string        $phone                   Phone number    (optional)
260
     * @param string        $picture_uri             Picture URI        (optional)
261
     * @param string        $authSource              Authentication source (defaults to 'platform', dependind on constant)
262
     * @param string        $expirationDate          Account expiration date (optional, defaults to null)
263
     * @param int           $active                  Whether the account is enabled or disabled by default
264
     * @param int           $hr_dept_id              The department of HR in which the user is registered (defaults to 0)
265
     * @param array         $extra                   Extra fields (prefix labels with "extra_")
266
     * @param string        $encrypt_method          Used if password is given encrypted. Set to an empty string by default
267
     * @param bool          $send_mail
268
     * @param bool          $isAdmin
269
     * @param string        $address
270
     * @param bool          $sendEmailToAllAdmins
271
     * @param FormValidator $form
272
     * @param int           $creatorId
273
     * @param array         $emailTemplate
274
     * @param string        $redirectToURLAfterLogin
275
     *
276
     * @return mixed new user id - if the new user creation succeeds, false otherwise
277
     * @desc The function tries to retrieve user id from the session.
278
     * If it exists, the current user id is the creator id. If a problem arises,
279
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
280
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
281
     */
282
    public static function create_user(
283
        $firstName,
284
        $lastName,
285
        $status,
286
        $email,
287
        $loginName,
288
        $password,
289
        $official_code = '',
290
        $language = '',
291
        $phone = '',
292
        $picture_uri = '',
293
        $authSource = PLATFORM_AUTH_SOURCE,
294
        $expirationDate = null,
295
        $active = 1,
296
        $hr_dept_id = 0,
297
        $extra = [],
298
        $encrypt_method = '',
299
        $send_mail = false,
300
        $isAdmin = false,
301
        $address = '',
302
        $sendEmailToAllAdmins = false,
303
        $form = null,
304
        $creatorId = 0,
305
        $emailTemplate = [],
306
        $redirectToURLAfterLogin = ''
307
    ) {
308
        $creatorId = empty($creatorId) ? api_get_user_id() : 0;
309
        $creatorInfo = api_get_user_info($creatorId);
310
        $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
311
312
        $hook = HookCreateUser::create();
313
        if (!empty($hook)) {
314
            $hook->notifyCreateUser(HOOK_EVENT_TYPE_PRE);
315
        }
316
317
        if ('true' === api_get_setting('registration', 'email')) {
318
            // Force email validation.
319
            if (false === api_valid_email($email)) {
320
                Display::addFlash(
321
                   Display::return_message(get_lang('PleaseEnterValidEmail').' - '.$email, 'warning')
322
               );
323
324
                return false;
325
            }
326
        } else {
327
            // Allow empty email. If email is set, check if is valid.
328
            if (!empty($email) && false === api_valid_email($email)) {
329
                Display::addFlash(
330
                    Display::return_message(get_lang('PleaseEnterValidEmail').' - '.$email, 'warning')
331
                );
332
333
                return false;
334
            }
335
        }
336
337
        if ('true' === api_get_setting('login_is_email')) {
338
            if (false === api_valid_email($loginName)) {
339
                Display::addFlash(
340
                    Display::return_message(get_lang('PleaseEnterValidEmail').' - '.$loginName, 'warning')
341
                );
342
343
                return false;
344
            }
345
        } else {
346
            if (false === self::is_username_valid($loginName)) {
347
                Display::addFlash(
348
                    Display::return_message(get_lang('UsernameWrong').' - '.$loginName, 'warning')
349
                );
350
351
                return false;
352
            }
353
        }
354
355
        // First check wether the login already exists
356
        if (!self::is_username_available($loginName)) {
357
            Display::addFlash(
358
                Display::return_message(get_lang('LoginAlreadyTaken').' - '.$loginName, 'warning')
359
            );
360
361
            return false;
362
        }
363
364
        global $_configuration;
365
        $original_password = $password;
366
367
        $access_url_id = 1;
368
        if (api_get_multiple_access_url()) {
369
            $access_url_id = api_get_current_access_url_id();
370
        } else {
371
            // In some cases, the first access_url ID might be different from 1
372
            // for example when using a DB cluster or hacking the DB manually.
373
            // In this case, we want the first row, not necessarily "1".
374
            $dbm = Database::getManager();
375
            /** @var AccessUrlRepository $accessUrlRepository */
376
            $accessUrlRepository = $dbm->getRepository('ChamiloCoreBundle:AccessUrl');
377
            $accessUrl = $accessUrlRepository->getFirstId();
378
            if (!empty($accessUrl[0]) && !empty($accessUrl[0][1])) {
379
                $access_url_id = $accessUrl[0][1];
380
            }
381
        }
382
383
        if (isset($_configuration[$access_url_id]) &&
384
            is_array($_configuration[$access_url_id]) &&
385
            isset($_configuration[$access_url_id]['hosting_limit_users']) &&
386
            $_configuration[$access_url_id]['hosting_limit_users'] > 0) {
387
            $num = self::get_number_of_users(null, $access_url_id);
388
            if ($num >= $_configuration[$access_url_id]['hosting_limit_users']) {
389
                api_warn_hosting_contact('hosting_limit_users');
390
                Display::addFlash(
391
                    Display::return_message(
392
                        get_lang('PortalUsersLimitReached'),
393
                        'warning'
394
                    )
395
                );
396
397
                return false;
398
            }
399
        }
400
401
        if ($status === 1 &&
402
            isset($_configuration[$access_url_id]) &&
403
            is_array($_configuration[$access_url_id]) &&
404
            isset($_configuration[$access_url_id]['hosting_limit_teachers']) &&
405
            $_configuration[$access_url_id]['hosting_limit_teachers'] > 0
406
        ) {
407
            $num = self::get_number_of_users(1, $access_url_id);
408
            if ($num >= $_configuration[$access_url_id]['hosting_limit_teachers']) {
409
                Display::addFlash(
410
                    Display::return_message(
411
                        get_lang('PortalTeachersLimitReached'),
412
                        'warning'
413
                    )
414
                );
415
                api_warn_hosting_contact('hosting_limit_teachers');
416
417
                return false;
418
            }
419
        }
420
421
        if (empty($password)) {
422
            if ($authSource === PLATFORM_AUTH_SOURCE) {
423
                Display::addFlash(
424
                    Display::return_message(
425
                        get_lang('ThisFieldIsRequired').': '.get_lang(
426
                            'Password'
427
                        ),
428
                        'warning'
429
                    )
430
                );
431
432
                return false;
433
            }
434
435
            // We use the authSource as password.
436
            // The real validation will be by processed by the auth
437
            // source not Chamilo
438
            $password = $authSource;
439
        }
440
441
        // database table definition
442
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
443
444
        // Checking the user language
445
        $languages = api_get_languages();
446
        $language = strtolower($language);
447
448
        if (isset($languages['folder'])) {
449
            if (!in_array($language, $languages['folder'])) {
450
                $language = api_get_setting('platformLanguage');
451
            }
452
        }
453
454
        $currentDate = api_get_utc_datetime();
455
        $now = new DateTime();
456
457
        if (empty($expirationDate) || $expirationDate == '0000-00-00 00:00:00') {
458
            // Default expiration date
459
            // if there is a default duration of a valid account then
460
            // we have to change the expiration_date accordingly
461
            // Accept 0000-00-00 00:00:00 as a null value to avoid issues with
462
            // third party code using this method with the previous (pre-1.10)
463
            // value of 0000...
464
            if (api_get_setting('account_valid_duration') != '') {
465
                $expirationDate = new DateTime($currentDate);
466
                $days = (int) api_get_setting('account_valid_duration');
467
                $expirationDate->modify('+'.$days.' day');
468
            }
469
        } else {
470
            $expirationDate = api_get_utc_datetime($expirationDate);
471
            $expirationDate = new \DateTime($expirationDate, new DateTimeZone('UTC'));
472
        }
473
474
        $userManager = self::getManager();
475
476
        /** @var User $user */
477
        $user = $userManager->createUser();
478
479
        $user
480
            ->setLastname($lastName)
481
            ->setFirstname($firstName)
482
            ->setUsername($loginName)
483
            ->setStatus($status)
484
            ->setPlainPassword($password)
485
            ->setEmail($email)
486
            ->setOfficialCode($official_code)
487
            ->setPictureUri($picture_uri)
488
            ->setCreatorId($creatorId)
489
            ->setAuthSource($authSource)
490
            ->setPhone($phone)
491
            ->setAddress($address)
492
            ->setLanguage($language)
493
            ->setRegistrationDate($now)
494
            ->setHrDeptId($hr_dept_id)
495
            ->setActive($active)
496
            ->setEnabled($active)
497
        ;
498
499
        if (!empty($expirationDate)) {
500
            $user->setExpirationDate($expirationDate);
501
        }
502
503
        $userManager->updateUser($user);
504
        $userId = $user->getId();
505
506
        if (!empty($userId)) {
507
            $return = $userId;
508
            $sql = "UPDATE $table_user SET user_id = $return WHERE id = $return";
509
            Database::query($sql);
510
511
            if ($isAdmin) {
512
                self::addUserAsAdmin($user);
513
            }
514
515
            if (api_get_multiple_access_url()) {
516
                UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
517
            } else {
518
                //we are adding by default the access_url_user table with access_url_id = 1
519
                UrlManager::add_user_to_url($userId, 1);
520
            }
521
522
            $extra['item_id'] = $userId;
523
524
            if (is_array($extra) && count($extra) > 0) {
525
                $userFieldValue = new ExtraFieldValue('user');
526
                // Force saving of extra fields (otherwise, if the current
527
                // user is not admin, fields not visible to the user - most
528
                // of them - are just ignored)
529
                $userFieldValue->saveFieldValues(
530
                    $extra,
531
                    true,
532
                    null,
533
                    null,
534
                    null,
535
                    true
536
                );
537
            } else {
538
                // Create notify settings by default
539
                self::update_extra_field_value(
540
                    $userId,
541
                    'mail_notify_invitation',
542
                    '1'
543
                );
544
                self::update_extra_field_value(
545
                    $userId,
546
                    'mail_notify_message',
547
                    '1'
548
                );
549
                self::update_extra_field_value(
550
                    $userId,
551
                    'mail_notify_group_message',
552
                    '1'
553
                );
554
            }
555
556
            self::update_extra_field_value(
557
                $userId,
558
                'already_logged_in',
559
                'false'
560
            );
561
562
            if (!empty($redirectToURLAfterLogin) && api_get_configuration_value('plugin_redirection_enabled')) {
563
                RedirectionPlugin::insert($userId, $redirectToURLAfterLogin);
564
            }
565
566
            if (!empty($email) && $send_mail) {
567
                $recipient_name = api_get_person_name(
568
                    $firstName,
569
                    $lastName,
570
                    null,
571
                    PERSON_NAME_EMAIL_ADDRESS
572
                );
573
                $tplSubject = new Template(
574
                    null,
575
                    false,
576
                    false,
577
                    false,
578
                    false,
579
                    false
580
                );
581
                $layoutSubject = $tplSubject->get_template('mail/subject_registration_platform.tpl');
582
                $emailSubject = $tplSubject->fetch($layoutSubject);
583
                $sender_name = api_get_person_name(
584
                    api_get_setting('administratorName'),
585
                    api_get_setting('administratorSurname'),
586
                    null,
587
                    PERSON_NAME_EMAIL_ADDRESS
588
                );
589
                $email_admin = api_get_setting('emailAdministrator');
590
591
                $url = api_get_path(WEB_PATH);
592
                if (api_is_multiple_url_enabled()) {
593
                    $access_url_id = api_get_current_access_url_id();
594
                    if ($access_url_id != -1) {
595
                        $urlInfo = api_get_access_url($access_url_id);
596
                        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...
597
                            $url = $urlInfo['url'];
598
                        }
599
                    }
600
                }
601
602
                $tplContent = new Template(
603
                    null,
604
                    false,
605
                    false,
606
                    false,
607
                    false,
608
                    false
609
                );
610
                // variables for the default template
611
                $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
612
                $tplContent->assign('login_name', $loginName);
613
                $tplContent->assign('original_password', stripslashes($original_password));
614
                $tplContent->assign('mailWebPath', $url);
615
                $tplContent->assign('new_user', $user);
616
                // Adding this variable but not used in default template, used for task BT19518 with a customized template
617
                $tplContent->assign('status_type', $status);
618
619
                $layoutContent = $tplContent->get_template('mail/content_registration_platform.tpl');
620
                $emailBody = $tplContent->fetch($layoutContent);
621
622
                $userInfo = api_get_user_info($userId);
623
                $mailTemplateManager = new MailTemplateManager();
624
625
                /* MANAGE EVENT WITH MAIL */
626
                if (EventsMail::check_if_using_class('user_registration')) {
627
                    $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...
628
                    $values["password"] = $original_password;
629
                    $values["send_to"] = [$return];
630
                    $values["prior_lang"] = null;
631
                    EventsDispatcher::events('user_registration', $values);
632
                } else {
633
                    $phoneNumber = isset($extra['mobile_phone_number']) ? $extra['mobile_phone_number'] : null;
634
                    $additionalParameters = [
635
                        'smsType' => SmsPlugin::WELCOME_LOGIN_PASSWORD,
636
                        'userId' => $return,
637
                        'mobilePhoneNumber' => $phoneNumber,
638
                        'password' => $original_password,
639
                    ];
640
641
                    $emailBodyTemplate = '';
642
                    if (!empty($emailTemplate)) {
643
                        if (isset($emailTemplate['content_registration_platform.tpl']) &&
644
                            !empty($emailTemplate['content_registration_platform.tpl'])
645
                        ) {
646
                            $emailBodyTemplate = $mailTemplateManager->parseTemplate(
647
                                $emailTemplate['content_registration_platform.tpl'],
648
                                $userInfo
649
                            );
650
                        }
651
                    }
652
653
                    $twoEmail = api_get_configuration_value('send_two_inscription_confirmation_mail');
654
                    if ($twoEmail === true) {
655
                        $layoutContent = $tplContent->get_template('mail/new_user_first_email_confirmation.tpl');
656
                        $emailBody = $tplContent->fetch($layoutContent);
657
658
                        if (!empty($emailTemplate) &&
659
                            isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
660
                            !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
661
                        ) {
662
                            $emailBody = $mailTemplateManager->parseTemplate(
663
                                $emailTemplate['new_user_first_email_confirmation.tpl'],
664
                                $userInfo
665
                            );
666
                        }
667
668
                        api_mail_html(
669
                            $recipient_name,
670
                            $email,
671
                            $emailSubject,
672
                            $emailBody,
673
                            $sender_name,
674
                            $email_admin,
675
                            null,
676
                            null,
677
                            null,
678
                            $additionalParameters,
679
                            $creatorEmail
680
                        );
681
682
                        $layoutContent = $tplContent->get_template('mail/new_user_second_email_confirmation.tpl');
683
                        $emailBody = $tplContent->fetch($layoutContent);
684
685
                        if (!empty($emailTemplate) &&
686
                            isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
687
                            !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
688
                        ) {
689
                            $emailBody = $mailTemplateManager->parseTemplate(
690
                                $emailTemplate['new_user_second_email_confirmation.tpl'],
691
                                $userInfo
692
                            );
693
                        }
694
695
                        api_mail_html(
696
                            $recipient_name,
697
                            $email,
698
                            $emailSubject,
699
                            $emailBody,
700
                            $sender_name,
701
                            $email_admin,
702
                            null,
703
                            null,
704
                            null,
705
                            $additionalParameters,
706
                            $creatorEmail
707
                        );
708
                    } else {
709
                        if (!empty($emailBodyTemplate)) {
710
                            $emailBody = $emailBodyTemplate;
711
                        }
712
                        $sendToInbox = api_get_configuration_value('send_inscription_msg_to_inbox');
713
                        if ($sendToInbox) {
714
                            $adminList = self::get_all_administrators();
715
                            $senderId = 1;
716
                            if (!empty($adminList)) {
717
                                $adminInfo = current($adminList);
718
                                $senderId = $adminInfo['user_id'];
719
                            }
720
721
                            MessageManager::send_message_simple(
722
                                $userId,
723
                                $emailSubject,
724
                                $emailBody,
725
                                $senderId
726
                            );
727
                        } else {
728
                            api_mail_html(
729
                                $recipient_name,
730
                                $email,
731
                                $emailSubject,
732
                                $emailBody,
733
                                $sender_name,
734
                                $email_admin,
735
                                null,
736
                                null,
737
                                null,
738
                                $additionalParameters,
739
                                $creatorEmail
740
                            );
741
                        }
742
                    }
743
744
                    $notification = api_get_configuration_value('send_notification_when_user_added');
745
                    if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
746
                        foreach ($notification['admins'] as $adminId) {
747
                            $emailSubjectToAdmin = get_lang('UserAdded').': '.api_get_person_name($firstName, $lastName);
748
                            MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
749
                        }
750
                    }
751
                }
752
753
                if ($sendEmailToAllAdmins) {
754
                    $adminList = self::get_all_administrators();
755
756
                    $tplContent = new Template(
757
                        null,
758
                        false,
759
                        false,
760
                        false,
761
                        false,
762
                        false
763
                    );
764
                    // variables for the default template
765
                    $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
766
                    $tplContent->assign('user_added', $user);
767
                    $renderer = FormValidator::getDefaultRenderer();
768
                    // Form template
769
                    $elementTemplate = ' {label}: {element} <br />';
770
                    $renderer->setElementTemplate($elementTemplate);
771
                    /** @var FormValidator $form */
772
                    $form->freeze(null, $elementTemplate);
773
                    $form->removeElement('submit');
774
                    $formData = $form->returnForm();
775
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
776
                    $tplContent->assign('link', Display::url($url, $url));
777
                    $tplContent->assign('form', $formData);
778
779
                    $layoutContent = $tplContent->get_template('mail/content_registration_platform_to_admin.tpl');
780
                    $emailBody = $tplContent->fetch($layoutContent);
781
782
                    if (!empty($emailTemplate) &&
783
                        isset($emailTemplate['content_registration_platform_to_admin.tpl']) &&
784
                        !empty($emailTemplate['content_registration_platform_to_admin.tpl'])
785
                    ) {
786
                        $emailBody = $mailTemplateManager->parseTemplate(
787
                            $emailTemplate['content_registration_platform_to_admin.tpl'],
788
                            $userInfo
789
                        );
790
                    }
791
792
                    $subject = get_lang('UserAdded');
793
                    foreach ($adminList as $adminId => $data) {
794
                        MessageManager::send_message_simple(
795
                            $adminId,
796
                            $subject,
797
                            $emailBody,
798
                            $userId
799
                        );
800
                    }
801
                }
802
                /* ENDS MANAGE EVENT WITH MAIL */
803
            }
804
805
            if (!empty($hook)) {
806
                $hook->setEventData([
807
                    'return' => $userId,
808
                    'originalPassword' => $original_password,
809
                ]);
810
                $hook->notifyCreateUser(HOOK_EVENT_TYPE_POST);
811
            }
812
            Event::addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId);
813
        } else {
814
            Display::addFlash(
815
                Display::return_message(get_lang('ErrorContactPlatformAdmin'))
816
            );
817
818
            return false;
819
        }
820
821
        return $return;
822
    }
823
824
    /**
825
     * Ensure the CAS-authenticated user exists in the database.
826
     *
827
     * @param $casUser string the CAS user identifier
828
     *
829
     * @throws Exception if more than one user share the same CAS user identifier
830
     *
831
     * @return string|bool the recognised user login name or false if not found
832
     */
833
    public static function casUserLoginName($casUser)
834
    {
835
        $loginName = false;
836
837
        // look inside the casUser extra field
838
        if (UserManager::is_extra_field_available('cas_user')) {
839
            $valueModel = new ExtraFieldValue('user');
840
            $itemList = $valueModel->get_item_id_from_field_variable_and_field_value(
841
                'cas_user',
842
                $casUser,
843
                false,
844
                false,
845
                true
846
            );
847
            if (false !== $itemList) {
848
                // at least one user has $casUser in the 'cas_user' extra field
849
                // we attempt to load each candidate user because there might be deleted ones
850
                // (extra field values of a deleted user might remain)
851
                foreach ($itemList as $item) {
852
                    $userId = intval($item['item_id']);
853
                    $user = UserManager::getRepository()->find($userId);
854
                    if (!is_null($user)) {
855
                        if (false === $loginName) {
856
                            $loginName = $user->getUsername();
857
                        } else {
858
                            throw new Exception(get_lang('MoreThanOneUserMatched'));
859
                        }
860
                    }
861
                }
862
            }
863
        }
864
865
        if (false === $loginName) {
866
            // no matching 'cas_user' extra field value, or no such extra field
867
            // falling back to the old behaviour: $casUser must be the login name
868
            $userId = UserManager::get_user_id_from_username($casUser);
869
            if (false !== $userId) {
870
                $loginName = $casUser;
871
            }
872
        }
873
874
        return $loginName;
875
    }
876
877
    /**
878
     * Checks the availability of extra field 'cas_user'
879
     * and creates it if missing.
880
     *
881
     * @throws Exception on failure
882
     */
883
    public static function ensureCASUserExtraFieldExists()
884
    {
885
        if (!self::is_extra_field_available('cas_user')) {
886
            $extraField = new ExtraField('user');
887
            if (false === $extraField->save(
888
                    [
889
                        'variable' => 'cas_user',
890
                        'field_type' => ExtraField::FIELD_TYPE_TEXT,
891
                        'display_text' => get_lang('CAS User Identifier'),
892
                        'visible_to_self' => true,
893
                        'filter' => true,
894
                    ]
895
                )) {
896
                throw new Exception(get_lang('FailedToCreateExtraFieldCasUser'));
897
            }
898
899
            $rules = api_get_configuration_value('cas_user_map');
900
            if (!empty($rules) && isset($rules['extra'])) {
901
                foreach ($rules['extra'] as $extra) {
902
                    $extraField->save(
903
                        [
904
                            'variable' => $extra,
905
                            'field_type' => ExtraField::FIELD_TYPE_TEXT,
906
                            'display_text' => $extra,
907
                            'visible_to_self' => false,
908
                            'filter' => false,
909
                        ]
910
                    );
911
                }
912
            }
913
        }
914
    }
915
916
    /**
917
     * Create a CAS-authenticated user from scratch, from its CAS user identifier, with temporary default values.
918
     *
919
     * @param string $casUser the CAS user identifier
920
     *
921
     * @throws Exception on error
922
     *
923
     * @return string the login name of the new user
924
     */
925
    public static function createCASAuthenticatedUserFromScratch($casUser)
926
    {
927
        self::ensureCASUserExtraFieldExists();
928
929
        $loginName = 'cas_user_'.$casUser;
930
        $defaultValue = get_lang('EditInProfile');
931
        $defaultEmailValue = get_lang('EditInProfile');
932
        require_once __DIR__.'/../../auth/external_login/functions.inc.php';
933
        if ('true' === api_get_setting('login_is_email')) {
934
            $defaultEmailValue = $casUser;
935
        }
936
        $userId = external_add_user(
937
            [
938
                'username' => $loginName,
939
                'auth_source' => CAS_AUTH_SOURCE,
940
                'firstname' => $defaultValue,
941
                'lastname' => $defaultValue,
942
                'email' => $defaultEmailValue,
943
            ]
944
        );
945
        if (false === $userId) {
946
            throw new Exception(get_lang('FailedUserCreation'));
947
        }
948
        // Not checking function update_extra_field_value return value because not reliable
949
        self::update_extra_field_value($userId, 'cas_user', $casUser);
950
951
        return $loginName;
952
    }
953
954
    public static function updateCasUser($_user)
955
    {
956
        $rules = api_get_configuration_value('cas_user_map');
957
958
        if (empty($_user)) {
959
            return false;
960
        }
961
962
        if (!empty($rules)) {
963
            $userEntity = api_get_user_entity($_user['id']);
964
            $attributes = phpCAS::getAttributes();
965
            if (isset($rules['fields'])) {
966
                $isAdmin = false;
967
                foreach ($rules['fields'] as $field => $attributeName) {
968
                    if (!isset($attributes[$attributeName])) {
969
                        continue;
970
                    }
971
                    $value = $attributes[$attributeName];
972
                    // Check replace.
973
                    if (isset($rules['replace'][$attributeName])) {
974
                        $value = $rules['replace'][$attributeName][$value];
975
                    }
976
977
                    switch ($field) {
978
                        case 'email':
979
                            $userEntity->setEmail($value);
980
                            break;
981
                        case 'firstname':
982
                            $userEntity->setFirstname($value);
983
                            break;
984
                        case 'lastname':
985
                            $userEntity->setLastname($value);
986
                            break;
987
                        case 'active':
988
                            $userEntity->setActive('false' === $value);
989
                            break;
990
                        case 'status':
991
                            if (PLATFORM_ADMIN === (int) $value) {
992
                                $value = COURSEMANAGER;
993
                                $isAdmin = true;
994
                            }
995
                            $userEntity->setStatus($value);
996
                            break;
997
                    }
998
999
                    Database::getManager()->persist($userEntity);
1000
                    Database::getManager()->flush();
1001
1002
                    if ($isAdmin) {
1003
                        self::addUserAsAdmin($userEntity);
1004
                    }
1005
                }
1006
            }
1007
1008
            if (isset($rules['extra'])) {
1009
                foreach ($rules['extra'] as $variable) {
1010
                    if (isset($attributes[$variable])) {
1011
                        self::update_extra_field_value(
1012
                            $_user['id'],
1013
                            $variable,
1014
                            $attributes[$variable]
1015
                        );
1016
                    }
1017
                }
1018
            }
1019
        }
1020
    }
1021
1022
    /**
1023
     * Create a CAS-authenticated user from LDAP, from its CAS user identifier.
1024
     *
1025
     * @param $casUser
1026
     *
1027
     * @throws Exception
1028
     *
1029
     * @return string login name of the new user
1030
     */
1031
    public static function createCASAuthenticatedUserFromLDAP($casUser)
1032
    {
1033
        self::ensureCASUserExtraFieldExists();
1034
1035
        require_once __DIR__.'/../../auth/external_login/ldap.inc.php';
1036
        $login = extldapCasUserLogin($casUser);
1037
        if (false !== $login) {
1038
            $ldapUser = extldap_authenticate($login, 'nopass', true);
1039
            if (false !== $ldapUser) {
1040
                require_once __DIR__.'/../../auth/external_login/functions.inc.php';
1041
                $user = extldap_get_chamilo_user($ldapUser);
1042
                $user['username'] = $login;
1043
                $user['auth_source'] = CAS_AUTH_SOURCE;
1044
                $userId = external_add_user($user);
1045
                if (false !== $userId) {
1046
                    // Not checking function update_extra_field_value return value because not reliable
1047
                    self::update_extra_field_value($userId, 'cas_user', $casUser);
1048
1049
                    return $login;
1050
                } else {
1051
                    throw new Exception('Could not create the new user '.$login);
1052
                }
1053
            } else {
1054
                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

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

2649
                /** @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...
2650
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
2651
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
2652
                @rename($path.$old_file, $path.$prefix.$old_file);
2653
            } else {
2654
                @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

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

2765
            /** @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...
2766
        }
2767
2768
        if (filter_extension($file)) {
2769
            if (@move_uploaded_file($source_file, $path.$file)) {
2770
                if ($extra_field) {
2771
                    return $extra_field.'/'.$file;
2772
                } else {
2773
                    return $file;
2774
                }
2775
            }
2776
        }
2777
2778
        return false; // this should be returned if anything went wrong with the upload
2779
    }
2780
2781
    /**
2782
     * Deletes user photos.
2783
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2784
     *
2785
     * @param int $userId the user internal identification number
2786
     *
2787
     * @return mixed returns empty string on success, FALSE on error
2788
     */
2789
    public static function deleteUserPicture($userId)
2790
    {
2791
        return self::update_user_picture($userId);
2792
    }
2793
2794
    /**
2795
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
2796
     * doesn't have any.
2797
     *
2798
     * If there has been a request to remove a production, the function will return
2799
     * without building the list unless forced to do so by the optional second
2800
     * parameter. This increases performance by avoiding to read through the
2801
     * productions on the filesystem before the removal request has been carried
2802
     * out because they'll have to be re-read afterwards anyway.
2803
     *
2804
     * @param int  $user_id    User id
2805
     * @param bool $force      Optional parameter to force building after a removal request
2806
     * @param bool $showDelete
2807
     *
2808
     * @return string A string containing the XHTML code to display the production list, or FALSE
2809
     */
2810
    public static function build_production_list($user_id, $force = false, $showDelete = false)
2811
    {
2812
        if (!$force && !empty($_POST['remove_production'])) {
2813
            return true; // postpone reading from the filesystem
2814
        }
2815
2816
        $productions = self::get_user_productions($user_id);
2817
2818
        if (empty($productions)) {
2819
            return false;
2820
        }
2821
2822
        $production_dir = self::getUserPathById($user_id, 'web');
2823
        $del_image = Display::returnIconPath('delete.png');
2824
        $add_image = Display::returnIconPath('archive.png');
2825
        $del_text = get_lang('Delete');
2826
        $production_list = '';
2827
        if (count($productions) > 0) {
2828
            $production_list = '<div class="files-production"><ul id="productions">';
2829
            foreach ($productions as $file) {
2830
                $production_list .= '<li>
2831
                    <img src="'.$add_image.'" />
2832
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2833
                        '.htmlentities($file).'
2834
                    </a>';
2835
                if ($showDelete) {
2836
                    $production_list .= '&nbsp;&nbsp;
2837
                        <input
2838
                            style="width:16px;"
2839
                            type="image"
2840
                            name="remove_production['.urlencode($file).']"
2841
                            src="'.$del_image.'"
2842
                            alt="'.$del_text.'"
2843
                            title="'.$del_text.' '.htmlentities($file).'"
2844
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2845
                }
2846
            }
2847
            $production_list .= '</ul></div>';
2848
        }
2849
2850
        return $production_list;
2851
    }
2852
2853
    /**
2854
     * Returns an array with the user's productions.
2855
     *
2856
     * @param int $user_id User id
2857
     *
2858
     * @return array An array containing the user's productions
2859
     */
2860
    public static function get_user_productions($user_id)
2861
    {
2862
        $production_repository = self::getUserPathById($user_id, 'system');
2863
        $productions = [];
2864
2865
        if (is_dir($production_repository)) {
2866
            $handle = opendir($production_repository);
2867
            while ($file = readdir($handle)) {
2868
                if ($file == '.' ||
2869
                    $file == '..' ||
2870
                    $file == '.htaccess' ||
2871
                    is_dir($production_repository.$file)
2872
                ) {
2873
                    // skip current/parent directory and .htaccess
2874
                    continue;
2875
                }
2876
2877
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2878
                    // User's photos should not be listed as productions.
2879
                    continue;
2880
                }
2881
                $productions[] = $file;
2882
            }
2883
        }
2884
2885
        return $productions;
2886
    }
2887
2888
    /**
2889
     * Remove a user production.
2890
     *
2891
     * @param int    $user_id    User id
2892
     * @param string $production The production to remove
2893
     *
2894
     * @return bool
2895
     */
2896
    public static function remove_user_production($user_id, $production)
2897
    {
2898
        $production_path = self::get_user_picture_path_by_id($user_id, 'system');
2899
        $production_file = $production_path['dir'].$production;
2900
        if (is_file($production_file)) {
2901
            unlink($production_file);
2902
2903
            return true;
2904
        }
2905
2906
        return false;
2907
    }
2908
2909
    /**
2910
     * Update an extra field value for a given user.
2911
     *
2912
     * @param int    $userId   User ID
2913
     * @param string $variable Field variable name
2914
     * @param string $value    Field value
2915
     *
2916
     * @return bool true if field updated, false otherwise
2917
     */
2918
    public static function update_extra_field_value($userId, $variable, $value = '')
2919
    {
2920
        $extraFieldValue = new ExtraFieldValue('user');
2921
        $params = [
2922
            'item_id' => $userId,
2923
            'variable' => $variable,
2924
            'value' => $value,
2925
        ];
2926
2927
        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...
2928
    }
2929
2930
    /**
2931
     * Get an array of extra fields with field details (type, default value and options).
2932
     *
2933
     * @param    int    Offset (from which row)
2934
     * @param    int    Number of items
2935
     * @param    int    Column on which sorting is made
2936
     * @param    string    Sorting direction
2937
     * @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...
2938
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2939
     *
2940
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2941
     */
2942
    public static function get_extra_fields(
2943
        $from = 0,
2944
        $number_of_items = 0,
2945
        $column = 5,
2946
        $direction = 'ASC',
2947
        $all_visibility = true,
2948
        $field_filter = null
2949
    ) {
2950
        $fields = [];
2951
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2952
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2953
        $columns = [
2954
            'id',
2955
            'variable',
2956
            'field_type',
2957
            'display_text',
2958
            'default_value',
2959
            'field_order',
2960
            'filter',
2961
        ];
2962
        $column = (int) $column;
2963
        $sort_direction = '';
2964
        if (!empty($direction)) {
2965
            if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2966
                $sort_direction = strtoupper($direction);
2967
            }
2968
        }
2969
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2970
        $sqlf = "SELECT * FROM $t_uf WHERE extra_field_type = $extraFieldType ";
2971
        if (!$all_visibility) {
2972
            $sqlf .= " AND visible_to_self = 1 ";
2973
        }
2974
        if (!is_null($field_filter)) {
2975
            $field_filter = (int) $field_filter;
2976
            $sqlf .= " AND filter = $field_filter ";
2977
        }
2978
        $sqlf .= " ORDER BY `".$columns[$column]."` $sort_direction ";
2979
        if ($number_of_items != 0) {
2980
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2981
        }
2982
        $resf = Database::query($sqlf);
2983
        if (Database::num_rows($resf) > 0) {
2984
            while ($rowf = Database::fetch_array($resf)) {
2985
                $fields[$rowf['id']] = [
2986
                    0 => $rowf['id'],
2987
                    1 => $rowf['variable'],
2988
                    2 => $rowf['field_type'],
2989
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2990
                    4 => $rowf['default_value'],
2991
                    5 => $rowf['field_order'],
2992
                    6 => $rowf['visible_to_self'],
2993
                    7 => $rowf['changeable'],
2994
                    8 => $rowf['filter'],
2995
                    9 => [],
2996
                    10 => '<a name="'.$rowf['id'].'"></a>',
2997
                ];
2998
2999
                $sqlo = "SELECT * FROM $t_ufo
3000
                         WHERE field_id = ".$rowf['id']."
3001
                         ORDER BY option_order ASC";
3002
                $reso = Database::query($sqlo);
3003
                if (Database::num_rows($reso) > 0) {
3004
                    while ($rowo = Database::fetch_array($reso)) {
3005
                        $fields[$rowf['id']][9][$rowo['id']] = [
3006
                            0 => $rowo['id'],
3007
                            1 => $rowo['option_value'],
3008
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
3009
                            3 => $rowo['option_order'],
3010
                        ];
3011
                    }
3012
                }
3013
            }
3014
        }
3015
3016
        return $fields;
3017
    }
3018
3019
    /**
3020
     * Build a list of extra file already uploaded in $user_folder/{$extra_field}/.
3021
     *
3022
     * @param $user_id
3023
     * @param $extra_field
3024
     * @param bool $force
3025
     * @param bool $showDelete
3026
     *
3027
     * @return bool|string
3028
     */
3029
    public static function build_user_extra_file_list(
3030
        $user_id,
3031
        $extra_field,
3032
        $force = false,
3033
        $showDelete = false
3034
    ) {
3035
        if (!$force && !empty($_POST['remove_'.$extra_field])) {
3036
            return true; // postpone reading from the filesystem
3037
        }
3038
3039
        $extra_files = self::get_user_extra_files($user_id, $extra_field);
3040
        if (empty($extra_files)) {
3041
            return false;
3042
        }
3043
3044
        $path_info = self::get_user_picture_path_by_id($user_id, 'web');
3045
        $path = $path_info['dir'];
3046
        $del_image = Display::returnIconPath('delete.png');
3047
3048
        $del_text = get_lang('Delete');
3049
        $extra_file_list = '';
3050
        if (count($extra_files) > 0) {
3051
            $extra_file_list = '<div class="files-production"><ul id="productions">';
3052
            foreach ($extra_files as $file) {
3053
                $filename = substr($file, strlen($extra_field) + 1);
3054
                $extra_file_list .= '<li>'.Display::return_icon('archive.png').
3055
                    '<a href="'.$path.$extra_field.'/'.urlencode($filename).'" target="_blank">
3056
                        '.htmlentities($filename).
3057
                    '</a> ';
3058
                if ($showDelete) {
3059
                    $extra_file_list .= '<input
3060
                        style="width:16px;"
3061
                        type="image"
3062
                        name="remove_extra_'.$extra_field.'['.urlencode($file).']"
3063
                        src="'.$del_image.'"
3064
                        alt="'.$del_text.'"
3065
                        title="'.$del_text.' '.htmlentities($filename).'"
3066
                        onclick="javascript: return confirmation(\''.htmlentities($filename).'\');" /></li>';
3067
                }
3068
            }
3069
            $extra_file_list .= '</ul></div>';
3070
        }
3071
3072
        return $extra_file_list;
3073
    }
3074
3075
    /**
3076
     * Get valid filenames in $user_folder/{$extra_field}/.
3077
     *
3078
     * @param $user_id
3079
     * @param $extra_field
3080
     * @param bool $full_path
3081
     *
3082
     * @return array
3083
     */
3084
    public static function get_user_extra_files($user_id, $extra_field, $full_path = false)
3085
    {
3086
        if (!$full_path) {
3087
            // Nothing to do
3088
        } else {
3089
            $path_info = self::get_user_picture_path_by_id($user_id, 'system');
3090
            $path = $path_info['dir'];
3091
        }
3092
        $extra_data = self::get_extra_user_data_by_field($user_id, $extra_field);
3093
        $extra_files = $extra_data[$extra_field];
3094
3095
        $files = [];
3096
        if (is_array($extra_files)) {
3097
            foreach ($extra_files as $key => $value) {
3098
                if (!$full_path) {
3099
                    // Relative path from user folder
3100
                    $files[] = $value;
3101
                } else {
3102
                    $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...
3103
                }
3104
            }
3105
        } elseif (!empty($extra_files)) {
3106
            if (!$full_path) {
3107
                // Relative path from user folder
3108
                $files[] = $extra_files;
3109
            } else {
3110
                $files[] = $path.$extra_files;
3111
            }
3112
        }
3113
3114
        return $files; // can be an empty array
3115
    }
3116
3117
    /**
3118
     * Remove an {$extra_file} from the user folder $user_folder/{$extra_field}/.
3119
     *
3120
     * @param int    $user_id
3121
     * @param string $extra_field
3122
     * @param string $extra_file
3123
     *
3124
     * @return bool
3125
     */
3126
    public static function remove_user_extra_file($user_id, $extra_field, $extra_file)
3127
    {
3128
        $extra_file = Security::filter_filename($extra_file);
3129
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
3130
        if (strpos($extra_file, $extra_field) !== false) {
3131
            $path_extra_file = $path_info['dir'].$extra_file;
3132
        } else {
3133
            $path_extra_file = $path_info['dir'].$extra_field.'/'.$extra_file;
3134
        }
3135
        if (is_file($path_extra_file)) {
3136
            unlink($path_extra_file);
3137
3138
            return true;
3139
        }
3140
3141
        return false;
3142
    }
3143
3144
    /**
3145
     * Creates a new extra field.
3146
     *
3147
     * @param string $variable    Field's internal variable name
3148
     * @param int    $fieldType   Field's type
3149
     * @param string $displayText Field's language var name
3150
     * @param string $default     Field's default value
3151
     *
3152
     * @return int
3153
     */
3154
    public static function create_extra_field(
3155
        $variable,
3156
        $fieldType,
3157
        $displayText,
3158
        $default
3159
    ) {
3160
        $extraField = new ExtraField('user');
3161
        $params = [
3162
            'variable' => $variable,
3163
            'field_type' => $fieldType,
3164
            'display_text' => $displayText,
3165
            'default_value' => $default,
3166
        ];
3167
3168
        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...
3169
    }
3170
3171
    /**
3172
     * Check if a field is available.
3173
     *
3174
     * @param string $variable
3175
     *
3176
     * @return bool
3177
     */
3178
    public static function is_extra_field_available($variable)
3179
    {
3180
        $extraField = new ExtraField('user');
3181
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
3182
3183
        return !empty($data) ? true : false;
3184
    }
3185
3186
    /**
3187
     * Gets user extra fields data.
3188
     *
3189
     * @param    int    User ID
3190
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
3191
     * @param    bool    Whether to return invisible fields as well
3192
     * @param    bool    Whether to split multiple-selection fields or not
3193
     *
3194
     * @return array Array of fields => value for the given user
3195
     */
3196
    public static function get_extra_user_data(
3197
        $user_id,
3198
        $prefix = false,
3199
        $allVisibility = true,
3200
        $splitMultiple = false,
3201
        $fieldFilter = null
3202
    ) {
3203
        $user_id = (int) $user_id;
3204
3205
        if (empty($user_id)) {
3206
            return [];
3207
        }
3208
3209
        $extra_data = [];
3210
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
3211
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3212
        $user_id = (int) $user_id;
3213
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
3214
                FROM $t_uf f
3215
                WHERE
3216
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
3217
                ";
3218
        $filter_cond = '';
3219
3220
        if (!$allVisibility) {
3221
            if (isset($fieldFilter)) {
3222
                $fieldFilter = (int) $fieldFilter;
3223
                $filter_cond .= " AND filter = $fieldFilter ";
3224
            }
3225
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
3226
        } else {
3227
            if (isset($fieldFilter)) {
3228
                $fieldFilter = (int) $fieldFilter;
3229
                $sql .= " AND filter = $fieldFilter ";
3230
            }
3231
        }
3232
3233
        $sql .= ' ORDER BY f.field_order';
3234
3235
        $res = Database::query($sql);
3236
        if (Database::num_rows($res) > 0) {
3237
            while ($row = Database::fetch_array($res)) {
3238
                if ($row['type'] == self::USER_FIELD_TYPE_TAG) {
3239
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
3240
                    $extra_data['extra_'.$row['fvar']] = $tags;
3241
                } else {
3242
                    $sqlu = "SELECT value as fval
3243
                            FROM $t_ufv
3244
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
3245
                    $resu = Database::query($sqlu);
3246
                    // get default value
3247
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
3248
                               WHERE id=".$row['id'];
3249
                    $res_df = Database::query($sql_df);
3250
3251
                    if (Database::num_rows($resu) > 0) {
3252
                        $rowu = Database::fetch_array($resu);
3253
                        $fval = $rowu['fval'];
3254
                        if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
3255
                            $fval = explode(';', $rowu['fval']);
3256
                        }
3257
                    } else {
3258
                        $row_df = Database::fetch_array($res_df);
3259
                        $fval = $row_df['fval_df'];
3260
                    }
3261
                    // We get here (and fill the $extra_data array) even if there
3262
                    // is no user with data (we fill it with default values)
3263
                    if ($prefix) {
3264
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
3265
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
3266
                        } else {
3267
                            $extra_data['extra_'.$row['fvar']] = $fval;
3268
                        }
3269
                    } else {
3270
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
3271
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
3272
                        } else {
3273
                            $extra_data[$row['fvar']] = $fval;
3274
                        }
3275
                    }
3276
                }
3277
            }
3278
        }
3279
3280
        return $extra_data;
3281
    }
3282
3283
    /**
3284
     * Get extra user data by field.
3285
     *
3286
     * @param int    user ID
3287
     * @param string the internal variable name of the field
3288
     *
3289
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
3290
     */
3291
    public static function get_extra_user_data_by_field(
3292
        $user_id,
3293
        $field_variable,
3294
        $prefix = false,
3295
        $all_visibility = true,
3296
        $splitmultiple = false
3297
    ) {
3298
        $user_id = (int) $user_id;
3299
3300
        if (empty($user_id)) {
3301
            return [];
3302
        }
3303
3304
        $extra_data = [];
3305
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
3306
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3307
3308
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
3309
                FROM $t_uf f
3310
                WHERE f.variable = '$field_variable' ";
3311
3312
        if (!$all_visibility) {
3313
            $sql .= " AND f.visible_to_self = 1 ";
3314
        }
3315
3316
        $sql .= " AND extra_field_type = ".EntityExtraField::USER_FIELD_TYPE;
3317
        $sql .= " ORDER BY f.field_order ";
3318
3319
        $res = Database::query($sql);
3320
        if (Database::num_rows($res) > 0) {
3321
            while ($row = Database::fetch_array($res)) {
3322
                $sqlu = "SELECT value as fval FROM $t_ufv v
3323
                         INNER JOIN $t_uf f
3324
                         ON (v.field_id = f.id)
3325
                         WHERE
3326
                            extra_field_type = ".EntityExtraField::USER_FIELD_TYPE." AND
3327
                            field_id = ".$row['id']." AND
3328
                            item_id = ".$user_id;
3329
                $resu = Database::query($sqlu);
3330
                $fval = '';
3331
                if (Database::num_rows($resu) > 0) {
3332
                    $rowu = Database::fetch_array($resu);
3333
                    $fval = $rowu['fval'];
3334
                    if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
3335
                        $fval = explode(';', $rowu['fval']);
3336
                    }
3337
                }
3338
                if ($prefix) {
3339
                    $extra_data['extra_'.$row['fvar']] = $fval;
3340
                } else {
3341
                    $extra_data[$row['fvar']] = $fval;
3342
                }
3343
            }
3344
        }
3345
3346
        return $extra_data;
3347
    }
3348
3349
    /**
3350
     * Get the extra field information for a certain field (the options as well).
3351
     *
3352
     * @param string $variable The name of the field we want to know everything about
3353
     *
3354
     * @return array Array containing all the information about the extra profile field
3355
     *               (first level of array contains field details, then 'options' sub-array contains options details,
3356
     *               as returned by the database)
3357
     *
3358
     * @author Julio Montoya
3359
     *
3360
     * @since v1.8.6
3361
     */
3362
    public static function get_extra_field_information_by_name($variable)
3363
    {
3364
        $extraField = new ExtraField('user');
3365
3366
        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...
3367
    }
3368
3369
    /**
3370
     * Get the extra field information for user tag (the options as well).
3371
     *
3372
     * @param int $variable The name of the field we want to know everything about
3373
     *
3374
     * @return array Array containing all the information about the extra profile field
3375
     *               (first level of array contains field details, then 'options' sub-array contains options details,
3376
     *               as returned by the database)
3377
     *
3378
     * @author José Loguercio
3379
     *
3380
     * @since v1.11.0
3381
     */
3382
    public static function get_extra_field_tags_information_by_name($variable)
3383
    {
3384
        $extraField = new ExtraField('user');
3385
3386
        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...
3387
    }
3388
3389
    /**
3390
     * @param string $type
3391
     *
3392
     * @return array
3393
     */
3394
    public static function get_all_extra_field_by_type($type)
3395
    {
3396
        $extraField = new ExtraField('user');
3397
3398
        return $extraField->get_all_extra_field_by_type($type);
3399
    }
3400
3401
    /**
3402
     * Get all the extra field information of a certain field (also the options).
3403
     *
3404
     * @param int $fieldId the ID of the field we want to know everything of
3405
     *
3406
     * @return array $return containing all th information about the extra profile field
3407
     *
3408
     * @author Julio Montoya
3409
     *
3410
     * @deprecated
3411
     * @since v1.8.6
3412
     */
3413
    public static function get_extra_field_information($fieldId)
3414
    {
3415
        $extraField = new ExtraField('user');
3416
3417
        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...
3418
    }
3419
3420
    /**
3421
     * Get extra user data by value.
3422
     *
3423
     * @param string $variable the internal variable name of the field
3424
     * @param string $value    the internal value of the field
3425
     * @param bool   $useLike
3426
     *
3427
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
3428
     */
3429
    public static function get_extra_user_data_by_value($variable, $value, $useLike = false)
3430
    {
3431
        $extraFieldValue = new ExtraFieldValue('user');
3432
        $extraField = new ExtraField('user');
3433
3434
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
3435
3436
        if (false === $info) {
3437
            return [];
3438
        }
3439
3440
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
3441
            $variable,
3442
            $value,
3443
            false,
3444
            false,
3445
            true,
3446
            $useLike
3447
        );
3448
3449
        $result = [];
3450
        if (!empty($data)) {
3451
            foreach ($data as $item) {
3452
                $result[] = $item['item_id'];
3453
            }
3454
        }
3455
3456
        return $result;
3457
    }
3458
3459
    /**
3460
     * Get extra user data by tags value.
3461
     *
3462
     * @param int    $fieldId the ID of the field we want to know everything of
3463
     * @param string $tag     the tag name for search
3464
     *
3465
     * @return array with extra data info of a user
3466
     *
3467
     * @author José Loguercio
3468
     *
3469
     * @since v1.11.0
3470
     */
3471
    public static function get_extra_user_data_by_tags($fieldId, $tag)
3472
    {
3473
        $extraField = new ExtraField('user');
3474
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
3475
        $array = [];
3476
        foreach ($result as $index => $user) {
3477
            $array[] = $user['user_id'];
3478
        }
3479
3480
        return $array;
3481
    }
3482
3483
    /**
3484
     * Get extra user data by field variable.
3485
     *
3486
     * @param string $variable field variable
3487
     *
3488
     * @return array data
3489
     */
3490
    public static function get_extra_user_data_by_field_variable($variable)
3491
    {
3492
        $extraInfo = self::get_extra_field_information_by_name($variable);
3493
        $field_id = (int) $extraInfo['id'];
3494
3495
        $extraField = new ExtraFieldValue('user');
3496
        $data = $extraField->getValuesByFieldId($field_id);
3497
3498
        if (!empty($data)) {
3499
            foreach ($data as $row) {
3500
                $user_id = $row['item_id'];
3501
                $data[$user_id] = $row;
3502
            }
3503
        }
3504
3505
        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...
3506
    }
3507
3508
    /**
3509
     * Get extra user data tags by field variable.
3510
     *
3511
     * @param string $variable field variable
3512
     *
3513
     * @return array
3514
     */
3515
    public static function get_extra_user_data_for_tags($variable)
3516
    {
3517
        $data = self::get_extra_field_tags_information_by_name($variable);
3518
3519
        return $data;
3520
    }
3521
3522
    /**
3523
     * Gives a list of [session_category][session_id] for the current user.
3524
     *
3525
     * @param int  $user_id
3526
     * @param bool $is_time_over                 whether to fill the first element or not
3527
     *                                           (to give space for courses out of categories)
3528
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
3529
     * @param bool $ignoreTimeLimit              ignore time start/end
3530
     * @param bool $getCount
3531
     *
3532
     * @return array list of statuses [session_category][session_id]
3533
     *
3534
     * @todo ensure multiple access urls are managed correctly
3535
     */
3536
    public static function get_sessions_by_category(
3537
        $user_id,
3538
        $is_time_over = true,
3539
        $ignore_visibility_for_admins = false,
3540
        $ignoreTimeLimit = false,
3541
        $getCount = false
3542
    ) {
3543
        $user_id = (int) $user_id;
3544
3545
        if (empty($user_id)) {
3546
            return [];
3547
        }
3548
3549
        $allowOrder = api_get_configuration_value('session_list_order');
3550
        $position = '';
3551
        if ($allowOrder) {
3552
            $position = ', s.position AS position ';
3553
        }
3554
3555
        // Get the list of sessions per user
3556
        $now = new DateTime('now', new DateTimeZone('UTC'));
3557
3558
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
3559
        // join would not catch session-courses where the user is general
3560
        // session coach but which do not have students nor coaches registered
3561
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
3562
3563
        if (!$getCount) {
3564
            $dqlSelect = " DISTINCT
3565
                s.id,
3566
                s.name,
3567
                s.accessStartDate AS access_start_date,
3568
                s.accessEndDate AS access_end_date,
3569
                s.duration,
3570
                sc.id AS session_category_id,
3571
                sc.name AS session_category_name,
3572
                sc.dateStart AS session_category_date_start,
3573
                sc.dateEnd AS session_category_date_end,
3574
                s.coachAccessStartDate AS coach_access_start_date,
3575
                s.coachAccessEndDate AS coach_access_end_date,
3576
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
3577
                $position
3578
            ";
3579
        }
3580
3581
        $dql = "SELECT $dqlSelect
3582
                FROM ChamiloCoreBundle:Session AS s
3583
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
3584
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.sessionId = s.id
3585
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc ";
3586
3587
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
3588
        // is awfully inefficient for large sets of data (1m25s for 58K
3589
        // sessions, BT#14115) but executing a similar query twice and grouping
3590
        // the results afterwards in PHP takes about 1/1000th of the time
3591
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
3592
        $dqlStudent = $dql.' WHERE scu.user = :user AND url.accessUrlId = :url ';
3593
        $dqlCoach = $dql.' WHERE s.generalCoach = :user AND url.accessUrlId = :url ';
3594
3595
        // Default order
3596
        $order = 'ORDER BY sc.name, s.name';
3597
3598
        // Order by date if showing all sessions
3599
        $showAllSessions = api_get_configuration_value('show_all_sessions_on_my_course_page') === true;
3600
        if ($showAllSessions) {
3601
            $order = 'ORDER BY s.accessStartDate';
3602
        }
3603
3604
        // Order by position
3605
        if ($allowOrder) {
3606
            $order = 'ORDER BY s.position';
3607
        }
3608
3609
        // Order by dates according to settings
3610
        $orderBySettings = api_get_configuration_value('my_courses_session_order');
3611
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
3612
            $field = $orderBySettings['field'];
3613
            $orderSetting = $orderBySettings['order'];
3614
            switch ($field) {
3615
                case 'start_date':
3616
                    $order = " ORDER BY s.accessStartDate $orderSetting";
3617
                    break;
3618
                case 'end_date':
3619
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
3620
                    if ($orderSetting === 'asc') {
3621
                        // Put null values at the end
3622
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
3623
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
3624
                    }
3625
                    break;
3626
                case 'name':
3627
                    $order = " ORDER BY s.name $orderSetting ";
3628
                    break;
3629
            }
3630
        }
3631
3632
        $dqlStudent .= $order;
3633
        $dqlCoach .= $order;
3634
3635
        $accessUrlId = api_get_current_access_url_id();
3636
        $dqlStudent = Database::getManager()
3637
            ->createQuery($dqlStudent)
3638
            ->setParameters(
3639
                ['user' => $user_id, 'url' => $accessUrlId]
3640
            )
3641
        ;
3642
        $dqlCoach = Database::getManager()
3643
            ->createQuery($dqlCoach)
3644
            ->setParameters(
3645
                ['user' => $user_id, 'url' => $accessUrlId]
3646
            )
3647
        ;
3648
3649
        if ($getCount) {
3650
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
3651
        }
3652
3653
        $sessionDataStudent = $dqlStudent->getResult();
3654
        $sessionDataCoach = $dqlCoach->getResult();
3655
3656
        $sessionData = [];
3657
        // First fill $sessionData with student sessions
3658
        if (!empty($sessionDataStudent)) {
3659
            foreach ($sessionDataStudent as $row) {
3660
                $sessionData[$row['id']] = $row;
3661
            }
3662
        }
3663
3664
        // Overwrite session data of the user as a student with session data
3665
        // of the user as a coach.
3666
        // There shouldn't be such duplicate rows, but just in case...
3667
        if (!empty($sessionDataCoach)) {
3668
            foreach ($sessionDataCoach as $row) {
3669
                $sessionData[$row['id']] = $row;
3670
            }
3671
        }
3672
3673
        $collapsable = api_get_configuration_value('allow_user_session_collapsable');
3674
        $extraField = new ExtraFieldValue('session');
3675
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
3676
3677
        if (empty($sessionData)) {
3678
            return [];
3679
        }
3680
3681
        $categories = [];
3682
        foreach ($sessionData as $row) {
3683
            $session_id = $row['id'];
3684
            $coachList = SessionManager::getCoachesBySession($session_id);
3685
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
3686
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
3687
            $courseList = self::get_courses_list_by_session($user_id, $session_id);
3688
3689
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
3690
3691
            // User portal filters:
3692
            if (false === $ignoreTimeLimit) {
3693
                if ($is_time_over) {
3694
                    // History
3695
                    if ($row['duration']) {
3696
                        if ($daysLeft >= 0) {
3697
                            continue;
3698
                        }
3699
                    } else {
3700
                        if (empty($row['access_end_date'])) {
3701
                            continue;
3702
                        } else {
3703
                            if ($row['access_end_date'] > $now) {
3704
                                continue;
3705
                            }
3706
                        }
3707
                    }
3708
                } else {
3709
                    // Current user portal
3710
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
3711
                    $isCoachOfCourse = in_array($user_id, $coachList);
3712
3713
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
3714
                        // Teachers can access the session depending in the access_coach date
3715
                    } else {
3716
                        if ($row['duration']) {
3717
                            if ($daysLeft <= 0) {
3718
                                continue;
3719
                            }
3720
                        } else {
3721
                            if (isset($row['access_end_date']) &&
3722
                                !empty($row['access_end_date'])
3723
                            ) {
3724
                                if ($row['access_end_date'] <= $now) {
3725
                                    continue;
3726
                                }
3727
                            }
3728
                        }
3729
                    }
3730
                }
3731
            }
3732
3733
            $categories[$row['session_category_id']]['session_category'] = [
3734
                'id' => $row['session_category_id'],
3735
                'name' => $row['session_category_name'],
3736
                'date_start' => $categoryStart,
3737
                'date_end' => $categoryEnd,
3738
            ];
3739
3740
            $visibility = api_get_session_visibility(
3741
                $session_id,
3742
                null,
3743
                $ignore_visibility_for_admins
3744
            );
3745
3746
            if ($visibility != SESSION_VISIBLE) {
3747
                // Course Coach session visibility.
3748
                $blockedCourseCount = 0;
3749
                $closedVisibilityList = [
3750
                    COURSE_VISIBILITY_CLOSED,
3751
                    COURSE_VISIBILITY_HIDDEN,
3752
                ];
3753
3754
                foreach ($courseList as $course) {
3755
                    // Checking session visibility
3756
                    $sessionCourseVisibility = api_get_session_visibility(
3757
                        $session_id,
3758
                        $course['real_id'],
3759
                        $ignore_visibility_for_admins
3760
                    );
3761
3762
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
3763
                    if ($courseIsVisible === false || $sessionCourseVisibility == SESSION_INVISIBLE) {
3764
                        $blockedCourseCount++;
3765
                    }
3766
                }
3767
3768
                // If all courses are blocked then no show in the list.
3769
                if ($blockedCourseCount === count($courseList)) {
3770
                    $visibility = SESSION_INVISIBLE;
3771
                } else {
3772
                    $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...
3773
                }
3774
            }
3775
3776
            switch ($visibility) {
3777
                case SESSION_VISIBLE_READ_ONLY:
3778
                case SESSION_VISIBLE:
3779
                case SESSION_AVAILABLE:
3780
                    break;
3781
                case SESSION_INVISIBLE:
3782
                    if ($ignore_visibility_for_admins === false) {
3783
                        continue 2;
3784
                    }
3785
            }
3786
3787
            $collapsed = '';
3788
            $collapsedAction = '';
3789
            if ($collapsable) {
3790
                $collapsableData = SessionManager::getCollapsableData(
3791
                    $user_id,
3792
                    $session_id,
3793
                    $extraField,
3794
                    $collapsableLink
3795
                );
3796
                $collapsed = $collapsableData['collapsed'];
3797
                $collapsedAction = $collapsableData['collapsable_link'];
3798
            }
3799
3800
            $categories[$row['session_category_id']]['sessions'][] = [
3801
                'session_name' => $row['name'],
3802
                'session_id' => $row['id'],
3803
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
3804
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
3805
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
3806
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
3807
                'courses' => $courseList,
3808
                'collapsed' => $collapsed,
3809
                'collapsable_link' => $collapsedAction,
3810
                'duration' => $row['duration'],
3811
            ];
3812
        }
3813
3814
        return $categories;
3815
    }
3816
3817
    /**
3818
     * Gives a list of [session_id-course_code] => [status] for the current user.
3819
     *
3820
     * @param int $user_id
3821
     * @param int $sessionLimit
3822
     *
3823
     * @return array list of statuses (session_id-course_code => status)
3824
     */
3825
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
3826
    {
3827
        // Database Table Definitions
3828
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3829
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
3830
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3831
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3832
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3833
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3834
3835
        $user_id = (int) $user_id;
3836
3837
        if (empty($user_id)) {
3838
            return [];
3839
        }
3840
3841
        // We filter the courses from the URL
3842
        $join_access_url = $where_access_url = '';
3843
        if (api_get_multiple_access_url()) {
3844
            $access_url_id = api_get_current_access_url_id();
3845
            if ($access_url_id != -1) {
3846
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3847
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
3848
                $where_access_url = " AND access_url_id = $access_url_id ";
3849
            }
3850
        }
3851
3852
        // Courses in which we subscribed out of any session
3853
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3854
3855
        $sql = "SELECT
3856
                    course.code,
3857
                    course_rel_user.status course_rel_status,
3858
                    course_rel_user.sort sort,
3859
                    course_rel_user.user_course_cat user_course_cat
3860
                 FROM $tbl_course_user course_rel_user
3861
                 LEFT JOIN $tbl_course course
3862
                 ON course.id = course_rel_user.c_id
3863
                 LEFT JOIN $tbl_user_course_category user_course_category
3864
                 ON course_rel_user.user_course_cat = user_course_category.id
3865
                 $join_access_url
3866
                 WHERE
3867
                    course_rel_user.user_id = '".$user_id."' AND
3868
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
3869
                    $where_access_url
3870
                 ORDER BY user_course_category.sort, course_rel_user.sort, course.title ASC";
3871
3872
        $course_list_sql_result = Database::query($sql);
3873
3874
        $personal_course_list = [];
3875
        if (Database::num_rows($course_list_sql_result) > 0) {
3876
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3877
                $course_info = api_get_course_info($result_row['code']);
3878
                $result_row['course_info'] = $course_info;
3879
                $personal_course_list[] = $result_row;
3880
            }
3881
        }
3882
3883
        $coachCourseConditions = '';
3884
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
3885
        if (api_is_allowed_to_create_course()) {
3886
            $sessionListFromCourseCoach = [];
3887
            $sql = " SELECT DISTINCT session_id
3888
                    FROM $tbl_session_course_user
3889
                    WHERE user_id = $user_id AND status = 2 ";
3890
3891
            $result = Database::query($sql);
3892
            if (Database::num_rows($result)) {
3893
                $result = Database::store_result($result);
3894
                foreach ($result as $session) {
3895
                    $sessionListFromCourseCoach[] = $session['session_id'];
3896
                }
3897
            }
3898
            if (!empty($sessionListFromCourseCoach)) {
3899
                $condition = implode("','", $sessionListFromCourseCoach);
3900
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
3901
            }
3902
        }
3903
3904
        // Get the list of sessions where the user is subscribed
3905
        // This is divided into two different queries
3906
        $sessions = [];
3907
        $sessionLimitRestriction = '';
3908
        if (!empty($sessionLimit)) {
3909
            $sessionLimit = (int) $sessionLimit;
3910
            $sessionLimitRestriction = "LIMIT $sessionLimit";
3911
        }
3912
3913
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
3914
                FROM $tbl_session_user su INNER JOIN $tbl_session s
3915
                ON (s.id = su.session_id)
3916
                WHERE (
3917
                    su.user_id = $user_id AND
3918
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
3919
                )
3920
                $coachCourseConditions
3921
                ORDER BY access_start_date, access_end_date, name
3922
                $sessionLimitRestriction
3923
        ";
3924
3925
        $result = Database::query($sql);
3926
        if (Database::num_rows($result) > 0) {
3927
            while ($row = Database::fetch_assoc($result)) {
3928
                $sessions[$row['id']] = $row;
3929
            }
3930
        }
3931
3932
        $sql = "SELECT DISTINCT
3933
                id, name, access_start_date, access_end_date
3934
                FROM $tbl_session s
3935
                WHERE (
3936
                    id_coach = $user_id
3937
                )
3938
                $coachCourseConditions
3939
                ORDER BY access_start_date, access_end_date, name";
3940
3941
        $result = Database::query($sql);
3942
        if (Database::num_rows($result) > 0) {
3943
            while ($row = Database::fetch_assoc($result)) {
3944
                if (empty($sessions[$row['id']])) {
3945
                    $sessions[$row['id']] = $row;
3946
                }
3947
            }
3948
        }
3949
3950
        if (api_is_allowed_to_create_course()) {
3951
            foreach ($sessions as $enreg) {
3952
                $session_id = $enreg['id'];
3953
                $session_visibility = api_get_session_visibility($session_id);
3954
3955
                if ($session_visibility == SESSION_INVISIBLE) {
3956
                    continue;
3957
                }
3958
3959
                // This query is horribly slow when more than a few thousand
3960
                // users and just a few sessions to which they are subscribed
3961
                $sql = "SELECT DISTINCT
3962
                        course.code code,
3963
                        course.title i,
3964
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
3965
                        email, course.course_language l,
3966
                        1 sort,
3967
                        category_code user_course_cat,
3968
                        access_start_date,
3969
                        access_end_date,
3970
                        session.id as session_id,
3971
                        session.name as session_name
3972
                    FROM $tbl_session_course_user as session_course_user
3973
                    INNER JOIN $tbl_course AS course
3974
                        ON course.id = session_course_user.c_id
3975
                    INNER JOIN $tbl_session as session
3976
                        ON session.id = session_course_user.session_id
3977
                    LEFT JOIN $tbl_user as user
3978
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
3979
                    WHERE
3980
                        session_course_user.session_id = $session_id AND (
3981
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
3982
                            OR session.id_coach = $user_id
3983
                        )
3984
                    ORDER BY i";
3985
                $course_list_sql_result = Database::query($sql);
3986
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3987
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3988
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3989
                    $personal_course_list[$key] = $result_row;
3990
                }
3991
            }
3992
        }
3993
3994
        foreach ($sessions as $enreg) {
3995
            $session_id = $enreg['id'];
3996
            $session_visibility = api_get_session_visibility($session_id);
3997
            if ($session_visibility == SESSION_INVISIBLE) {
3998
                continue;
3999
            }
4000
4001
            /* This query is very similar to the above query,
4002
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
4003
            $sql = "SELECT DISTINCT
4004
                course.code code,
4005
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
4006
                email,
4007
                course.course_language l,
4008
                1 sort,
4009
                category_code user_course_cat,
4010
                access_start_date,
4011
                access_end_date,
4012
                session.id as session_id,
4013
                session.name as session_name,
4014
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
4015
            FROM $tbl_session_course_user as session_course_user
4016
            INNER JOIN $tbl_course AS course
4017
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
4018
            INNER JOIN $tbl_session as session
4019
            ON session_course_user.session_id = session.id
4020
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
4021
            WHERE session_course_user.user_id = $user_id
4022
            ORDER BY i";
4023
4024
            $course_list_sql_result = Database::query($sql);
4025
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
4026
                $result_row['course_info'] = api_get_course_info($result_row['code']);
4027
                $key = $result_row['session_id'].' - '.$result_row['code'];
4028
                if (!isset($personal_course_list[$key])) {
4029
                    $personal_course_list[$key] = $result_row;
4030
                }
4031
            }
4032
        }
4033
4034
        return $personal_course_list;
4035
    }
4036
4037
    /**
4038
     * Gives a list of courses for the given user in the given session.
4039
     *
4040
     * @param int $user_id
4041
     * @param int $session_id
4042
     *
4043
     * @return array list of statuses (session_id-course_code => status)
4044
     */
4045
    public static function get_courses_list_by_session($user_id, $session_id)
4046
    {
4047
        // Database Table Definitions
4048
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4049
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
4050
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4051
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4052
4053
        $user_id = (int) $user_id;
4054
        $session_id = (int) $session_id;
4055
        // We filter the courses from the URL
4056
        $join_access_url = $where_access_url = '';
4057
        if (api_get_multiple_access_url()) {
4058
            $urlId = api_get_current_access_url_id();
4059
            if (-1 != $urlId) {
4060
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4061
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
4062
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
4063
            }
4064
        }
4065
4066
        $exlearnerCondition = "";
4067
        if (false !== api_get_configuration_value('user_edition_extra_field_to_check')) {
4068
            $exlearnerCondition = " AND scu.status NOT IN(".COURSE_EXLEARNER.")";
4069
        }
4070
4071
        /* This query is very similar to the query below, but it will check the
4072
        session_rel_course_user table if there are courses registered
4073
        to our user or not */
4074
        $sql = "SELECT DISTINCT
4075
                    c.title,
4076
                    c.visibility,
4077
                    c.id as real_id,
4078
                    c.code as course_code,
4079
                    sc.position,
4080
                    c.unsubscribe
4081
                FROM $tbl_session_course_user as scu
4082
                INNER JOIN $tbl_session_course sc
4083
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
4084
                INNER JOIN $tableCourse as c
4085
                ON (scu.c_id = c.id)
4086
                $join_access_url
4087
                WHERE
4088
                    scu.user_id = $user_id AND
4089
                    scu.session_id = $session_id
4090
                    $where_access_url
4091
                    $exlearnerCondition
4092
                ORDER BY sc.position ASC";
4093
4094
        $myCourseList = [];
4095
        $courses = [];
4096
        $result = Database::query($sql);
4097
        if (Database::num_rows($result) > 0) {
4098
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
4099
                $result_row['status'] = 5;
4100
                if (!in_array($result_row['real_id'], $courses)) {
4101
                    $position = $result_row['position'];
4102
                    if (!isset($myCourseList[$position])) {
4103
                        $myCourseList[$position] = $result_row;
4104
                    } else {
4105
                        $myCourseList[] = $result_row;
4106
                    }
4107
                    $courses[] = $result_row['real_id'];
4108
                }
4109
            }
4110
        }
4111
4112
        if (api_is_allowed_to_create_course()) {
4113
            $sql = "SELECT DISTINCT
4114
                        c.title,
4115
                        c.visibility,
4116
                        c.id as real_id,
4117
                        c.code as course_code,
4118
                        sc.position,
4119
                        c.unsubscribe
4120
                    FROM $tbl_session_course_user as scu
4121
                    INNER JOIN $tbl_session as s
4122
                    ON (scu.session_id = s.id)
4123
                    INNER JOIN $tbl_session_course sc
4124
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
4125
                    INNER JOIN $tableCourse as c
4126
                    ON (scu.c_id = c.id)
4127
                    $join_access_url
4128
                    WHERE
4129
                      s.id = $session_id AND
4130
                      (
4131
                        (scu.user_id = $user_id AND scu.status = 2) OR
4132
                        s.id_coach = $user_id
4133
                      )
4134
                    $where_access_url
4135
                    $exlearnerCondition
4136
                    ORDER BY sc.position ASC";
4137
            $result = Database::query($sql);
4138
4139
            if (Database::num_rows($result) > 0) {
4140
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
4141
                    $result_row['status'] = 2;
4142
                    if (!in_array($result_row['real_id'], $courses)) {
4143
                        $position = $result_row['position'];
4144
                        if (!isset($myCourseList[$position])) {
4145
                            $myCourseList[$position] = $result_row;
4146
                        } else {
4147
                            $myCourseList[] = $result_row;
4148
                        }
4149
                        $courses[] = $result_row['real_id'];
4150
                    }
4151
                }
4152
            }
4153
        }
4154
4155
        if (api_is_drh()) {
4156
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
4157
            $sessionList = array_keys($sessionList);
4158
            if (in_array($session_id, $sessionList)) {
4159
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
4160
                if (!empty($courseList)) {
4161
                    foreach ($courseList as $course) {
4162
                        if (!in_array($course['id'], $courses)) {
4163
                            $position = $course['position'];
4164
                            if (!isset($myCourseList[$position])) {
4165
                                $myCourseList[$position] = $course;
4166
                            } else {
4167
                                $myCourseList[] = $course;
4168
                            }
4169
                        }
4170
                    }
4171
                }
4172
            }
4173
        } else {
4174
            //check if user is general coach for this session
4175
            $sessionInfo = api_get_session_info($session_id);
4176
            if ($sessionInfo['id_coach'] == $user_id) {
4177
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
4178
                if (!empty($courseList)) {
4179
                    foreach ($courseList as $course) {
4180
                        if (!in_array($course['id'], $courses)) {
4181
                            $position = $course['position'];
4182
                            if (!isset($myCourseList[$position])) {
4183
                                $myCourseList[$position] = $course;
4184
                            } else {
4185
                                $myCourseList[] = $course;
4186
                            }
4187
                        }
4188
                    }
4189
                }
4190
            }
4191
        }
4192
4193
        if (!empty($myCourseList)) {
4194
            ksort($myCourseList);
4195
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
4196
            if (empty($checkPosition)) {
4197
                // The session course list doesn't have any position,
4198
                // then order the course list by course code.
4199
                $orderByCode = array_column($myCourseList, 'course_code');
4200
                sort($orderByCode, SORT_NATURAL);
4201
                $newCourseList = [];
4202
                foreach ($orderByCode as $code) {
4203
                    foreach ($myCourseList as $course) {
4204
                        if ($code === $course['course_code']) {
4205
                            $newCourseList[] = $course;
4206
                            break;
4207
                        }
4208
                    }
4209
                }
4210
                $myCourseList = $newCourseList;
4211
            }
4212
        }
4213
4214
        return $myCourseList;
4215
    }
4216
4217
    /**
4218
     * Get user id from a username.
4219
     *
4220
     * @param string $username
4221
     *
4222
     * @return int User ID (or false if not found)
4223
     */
4224
    public static function get_user_id_from_username($username)
4225
    {
4226
        if (empty($username)) {
4227
            return false;
4228
        }
4229
        $username = trim($username);
4230
        $username = Database::escape_string($username);
4231
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
4232
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
4233
        $res = Database::query($sql);
4234
4235
        if ($res === false) {
4236
            return false;
4237
        }
4238
        if (Database::num_rows($res) !== 1) {
4239
            return false;
4240
        }
4241
        $row = Database::fetch_array($res);
4242
4243
        return $row['id'];
4244
    }
4245
4246
    /**
4247
     * Get the users files upload from his share_folder.
4248
     *
4249
     * @param string $user_id      User ID
4250
     * @param string $course       course directory
4251
     * @param string $resourceType resource type: images, all
4252
     *
4253
     * @return string
4254
     */
4255
    public static function get_user_upload_files_by_course(
4256
        $user_id,
4257
        $course,
4258
        $resourceType = 'all'
4259
    ) {
4260
        $return = '';
4261
        $user_id = (int) $user_id;
4262
4263
        if (!empty($user_id) && !empty($course)) {
4264
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
4265
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
4266
            $file_list = [];
4267
4268
            if (is_dir($path)) {
4269
                $handle = opendir($path);
4270
                while ($file = readdir($handle)) {
4271
                    if ($file == '.' || $file == '..' || $file == '.htaccess' || is_dir($path.$file)) {
4272
                        continue; // skip current/parent directory and .htaccess
4273
                    }
4274
                    $file_list[] = $file;
4275
                }
4276
                if (count($file_list) > 0) {
4277
                    $return = "<h4>$course</h4>";
4278
                    $return .= '<ul class="thumbnails">';
4279
                }
4280
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
4281
                foreach ($file_list as $file) {
4282
                    if ($resourceType == 'all') {
4283
                        $return .= '<li>
4284
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
4285
                    } elseif ($resourceType == 'images') {
4286
                        //get extension
4287
                        $ext = explode('.', $file);
4288
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
4289
                            $return .= '<li class="span2">
4290
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
4291
                                                <img src="'.$web_path.urlencode($file).'" >
4292
                                            </a>
4293
                                        </li>';
4294
                        }
4295
                    }
4296
                }
4297
                if (count($file_list) > 0) {
4298
                    $return .= '</ul>';
4299
                }
4300
            }
4301
        }
4302
4303
        return $return;
4304
    }
4305
4306
    /**
4307
     * Gets the API key (or keys) and return them into an array.
4308
     *
4309
     * @param int     Optional user id (defaults to the result of api_get_user_id())
4310
     * @param string $api_service
4311
     *
4312
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
4313
     */
4314
    public static function get_api_keys($user_id = null, $api_service = 'dokeos')
4315
    {
4316
        if ($user_id != strval(intval($user_id))) {
4317
            return false;
4318
        }
4319
        if (empty($user_id)) {
4320
            $user_id = api_get_user_id();
4321
        }
4322
        if ($user_id === false) {
4323
            return false;
4324
        }
4325
        $service_name = Database::escape_string($api_service);
4326
        if (is_string($service_name) === false) {
4327
            return false;
4328
        }
4329
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4330
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
4331
        $res = Database::query($sql);
4332
        if ($res === false) {
4333
            return false;
4334
        } //error during query
4335
        $num = Database::num_rows($res);
4336
        if ($num == 0) {
4337
            return false;
4338
        }
4339
        $list = [];
4340
        while ($row = Database::fetch_array($res)) {
4341
            $list[$row['id']] = $row['api_key'];
4342
        }
4343
4344
        return $list;
4345
    }
4346
4347
    /**
4348
     * Adds a new API key to the users' account.
4349
     *
4350
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
4351
     * @param string $api_service
4352
     *
4353
     * @return bool True on success, false on failure
4354
     */
4355
    public static function add_api_key($user_id = null, $api_service = 'dokeos')
4356
    {
4357
        if ($user_id != strval(intval($user_id))) {
4358
            return false;
4359
        }
4360
        if (empty($user_id)) {
4361
            $user_id = api_get_user_id();
4362
        }
4363
        if ($user_id === false) {
4364
            return false;
4365
        }
4366
        $service_name = Database::escape_string($api_service);
4367
        if (is_string($service_name) === false) {
4368
            return false;
4369
        }
4370
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4371
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
4372
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
4373
        $res = Database::query($sql);
4374
        if ($res === false) {
4375
            return false;
4376
        } //error during query
4377
        $num = Database::insert_id();
4378
4379
        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...
4380
    }
4381
4382
    /**
4383
     * Deletes an API key from the user's account.
4384
     *
4385
     * @param   int     API key's internal ID
4386
     *
4387
     * @return bool True on success, false on failure
4388
     */
4389
    public static function delete_api_key($key_id)
4390
    {
4391
        if ($key_id != strval(intval($key_id))) {
4392
            return false;
4393
        }
4394
        if ($key_id === false) {
4395
            return false;
4396
        }
4397
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4398
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
4399
        $res = Database::query($sql);
4400
        if ($res === false) {
4401
            return false;
4402
        } //error during query
4403
        $num = Database::num_rows($res);
4404
        if ($num !== 1) {
4405
            return false;
4406
        }
4407
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
4408
        $res = Database::query($sql);
4409
        if ($res === false) {
4410
            return false;
4411
        } //error during query
4412
4413
        return true;
4414
    }
4415
4416
    /**
4417
     * Regenerate an API key from the user's account.
4418
     *
4419
     * @param   int     user ID (defaults to the results of api_get_user_id())
4420
     * @param   string  API key's internal ID
4421
     *
4422
     * @return int num
4423
     */
4424
    public static function update_api_key($user_id, $api_service)
4425
    {
4426
        if ($user_id != strval(intval($user_id))) {
4427
            return false;
4428
        }
4429
        if ($user_id === false) {
4430
            return false;
4431
        }
4432
        $service_name = Database::escape_string($api_service);
4433
        if (is_string($service_name) === false) {
4434
            return false;
4435
        }
4436
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4437
        $sql = "SELECT id FROM $t_api
4438
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
4439
        $res = Database::query($sql);
4440
        $num = Database::num_rows($res);
4441
        if ($num == 1) {
4442
            $id_key = Database::fetch_array($res, 'ASSOC');
4443
            self::delete_api_key($id_key['id']);
4444
            $num = self::add_api_key($user_id, $api_service);
4445
        } elseif ($num == 0) {
4446
            $num = self::add_api_key($user_id, $api_service);
4447
        }
4448
4449
        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...
4450
    }
4451
4452
    /**
4453
     * @param   int     user ID (defaults to the results of api_get_user_id())
4454
     * @param   string    API key's internal ID
4455
     *
4456
     * @return int row ID, or return false if not found
4457
     */
4458
    public static function get_api_key_id($user_id, $api_service)
4459
    {
4460
        if ($user_id != strval(intval($user_id))) {
4461
            return false;
4462
        }
4463
        if ($user_id === false) {
4464
            return false;
4465
        }
4466
        if (empty($api_service)) {
4467
            return false;
4468
        }
4469
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4470
        $api_service = Database::escape_string($api_service);
4471
        $sql = "SELECT id FROM $t_api
4472
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
4473
        $res = Database::query($sql);
4474
        if (Database::num_rows($res) < 1) {
4475
            return false;
4476
        }
4477
        $row = Database::fetch_array($res, 'ASSOC');
4478
4479
        return $row['id'];
4480
    }
4481
4482
    /**
4483
     * Checks if a user_id is platform admin.
4484
     *
4485
     * @param   int user ID
4486
     *
4487
     * @return bool True if is admin, false otherwise
4488
     *
4489
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
4490
     */
4491
    public static function is_admin($user_id)
4492
    {
4493
        $user_id = (int) $user_id;
4494
        if (empty($user_id)) {
4495
            return false;
4496
        }
4497
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
4498
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
4499
        $res = Database::query($sql);
4500
4501
        return Database::num_rows($res) === 1;
4502
    }
4503
4504
    /**
4505
     * Get the total count of users.
4506
     *
4507
     * @param int $status        Status of users to be counted
4508
     * @param int $access_url_id Access URL ID (optional)
4509
     * @param int $active
4510
     *
4511
     * @return mixed Number of users or false on error
4512
     */
4513
    public static function get_number_of_users($status = 0, $access_url_id = 1, $active = null)
4514
    {
4515
        $t_u = Database::get_main_table(TABLE_MAIN_USER);
4516
        $t_a = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4517
4518
        if (api_is_multiple_url_enabled()) {
4519
            $sql = "SELECT count(u.id)
4520
                    FROM $t_u u
4521
                    INNER JOIN $t_a url_user
4522
                    ON (u.id = url_user.user_id)
4523
                    WHERE url_user.access_url_id = $access_url_id
4524
            ";
4525
        } else {
4526
            $sql = "SELECT count(u.id)
4527
                    FROM $t_u u
4528
                    WHERE 1 = 1 ";
4529
        }
4530
4531
        if (is_int($status) && $status > 0) {
4532
            $status = (int) $status;
4533
            $sql .= " AND u.status = $status ";
4534
        }
4535
4536
        if ($active !== null) {
4537
            $active = (int) $active;
4538
            $sql .= " AND u.active = $active ";
4539
        }
4540
4541
        $res = Database::query($sql);
4542
        if (Database::num_rows($res) === 1) {
4543
            return (int) Database::result($res, 0, 0);
4544
        }
4545
4546
        return false;
4547
    }
4548
4549
    /**
4550
     * Gets the tags of a specific field_id
4551
     * USER TAGS.
4552
     *
4553
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
4554
     *
4555
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
4556
     *    Called it "books" for example.
4557
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
4558
     * 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
4559
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
4560
     * 5. Test and enjoy.
4561
     *
4562
     * @param string $tag
4563
     * @param int    $field_id      field_id
4564
     * @param string $return_format how we are going to result value in array or in a string (json)
4565
     * @param $limit
4566
     *
4567
     * @return mixed
4568
     */
4569
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
4570
    {
4571
        // database table definition
4572
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4573
        $field_id = (int) $field_id;
4574
        $limit = (int) $limit;
4575
        $tag = trim(Database::escape_string($tag));
4576
4577
        // all the information of the field
4578
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
4579
                WHERE field_id = $field_id AND tag LIKE '$tag%'
4580
                ORDER BY tag
4581
                LIMIT $limit";
4582
        $result = Database::query($sql);
4583
        $return = [];
4584
        if (Database::num_rows($result) > 0) {
4585
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4586
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
4587
            }
4588
        }
4589
        if ($return_format === 'json') {
4590
            $return = json_encode($return);
4591
        }
4592
4593
        return $return;
4594
    }
4595
4596
    /**
4597
     * @param int $field_id
4598
     * @param int $limit
4599
     *
4600
     * @return array
4601
     */
4602
    public static function get_top_tags($field_id, $limit = 100)
4603
    {
4604
        // database table definition
4605
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4606
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4607
        $field_id = (int) $field_id;
4608
        $limit = (int) $limit;
4609
        // all the information of the field
4610
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
4611
                INNER JOIN $table_user_tag ut
4612
                ON (ut.id = uv.tag_id)
4613
                WHERE field_id = $field_id
4614
                GROUP BY tag_id
4615
                ORDER BY count DESC
4616
                LIMIT $limit";
4617
        $result = Database::query($sql);
4618
        $return = [];
4619
        if (Database::num_rows($result) > 0) {
4620
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4621
                $return[] = $row;
4622
            }
4623
        }
4624
4625
        return $return;
4626
    }
4627
4628
    /**
4629
     * Get user's tags.
4630
     *
4631
     * @param int $user_id
4632
     * @param int $field_id
4633
     *
4634
     * @return array
4635
     */
4636
    public static function get_user_tags($user_id, $field_id)
4637
    {
4638
        // database table definition
4639
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4640
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4641
        $field_id = (int) $field_id;
4642
        $user_id = (int) $user_id;
4643
4644
        // all the information of the field
4645
        $sql = "SELECT ut.id, tag, count
4646
                FROM $table_user_tag ut
4647
                INNER JOIN $table_user_tag_values uv
4648
                ON (uv.tag_id=ut.ID)
4649
                WHERE field_id = $field_id AND user_id = $user_id
4650
                ORDER BY tag";
4651
        $result = Database::query($sql);
4652
        $return = [];
4653
        if (Database::num_rows($result) > 0) {
4654
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4655
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
4656
            }
4657
        }
4658
4659
        return $return;
4660
    }
4661
4662
    /**
4663
     * Get user's tags.
4664
     *
4665
     * @param int  $user_id
4666
     * @param int  $field_id
4667
     * @param bool $show_links show links or not
4668
     *
4669
     * @return string
4670
     */
4671
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
4672
    {
4673
        // database table definition
4674
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4675
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4676
        $field_id = (int) $field_id;
4677
        $user_id = (int) $user_id;
4678
4679
        // all the information of the field
4680
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
4681
                INNER JOIN $table_user_tag_values uv
4682
                ON (uv.tag_id = ut.id)
4683
                WHERE field_id = $field_id AND user_id = $user_id
4684
                ORDER BY tag";
4685
4686
        $result = Database::query($sql);
4687
        $return = [];
4688
        if (Database::num_rows($result) > 0) {
4689
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4690
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
4691
            }
4692
        }
4693
        $user_tags = $return;
4694
        $tag_tmp = [];
4695
        foreach ($user_tags as $tag) {
4696
            if ($show_links) {
4697
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
4698
                    $tag['tag'].
4699
                '</a>';
4700
            } else {
4701
                $tag_tmp[] = $tag['tag'];
4702
            }
4703
        }
4704
4705
        if (is_array($user_tags) && count($user_tags) > 0) {
4706
            return implode(', ', $tag_tmp);
4707
        } else {
4708
            return '';
4709
        }
4710
    }
4711
4712
    /**
4713
     * Get the tag id.
4714
     *
4715
     * @param int $tag
4716
     * @param int $field_id
4717
     *
4718
     * @return int returns 0 if fails otherwise the tag id
4719
     */
4720
    public static function get_tag_id($tag, $field_id)
4721
    {
4722
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4723
        $tag = Database::escape_string($tag);
4724
        $field_id = (int) $field_id;
4725
        //with COLLATE latin1_bin to select query in a case sensitive mode
4726
        $sql = "SELECT id FROM $table_user_tag
4727
                WHERE tag LIKE '$tag' AND field_id = $field_id";
4728
        $result = Database::query($sql);
4729
        if (Database::num_rows($result) > 0) {
4730
            $row = Database::fetch_array($result, 'ASSOC');
4731
4732
            return $row['id'];
4733
        } else {
4734
            return 0;
4735
        }
4736
    }
4737
4738
    /**
4739
     * Get the tag id.
4740
     *
4741
     * @param int $tag_id
4742
     * @param int $field_id
4743
     *
4744
     * @return int 0 if fails otherwise the tag id
4745
     */
4746
    public static function get_tag_id_from_id($tag_id, $field_id)
4747
    {
4748
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4749
        $tag_id = (int) $tag_id;
4750
        $field_id = (int) $field_id;
4751
        $sql = "SELECT id FROM $table_user_tag
4752
                WHERE id = '$tag_id' AND field_id = $field_id";
4753
        $result = Database::query($sql);
4754
        if (Database::num_rows($result) > 0) {
4755
            $row = Database::fetch_array($result, 'ASSOC');
4756
4757
            return $row['id'];
4758
        } else {
4759
            return false;
4760
        }
4761
    }
4762
4763
    /**
4764
     * Adds a user-tag value.
4765
     *
4766
     * @param mixed $tag
4767
     * @param int   $user_id
4768
     * @param int   $field_id field id of the tag
4769
     *
4770
     * @return bool True if the tag was inserted or updated. False otherwise.
4771
     *              The return value doesn't take into account *values* added to the tag.
4772
     *              Only the creation/update of the tag field itself.
4773
     */
4774
    public static function add_tag($tag, $user_id, $field_id)
4775
    {
4776
        // database table definition
4777
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4778
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4779
        $tag = trim(Database::escape_string($tag));
4780
        $user_id = (int) $user_id;
4781
        $field_id = (int) $field_id;
4782
4783
        $tag_id = self::get_tag_id($tag, $field_id);
4784
4785
        /* IMPORTANT
4786
         *  @todo we don't create tags with numbers
4787
         *
4788
         */
4789
        if (is_numeric($tag)) {
4790
            //the form is sending an id this means that the user select it from the list so it MUST exists
4791
            /* $new_tag_id = self::get_tag_id_from_id($tag,$field_id);
4792
              if ($new_tag_id !== false) {
4793
              $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $new_tag_id";
4794
              $result = Database::query($sql);
4795
              $last_insert_id = $new_tag_id;
4796
              } else {
4797
              $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
4798
              $result = Database::query($sql);
4799
              $last_insert_id = Database::insert_id();
4800
              } */
4801
        }
4802
4803
        //this is a new tag
4804
        if ($tag_id == 0) {
4805
            //the tag doesn't exist
4806
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
4807
            Database::query($sql);
4808
            $last_insert_id = Database::insert_id();
4809
        } else {
4810
            //the tag exists we update it
4811
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
4812
            Database::query($sql);
4813
            $last_insert_id = $tag_id;
4814
        }
4815
4816
        if (!empty($last_insert_id) && ($last_insert_id != 0)) {
4817
            //we insert the relationship user-tag
4818
            $sql = "SELECT tag_id FROM $table_user_tag_values
4819
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
4820
            $result = Database::query($sql);
4821
            //if the relationship does not exist we create it
4822
            if (Database::num_rows($result) == 0) {
4823
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
4824
                Database::query($sql);
4825
            }
4826
4827
            return true;
4828
        }
4829
4830
        return false;
4831
    }
4832
4833
    /**
4834
     * Deletes an user tag.
4835
     *
4836
     * @param int $user_id
4837
     * @param int $field_id
4838
     */
4839
    public static function delete_user_tags($user_id, $field_id)
4840
    {
4841
        // database table definition
4842
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4843
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4844
        $user_id = (int) $user_id;
4845
4846
        $tags = self::get_user_tags($user_id, $field_id);
4847
        if (is_array($tags) && count($tags) > 0) {
4848
            foreach ($tags as $key => $tag) {
4849
                if ($tag['count'] > '0') {
4850
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
4851
                    Database::query($sql);
4852
                }
4853
                $sql = "DELETE FROM $table_user_tag_values
4854
                        WHERE user_id = $user_id AND tag_id = $key";
4855
                Database::query($sql);
4856
            }
4857
        }
4858
    }
4859
4860
    /**
4861
     * Process the tag list comes from the UserManager::update_extra_field_value() function.
4862
     *
4863
     * @param array $tags     the tag list that will be added
4864
     * @param int   $user_id
4865
     * @param int   $field_id
4866
     *
4867
     * @return bool
4868
     */
4869
    public static function process_tags($tags, $user_id, $field_id)
4870
    {
4871
        // We loop the tags and add it to the DB
4872
        if (is_array($tags)) {
4873
            foreach ($tags as $tag) {
4874
                self::add_tag($tag, $user_id, $field_id);
4875
            }
4876
        } else {
4877
            self::add_tag($tags, $user_id, $field_id);
4878
        }
4879
4880
        return true;
4881
    }
4882
4883
    /**
4884
     * Returns a list of all administrators.
4885
     *
4886
     * @return array
4887
     */
4888
    public static function get_all_administrators()
4889
    {
4890
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4891
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
4892
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4893
        $access_url_id = api_get_current_access_url_id();
4894
        if (api_get_multiple_access_url()) {
4895
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
4896
                    FROM $tbl_url_rel_user as url
4897
                    INNER JOIN $table_admin as admin
4898
                    ON (admin.user_id=url.user_id)
4899
                    INNER JOIN $table_user u
4900
                    ON (u.id=admin.user_id)
4901
                    WHERE access_url_id ='".$access_url_id."'";
4902
        } else {
4903
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
4904
                    FROM $table_admin as admin
4905
                    INNER JOIN $table_user u
4906
                    ON (u.id=admin.user_id)";
4907
        }
4908
        $result = Database::query($sql);
4909
        $return = [];
4910
        if (Database::num_rows($result) > 0) {
4911
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4912
                $return[$row['user_id']] = $row;
4913
            }
4914
        }
4915
4916
        return $return;
4917
    }
4918
4919
    /**
4920
     * Search an user (tags, first name, last name and email ).
4921
     *
4922
     * @param string $tag
4923
     * @param int    $field_id        field id of the tag
4924
     * @param int    $from            where to start in the query
4925
     * @param int    $number_of_items
4926
     * @param bool   $getCount        get count or not
4927
     *
4928
     * @return array
4929
     */
4930
    public static function get_all_user_tags(
4931
        $tag,
4932
        $field_id = 0,
4933
        $from = 0,
4934
        $number_of_items = 10,
4935
        $getCount = false
4936
    ) {
4937
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
4938
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4939
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4940
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4941
4942
        $field_id = intval($field_id);
4943
        $from = intval($from);
4944
        $number_of_items = intval($number_of_items);
4945
4946
        $where_field = "";
4947
        $where_extra_fields = self::get_search_form_where_extra_fields();
4948
        if ($field_id != 0) {
4949
            $where_field = " field_id = $field_id AND ";
4950
        }
4951
4952
        // all the information of the field
4953
        if ($getCount) {
4954
            $select = "SELECT count(DISTINCT u.id) count";
4955
        } else {
4956
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, picture_uri";
4957
        }
4958
4959
        $sql = " $select
4960
                FROM $user_table u
4961
                INNER JOIN $access_url_rel_user_table url_rel_user
4962
                ON (u.id = url_rel_user.user_id)
4963
                LEFT JOIN $table_user_tag_values uv
4964
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
4965
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
4966
                WHERE
4967
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
4968
                    (
4969
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
4970
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
4971
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
4972
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
4973
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
4974
                     )
4975
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
4976
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
4977
4978
        $keyword_active = true;
4979
        // only active users
4980
        if ($keyword_active) {
4981
            $sql .= " AND u.active='1'";
4982
        }
4983
        // avoid anonymous
4984
        $sql .= " AND u.status <> 6 ";
4985
        $sql .= " ORDER BY username";
4986
        $sql .= " LIMIT $from , $number_of_items";
4987
4988
        $result = Database::query($sql);
4989
        $return = [];
4990
4991
        if (Database::num_rows($result) > 0) {
4992
            if ($getCount) {
4993
                $row = Database::fetch_array($result, 'ASSOC');
4994
4995
                return $row['count'];
4996
            }
4997
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4998
                $return[$row['id']] = $row;
4999
            }
5000
        }
5001
5002
        return $return;
5003
    }
5004
5005
    /**
5006
     * Get extra filterable user fields (only type select).
5007
     *
5008
     * @return array Array of extra fields as [int => ['name' => ..., 'variable' => ..., 'data' => ...]] (
5009
     *               or empty array if no extra field)
5010
     */
5011
    public static function getExtraFilterableFields()
5012
    {
5013
        $extraFieldList = self::get_extra_fields();
5014
        $fields = [];
5015
        if (is_array($extraFieldList)) {
5016
            foreach ($extraFieldList as $extraField) {
5017
                // If is enabled to filter and is a "<select>" field type
5018
                if ($extraField[8] == 1 && $extraField[2] == 4) {
5019
                    $fields[] = [
5020
                        'name' => $extraField[3],
5021
                        'variable' => $extraField[1],
5022
                        'data' => $extraField[9],
5023
                    ];
5024
                }
5025
            }
5026
        }
5027
5028
        return $fields;
5029
    }
5030
5031
    /**
5032
     * Get extra where clauses for finding users based on extra filterable user fields (type select).
5033
     *
5034
     * @return string With AND clauses based on user's ID which have the values to search in extra user fields
5035
     *                (or empty if no extra field exists)
5036
     */
5037
    public static function get_search_form_where_extra_fields()
5038
    {
5039
        $useExtraFields = false;
5040
        $extraFields = self::getExtraFilterableFields();
5041
        $extraFieldResult = [];
5042
        if (is_array($extraFields) && count($extraFields) > 0) {
5043
            foreach ($extraFields as $extraField) {
5044
                $varName = 'field_'.$extraField['variable'];
5045
                if (self::is_extra_field_available($extraField['variable'])) {
5046
                    if (isset($_GET[$varName]) && $_GET[$varName] != '0') {
5047
                        $useExtraFields = true;
5048
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
5049
                            $extraField['variable'],
5050
                            $_GET[$varName]
5051
                        );
5052
                    }
5053
                }
5054
            }
5055
        }
5056
5057
        if ($useExtraFields) {
5058
            $finalResult = [];
5059
            if (count($extraFieldResult) > 1) {
5060
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
5061
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
5062
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
5063
                    }
5064
                }
5065
            } else {
5066
                $finalResult = $extraFieldResult[0];
5067
            }
5068
5069
            if (is_array($finalResult) && count($finalResult) > 0) {
5070
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
5071
            } else {
5072
                //no results
5073
                $whereFilter = " AND u.id  = -1 ";
5074
            }
5075
5076
            return $whereFilter;
5077
        }
5078
5079
        return '';
5080
    }
5081
5082
    /**
5083
     * Show the search form.
5084
     *
5085
     * @param string $query the value of the search box
5086
     *
5087
     * @throws Exception
5088
     *
5089
     * @return string HTML form
5090
     */
5091
    public static function get_search_form($query, $defaultParams = [])
5092
    {
5093
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
5094
        $form = new FormValidator(
5095
            'search_user',
5096
            'get',
5097
            api_get_path(WEB_PATH).'main/social/search.php',
5098
            '',
5099
            [],
5100
            FormValidator::LAYOUT_HORIZONTAL
5101
        );
5102
5103
        $query = Security::remove_XSS($query);
5104
5105
        if (!empty($query)) {
5106
            $form->addHeader(get_lang('Results').' "'.$query.'"');
5107
        }
5108
5109
        $form->addText(
5110
            'q',
5111
            get_lang('UsersGroups'),
5112
            false,
5113
            [
5114
                'id' => 'q',
5115
            ]
5116
        );
5117
        $options = [
5118
            0 => get_lang('Select'),
5119
            1 => get_lang('User'),
5120
            2 => get_lang('Group'),
5121
        ];
5122
        $form->addSelect(
5123
            'search_type',
5124
            get_lang('Type'),
5125
            $options,
5126
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
5127
        );
5128
5129
        // Extra fields
5130
        $extraFields = self::getExtraFilterableFields();
5131
        $defaults = [];
5132
        if (is_array($extraFields) && count($extraFields) > 0) {
5133
            foreach ($extraFields as $extraField) {
5134
                $varName = 'field_'.$extraField['variable'];
5135
                $options = [
5136
                    0 => get_lang('Select'),
5137
                ];
5138
                foreach ($extraField['data'] as $option) {
5139
                    if (isset($_GET[$varName])) {
5140
                        if ($_GET[$varName] == $option[1]) {
5141
                            $defaults[$option[1]] = true;
5142
                        }
5143
                    }
5144
5145
                    $options[$option[1]] = $option[1];
5146
                }
5147
                $form->addSelect($varName, $extraField['name'], $options);
5148
            }
5149
        }
5150
5151
        $defaults['search_type'] = (int) $searchType;
5152
        $defaults['q'] = $query;
5153
5154
        if (!empty($defaultParams)) {
5155
            $defaults = array_merge($defaults, $defaultParams);
5156
        }
5157
        $form->setDefaults($defaults);
5158
        $form->addButtonSearch(get_lang('Search'));
5159
5160
        $js = '<script>
5161
        extra_field_toogle();
5162
        function extra_field_toogle() {
5163
            if (jQuery("select[name=search_type]").val() != "1") {
5164
                jQuery(".extra_field").hide();
5165
            } else {
5166
                jQuery(".extra_field").show();
5167
            }
5168
        }
5169
        </script>';
5170
5171
        return $js.$form->returnForm();
5172
    }
5173
5174
    /**
5175
     * Shows the user menu.
5176
     */
5177
    public static function show_menu()
5178
    {
5179
        echo '<div class="actions">';
5180
        echo '<a href="/main/auth/profile.php">'.
5181
            Display::return_icon('profile.png').' '.get_lang('PersonalData').'</a>';
5182
        echo '<a href="/main/messages/inbox.php">'.
5183
            Display::return_icon('inbox.png').' '.get_lang('Inbox').'</a>';
5184
        echo '<a href="/main/messages/outbox.php">'.
5185
            Display::return_icon('outbox.png').' '.get_lang('Outbox').'</a>';
5186
        echo '<span style="float:right; padding-top:7px;">'.
5187
        '<a href="/main/auth/profile.php?show=1">'.
5188
            Display::return_icon('edit.gif').' '.get_lang('Configuration').'</a>';
5189
        echo '</span>';
5190
        echo '</div>';
5191
    }
5192
5193
    /**
5194
     * Allow to register contact to social network.
5195
     *
5196
     * @param int $friend_id     user friend id
5197
     * @param int $my_user_id    user id
5198
     * @param int $relation_type relation between users see constants definition
5199
     *
5200
     * @return bool
5201
     */
5202
    public static function relate_users($friend_id, $my_user_id, $relation_type)
5203
    {
5204
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5205
5206
        $friend_id = (int) $friend_id;
5207
        $my_user_id = (int) $my_user_id;
5208
        $relation_type = (int) $relation_type;
5209
5210
        $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
5211
                WHERE
5212
                    friend_user_id='.$friend_id.' AND
5213
                    user_id='.$my_user_id.' AND
5214
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
5215
        $result = Database::query($sql);
5216
        $row = Database::fetch_array($result, 'ASSOC');
5217
        $current_date = api_get_utc_datetime();
5218
5219
        if ($row['count'] == 0) {
5220
            $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
5221
                    VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
5222
            Database::query($sql);
5223
5224
            return true;
5225
        }
5226
5227
        $sql = 'SELECT COUNT(*) as count, relation_type  FROM '.$tbl_my_friend.'
5228
                WHERE
5229
                    friend_user_id='.$friend_id.' AND
5230
                    user_id='.$my_user_id.' AND
5231
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
5232
        $result = Database::query($sql);
5233
        $row = Database::fetch_array($result, 'ASSOC');
5234
5235
        if ($row['count'] == 1) {
5236
            //only for the case of a RRHH or a Student BOSS
5237
            if ($row['relation_type'] != $relation_type &&
5238
                ($relation_type == USER_RELATION_TYPE_RRHH || $relation_type == USER_RELATION_TYPE_BOSS)
5239
            ) {
5240
                $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
5241
                        VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
5242
            } else {
5243
                $sql = 'UPDATE '.$tbl_my_friend.' SET relation_type='.$relation_type.'
5244
                        WHERE friend_user_id='.$friend_id.' AND user_id='.$my_user_id;
5245
            }
5246
            Database::query($sql);
5247
5248
            return true;
5249
        }
5250
5251
        return false;
5252
    }
5253
5254
    /**
5255
     * Deletes a contact.
5256
     *
5257
     * @param bool   $friend_id
5258
     * @param bool   $real_removed          true will delete ALL friends relationship
5259
     * @param string $with_status_condition
5260
     *
5261
     * @author isaac flores paz <[email protected]>
5262
     * @author Julio Montoya <[email protected]> Cleaning code
5263
     */
5264
    public static function remove_user_rel_user(
5265
        $friend_id,
5266
        $real_removed = false,
5267
        $with_status_condition = ''
5268
    ) {
5269
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5270
        $tbl_my_message = Database::get_main_table(TABLE_MESSAGE);
5271
        $friend_id = (int) $friend_id;
5272
        $user_id = api_get_user_id();
5273
5274
        if ($real_removed) {
5275
            $extra_condition = '';
5276
            if ($with_status_condition != '') {
5277
                $extra_condition = ' AND relation_type = '.intval($with_status_condition);
5278
            }
5279
            $sql = 'DELETE FROM '.$tbl_my_friend.'
5280
                    WHERE
5281
                        relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
5282
                        friend_user_id='.$friend_id.' '.$extra_condition;
5283
            Database::query($sql);
5284
            $sql = 'DELETE FROM '.$tbl_my_friend.'
5285
                   WHERE
5286
                    relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
5287
                    user_id='.$friend_id.' '.$extra_condition;
5288
            Database::query($sql);
5289
        } else {
5290
            $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
5291
                    WHERE
5292
                        user_id='.$user_id.' AND
5293
                        relation_type NOT IN('.USER_RELATION_TYPE_DELETED.', '.USER_RELATION_TYPE_RRHH.') AND
5294
                        friend_user_id='.$friend_id;
5295
            $result = Database::query($sql);
5296
            $row = Database::fetch_array($result, 'ASSOC');
5297
            if ($row['count'] == 1) {
5298
                //Delete user rel user
5299
                $sql_i = 'UPDATE '.$tbl_my_friend.' SET relation_type='.USER_RELATION_TYPE_DELETED.'
5300
                          WHERE user_id='.$user_id.' AND friend_user_id='.$friend_id;
5301
5302
                $sql_j = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
5303
                          WHERE
5304
                                user_receiver_id='.$user_id.' AND
5305
                                user_sender_id='.$friend_id.' AND update_date="0000-00-00 00:00:00" ';
5306
                // Delete user
5307
                $sql_ij = 'UPDATE '.$tbl_my_friend.'  SET relation_type='.USER_RELATION_TYPE_DELETED.'
5308
                           WHERE user_id='.$friend_id.' AND friend_user_id='.$user_id;
5309
                $sql_ji = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
5310
                           WHERE
5311
                                user_receiver_id='.$friend_id.' AND
5312
                                user_sender_id='.$user_id.' AND
5313
                                update_date="0000-00-00 00:00:00" ';
5314
                Database::query($sql_i);
5315
                Database::query($sql_j);
5316
                Database::query($sql_ij);
5317
                Database::query($sql_ji);
5318
            }
5319
        }
5320
5321
        // Delete accepted invitations
5322
        $sql = "DELETE FROM $tbl_my_message
5323
                WHERE
5324
                    msg_status = ".MESSAGE_STATUS_INVITATION_ACCEPTED." AND
5325
                    (
5326
                        user_receiver_id = $user_id AND
5327
                        user_sender_id = $friend_id
5328
                    ) OR
5329
                    (
5330
                        user_sender_id = $user_id AND
5331
                        user_receiver_id = $friend_id
5332
                    )
5333
        ";
5334
        Database::query($sql);
5335
    }
5336
5337
    /**
5338
     * @param int $userId
5339
     *
5340
     * @return array
5341
     */
5342
    public static function getDrhListFromUser($userId)
5343
    {
5344
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
5345
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5346
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5347
        $userId = (int) $userId;
5348
5349
        $orderBy = null;
5350
        if (api_is_western_name_order()) {
5351
            $orderBy .= ' ORDER BY firstname, lastname ';
5352
        } else {
5353
            $orderBy .= ' ORDER BY lastname, firstname ';
5354
        }
5355
5356
        $sql = "SELECT u.id, username, u.firstname, u.lastname
5357
                FROM $tblUser u
5358
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
5359
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
5360
                WHERE
5361
                    access_url_id = ".api_get_current_access_url_id()." AND
5362
                    uru.user_id = '$userId' AND
5363
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
5364
                $orderBy
5365
                ";
5366
        $result = Database::query($sql);
5367
5368
        return Database::store_result($result);
5369
    }
5370
5371
    /**
5372
     * get users followed by human resource manager.
5373
     *
5374
     * @param int    $userId
5375
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
5376
     * @param bool   $getOnlyUserId
5377
     * @param bool   $getSql
5378
     * @param bool   $getCount
5379
     * @param int    $from
5380
     * @param int    $numberItems
5381
     * @param int    $column
5382
     * @param string $direction
5383
     * @param int    $active
5384
     * @param string $lastConnectionDate
5385
     *
5386
     * @return array users
5387
     */
5388
    public static function get_users_followed_by_drh(
5389
        $userId,
5390
        $userStatus = 0,
5391
        $getOnlyUserId = false,
5392
        $getSql = false,
5393
        $getCount = false,
5394
        $from = null,
5395
        $numberItems = null,
5396
        $column = null,
5397
        $direction = null,
5398
        $active = null,
5399
        $lastConnectionDate = null
5400
    ) {
5401
        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...
5402
            $userId,
5403
            $userStatus,
5404
            $getOnlyUserId,
5405
            $getSql,
5406
            $getCount,
5407
            $from,
5408
            $numberItems,
5409
            $column,
5410
            $direction,
5411
            $active,
5412
            $lastConnectionDate,
5413
            DRH
5414
        );
5415
    }
5416
5417
    /**
5418
     * Get users followed by human resource manager.
5419
     *
5420
     * @param int    $userId
5421
     * @param int    $userStatus             Filter users by status (STUDENT, COURSEMANAGER, etc)
5422
     * @param bool   $getOnlyUserId
5423
     * @param bool   $getSql
5424
     * @param bool   $getCount
5425
     * @param int    $from
5426
     * @param int    $numberItems
5427
     * @param int    $column
5428
     * @param string $direction
5429
     * @param int    $active
5430
     * @param string $lastConnectionDate
5431
     * @param int    $status                 the function is called by who? COURSEMANAGER, DRH?
5432
     * @param string $keyword
5433
     * @param bool   $checkSessionVisibility
5434
     *
5435
     * @return mixed Users list (array) or the SQL query if $getSQL was set to true
5436
     */
5437
    public static function getUsersFollowedByUser(
5438
        $userId,
5439
        $userStatus = null,
5440
        $getOnlyUserId = false,
5441
        $getSql = false,
5442
        $getCount = false,
5443
        $from = null,
5444
        $numberItems = null,
5445
        $column = null,
5446
        $direction = null,
5447
        $active = null,
5448
        $lastConnectionDate = null,
5449
        $status = null,
5450
        $keyword = null,
5451
        $checkSessionVisibility = false
5452
    ) {
5453
        // Database Table Definitions
5454
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
5455
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5456
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5457
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
5458
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5459
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5460
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
5461
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
5462
5463
        $userId = (int) $userId;
5464
        $limitCondition = '';
5465
5466
        if (isset($from) && isset($numberItems)) {
5467
            $from = (int) $from;
5468
            $numberItems = (int) $numberItems;
5469
            $limitCondition = "LIMIT $from, $numberItems";
5470
        }
5471
5472
        $column = Database::escape_string($column);
5473
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
5474
5475
        $userConditions = '';
5476
        if (!empty($userStatus)) {
5477
            $userConditions .= ' AND u.status = '.intval($userStatus);
5478
        }
5479
5480
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
5481
        if ($getOnlyUserId) {
5482
            $select = " SELECT DISTINCT u.id user_id";
5483
        }
5484
5485
        $masterSelect = "SELECT DISTINCT * FROM ";
5486
5487
        if ($getCount) {
5488
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
5489
            $select = " SELECT DISTINCT(u.id) user_id";
5490
        }
5491
5492
        if (!is_null($active)) {
5493
            $active = intval($active);
5494
            $userConditions .= " AND u.active = $active ";
5495
        }
5496
5497
        if (!empty($keyword)) {
5498
            $keyword = trim(Database::escape_string($keyword));
5499
            $keywordParts = array_filter(explode(' ', $keyword));
5500
            $extraKeyword = '';
5501
            if (!empty($keywordParts)) {
5502
                $keywordPartsFixed = Database::escape_string(implode('%', $keywordParts));
5503
                if (!empty($keywordPartsFixed)) {
5504
                    $extraKeyword .= " OR
5505
                        CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keywordPartsFixed%' OR
5506
                        CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keywordPartsFixed%' ";
5507
                }
5508
            }
5509
5510
            $userConditions .= " AND (
5511
                u.username LIKE '%$keyword%' OR
5512
                u.firstname LIKE '%$keyword%' OR
5513
                u.lastname LIKE '%$keyword%' OR
5514
                u.official_code LIKE '%$keyword%' OR
5515
                u.email LIKE '%$keyword%' OR
5516
                CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keyword%' OR
5517
                CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keyword%'
5518
                $extraKeyword
5519
            )";
5520
        }
5521
5522
        if (!empty($lastConnectionDate)) {
5523
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
5524
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
5525
        }
5526
5527
        $sessionConditionsCoach = null;
5528
        $dateCondition = '';
5529
        $drhConditions = null;
5530
        $teacherSelect = null;
5531
5532
        $urlId = api_get_current_access_url_id();
5533
5534
        switch ($status) {
5535
            case DRH:
5536
                $drhConditions .= " AND
5537
                    friend_user_id = '$userId' AND
5538
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
5539
                ";
5540
                break;
5541
            case COURSEMANAGER:
5542
                $drhConditions .= " AND
5543
                    friend_user_id = '$userId' AND
5544
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
5545
                ";
5546
5547
                $sessionConditionsCoach .= " AND
5548
                    (s.id_coach = '$userId')
5549
                ";
5550
5551
                $sessionConditionsTeacher = " AND
5552
                    (scu.status = 2 AND scu.user_id = '$userId')
5553
                ";
5554
5555
                if ($checkSessionVisibility) {
5556
                    $today = api_strtotime('now', 'UTC');
5557
                    $today = date('Y-m-d', $today);
5558
                    $dateCondition = "
5559
                        AND
5560
                        (
5561
                            (s.access_start_date <= '$today' AND '$today' <= s.access_end_date) OR
5562
                            (s.access_start_date IS NULL AND s.access_end_date IS NULL) OR
5563
                            (s.access_start_date <= '$today' AND s.access_end_date IS NULL) OR
5564
                            ('$today' <= s.access_end_date AND s.access_start_date IS NULL)
5565
                        )
5566
					";
5567
                }
5568
5569
                // Use $tbl_session_rel_course_rel_user instead of $tbl_session_rel_user
5570
                /*
5571
                INNER JOIN $tbl_session_rel_user sru
5572
                ON (sru.user_id = u.id)
5573
                INNER JOIN $tbl_session_rel_course_rel_user scu
5574
                ON (scu.user_id = u.id AND scu.c_id IS NOT NULL AND visibility = 1)*/
5575
                $teacherSelect =
5576
                "UNION ALL (
5577
                        $select
5578
                        FROM $tbl_user u
5579
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
5580
                        WHERE
5581
                            (
5582
                                sru.session_id IN (
5583
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
5584
                                    $tbl_session_rel_access_url session_rel_access_rel_user
5585
                                    ON session_rel_access_rel_user.session_id = s.id
5586
                                    WHERE access_url_id = ".$urlId."
5587
                                    $sessionConditionsCoach
5588
                                ) OR sru.session_id IN (
5589
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
5590
                                    INNER JOIN $tbl_session_rel_access_url url
5591
                                    ON (url.session_id = s.id)
5592
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
5593
                                    ON (scu.session_id = s.id)
5594
                                    WHERE access_url_id = ".$urlId."
5595
                                    $sessionConditionsTeacher
5596
                                    $dateCondition
5597
                                )
5598
                            )
5599
                            $userConditions
5600
                    )
5601
                    UNION ALL(
5602
                        $select
5603
                        FROM $tbl_user u
5604
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
5605
                        WHERE cu.c_id IN (
5606
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
5607
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
5608
                        )
5609
                        $userConditions
5610
                    )"
5611
                ;
5612
                break;
5613
            case STUDENT_BOSS:
5614
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
5615
                break;
5616
            case HRM_REQUEST:
5617
                $drhConditions .= " AND
5618
                    friend_user_id = '$userId' AND
5619
                    relation_type = '".USER_RELATION_TYPE_HRM_REQUEST."'
5620
                ";
5621
                break;
5622
        }
5623
5624
        $join = null;
5625
        $sql = " $masterSelect
5626
                (
5627
                    (
5628
                        $select
5629
                        FROM $tbl_user u
5630
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
5631
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
5632
                        $join
5633
                        WHERE
5634
                            access_url_id = ".$urlId."
5635
                            $drhConditions
5636
                            $userConditions
5637
                    )
5638
                    $teacherSelect
5639
                ) as t1";
5640
5641
        if ($getSql) {
5642
            return $sql;
5643
        }
5644
5645
        if ($getCount) {
5646
            $result = Database::query($sql);
5647
            $row = Database::fetch_array($result);
5648
5649
            return $row['count'];
5650
        }
5651
5652
        $orderBy = null;
5653
        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...
5654
            if (api_is_western_name_order()) {
5655
                $orderBy .= " ORDER BY firstname, lastname ";
5656
            } else {
5657
                $orderBy .= " ORDER BY lastname, firstname ";
5658
            }
5659
5660
            if (!empty($column) && !empty($direction)) {
5661
                // Fixing order due the UNIONs
5662
                $column = str_replace('u.', '', $column);
5663
                $column = trim($column);
5664
                $orderBy = " ORDER BY `$column` $direction ";
5665
            }
5666
        }
5667
5668
        $sql .= $orderBy;
5669
        $sql .= $limitCondition;
5670
5671
        $result = Database::query($sql);
5672
        $users = [];
5673
        if (Database::num_rows($result) > 0) {
5674
            while ($row = Database::fetch_array($result)) {
5675
                $users[$row['user_id']] = $row;
5676
            }
5677
        }
5678
5679
        return $users;
5680
    }
5681
5682
    /**
5683
     * Subscribes users to human resource manager (Dashboard feature).
5684
     *
5685
     * @param int   $hr_dept_id
5686
     * @param array $users_id
5687
     * @param bool  $deleteOtherAssignedUsers
5688
     *
5689
     * @return int
5690
     */
5691
    public static function subscribeUsersToHRManager(
5692
        $hr_dept_id,
5693
        $users_id,
5694
        $deleteOtherAssignedUsers = true
5695
    ) {
5696
        return self::subscribeUsersToUser(
5697
            $hr_dept_id,
5698
            $users_id,
5699
            USER_RELATION_TYPE_RRHH,
5700
            false,
5701
            $deleteOtherAssignedUsers
5702
        );
5703
    }
5704
5705
    /**
5706
     * Register request to assign users to HRM.
5707
     *
5708
     * @param int   $hrmId   The HRM ID
5709
     * @param array $usersId The users IDs
5710
     *
5711
     * @return int
5712
     */
5713
    public static function requestUsersToHRManager($hrmId, $usersId)
5714
    {
5715
        return self::subscribeUsersToUser(
5716
            $hrmId,
5717
            $usersId,
5718
            USER_RELATION_TYPE_HRM_REQUEST,
5719
            false,
5720
            false
5721
        );
5722
    }
5723
5724
    /**
5725
     * Remove the requests for assign a user to a HRM.
5726
     *
5727
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
5728
     */
5729
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
5730
    {
5731
        $users = implode(', ', $usersId);
5732
        Database::getManager()
5733
            ->createQuery('
5734
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
5735
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
5736
            ')
5737
            ->execute(['hrm_id' => $hrmId, 'relation_type' => USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
5738
    }
5739
5740
    /**
5741
     * Add subscribed users to a user by relation type.
5742
     *
5743
     * @param int    $userId                   The user id
5744
     * @param array  $subscribedUsersId        The id of subscribed users
5745
     * @param string $relationType             The relation type
5746
     * @param bool   $deleteUsersBeforeInsert
5747
     * @param bool   $deleteOtherAssignedUsers
5748
     *
5749
     * @return int
5750
     */
5751
    public static function subscribeUsersToUser(
5752
        $userId,
5753
        $subscribedUsersId,
5754
        $relationType,
5755
        $deleteUsersBeforeInsert = false,
5756
        $deleteOtherAssignedUsers = true
5757
    ) {
5758
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5759
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5760
5761
        $userId = (int) $userId;
5762
        $relationType = (int) $relationType;
5763
        $affectedRows = 0;
5764
5765
        if ($deleteOtherAssignedUsers) {
5766
            if (api_get_multiple_access_url()) {
5767
                // Deleting assigned users to hrm_id
5768
                $sql = "SELECT s.user_id
5769
                        FROM $userRelUserTable s
5770
                        INNER JOIN $userRelAccessUrlTable a
5771
                        ON (a.user_id = s.user_id)
5772
                        WHERE
5773
                            friend_user_id = $userId AND
5774
                            relation_type = $relationType AND
5775
                            access_url_id = ".api_get_current_access_url_id();
5776
            } else {
5777
                $sql = "SELECT user_id
5778
                        FROM $userRelUserTable
5779
                        WHERE
5780
                            friend_user_id = $userId AND
5781
                            relation_type = $relationType";
5782
            }
5783
            $result = Database::query($sql);
5784
5785
            if (Database::num_rows($result) > 0) {
5786
                while ($row = Database::fetch_array($result)) {
5787
                    $sql = "DELETE FROM $userRelUserTable
5788
                            WHERE
5789
                                user_id = {$row['user_id']} AND
5790
                                friend_user_id = $userId AND
5791
                                relation_type = $relationType";
5792
                    Database::query($sql);
5793
                }
5794
            }
5795
        }
5796
5797
        if ($deleteUsersBeforeInsert) {
5798
            $sql = "DELETE FROM $userRelUserTable
5799
                    WHERE
5800
                        user_id = $userId AND
5801
                        relation_type = $relationType";
5802
            Database::query($sql);
5803
        }
5804
5805
        // Inserting new user list
5806
        if (is_array($subscribedUsersId)) {
5807
            foreach ($subscribedUsersId as $subscribedUserId) {
5808
                $subscribedUserId = (int) $subscribedUserId;
5809
                $sql = "SELECT id
5810
                        FROM $userRelUserTable
5811
                        WHERE
5812
                            user_id = $subscribedUserId AND
5813
                            friend_user_id = $userId AND
5814
                            relation_type = $relationType";
5815
5816
                $result = Database::query($sql);
5817
                $num = Database::num_rows($result);
5818
                if ($num === 0) {
5819
                    $date = api_get_utc_datetime();
5820
                    $sql = "INSERT INTO $userRelUserTable (user_id, friend_user_id, relation_type, last_edit)
5821
                            VALUES ($subscribedUserId, $userId, $relationType, '$date')";
5822
                    $result = Database::query($sql);
5823
                    $affectedRows += Database::affected_rows($result);
5824
                }
5825
            }
5826
        }
5827
5828
        return $affectedRows;
5829
    }
5830
5831
    /**
5832
     * This function check if an user is followed by human resources manager.
5833
     *
5834
     * @param int $user_id
5835
     * @param int $hr_dept_id Human resources manager
5836
     *
5837
     * @return bool
5838
     */
5839
    public static function is_user_followed_by_drh($user_id, $hr_dept_id)
5840
    {
5841
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5842
        $user_id = (int) $user_id;
5843
        $hr_dept_id = (int) $hr_dept_id;
5844
        $result = false;
5845
5846
        $sql = "SELECT user_id FROM $tbl_user_rel_user
5847
                WHERE
5848
                    user_id = $user_id AND
5849
                    friend_user_id = $hr_dept_id AND
5850
                    relation_type = ".USER_RELATION_TYPE_RRHH;
5851
        $rs = Database::query($sql);
5852
        if (Database::num_rows($rs) > 0) {
5853
            $result = true;
5854
        }
5855
5856
        return $result;
5857
    }
5858
5859
    /**
5860
     * Return the user id of teacher or session administrator.
5861
     *
5862
     * @return int|bool The user id, or false if the session ID was negative
5863
     */
5864
    public static function get_user_id_of_course_admin_or_session_admin(array $courseInfo)
5865
    {
5866
        $session = api_get_session_id();
5867
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5868
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5869
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5870
5871
        if (empty($courseInfo)) {
5872
            return false;
5873
        }
5874
5875
        $courseId = $courseInfo['real_id'];
5876
5877
        if (empty($session)) {
5878
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
5879
                    INNER JOIN '.$table_course_user.' ru
5880
                    ON ru.user_id = u.id
5881
                    WHERE
5882
                        ru.status = '.COURSEMANAGER.' AND
5883
                        ru.c_id = "'.$courseId.'" ';
5884
        } else {
5885
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
5886
                    INNER JOIN '.$table_session_course_user.' sru
5887
                    ON sru.user_id=u.id
5888
                    WHERE
5889
                        sru.c_id="'.$courseId.'" AND
5890
                        sru.session_id="'.$session.'" AND
5891
                        sru.status = '.SessionEntity::COACH;
5892
        }
5893
5894
        $rs = Database::query($sql);
5895
        $num_rows = Database::num_rows($rs);
5896
5897
        if (0 === $num_rows) {
5898
            return false;
5899
        }
5900
5901
        if (1 === $num_rows) {
5902
            $row = Database::fetch_array($rs);
5903
5904
            return (int) $row['uid'];
5905
        }
5906
5907
        $my_num_rows = $num_rows;
5908
        $my_user_id = Database::result($rs, $my_num_rows - 1, 'uid');
5909
5910
        return (int) $my_user_id;
5911
    }
5912
5913
    /**
5914
     * Determines if a user is a gradebook certified.
5915
     *
5916
     * @param int $cat_id  The category id of gradebook
5917
     * @param int $user_id The user id
5918
     *
5919
     * @return bool
5920
     */
5921
    public static function is_user_certified($cat_id, $user_id)
5922
    {
5923
        $cat_id = (int) $cat_id;
5924
        $user_id = (int) $user_id;
5925
5926
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
5927
        $sql = 'SELECT path_certificate
5928
                FROM '.$table.'
5929
                WHERE
5930
                    cat_id = "'.$cat_id.'" AND
5931
                    user_id = "'.$user_id.'"';
5932
        $rs = Database::query($sql);
5933
        $row = Database::fetch_array($rs);
5934
5935
        if ($row['path_certificate'] == '' || is_null($row['path_certificate'])) {
5936
            return false;
5937
        }
5938
5939
        return true;
5940
    }
5941
5942
    /**
5943
     * Gets the info about a gradebook certificate for a user by course.
5944
     *
5945
     * @param string $course_code The course code
5946
     * @param int    $session_id
5947
     * @param int    $user_id     The user id
5948
     * @param string $startDate   date string
5949
     * @param string $endDate     date string
5950
     *
5951
     * @return array if there is not information return false
5952
     */
5953
    public static function get_info_gradebook_certificate(
5954
        $course_code,
5955
        $session_id,
5956
        $user_id,
5957
        $startDate = null,
5958
        $endDate = null
5959
    ) {
5960
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
5961
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
5962
        $session_id = (int) $session_id;
5963
        $user_id = (int) $user_id;
5964
5965
        if (empty($session_id)) {
5966
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
5967
        } else {
5968
            $session_condition = " AND session_id = $session_id";
5969
        }
5970
5971
        $dateConditions = "";
5972
        if (!empty($startDate)) {
5973
            $startDate = api_get_utc_datetime($startDate, false, true);
5974
            $dateConditions .= " AND created_at >= '".$startDate->format('Y-m-d 00:00:00')."' ";
5975
        }
5976
        if (!empty($endDate)) {
5977
            $endDate = api_get_utc_datetime($endDate, false, true);
5978
            $dateConditions .= " AND created_at <= '".$endDate->format('Y-m-d 23:59:59')."' ";
5979
        }
5980
5981
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.'
5982
                WHERE cat_id = (
5983
                    SELECT id FROM '.$tbl_grade_category.'
5984
                    WHERE
5985
                        course_code = "'.Database::escape_string($course_code).'" '.$session_condition.' '.$dateConditions.'
5986
                    LIMIT 1
5987
                ) AND user_id='.$user_id;
5988
5989
        $rs = Database::query($sql);
5990
        if (Database::num_rows($rs) > 0) {
5991
            $row = Database::fetch_array($rs, 'ASSOC');
5992
            $score = $row['score_certificate'];
5993
            $category_id = $row['cat_id'];
5994
            $cat = Category::load($category_id);
5995
            $displayscore = ScoreDisplay::instance();
5996
            if (isset($cat) && $displayscore->is_custom()) {
5997
                $grade = $displayscore->display_score(
5998
                    [$score, $cat[0]->get_weight()],
5999
                    SCORE_DIV_PERCENT_WITH_CUSTOM
6000
                );
6001
            } else {
6002
                $grade = $displayscore->display_score(
6003
                    [$score, $cat[0]->get_weight()]
6004
                );
6005
            }
6006
            $row['grade'] = $grade;
6007
6008
            return $row;
6009
        }
6010
6011
        return false;
6012
    }
6013
6014
    /**
6015
     * This function check if the user is a coach inside session course.
6016
     *
6017
     * @param int $user_id    User id
6018
     * @param int $courseId
6019
     * @param int $session_id
6020
     *
6021
     * @return bool True if the user is a coach
6022
     */
6023
    public static function is_session_course_coach($user_id, $courseId, $session_id)
6024
    {
6025
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
6026
        // Protect data
6027
        $user_id = intval($user_id);
6028
        $courseId = intval($courseId);
6029
        $session_id = intval($session_id);
6030
        $result = false;
6031
6032
        $sql = "SELECT session_id FROM $table
6033
                WHERE
6034
                  session_id = $session_id AND
6035
                  c_id = $courseId AND
6036
                  user_id = $user_id AND
6037
                  status = 2 ";
6038
        $res = Database::query($sql);
6039
6040
        if (Database::num_rows($res) > 0) {
6041
            $result = true;
6042
        }
6043
6044
        return $result;
6045
    }
6046
6047
    /**
6048
     * This function returns an icon path that represents the favicon of the website of which the url given.
6049
     * Defaults to the current Chamilo favicon.
6050
     *
6051
     * @param string $url1 URL of website where to look for favicon.ico
6052
     * @param string $url2 Optional second URL of website where to look for favicon.ico
6053
     *
6054
     * @return string Path of icon to load
6055
     */
6056
    public static function get_favicon_from_url($url1, $url2 = null)
6057
    {
6058
        $icon_link = '';
6059
        $url = $url1;
6060
        if (empty($url1)) {
6061
            $url = $url2;
6062
            if (empty($url)) {
6063
                $url = api_get_access_url(api_get_current_access_url_id());
6064
                $url = $url[0];
6065
            }
6066
        }
6067
        if (!empty($url)) {
6068
            $pieces = parse_url($url);
6069
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
6070
        }
6071
6072
        return $icon_link;
6073
    }
6074
6075
    public static function addUserAsAdmin(User $user)
6076
    {
6077
        if ($user) {
0 ignored issues
show
introduced by
$user is of type Chamilo\UserBundle\Entity\User, thus it always evaluated to true.
Loading history...
6078
            $userId = $user->getId();
6079
            if (!self::is_admin($userId)) {
6080
                $table = Database::get_main_table(TABLE_MAIN_ADMIN);
6081
                $sql = "INSERT INTO $table SET user_id = $userId";
6082
                Database::query($sql);
6083
            }
6084
6085
            $user->addRole('ROLE_SUPER_ADMIN');
6086
            self::getManager()->updateUser($user, true);
6087
        }
6088
    }
6089
6090
    public static function removeUserAdmin(User $user)
6091
    {
6092
        $userId = (int) $user->getId();
6093
        if (self::is_admin($userId)) {
6094
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
6095
            $sql = "DELETE FROM $table WHERE user_id = $userId";
6096
            Database::query($sql);
6097
            $user->removeRole('ROLE_SUPER_ADMIN');
6098
            self::getManager()->updateUser($user, true);
6099
        }
6100
    }
6101
6102
    /**
6103
     * @param string $from
6104
     * @param string $to
6105
     */
6106
    public static function update_all_user_languages($from, $to)
6107
    {
6108
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6109
        $from = Database::escape_string($from);
6110
        $to = Database::escape_string($to);
6111
6112
        if (!empty($to) && !empty($from)) {
6113
            $sql = "UPDATE $table_user SET language = '$to'
6114
                    WHERE language = '$from'";
6115
            Database::query($sql);
6116
        }
6117
    }
6118
6119
    /**
6120
     * Subscribe boss to students.
6121
     *
6122
     * @param int   $bossId                   The boss id
6123
     * @param array $usersId                  The users array
6124
     * @param bool  $deleteOtherAssignedUsers
6125
     *
6126
     * @return int Affected rows
6127
     */
6128
    public static function subscribeBossToUsers($bossId, $usersId, $deleteOtherAssignedUsers = true)
6129
    {
6130
        return self::subscribeUsersToUser(
6131
            $bossId,
6132
            $usersId,
6133
            USER_RELATION_TYPE_BOSS,
6134
            false,
6135
            $deleteOtherAssignedUsers
6136
        );
6137
    }
6138
6139
    /**
6140
     * @param int $userId
6141
     *
6142
     * @return bool
6143
     */
6144
    public static function removeAllBossFromStudent($userId)
6145
    {
6146
        $userId = (int) $userId;
6147
6148
        if (empty($userId)) {
6149
            return false;
6150
        }
6151
6152
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6153
        $sql = "DELETE FROM $userRelUserTable
6154
                WHERE user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
6155
        Database::query($sql);
6156
6157
        return true;
6158
    }
6159
6160
    /**
6161
     * It updates course relation type as EX-LEARNER if project name (extra field from user_edition_extra_field_to_check) is changed.
6162
     *
6163
     * @param $userId
6164
     * @param $extraValue
6165
     *
6166
     * @throws \Doctrine\DBAL\Exception
6167
     */
6168
    public static function updateCourseRelationTypeExLearner($userId, $extraValue)
6169
    {
6170
        if (false !== api_get_configuration_value('user_edition_extra_field_to_check')) {
6171
            $extraToCheck = api_get_configuration_value('user_edition_extra_field_to_check');
6172
6173
            // Get the old user extra value to check
6174
            $userExtra = UserManager::get_extra_user_data_by_field($userId, $extraToCheck);
6175
            if (isset($userExtra[$extraToCheck]) && $userExtra[$extraToCheck] != $extraValue) {
6176
                // it searchs the courses with the user old extravalue
6177
                $extraFieldValues = new ExtraFieldValue('course');
6178
                $extraItems = $extraFieldValues->get_item_id_from_field_variable_and_field_value($extraToCheck, $userExtra[$extraToCheck], false, false, true);
6179
                $coursesTocheck = [];
6180
                if (!empty($extraItems)) {
6181
                    foreach ($extraItems as $items) {
6182
                        $coursesTocheck[] = $items['item_id'];
6183
                    }
6184
                }
6185
6186
                $tblUserGroupRelUser = Database::get_main_table(TABLE_USERGROUP_REL_USER);
6187
                $tblUserGroupRelCourse = Database::get_main_table(TABLE_USERGROUP_REL_COURSE);
6188
                $tblUserGroupRelSession = Database::get_main_table(TABLE_USERGROUP_REL_SESSION);
6189
                $tblSessionUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
6190
                $tblCourseUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
6191
6192
                // To check in main course
6193
                if (!empty($coursesTocheck)) {
6194
                    foreach ($coursesTocheck as $courseId) {
6195
                        $sql = "SELECT id FROM $tblCourseUser
6196
                                WHERE user_id = $userId AND c_id = $courseId";
6197
                        $rs = Database::query($sql);
6198
                        if (Database::num_rows($rs) > 0) {
6199
                            $id = Database::result($rs, 0, 0);
6200
                            $sql = "UPDATE $tblCourseUser SET relation_type = ".COURSE_EXLEARNER."
6201
                                    WHERE id = $id";
6202
                            Database::query($sql);
6203
                        }
6204
                    }
6205
                }
6206
6207
                // To check in sessions
6208
                if (!empty($coursesTocheck)) {
6209
                    $tblSessionCourseUser = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
6210
                    $sessionsToCheck = [];
6211
                    foreach ($coursesTocheck as $courseId) {
6212
                        $sql = "SELECT id, session_id FROM $tblSessionCourseUser
6213
                                WHERE user_id = $userId AND c_id = $courseId";
6214
                        $rs = Database::query($sql);
6215
                        if (Database::num_rows($rs) > 0) {
6216
                            $row = Database::fetch_array($rs);
6217
                            $id = $row['id'];
6218
                            $sessionId = $row['session_id'];
6219
                            $sql = "UPDATE $tblSessionCourseUser SET status = ".COURSE_EXLEARNER."
6220
                                    WHERE id = $id";
6221
                            Database::query($sql);
6222
                            $sessionsToCheck[] = $sessionId;
6223
                        }
6224
                    }
6225
                    // It checks if user is ex-learner in all courses in the session to update the session relation type
6226
                    if (!empty($sessionsToCheck)) {
6227
                        $sessionsToCheck = array_unique($sessionsToCheck);
6228
                        foreach ($sessionsToCheck as $sessionId) {
6229
                            $checkAll = Database::query("SELECT count(id) FROM $tblSessionCourseUser WHERE user_id = $userId AND session_id = $sessionId");
6230
                            $countAll = Database::result($checkAll, 0, 0);
6231
                            $checkExLearner = Database::query("SELECT count(id) FROM $tblSessionCourseUser WHERE status = ".COURSE_EXLEARNER." AND user_id = $userId AND session_id = $sessionId");
6232
                            $countExLearner = Database::result($checkExLearner, 0, 0);
6233
                            if ($countAll > 0 && $countAll == $countExLearner) {
6234
                                $sql = "UPDATE $tblSessionUser SET relation_type = ".COURSE_EXLEARNER."
6235
                                    WHERE user_id = $userId AND session_id = $sessionId";
6236
                                Database::query($sql);
6237
                            }
6238
                        }
6239
                    }
6240
                }
6241
                // To check users inside a class
6242
                $rsUser = Database::query("SELECT usergroup_id FROM $tblUserGroupRelUser WHERE user_id = $userId");
6243
                if (Database::num_rows($rsUser) > 0) {
6244
                    while ($rowUser = Database::fetch_array($rsUser)) {
6245
                        $usergroupId = $rowUser['usergroup_id'];
6246
6247
                        // Count courses with exlearners
6248
                        $sqlC1 = "SELECT count(id) FROM $tblUserGroupRelCourse WHERE usergroup_id = $usergroupId";
6249
                        $rsCourses = Database::query($sqlC1);
6250
                        $countGroupCourses = Database::result($rsCourses, 0, 0);
6251
6252
                        $sqlC2 = "SELECT count(cu.id)
6253
                                FROM $tblCourseUser cu
6254
                                INNER JOIN $tblUserGroupRelCourse gc
6255
                                    ON gc.course_id = cu.c_id
6256
                                WHERE
6257
                                    cu.user_id = $userId AND
6258
                                    usergroup_id = $usergroupId AND
6259
                                    relation_type = ".COURSE_EXLEARNER;
6260
                        $rsExCourses = Database::query($sqlC2);
6261
                        $countExCourses = Database::result($rsExCourses, 0, 0);
6262
                        $checkedExCourses = $countGroupCourses > 0 && ($countExCourses == $countGroupCourses);
6263
6264
                        // Count sessions with exlearners
6265
                        $sqlS1 = "SELECT count(id) FROM $tblUserGroupRelSession WHERE usergroup_id = $usergroupId";
6266
                        $rsSessions = Database::query($sqlS1);
6267
                        $countGroupSessions = Database::result($rsSessions, 0, 0);
6268
6269
                        $sqlS2 = "SELECT count(su.id)
6270
                                FROM $tblSessionUser su
6271
                                INNER JOIN $tblUserGroupRelSession gs
6272
                                    ON gs.session_id = su.session_id
6273
                                WHERE
6274
                                    su.user_id = $userId AND
6275
                                    usergroup_id = $usergroupId AND
6276
                                    relation_type = ".COURSE_EXLEARNER;
6277
                        $rsExSessions = Database::query($sqlS2);
6278
                        $countExSessions = Database::result($rsExSessions, 0, 0);
6279
                        $checkedExSessions = $countGroupSessions > 0 && ($countExSessions == $countGroupSessions);
6280
6281
                        // it checks if usergroup user should be set to EXLEARNER
6282
                        $checkedExClassLearner = false;
6283
                        if ($countGroupCourses > 0 && $countGroupSessions == 0) {
6284
                            $checkedExClassLearner = $checkedExCourses;
6285
                        } elseif ($countGroupCourses == 0 && $countGroupSessions > 0) {
6286
                            $checkedExClassLearner = $checkedExSessions;
6287
                        } elseif ($countGroupCourses > 0 && $countGroupSessions > 0) {
6288
                            $checkedExClassLearner = ($checkedExCourses && $checkedExSessions);
6289
                        }
6290
6291
                        if ($checkedExClassLearner) {
6292
                            Database::query("UPDATE $tblUserGroupRelUser SET relation_type = ".COURSE_EXLEARNER." WHERE user_id = $userId AND usergroup_id = $usergroupId");
6293
                        }
6294
                    }
6295
                }
6296
            }
6297
        }
6298
    }
6299
6300
    /**
6301
     * Subscribe boss to students, if $bossList is empty then the boss list will be empty too.
6302
     *
6303
     * @param int   $studentId
6304
     * @param array $bossList
6305
     * @param bool  $sendNotification
6306
     *
6307
     * @return mixed Affected rows or false on failure
6308
     */
6309
    public static function subscribeUserToBossList(
6310
        $studentId,
6311
        $bossList,
6312
        $sendNotification = false
6313
    ) {
6314
        $inserted = 0;
6315
        if (!empty($bossList)) {
6316
            sort($bossList);
6317
            $studentId = (int) $studentId;
6318
            $studentInfo = api_get_user_info($studentId);
6319
6320
            if (empty($studentInfo)) {
6321
                return false;
6322
            }
6323
6324
            $previousBossList = self::getStudentBossList($studentId);
6325
            $previousBossList = !empty($previousBossList) ? array_column($previousBossList, 'boss_id') : [];
6326
            sort($previousBossList);
6327
6328
            // Boss list is the same, nothing changed.
6329
            if ($bossList == $previousBossList) {
6330
                return false;
6331
            }
6332
6333
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6334
            self::removeAllBossFromStudent($studentId);
6335
6336
            foreach ($bossList as $bossId) {
6337
                $bossId = (int) $bossId;
6338
                $bossInfo = api_get_user_info($bossId);
6339
6340
                if (empty($bossInfo)) {
6341
                    continue;
6342
                }
6343
6344
                $bossLanguage = $bossInfo['language'];
6345
6346
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
6347
                        VALUES ($studentId, $bossId, ".USER_RELATION_TYPE_BOSS.")";
6348
                $insertId = Database::query($sql);
6349
6350
                if ($insertId) {
6351
                    if ($sendNotification) {
6352
                        $name = $studentInfo['complete_name'];
6353
                        $url = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?student='.$studentId;
6354
                        $url = Display::url($url, $url);
6355
                        $subject = sprintf(
6356
                            get_lang('UserXHasBeenAssignedToBoss', false, $bossLanguage),
6357
                            $name
6358
                        );
6359
                        $message = sprintf(
6360
                            get_lang('UserXHasBeenAssignedToBossWithUrlX', false, $bossLanguage),
6361
                            $name,
6362
                            $url
6363
                        );
6364
                        MessageManager::send_message_simple(
6365
                            $bossId,
6366
                            $subject,
6367
                            $message
6368
                        );
6369
                    }
6370
                    $inserted++;
6371
                }
6372
            }
6373
        } else {
6374
            self::removeAllBossFromStudent($studentId);
6375
        }
6376
6377
        return $inserted;
6378
    }
6379
6380
    /**
6381
     * Get users followed by student boss.
6382
     *
6383
     * @param int    $userId
6384
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
6385
     * @param bool   $getOnlyUserId
6386
     * @param bool   $getSql
6387
     * @param bool   $getCount
6388
     * @param int    $from
6389
     * @param int    $numberItems
6390
     * @param int    $column
6391
     * @param string $direction
6392
     * @param int    $active
6393
     * @param string $lastConnectionDate
6394
     *
6395
     * @return array users
6396
     */
6397
    public static function getUsersFollowedByStudentBoss(
6398
        $userId,
6399
        $userStatus = 0,
6400
        $getOnlyUserId = false,
6401
        $getSql = false,
6402
        $getCount = false,
6403
        $from = null,
6404
        $numberItems = null,
6405
        $column = null,
6406
        $direction = null,
6407
        $active = null,
6408
        $lastConnectionDate = null
6409
    ) {
6410
        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...
6411
            $userId,
6412
            $userStatus,
6413
            $getOnlyUserId,
6414
            $getSql,
6415
            $getCount,
6416
            $from,
6417
            $numberItems,
6418
            $column,
6419
            $direction,
6420
            $active,
6421
            $lastConnectionDate,
6422
            STUDENT_BOSS
6423
        );
6424
    }
6425
6426
    /**
6427
     * @return array
6428
     */
6429
    public static function getOfficialCodeGrouped()
6430
    {
6431
        $user = Database::get_main_table(TABLE_MAIN_USER);
6432
        $sql = "SELECT DISTINCT official_code
6433
                FROM $user
6434
                GROUP BY official_code";
6435
        $result = Database::query($sql);
6436
        $values = Database::store_result($result, 'ASSOC');
6437
        $result = [];
6438
        foreach ($values as $value) {
6439
            $result[$value['official_code']] = $value['official_code'];
6440
        }
6441
6442
        return $result;
6443
    }
6444
6445
    /**
6446
     * @param string $officialCode
6447
     *
6448
     * @return array
6449
     */
6450
    public static function getUsersByOfficialCode($officialCode)
6451
    {
6452
        $user = Database::get_main_table(TABLE_MAIN_USER);
6453
        $officialCode = Database::escape_string($officialCode);
6454
6455
        $sql = "SELECT DISTINCT id
6456
                FROM $user
6457
                WHERE official_code = '$officialCode'
6458
                ";
6459
        $result = Database::query($sql);
6460
6461
        $users = [];
6462
        while ($row = Database::fetch_array($result)) {
6463
            $users[] = $row['id'];
6464
        }
6465
6466
        return $users;
6467
    }
6468
6469
    /**
6470
     * Calc the expended time (in seconds) by a user in a course.
6471
     *
6472
     * @param int    $userId    The user id
6473
     * @param int    $courseId  The course id
6474
     * @param int    $sessionId Optional. The session id
6475
     * @param string $from      Optional. From date
6476
     * @param string $until     Optional. Until date
6477
     *
6478
     * @return int The time
6479
     */
6480
    public static function getTimeSpentInCourses(
6481
        $userId,
6482
        $courseId,
6483
        $sessionId = 0,
6484
        $from = '',
6485
        $until = ''
6486
    ) {
6487
        $userId = (int) $userId;
6488
        $sessionId = (int) $sessionId;
6489
6490
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6491
        $whereConditions = [
6492
            'user_id = ? ' => $userId,
6493
            'AND c_id = ? ' => $courseId,
6494
            'AND session_id = ? ' => $sessionId,
6495
        ];
6496
6497
        if (!empty($from) && !empty($until)) {
6498
            $whereConditions["AND (login_course_date >= '?' "] = $from;
6499
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
6500
        }
6501
6502
        $trackResult = Database::select(
6503
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
6504
            $trackCourseAccessTable,
6505
            [
6506
                'where' => $whereConditions,
6507
            ],
6508
            'first'
6509
        );
6510
6511
        if ($trackResult != false) {
6512
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
6513
        }
6514
6515
        return 0;
6516
    }
6517
6518
    /**
6519
     * Get the boss user ID from a followed user id.
6520
     *
6521
     * @param $userId
6522
     *
6523
     * @return bool
6524
     */
6525
    public static function getFirstStudentBoss($userId)
6526
    {
6527
        $userId = (int) $userId;
6528
        if ($userId > 0) {
6529
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6530
            $row = Database::select(
6531
                'DISTINCT friend_user_id AS boss_id',
6532
                $userRelTable,
6533
                [
6534
                    'where' => [
6535
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
6536
                            $userId,
6537
                            USER_RELATION_TYPE_BOSS,
6538
                        ],
6539
                    ],
6540
                ]
6541
            );
6542
            if (!empty($row)) {
6543
                return $row[0]['boss_id'];
6544
            }
6545
        }
6546
6547
        return false;
6548
    }
6549
6550
    /**
6551
     * Get the boss user ID from a followed user id.
6552
     *
6553
     * @param int $userId student id
6554
     *
6555
     * @return array
6556
     */
6557
    public static function getStudentBossList($userId)
6558
    {
6559
        $userId = (int) $userId;
6560
6561
        if ($userId > 0) {
6562
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6563
6564
            return Database::select(
6565
                'DISTINCT friend_user_id AS boss_id',
6566
                $userRelTable,
6567
                [
6568
                    'where' => [
6569
                        'user_id = ? AND relation_type = ? ' => [
6570
                            $userId,
6571
                            USER_RELATION_TYPE_BOSS,
6572
                        ],
6573
                    ],
6574
                ]
6575
            );
6576
        }
6577
6578
        return [];
6579
    }
6580
6581
    /**
6582
     * @param int $bossId
6583
     * @param int $studentId
6584
     *
6585
     * @return bool
6586
     */
6587
    public static function userIsBossOfStudent($bossId, $studentId)
6588
    {
6589
        $result = false;
6590
        $bossList = self::getStudentBossList($studentId);
6591
        if (!empty($bossList)) {
6592
            $bossList = array_column($bossList, 'boss_id');
6593
            if (in_array($bossId, $bossList)) {
6594
                $result = true;
6595
            }
6596
        }
6597
6598
        return $result;
6599
    }
6600
6601
    /**
6602
     * Displays the name of the user and makes the link to the user profile.
6603
     *
6604
     * @param array $userInfo
6605
     *
6606
     * @return string
6607
     */
6608
    public static function getUserProfileLink($userInfo)
6609
    {
6610
        if (isset($userInfo) && isset($userInfo['user_id'])) {
6611
            return Display::url(
6612
                $userInfo['complete_name_with_username'],
6613
                $userInfo['profile_url']
6614
            );
6615
        }
6616
6617
        return get_lang('Anonymous');
6618
    }
6619
6620
    /**
6621
     * Get users whose name matches $firstname and $lastname.
6622
     *
6623
     * @param string $firstname Firstname to search
6624
     * @param string $lastname  Lastname to search
6625
     *
6626
     * @return array The user list
6627
     */
6628
    public static function getUsersByName($firstname, $lastname)
6629
    {
6630
        $firstname = Database::escape_string($firstname);
6631
        $lastname = Database::escape_string($lastname);
6632
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
6633
6634
        $sql = <<<SQL
6635
            SELECT id, username, lastname, firstname
6636
            FROM $userTable
6637
            WHERE
6638
                firstname LIKE '$firstname%' AND
6639
                lastname LIKE '$lastname%'
6640
SQL;
6641
        $result = Database::query($sql);
6642
        $users = [];
6643
        while ($resultData = Database::fetch_object($result)) {
6644
            $users[] = $resultData;
6645
        }
6646
6647
        return $users;
6648
    }
6649
6650
    /**
6651
     * @param int $optionSelected
6652
     *
6653
     * @return string
6654
     */
6655
    public static function getUserSubscriptionTab($optionSelected = 1)
6656
    {
6657
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
6658
        if (($allowAdmin === 'true' && api_is_allowed_to_edit()) ||
6659
            api_is_platform_admin()
6660
        ) {
6661
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
6662
6663
            $headers = [
6664
                [
6665
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
6666
                    'content' => get_lang('Students'),
6667
                ],
6668
                [
6669
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
6670
                    'content' => get_lang('Teachers'),
6671
                ],
6672
                /*[
6673
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
6674
                    'content' => get_lang('Students'),
6675
                ],
6676
                [
6677
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
6678
                    'content' => get_lang('Teachers'),
6679
                ],*/
6680
                [
6681
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
6682
                    'content' => get_lang('Groups'),
6683
                ],
6684
                'classes' => [
6685
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
6686
                    'content' => get_lang('Classes'),
6687
                ],
6688
            ];
6689
6690
            if (api_get_configuration_value('session_classes_tab_disable')
6691
                && !api_is_platform_admin()
6692
                && api_get_session_id()
6693
            ) {
6694
                unset($headers['classes']);
6695
            }
6696
6697
            return Display::tabsOnlyLink($headers, $optionSelected);
6698
        }
6699
6700
        return '';
6701
    }
6702
6703
    /**
6704
     * Make sure this function is protected because it does NOT check password!
6705
     *
6706
     * This function defines globals.
6707
     *
6708
     * @param int  $userId
6709
     * @param bool $checkIfUserCanLoginAs
6710
     *
6711
     * @return bool
6712
     *
6713
     * @author Evie Embrechts
6714
     * @author Yannick Warnier <[email protected]>
6715
     */
6716
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
6717
    {
6718
        $userId = (int) $userId;
6719
        $userInfo = api_get_user_info($userId);
6720
6721
        // Check if the user is allowed to 'login_as'
6722
        $canLoginAs = true;
6723
        if ($checkIfUserCanLoginAs) {
6724
            $canLoginAs = api_can_login_as($userId);
6725
        }
6726
6727
        if (!$canLoginAs || empty($userInfo)) {
6728
            return false;
6729
        }
6730
6731
        if ($userId) {
6732
            $logInfo = [
6733
                'tool' => 'logout',
6734
                'tool_id' => 0,
6735
                'tool_id_detail' => 0,
6736
                'action' => '',
6737
                'info' => 'Change user (login as)',
6738
            ];
6739
            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

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