UserManager::updateCourseRelationTypeExLearner()   F
last analyzed

Complexity

Conditions 28
Paths 506

Size

Total Lines 125
Code Lines 88

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 88
c 0
b 0
f 0
dl 0
loc 125
rs 0.6861
cc 28
nc 506
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
            // the expiration_date has to be set taking it into account before calling create_user()
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
            $expirationDate = null;
465
        } else {
466
            $expirationDate = api_get_utc_datetime($expirationDate);
467
            $expirationDate = new \DateTime($expirationDate, new DateTimeZone('UTC'));
468
        }
469
470
        $userManager = self::getManager();
471
472
        /** @var User $user */
473
        $user = $userManager->createUser();
474
475
        $user
476
            ->setLastname($lastName)
477
            ->setFirstname($firstName)
478
            ->setUsername($loginName)
479
            ->setStatus($status)
480
            ->setPlainPassword($password)
481
            ->setEmail($email)
482
            ->setOfficialCode($official_code)
483
            ->setPictureUri($picture_uri)
484
            ->setCreatorId($creatorId)
485
            ->setAuthSource($authSource)
486
            ->setPhone($phone)
487
            ->setAddress($address)
488
            ->setLanguage($language)
489
            ->setRegistrationDate($now)
490
            ->setHrDeptId($hr_dept_id)
491
            ->setActive($active)
492
            ->setEnabled($active)
493
        ;
494
495
        if (!empty($expirationDate)) {
496
            $user->setExpirationDate($expirationDate);
497
        }
498
499
        $userManager->updateUser($user);
500
        $userId = $user->getId();
501
502
        if (!empty($userId)) {
503
            $return = $userId;
504
            $sql = "UPDATE $table_user SET user_id = $return WHERE id = $return";
505
            Database::query($sql);
506
507
            if ($isAdmin) {
508
                self::addUserAsAdmin($user);
509
            }
510
511
            if (api_get_multiple_access_url()) {
512
                UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
513
            } else {
514
                //we are adding by default the access_url_user table with access_url_id = 1
515
                UrlManager::add_user_to_url($userId, 1);
516
            }
517
518
            $extra['item_id'] = $userId;
519
520
            if (is_array($extra) && count($extra) > 0) {
521
                $userFieldValue = new ExtraFieldValue('user');
522
                // Force saving of extra fields (otherwise, if the current
523
                // user is not admin, fields not visible to the user - most
524
                // of them - are just ignored)
525
                $userFieldValue->saveFieldValues(
526
                    $extra,
527
                    true,
528
                    null,
529
                    null,
530
                    null,
531
                    true
532
                );
533
            } else {
534
                // Create notify settings by default
535
                self::update_extra_field_value(
536
                    $userId,
537
                    'mail_notify_invitation',
538
                    '1'
539
                );
540
                self::update_extra_field_value(
541
                    $userId,
542
                    'mail_notify_message',
543
                    '1'
544
                );
545
                self::update_extra_field_value(
546
                    $userId,
547
                    'mail_notify_group_message',
548
                    '1'
549
                );
550
            }
551
552
            self::update_extra_field_value(
553
                $userId,
554
                'already_logged_in',
555
                'false'
556
            );
557
558
            if (!empty($redirectToURLAfterLogin) && api_get_configuration_value('plugin_redirection_enabled')) {
559
                RedirectionPlugin::insert($userId, $redirectToURLAfterLogin);
560
            }
561
562
            if (!empty($email) && $send_mail) {
563
                $recipient_name = api_get_person_name(
564
                    $firstName,
565
                    $lastName,
566
                    null,
567
                    PERSON_NAME_EMAIL_ADDRESS
568
                );
569
                $tplSubject = new Template(
570
                    null,
571
                    false,
572
                    false,
573
                    false,
574
                    false,
575
                    false
576
        );
577
                // the complete_name is not used in the default Chamilo template but used in a specific template -refs BT#21334
578
                $tplSubject->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
579
                $layoutSubject = $tplSubject->get_template('mail/subject_registration_platform.tpl');
580
                $emailSubject = $tplSubject->fetch($layoutSubject);
581
                $sender_name = api_get_person_name(
582
                    api_get_setting('administratorName'),
583
                    api_get_setting('administratorSurname'),
584
                    null,
585
                    PERSON_NAME_EMAIL_ADDRESS
586
                );
587
                $email_admin = api_get_setting('emailAdministrator');
588
589
                $url = api_get_path(WEB_PATH);
590
                if (api_is_multiple_url_enabled()) {
591
                    $access_url_id = api_get_current_access_url_id();
592
                    if ($access_url_id != -1) {
593
                        $urlInfo = api_get_access_url($access_url_id);
594
                        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...
595
                            $url = $urlInfo['url'];
596
                        }
597
                    }
598
                }
599
600
                $tplContent = new Template(
601
                    null,
602
                    false,
603
                    false,
604
                    false,
605
                    false,
606
                    false
607
                );
608
                // variables for the default template
609
                $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
610
                $tplContent->assign('login_name', $loginName);
611
                $tplContent->assign('original_password', stripslashes($original_password));
612
                $tplContent->assign('mailWebPath', $url);
613
                $tplContent->assign('new_user', $user);
614
                // Adding this variable but not used in default template, used for task BT19518 with a customized template
615
                $tplContent->assign('status_type', $status);
616
617
                $layoutContent = $tplContent->get_template('mail/content_registration_platform.tpl');
618
                $emailBody = $tplContent->fetch($layoutContent);
619
620
                $userInfo = api_get_user_info($userId);
621
                $mailTemplateManager = new MailTemplateManager();
622
623
                /* MANAGE EVENT WITH MAIL */
624
                if (EventsMail::check_if_using_class('user_registration')) {
625
                    $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...
626
                    $values["password"] = $original_password;
627
                    $values["send_to"] = [$return];
628
                    $values["prior_lang"] = null;
629
                    EventsDispatcher::events('user_registration', $values);
630
                } else {
631
                    $phoneNumber = isset($extra['mobile_phone_number']) ? $extra['mobile_phone_number'] : null;
632
                    $additionalParameters = [
633
                        'smsType' => SmsPlugin::WELCOME_LOGIN_PASSWORD,
634
                        'userId' => $return,
635
                        'mobilePhoneNumber' => $phoneNumber,
636
                        'password' => $original_password,
637
                    ];
638
639
                    $emailBodyTemplate = '';
640
                    if (!empty($emailTemplate)) {
641
                        if (isset($emailTemplate['content_registration_platform.tpl']) &&
642
                            !empty($emailTemplate['content_registration_platform.tpl'])
643
                        ) {
644
                            $emailBodyTemplate = $mailTemplateManager->parseTemplate(
645
                                $emailTemplate['content_registration_platform.tpl'],
646
                                $userInfo
647
                            );
648
                        }
649
                    }
650
651
                    $twoEmail = api_get_configuration_value('send_two_inscription_confirmation_mail');
652
                    if ($twoEmail === true) {
653
                        $layoutContent = $tplContent->get_template('mail/new_user_first_email_confirmation.tpl');
654
                        $emailBody = $tplContent->fetch($layoutContent);
655
656
                        if (!empty($emailTemplate) &&
657
                            isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
658
                            !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
659
                        ) {
660
                            $emailBody = $mailTemplateManager->parseTemplate(
661
                                $emailTemplate['new_user_first_email_confirmation.tpl'],
662
                                $userInfo
663
                            );
664
                        }
665
666
                        api_mail_html(
667
                            $recipient_name,
668
                            $email,
669
                            $emailSubject,
670
                            $emailBody,
671
                            $sender_name,
672
                            $email_admin,
673
                            null,
674
                            null,
675
                            null,
676
                            $additionalParameters,
677
                            $creatorEmail
678
                        );
679
680
                        $layoutContent = $tplContent->get_template('mail/new_user_second_email_confirmation.tpl');
681
                        $emailBody = $tplContent->fetch($layoutContent);
682
683
                        if (!empty($emailTemplate) &&
684
                            isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
685
                            !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
686
                        ) {
687
                            $emailBody = $mailTemplateManager->parseTemplate(
688
                                $emailTemplate['new_user_second_email_confirmation.tpl'],
689
                                $userInfo
690
                            );
691
                        }
692
693
                        api_mail_html(
694
                            $recipient_name,
695
                            $email,
696
                            $emailSubject,
697
                            $emailBody,
698
                            $sender_name,
699
                            $email_admin,
700
                            null,
701
                            null,
702
                            null,
703
                            $additionalParameters,
704
                            $creatorEmail
705
                        );
706
                    } else {
707
                        if (!empty($emailBodyTemplate)) {
708
                            $emailBody = $emailBodyTemplate;
709
                        }
710
                        $sendToInbox = api_get_configuration_value('send_inscription_msg_to_inbox');
711
                        if ($sendToInbox) {
712
                            $adminList = self::get_all_administrators();
713
                            $senderId = 1;
714
                            if (!empty($adminList)) {
715
                                $adminInfo = current($adminList);
716
                                $senderId = $adminInfo['user_id'];
717
                            }
718
719
                            MessageManager::send_message_simple(
720
                                $userId,
721
                                $emailSubject,
722
                                $emailBody,
723
                                $senderId
724
                            );
725
                        } else {
726
                            api_mail_html(
727
                                $recipient_name,
728
                                $email,
729
                                $emailSubject,
730
                                $emailBody,
731
                                $sender_name,
732
                                $email_admin,
733
                                null,
734
                                null,
735
                                null,
736
                                $additionalParameters,
737
                                $creatorEmail
738
                            );
739
                        }
740
                    }
741
742
                    $notification = api_get_configuration_value('send_notification_when_user_added');
743
                    if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
744
                        foreach ($notification['admins'] as $adminId) {
745
                            $emailSubjectToAdmin = get_lang('UserAdded').': '.api_get_person_name($firstName, $lastName);
746
                            MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
747
                        }
748
                    }
749
                }
750
751
                if ($sendEmailToAllAdmins) {
752
                    $adminList = self::get_all_administrators();
753
754
                    $tplContent = new Template(
755
                        null,
756
                        false,
757
                        false,
758
                        false,
759
                        false,
760
                        false
761
                    );
762
                    // variables for the default template
763
                    $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
764
                    $tplContent->assign('user_added', $user);
765
                    $renderer = FormValidator::getDefaultRenderer();
766
                    // Form template
767
                    $elementTemplate = ' {label}: {element} <br />';
768
                    $renderer->setElementTemplate($elementTemplate);
769
                    /** @var FormValidator $form */
770
                    $form->freeze(null, $elementTemplate);
771
                    $form->removeElement('submit');
772
                    $formData = $form->returnForm();
773
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
774
                    $tplContent->assign('link', Display::url($url, $url));
775
                    $tplContent->assign('form', $formData);
776
777
                    $layoutContent = $tplContent->get_template('mail/content_registration_platform_to_admin.tpl');
778
                    $emailBody = $tplContent->fetch($layoutContent);
779
780
                    if (!empty($emailTemplate) &&
781
                        isset($emailTemplate['content_registration_platform_to_admin.tpl']) &&
782
                        !empty($emailTemplate['content_registration_platform_to_admin.tpl'])
783
                    ) {
784
                        $emailBody = $mailTemplateManager->parseTemplate(
785
                            $emailTemplate['content_registration_platform_to_admin.tpl'],
786
                            $userInfo
787
                        );
788
                    }
789
790
                    $subject = get_lang('UserAdded');
791
                    foreach ($adminList as $adminId => $data) {
792
                        MessageManager::send_message_simple(
793
                            $adminId,
794
                            $subject,
795
                            $emailBody,
796
                            $userId
797
                        );
798
                    }
799
                }
800
                /* ENDS MANAGE EVENT WITH MAIL */
801
            }
802
803
            if (!empty($hook)) {
804
                $hook->setEventData([
805
                    'return' => $userId,
806
                    'originalPassword' => $original_password,
807
                ]);
808
                $hook->notifyCreateUser(HOOK_EVENT_TYPE_POST);
809
            }
810
            Event::addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId);
811
        } else {
812
            Display::addFlash(
813
                Display::return_message(get_lang('ErrorContactPlatformAdmin'))
814
            );
815
816
            return false;
817
        }
818
819
        return $return;
820
    }
821
822
    /**
823
     * Ensure the CAS-authenticated user exists in the database.
824
     *
825
     * @param $casUser string the CAS user identifier
826
     *
827
     * @throws Exception if more than one user share the same CAS user identifier
828
     *
829
     * @return string|bool the recognised user login name or false if not found
830
     */
831
    public static function casUserLoginName($casUser)
832
    {
833
        $loginName = false;
834
835
        // look inside the casUser extra field
836
        if (UserManager::is_extra_field_available('cas_user')) {
837
            $valueModel = new ExtraFieldValue('user');
838
            $itemList = $valueModel->get_item_id_from_field_variable_and_field_value(
839
                'cas_user',
840
                $casUser,
841
                false,
842
                false,
843
                true
844
            );
845
            if (false !== $itemList) {
846
                // at least one user has $casUser in the 'cas_user' extra field
847
                // we attempt to load each candidate user because there might be deleted ones
848
                // (extra field values of a deleted user might remain)
849
                foreach ($itemList as $item) {
850
                    $userId = intval($item['item_id']);
851
                    $user = UserManager::getRepository()->find($userId);
852
                    if (!is_null($user)) {
853
                        if (false === $loginName) {
854
                            $loginName = $user->getUsername();
855
                        } else {
856
                            throw new Exception(get_lang('MoreThanOneUserMatched'));
857
                        }
858
                    }
859
                }
860
            }
861
        }
862
863
        if (false === $loginName) {
864
            // no matching 'cas_user' extra field value, or no such extra field
865
            // falling back to the old behaviour: $casUser must be the login name
866
            $userId = UserManager::get_user_id_from_username($casUser);
867
            if (false !== $userId) {
868
                $loginName = $casUser;
869
            }
870
        }
871
872
        return $loginName;
873
    }
874
875
    /**
876
     * Checks the availability of extra field 'cas_user'
877
     * and creates it if missing.
878
     *
879
     * @throws Exception on failure
880
     */
881
    public static function ensureCASUserExtraFieldExists()
882
    {
883
        if (!self::is_extra_field_available('cas_user')) {
884
            $extraField = new ExtraField('user');
885
            if (false === $extraField->save(
886
                    [
887
                        'variable' => 'cas_user',
888
                        'field_type' => ExtraField::FIELD_TYPE_TEXT,
889
                        'display_text' => get_lang('CAS User Identifier'),
890
                        'visible_to_self' => true,
891
                        'filter' => true,
892
                    ]
893
                )) {
894
                throw new Exception(get_lang('FailedToCreateExtraFieldCasUser'));
895
            }
896
897
            $rules = api_get_configuration_value('cas_user_map');
898
            if (!empty($rules) && isset($rules['extra'])) {
899
                foreach ($rules['extra'] as $extra) {
900
                    $extraField->save(
901
                        [
902
                            'variable' => $extra,
903
                            'field_type' => ExtraField::FIELD_TYPE_TEXT,
904
                            'display_text' => $extra,
905
                            'visible_to_self' => false,
906
                            'filter' => false,
907
                        ]
908
                    );
909
                }
910
            }
911
        }
912
    }
913
914
    /**
915
     * Create a CAS-authenticated user from scratch, from its CAS user identifier, with temporary default values.
916
     *
917
     * @param string $casUser the CAS user identifier
918
     *
919
     * @throws Exception on error
920
     *
921
     * @return string the login name of the new user
922
     */
923
    public static function createCASAuthenticatedUserFromScratch($casUser)
924
    {
925
        self::ensureCASUserExtraFieldExists();
926
927
        $loginName = 'cas_user_'.$casUser;
928
        $defaultValue = get_lang('EditInProfile');
929
        $defaultEmailValue = get_lang('EditInProfile');
930
        require_once __DIR__.'/../../auth/external_login/functions.inc.php';
931
        if ('true' === api_get_setting('login_is_email')) {
932
            $defaultEmailValue = $casUser;
933
        }
934
        $userId = external_add_user(
935
            [
936
                'username' => $loginName,
937
                'auth_source' => CAS_AUTH_SOURCE,
938
                'firstname' => $defaultValue,
939
                'lastname' => $defaultValue,
940
                'email' => $defaultEmailValue,
941
            ]
942
        );
943
        if (false === $userId) {
944
            throw new Exception(get_lang('FailedUserCreation'));
945
        }
946
        // Not checking function update_extra_field_value return value because not reliable
947
        self::update_extra_field_value($userId, 'cas_user', $casUser);
948
949
        return $loginName;
950
    }
951
952
    public static function updateCasUser($_user)
953
    {
954
        $rules = api_get_configuration_value('cas_user_map');
955
956
        if (empty($_user)) {
957
            return false;
958
        }
959
960
        if (!empty($rules)) {
961
            $userEntity = api_get_user_entity($_user['id']);
962
            $attributes = phpCAS::getAttributes();
963
            if (isset($rules['fields'])) {
964
                $isAdmin = false;
965
                foreach ($rules['fields'] as $field => $attributeName) {
966
                    if (!isset($attributes[$attributeName])) {
967
                        continue;
968
                    }
969
                    $value = $attributes[$attributeName];
970
                    // Check replace.
971
                    if (isset($rules['replace'][$attributeName])) {
972
                        $value = $rules['replace'][$attributeName][$value];
973
                    }
974
975
                    switch ($field) {
976
                        case 'email':
977
                            $userEntity->setEmail($value);
978
                            break;
979
                        case 'firstname':
980
                            $userEntity->setFirstname($value);
981
                            break;
982
                        case 'lastname':
983
                            $userEntity->setLastname($value);
984
                            break;
985
                        case 'active':
986
                            $userEntity->setActive('false' === $value);
987
                            break;
988
                        case 'status':
989
                            if (PLATFORM_ADMIN === (int) $value) {
990
                                $value = COURSEMANAGER;
991
                                $isAdmin = true;
992
                            }
993
                            $userEntity->setStatus($value);
994
                            break;
995
                    }
996
997
                    Database::getManager()->persist($userEntity);
998
                    Database::getManager()->flush();
999
1000
                    if ($isAdmin) {
1001
                        self::addUserAsAdmin($userEntity);
1002
                    }
1003
                }
1004
            }
1005
1006
            if (isset($rules['extra'])) {
1007
                foreach ($rules['extra'] as $variable) {
1008
                    if (isset($attributes[$variable])) {
1009
                        self::update_extra_field_value(
1010
                            $_user['id'],
1011
                            $variable,
1012
                            $attributes[$variable]
1013
                        );
1014
                    }
1015
                }
1016
            }
1017
        }
1018
    }
1019
1020
    /**
1021
     * Create a CAS-authenticated user from LDAP, from its CAS user identifier.
1022
     *
1023
     * @param $casUser
1024
     *
1025
     * @throws Exception
1026
     *
1027
     * @return string login name of the new user
1028
     */
1029
    public static function createCASAuthenticatedUserFromLDAP($casUser)
1030
    {
1031
        self::ensureCASUserExtraFieldExists();
1032
1033
        require_once __DIR__.'/../../auth/external_login/ldap.inc.php';
1034
        $login = extldapCasUserLogin($casUser);
1035
        if (false !== $login) {
1036
            $ldapUser = extldap_authenticate($login, 'nopass', true);
1037
            if (false !== $ldapUser) {
1038
                require_once __DIR__.'/../../auth/external_login/functions.inc.php';
1039
                $user = extldap_get_chamilo_user($ldapUser);
1040
                $user['username'] = $login;
1041
                $user['auth_source'] = CAS_AUTH_SOURCE;
1042
                $userId = external_add_user($user);
1043
                if (false !== $userId) {
1044
                    // Not checking function update_extra_field_value return value because not reliable
1045
                    self::update_extra_field_value($userId, 'cas_user', $casUser);
1046
1047
                    return $login;
1048
                } else {
1049
                    throw new Exception('Could not create the new user '.$login);
1050
                }
1051
            } else {
1052
                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

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

2796
                /** @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...
2797
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
2798
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
2799
                @rename($path.$old_file, $path.$prefix.$old_file);
2800
            } else {
2801
                @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

2801
                /** @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...
2802
                @unlink($path.'medium_'.$old_file);
2803
                @unlink($path.'big_'.$old_file);
2804
                @unlink($path.$old_file);
2805
            }
2806
        }
2807
2808
        // Exit if only deletion has been requested. Return an empty picture name.
2809
        if ($delete) {
2810
            return '';
2811
        }
2812
2813
        // Validation 2.
2814
        $allowed_types = api_get_supported_image_extensions();
2815
        $file = str_replace('\\', '/', $file);
2816
        $filename = (($pos = strrpos($file, '/')) !== false) ? substr($file, $pos + 1) : $file;
2817
        $extension = strtolower(substr(strrchr($filename, '.'), 1));
2818
        if (!in_array($extension, $allowed_types)) {
2819
            return false;
2820
        }
2821
2822
        // This is the common name for the new photos.
2823
        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...
2824
            $old_extension = strtolower(substr(strrchr($old_file, '.'), 1));
2825
            $filename = in_array($old_extension, $allowed_types) ? substr($old_file, 0, -strlen($old_extension)) : $old_file;
2826
            $filename = (substr($filename, -1) == '.') ? $filename.$extension : $filename.'.'.$extension;
2827
        } else {
2828
            $filename = api_replace_dangerous_char($filename);
2829
            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...
2830
                $filename = uniqid('').'_'.$filename;
2831
            }
2832
            // We always prefix user photos with user ids, so on setting
2833
            // api_get_setting('split_users_upload_directory') === 'true'
2834
            // the correspondent directories to be found successfully.
2835
            $filename = $user_id.'_'.$filename;
2836
        }
2837
2838
        if (!file_exists($source_file)) {
2839
            return false;
2840
        }
2841
2842
        $mimeContentType = mime_content_type($source_file);
2843
        if (false === strpos($mimeContentType, 'image')) {
2844
            return false;
2845
        }
2846
2847
        //Crop the image to adjust 1:1 ratio
2848
        $image = new Image($source_file);
2849
        $image->crop($cropParameters);
2850
2851
        // Storing the new photos in 4 versions with various sizes.
2852
        $userPath = self::getUserPathById($user_id, 'system');
2853
2854
        // If this path does not exist - we create it.
2855
        if (!file_exists($userPath)) {
2856
            mkdir($userPath, api_get_permissions_for_new_directories(), true);
2857
        }
2858
        $small = new Image($source_file);
2859
        $small->resize(32);
2860
        $small->send_image($userPath.'small_'.$filename);
2861
        $medium = new Image($source_file);
2862
        $medium->resize(85);
2863
        $medium->send_image($userPath.'medium_'.$filename);
2864
        $normal = new Image($source_file);
2865
        $normal->resize(200);
2866
        $normal->send_image($userPath.$filename);
2867
2868
        $big = new Image($source_file); // This is the original picture.
2869
        $big->send_image($userPath.'big_'.$filename);
2870
2871
        $result = $small && $medium && $normal && $big;
0 ignored issues
show
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...
introduced by
$normal is of type Image, thus it always evaluated to true.
Loading history...
2872
2873
        return $result ? $filename : false;
2874
    }
2875
2876
    /**
2877
     * Update User extra field file type into {user_folder}/{$extra_field}.
2878
     *
2879
     * @param int    $user_id     The user internal identification number
2880
     * @param string $extra_field The $extra_field The extra field name
2881
     * @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...
2882
     * @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...
2883
     *
2884
     * @return bool|null return filename if success, but false
2885
     */
2886
    public static function update_user_extra_file(
2887
        $user_id,
2888
        $extra_field = '',
2889
        $file = null,
2890
        $source_file = null
2891
    ) {
2892
        // Add Filter
2893
        $source_file = Security::filter_filename($source_file);
2894
        $file = Security::filter_filename($file);
2895
2896
        if (empty($user_id)) {
2897
            return false;
2898
        }
2899
2900
        if (empty($source_file)) {
2901
            $source_file = $file;
2902
        }
2903
2904
        // User-reserved directory where extra file have to be placed.
2905
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2906
        $path = $path_info['dir'];
2907
        if (!empty($extra_field)) {
2908
            $path .= $extra_field.'/';
2909
        }
2910
        // If this directory does not exist - we create it.
2911
        if (!file_exists($path)) {
2912
            @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

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

6912
            Event::/** @scrutinizer ignore-call */ 
6913
                   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...
6913
6914
            // Logout the current user
6915
            self::loginDelete(api_get_user_id());
6916
6917
            Session::erase('_user');
6918
            Session::erase('is_platformAdmin');
6919
            Session::erase('is_allowedCreateCourse');
6920
            Session::erase('_uid');
6921
6922
            // Cleaning session variables
6923
            $_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...
6924
            $_user['lastName'] = $userInfo['lastname'];
6925
            $_user['mail'] = $userInfo['email'];
6926
            $_user['official_code'] = $userInfo['official_code'];
6927
            $_user['picture_uri'] = $userInfo['picture_uri'];
6928
            $_user['user_id'] = $userId;
6929
            $_user['id'] = $userId;
6930
            $_user['status'] = $userInfo['status'];
6931
6932
            // Filling session variables with new data
6933
            Session::write('_uid', $userId);
6934
            Session::write('_user', $userInfo);
6935
            Session::write('is_platformAdmin', (bool) self::is_admin($userId));
6936
            Session::write('is_allowedCreateCourse', $userInfo['status'] == 1);
6937
            // will be useful later to know if the user is actually an admin or not (example reporting)
6938
            Session::write('login_as', true);
6939
            $logInfo = [
6940
                'tool' => 'login',
6941
                'tool_id' => 0,
6942
                'tool_id_detail' => 0,
6943
                'info' => $userId,
6944
            ];
6945
            Event::registerLog($logInfo);
6946
6947
            return true;
6948
        }
6949
6950
        return false;
6951
    }
6952
6953
    /**
6954
     * Remove all login records from the track_e_online stats table,
6955
     * for the given user ID.
6956
     *
6957
     * @param int $userId User ID
6958
     */
6959
    public static function loginDelete($userId)
6960
    {
6961
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6962
        $userId = (int) $userId;
6963
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
6964
        Database::query($query);
6965
    }
6966
6967
    /**
6968
     * Login as first admin user registered in the platform.
6969
     *
6970
     * @return array
6971
     */
6972
    public static function logInAsFirstAdmin()
6973
    {
6974
        $adminList = self::get_all_administrators();
6975
6976
        if (!empty($adminList)) {
6977
            $userInfo = current($adminList);
6978
            if (!empty($userInfo)) {
6979
                $result = self::loginAsUser($userInfo['user_id'], false);
6980
                if ($result && api_is_platform_admin()) {
6981
                    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...
6982
                }
6983
            }
6984
        }
6985
6986
        return [];
6987
    }
6988
6989
    public static function blockIfMaxLoginAttempts(array $userInfo)
6990
    {
6991
        if (false === (bool) $userInfo['active'] || null === $userInfo['last_login']) {
6992
            return;
6993
        }
6994
6995
        $maxAllowed = (int) api_get_configuration_value('login_max_attempt_before_blocking_account');
6996
6997
        if ($maxAllowed <= 0) {
6998
            return;
6999
        }
7000
7001
        $em = Database::getManager();
7002
7003
        $countFailedAttempts = $em
7004
            ->getRepository(TrackELoginAttempt::class)
7005
            ->createQueryBuilder('la')
7006
            ->select('COUNT(la)')
7007
            ->where('la.username = :username')
7008
            ->andWhere('la.loginDate >= :last_login')
7009
            ->andWhere('la.success <> TRUE')
7010
            ->setParameters(
7011
                [
7012
                    'username' => $userInfo['username'],
7013
                    'last_login' => $userInfo['last_login'],
7014
                ]
7015
            )
7016
            ->getQuery()
7017
            ->getSingleScalarResult()
7018
        ;
7019
7020
        if ($countFailedAttempts >= $maxAllowed) {
7021
            Database::update(
7022
                Database::get_main_table(TABLE_MAIN_USER),
7023
                ['active' => false],
7024
                ['username = ?' => $userInfo['username']]
7025
            );
7026
7027
            Display::addFlash(
7028
                Display::return_message(
7029
                    sprintf(
7030
                        get_lang('XAccountDisabledByYAttempts'),
7031
                        $userInfo['username'],
7032
                        $countFailedAttempts
7033
                    ),
7034
                    'error',
7035
                    false
7036
                )
7037
            );
7038
        }
7039
    }
7040
7041
    /**
7042
     * Check if user is teacher of a student based in their courses.
7043
     *
7044
     * @param $teacherId
7045
     * @param $studentId
7046
     *
7047
     * @return array
7048
     */
7049
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
7050
    {
7051
        $courses = CourseManager::getCoursesFollowedByUser(
7052
            $teacherId,
7053
            COURSEMANAGER
7054
        );
7055
        if (empty($courses)) {
7056
            return false;
7057
        }
7058
7059
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
7060
        if (empty($coursesFromUser)) {
7061
            return false;
7062
        }
7063
7064
        $coursesCodeList = array_column($courses, 'code');
7065
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
7066
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
7067
        $commonCourses = array_filter($commonCourses);
7068
7069
        if (!empty($commonCourses)) {
7070
            return $commonCourses;
7071
        }
7072
7073
        return [];
7074
    }
7075
7076
    /**
7077
     * @param int $teacherId
7078
     * @param int $studentId
7079
     *
7080
     * @return bool
7081
     */
7082
    public static function isTeacherOfStudent($teacherId, $studentId)
7083
    {
7084
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
7085
            $teacherId,
7086
            $studentId
7087
        );
7088
7089
        if (!empty($courses)) {
7090
            return true;
7091
        }
7092
7093
        return false;
7094
    }
7095
7096
    /**
7097
     * Send user confirmation mail.
7098
     *
7099
     * @throws Exception
7100
     */
7101
    public static function sendUserConfirmationMail(User $user)
7102
    {
7103
        $uniqueId = api_get_unique_id();
7104
        $user->setConfirmationToken($uniqueId);
7105
7106
        Database::getManager()->persist($user);
7107
        Database::getManager()->flush();
7108
7109
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
7110
7111
        // Check if the user was originally set for an automated subscription to a course or session
7112
        $courseCodeToRedirect = Session::read('course_redirect');
7113
        $sessionToRedirect = Session::read('session_redirect');
7114
        if (!empty($courseCodeToRedirect)) {
7115
            $url .= '&c='.$courseCodeToRedirect;
7116
        }
7117
        if (!empty($sessionToRedirect)) {
7118
            $url .= '&s='.$sessionToRedirect;
7119
        }
7120
        $mailSubject = get_lang('RegistrationConfirmation');
7121
        $mailBody = get_lang('RegistrationConfirmationEmailMessage')
7122
            .PHP_EOL
7123
            .Display::url($url, $url);
7124
7125
        api_mail_html(
7126
            self::formatUserFullName($user),
7127
            $user->getEmail(),
7128
            $mailSubject,
7129
            $mailBody
7130
        );
7131
        Display::addFlash(Display::return_message(get_lang('CheckYourEmailAndFollowInstructions')));
7132
    }
7133
7134
    /**
7135
     * Anonymize a user. Replace personal info by anonymous info.
7136
     *
7137
     * @param int  $userId   User id
7138
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
7139
     *
7140
     * @throws \Exception
7141
     *
7142
     * @return bool
7143
     * @assert (0) === false
7144
     */
7145
    public static function anonymize($userId, $deleteIP = true)
7146
    {
7147
        global $debug;
7148
7149
        $userId = (int) $userId;
7150
7151
        if (empty($userId)) {
7152
            return false;
7153
        }
7154
7155
        $em = Database::getManager();
7156
        $user = api_get_user_entity($userId);
7157
        $uniqueId = uniqid('anon', true);
7158
        $user
7159
            ->setFirstname($uniqueId)
7160
            ->setLastname($uniqueId)
7161
            ->setBiography('')
7162
            ->setAddress('')
7163
            ->setCurriculumItems(null)
7164
            ->setDateOfBirth(null)
7165
            ->setCompetences('')
7166
            ->setDiplomas('')
7167
            ->setOpenarea('')
7168
            ->setTeach('')
7169
            ->setProductions(null)
7170
            ->setOpenid('')
7171
            ->setEmailCanonical($uniqueId.'@example.com')
7172
            ->setEmail($uniqueId.'@example.com')
7173
            ->setUsername($uniqueId)
7174
            ->setUsernameCanonical($uniqueId)
7175
            ->setPhone('')
7176
            ->setOfficialCode('')
7177
        ;
7178
7179
        self::deleteUserPicture($userId);
7180
        self::cleanUserRequestsOfRemoval($userId);
7181
7182
        // The IP address is a border-case personal data, as it does
7183
        // not directly allow for personal identification (it is not
7184
        // a completely safe value in most countries - the IP could
7185
        // be used by neighbours and crackers)
7186
        if ($deleteIP) {
7187
            $substitute = '127.0.0.1';
7188
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
7189
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
7190
            $res = Database::query($sql);
7191
            if ($res === false && $debug > 0) {
7192
                error_log("Could not anonymize IP address for user $userId ($sql)");
7193
            }
7194
7195
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
7196
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
7197
            $res = Database::query($sql);
7198
            if ($res === false && $debug > 0) {
7199
                error_log("Could not anonymize IP address for user $userId ($sql)");
7200
            }
7201
7202
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
7203
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE exe_user_id = $userId";
7204
            $res = Database::query($sql);
7205
            if ($res === false && $debug > 0) {
7206
                error_log("Could not anonymize IP address for user $userId ($sql)");
7207
            }
7208
7209
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
7210
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
7211
            $res = Database::query($sql);
7212
            if ($res === false && $debug > 0) {
7213
                error_log("Could not anonymize IP address for user $userId ($sql)");
7214
            }
7215
7216
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
7217
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
7218
            $res = Database::query($sql);
7219
            if ($res === false && $debug > 0) {
7220
                error_log("Could not anonymize IP address for user $userId ($sql)");
7221
            }
7222
7223
            $table = Database::get_course_table(TABLE_WIKI);
7224
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
7225
            $res = Database::query($sql);
7226
            if ($res === false && $debug > 0) {
7227
                error_log("Could not anonymize IP address for user $userId ($sql)");
7228
            }
7229
7230
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
7231
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
7232
            $res = Database::query($sql);
7233
            if ($res === false && $debug > 0) {
7234
                error_log("Could not anonymize IP address for user $userId ($sql)");
7235
            }
7236
7237
            $table = Database::get_course_table(TABLE_WIKI);
7238
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
7239
            $res = Database::query($sql);
7240
            if ($res === false && $debug > 0) {
7241
                error_log("Could not anonymize IP address for user $userId ($sql)");
7242
            }
7243
        }
7244
        $em->persist($user);
7245
        $em->flush($user);
7246
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
7247
7248
        return true;
7249
    }
7250
7251
    /**
7252
     * @param int $userId
7253
     *
7254
     * @throws Exception
7255
     *
7256
     * @return string
7257
     */
7258
    public static function anonymizeUserWithVerification($userId)
7259
    {
7260
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
7261
7262
        $message = '';
7263
        if (api_is_platform_admin() ||
7264
            ($allowDelete && api_is_session_admin())
7265
        ) {
7266
            $userToUpdateInfo = api_get_user_info($userId);
7267
            $currentUserId = api_get_user_id();
7268
7269
            if ($userToUpdateInfo &&
7270
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
7271
            ) {
7272
                if ($userId != $currentUserId &&
7273
                    self::anonymize($userId)
7274
                ) {
7275
                    $message = Display::return_message(
7276
                        sprintf(get_lang('UserXAnonymized'), $userToUpdateInfo['complete_name_with_username']),
7277
                        'confirmation'
7278
                    );
7279
                } else {
7280
                    $message = Display::return_message(
7281
                        sprintf(get_lang('CannotAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
7282
                        'error'
7283
                    );
7284
                }
7285
            } else {
7286
                $message = Display::return_message(
7287
                    sprintf(get_lang('NoPermissionToAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
7288
                    'error'
7289
                );
7290
            }
7291
        }
7292
7293
        return $message;
7294
    }
7295
7296
    /**
7297
     * @param int $userId
7298
     *
7299
     * @throws Exception
7300
     *
7301
     * @return string
7302
     */
7303
    public static function deleteUserWithVerification($userId)
7304
    {
7305
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
7306
        $message = Display::return_message(get_lang('CannotDeleteUser'), 'error');
7307
        $userToUpdateInfo = api_get_user_info($userId);
7308
7309
        // User must exist.
7310
        if (empty($userToUpdateInfo)) {
7311
            return $message;
7312
        }
7313
7314
        $currentUserId = api_get_user_id();
7315
7316
        // Cannot delete myself.
7317
        if ($userId == $currentUserId) {
7318
            return $message;
7319
        }
7320
7321
        if (api_is_platform_admin() ||
7322
            ($allowDelete && api_is_session_admin())
7323
        ) {
7324
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
7325
                if (self::delete_user($userId)) {
7326
                    $message = Display::return_message(
7327
                        get_lang('UserDeleted').': '.$userToUpdateInfo['complete_name_with_username'],
7328
                        'confirmation'
7329
                    );
7330
                } else {
7331
                    $message = Display::return_message(get_lang('CannotDeleteUserBecauseOwnsCourse'), 'error');
7332
                }
7333
            }
7334
        }
7335
7336
        return $message;
7337
    }
7338
7339
    /**
7340
     * @return array
7341
     */
7342
    public static function createDataPrivacyExtraFields()
7343
    {
7344
        self::create_extra_field(
7345
            'request_for_legal_agreement_consent_removal_justification',
7346
            1, //text
7347
            'Request for legal agreement consent removal justification	',
7348
            ''
7349
        );
7350
7351
        self::create_extra_field(
7352
            'request_for_delete_account_justification',
7353
            1, //text
7354
            'Request for delete account justification',
7355
            ''
7356
        );
7357
7358
        $extraFieldId = self::create_extra_field(
7359
            'request_for_legal_agreement_consent_removal',
7360
            1, //text
7361
            'Request for legal agreement consent removal',
7362
            ''
7363
        );
7364
7365
        $extraFieldIdDeleteAccount = self::create_extra_field(
7366
            'request_for_delete_account',
7367
            1, //text
7368
            'Request for delete user account',
7369
            ''
7370
        );
7371
7372
        return [
7373
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
7374
            'delete_legal' => $extraFieldId,
7375
        ];
7376
    }
7377
7378
    /**
7379
     * @param int $userId
7380
     */
7381
    public static function cleanUserRequestsOfRemoval($userId)
7382
    {
7383
        $userId = (int) $userId;
7384
7385
        $extraFieldValue = new ExtraFieldValue('user');
7386
        $extraFieldsToDelete = [
7387
            'legal_accept',
7388
            'request_for_legal_agreement_consent_removal',
7389
            'request_for_legal_agreement_consent_removal_justification',
7390
            'request_for_delete_account_justification', // just in case delete also this
7391
            'request_for_delete_account',
7392
        ];
7393
7394
        foreach ($extraFieldsToDelete as $variable) {
7395
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
7396
                $userId,
7397
                $variable
7398
            );
7399
            if ($value && isset($value['id'])) {
7400
                $extraFieldValue->delete($value['id']);
7401
            }
7402
        }
7403
    }
7404
7405
    /**
7406
     * @param int $searchYear
7407
     *
7408
     * @throws Exception
7409
     *
7410
     * @return array
7411
     */
7412
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
7413
    {
7414
        $timezone = new DateTimeZone(api_get_timezone());
7415
7416
        $sessions = [];
7417
        if (DRH == $userInfo['status']) {
7418
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
7419
        } elseif (api_is_platform_admin(true)) {
7420
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
7421
        } else {
7422
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
7423
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
7424
7425
            foreach ($sessionsByCategory as $sessionsInCategory) {
7426
                $sessions = array_merge($sessions, $sessionsInCategory);
7427
            }
7428
        }
7429
7430
        $sessions = array_map(
7431
            function ($sessionInfo) {
7432
                if (!isset($sessionInfo['session_id'])) {
7433
                    $sessionInfo['session_id'] = $sessionInfo['id'];
7434
                }
7435
                if (!isset($sessionInfo['session_name'])) {
7436
                    $sessionInfo['session_name'] = $sessionInfo['name'];
7437
                }
7438
7439
                return $sessionInfo;
7440
            },
7441
            $sessions
7442
        );
7443
7444
        $calendarSessions = [];
7445
7446
        foreach ($sessions as $sessionInfo) {
7447
            if (!empty($sessionInfo['duration'])) {
7448
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
7449
                    $sessionInfo['session_id'],
7450
                    $userInfo['id']
7451
                );
7452
7453
                if (empty($courseAccess)) {
7454
                    continue;
7455
                }
7456
7457
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
7458
                $lastAccessDate = clone $firstAcessDate;
7459
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
7460
7461
                $firstAccessYear = (int) $firstAcessDate->format('Y');
7462
                $lastAccessYear = (int) $lastAccessDate->format('Y');
7463
7464
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
7465
                    $calendarSessions[$sessionInfo['session_id']] = [
7466
                        'name' => $sessionInfo['session_name'],
7467
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
7468
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
7469
                    ];
7470
                }
7471
7472
                continue;
7473
            }
7474
7475
            $accessStartDate = !empty($sessionInfo['access_start_date'])
7476
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
7477
                : null;
7478
            $accessEndDate = !empty($sessionInfo['access_end_date'])
7479
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
7480
                : null;
7481
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
7482
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
7483
7484
            $isValid = false;
7485
7486
            if ($accessStartYear && $accessEndYear) {
7487
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
7488
                    $isValid = true;
7489
                }
7490
            }
7491
7492
            if ($accessStartYear && !$accessEndYear) {
7493
                if ($accessStartYear == $searchYear) {
7494
                    $isValid = true;
7495
                }
7496
            }
7497
7498
            if (!$accessStartYear && $accessEndYear) {
7499
                if ($accessEndYear == $searchYear) {
7500
                    $isValid = true;
7501
                }
7502
            }
7503
7504
            if ($isValid) {
7505
                $calendarSessions[$sessionInfo['session_id']] = [
7506
                    'name' => $sessionInfo['session_name'],
7507
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
7508
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
7509
                ];
7510
            }
7511
        }
7512
7513
        return $calendarSessions;
7514
    }
7515
7516
    /**
7517
     * Get sessions info for planification calendar.
7518
     *
7519
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
7520
     * @param int   $searchYear
7521
     *
7522
     * @throws Exception
7523
     *
7524
     * @return array
7525
     */
7526
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
7527
    {
7528
        $timezone = new DateTimeZone(api_get_timezone());
7529
        $calendar = [];
7530
7531
        foreach ($sessionsList as $sessionId => $sessionInfo) {
7532
            $startDate = $sessionInfo['access_start_date']
7533
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
7534
                : null;
7535
            $endDate = $sessionInfo['access_end_date']
7536
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
7537
                : null;
7538
7539
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
7540
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
7541
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
7542
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
7543
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
7544
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
7545
7546
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
7547
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
7548
7549
            $calendar[] = [
7550
                'id' => $sessionId,
7551
                'name' => $sessionInfo['name'],
7552
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
7553
                'start_in_last_year' => $startYear < $searchYear,
7554
                'end_in_next_year' => $endYear > $searchYear,
7555
                'no_start' => !$startWeek,
7556
                'no_end' => !$endWeek,
7557
                'start' => $start,
7558
                'duration' => $duration > 0 ? $duration : 1,
7559
            ];
7560
        }
7561
7562
        usort(
7563
            $calendar,
7564
            function ($sA, $sB) {
7565
                if ($sA['start'] == $sB['start']) {
7566
                    return 0;
7567
                }
7568
7569
                if ($sA['start'] < $sB['start']) {
7570
                    return -1;
7571
                }
7572
7573
                return 1;
7574
            }
7575
        );
7576
7577
        return $calendar;
7578
    }
7579
7580
    /**
7581
     * Return the user's full name. Optionally with the username.
7582
     *
7583
     * @param bool $includeUsername Optional. By default username is not included.
7584
     *
7585
     * @return string
7586
     */
7587
    public static function formatUserFullName(User $user, $includeUsername = false)
7588
    {
7589
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
7590
7591
        if ($includeUsername && api_get_configuration_value('hide_username_with_complete_name') !== true) {
7592
            $username = $user->getUsername();
7593
7594
            return "$fullName ($username)";
7595
        }
7596
7597
        return $fullName;
7598
    }
7599
7600
    /**
7601
     * @param int $userId
7602
     *
7603
     * @return array
7604
     */
7605
    public static function getUserCareers($userId)
7606
    {
7607
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7608
        $tableCareer = Database::get_main_table(TABLE_CAREER);
7609
        $userId = (int) $userId;
7610
7611
        $sql = "SELECT c.id, c.name
7612
                FROM $table uc
7613
                INNER JOIN $tableCareer c
7614
                ON uc.career_id = c.id
7615
                WHERE user_id = $userId
7616
                ORDER BY uc.created_at
7617
                ";
7618
        $result = Database::query($sql);
7619
7620
        return Database::store_result($result, 'ASSOC');
7621
    }
7622
7623
    /**
7624
     * @param int $userId
7625
     * @param int $careerId
7626
     */
7627
    public static function addUserCareer($userId, $careerId)
7628
    {
7629
        if (!api_get_configuration_value('allow_career_users')) {
7630
            return false;
7631
        }
7632
7633
        if (self::userHasCareer($userId, $careerId) === false) {
7634
            $params = [
7635
                'user_id' => $userId,
7636
                'career_id' => $careerId,
7637
                'created_at' => api_get_utc_datetime(),
7638
                'updated_at' => api_get_utc_datetime(),
7639
            ];
7640
            $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7641
            Database::insert($table, $params);
7642
        }
7643
7644
        return true;
7645
    }
7646
7647
    /**
7648
     * @param int   $userCareerId
7649
     * @param array $data
7650
     *
7651
     * @return bool
7652
     */
7653
    public static function updateUserCareer($userCareerId, $data)
7654
    {
7655
        if (!api_get_configuration_value('allow_career_users')) {
7656
            return false;
7657
        }
7658
7659
        $params = ['extra_data' => $data, 'updated_at' => api_get_utc_datetime()];
7660
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7661
        Database::update(
7662
            $table,
7663
            $params,
7664
            ['id = ?' => (int) $userCareerId]
7665
        );
7666
7667
        return true;
7668
    }
7669
7670
    /**
7671
     * @param int $userId
7672
     * @param int $careerId
7673
     *
7674
     * @return array
7675
     */
7676
    public static function getUserCareer($userId, $careerId)
7677
    {
7678
        $userId = (int) $userId;
7679
        $careerId = (int) $careerId;
7680
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7681
7682
        $sql = "SELECT * FROM $table WHERE user_id = $userId AND career_id = $careerId";
7683
        $result = Database::query($sql);
7684
7685
        return Database::fetch_array($result, 'ASSOC');
7686
    }
7687
7688
    /**
7689
     * @param int $userId
7690
     * @param int $careerId
7691
     *
7692
     * @return bool
7693
     */
7694
    public static function userHasCareer($userId, $careerId)
7695
    {
7696
        $userId = (int) $userId;
7697
        $careerId = (int) $careerId;
7698
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7699
7700
        $sql = "SELECT id FROM $table WHERE user_id = $userId AND career_id = $careerId";
7701
        $result = Database::query($sql);
7702
7703
        return Database::num_rows($result) > 0;
7704
    }
7705
7706
    /**
7707
     * @param int $userId
7708
     *
7709
     * @throws Exception
7710
     */
7711
    public static function deleteUserFiles($userId)
7712
    {
7713
        $path = self::getUserPathById($userId, 'system');
7714
7715
        $fs = new Filesystem();
7716
        $fs->remove($path);
7717
    }
7718
7719
    public static function redirectToResetPassword($userId)
7720
    {
7721
        $forceRenew = api_get_configuration_value('force_renew_password_at_first_login');
7722
7723
        if ($forceRenew) {
7724
            $askPassword = self::get_extra_user_data_by_field(
7725
                $userId,
7726
                'ask_new_password'
7727
            );
7728
7729
            if (!empty($askPassword) && isset($askPassword['ask_new_password']) &&
7730
                1 === (int) $askPassword['ask_new_password']
7731
            ) {
7732
                $uniqueId = api_get_unique_id();
7733
                $userObj = api_get_user_entity($userId);
7734
7735
                $userObj->setConfirmationToken($uniqueId);
7736
                $userObj->setPasswordRequestedAt(new \DateTime());
7737
7738
                Database::getManager()->persist($userObj);
7739
                Database::getManager()->flush();
7740
7741
                $url = api_get_path(WEB_CODE_PATH).'auth/reset.php?token='.$uniqueId;
7742
                api_location($url);
7743
            }
7744
        }
7745
7746
        $forceRotateDays = api_get_configuration_value('security_password_rotate_days');
7747
        $forceRotate = false;
7748
7749
        if ($forceRotateDays > 0) {
7750
            // get the date of the last password update recorded
7751
            $lastUpdate = self::get_extra_user_data_by_field(
7752
                $userId,
7753
                'password_updated_at'
7754
            );
7755
7756
            if (empty($lastUpdate) or empty($lastUpdate['password_updated_at'])) {
7757
                $userObj = api_get_user_entity($userId);
7758
                $registrationDate = $userObj->getRegistrationDate();
7759
                $now = new \DateTime(null, new DateTimeZone('UTC'));
7760
                $interval = $now->diff($registrationDate);
7761
                $daysSince = $interval->format('%a');
7762
                if ($daysSince > $forceRotateDays) {
7763
                    $forceRotate = true;
7764
                }
7765
            } else {
7766
                $now = new \DateTime(null, new DateTimeZone('UTC'));
7767
                // In some cases, old records might contain an incomplete Y-m-d H:i:s format
7768
                if (strlen($lastUpdate['password_updated_at']) == 10) {
7769
                    $lastUpdate['password_updated_at'] .= ' 00:00:00';
7770
                }
7771
                if (strlen($lastUpdate['password_updated_at']) == 16) {
7772
                    $lastUpdate['password_updated_at'] .= ':00';
7773
                }
7774
                $date = \DateTime::createFromFormat('Y-m-d H:i:s', $lastUpdate['password_updated_at'], new DateTimeZone('UTC'));
7775
                $interval = $now->diff($date);
7776
                $daysSince = $interval->format('%a');
7777
                if ($daysSince > $forceRotateDays) {
7778
                    $forceRotate = true;
7779
                }
7780
            }
7781
            if ($forceRotate) {
7782
                $uniqueId = api_get_unique_id();
7783
                $userObj = api_get_user_entity($userId);
7784
7785
                $userObj->setConfirmationToken($uniqueId);
7786
                $userObj->setPasswordRequestedAt(new \DateTime());
7787
7788
                Database::getManager()->persist($userObj);
7789
                Database::getManager()->flush();
7790
7791
                $url = api_get_path(WEB_CODE_PATH).'auth/reset.php?token='.$uniqueId.'&rotate=1';
7792
                api_location($url);
7793
            }
7794
        }
7795
    }
7796
7797
    /**
7798
     * It returns the list of user status available.
7799
     *
7800
     * @return array
7801
     */
7802
    public static function getUserStatusList()
7803
    {
7804
        $userStatusConfig = [];
7805
        // it gets the roles to show in creation/edition user
7806
        if (true === api_get_configuration_value('user_status_show_options_enabled')) {
7807
            $userStatusConfig = api_get_configuration_value('user_status_show_option');
7808
        }
7809
        // it gets the roles to show in creation/edition user (only for admins)
7810
        if (true === api_get_configuration_value('user_status_option_only_for_admin_enabled') && api_is_platform_admin()) {
7811
            $userStatusConfig = api_get_configuration_value('user_status_option_show_only_for_admin');
7812
        }
7813
7814
        $status = [];
7815
        if (!empty($userStatusConfig)) {
7816
            $statusLang = api_get_status_langvars();
7817
            foreach ($userStatusConfig as $role => $enabled) {
7818
                if ($enabled) {
7819
                    $constStatus = constant($role);
7820
                    $status[$constStatus] = $statusLang[$constStatus];
7821
                }
7822
            }
7823
        } else {
7824
            $status[COURSEMANAGER] = get_lang('Teacher');
7825
            $status[STUDENT] = get_lang('Learner');
7826
            $status[DRH] = get_lang('Drh');
7827
            $status[SESSIONADMIN] = get_lang('SessionsAdmin');
7828
            $status[STUDENT_BOSS] = get_lang('RoleStudentBoss');
7829
            $status[INVITEE] = get_lang('Invitee');
7830
        }
7831
7832
        return $status;
7833
    }
7834
7835
    /**
7836
     * Get the expiration date by user status from configuration value.
7837
     *
7838
     * @param $status
7839
     *
7840
     * @throws Exception
7841
     *
7842
     * @return array
7843
     */
7844
    public static function getExpirationDateByRole($status)
7845
    {
7846
        $status = (int) $status;
7847
        $nbDaysByRole = api_get_configuration_value('user_number_of_days_for_default_expiration_date_per_role');
7848
        $dates = [];
7849
        if (!empty($nbDaysByRole)) {
7850
            $date = new DateTime();
7851
            foreach ($nbDaysByRole as $strVariable => $nDays) {
7852
                $constStatus = constant($strVariable);
7853
                if ($status == $constStatus) {
7854
                    $duration = "P{$nDays}D";
7855
                    $date->add(new DateInterval($duration));
7856
                    $newExpirationDate = $date->format('Y-m-d H:i');
7857
                    $formatted = api_format_date($newExpirationDate, DATE_TIME_FORMAT_LONG_24H);
7858
                    $dates = ['formatted' => $formatted, 'date' => $newExpirationDate];
7859
                }
7860
            }
7861
        }
7862
7863
        return $dates;
7864
    }
7865
7866
    public static function getAllowedRolesAsTeacher(): array
7867
    {
7868
        if (api_get_configuration_value('course_allow_student_role_to_be_teacher')) {
7869
            return [
7870
                STUDENT,
7871
                COURSEMANAGER,
7872
                SESSIONADMIN,
7873
            ];
7874
        } else {
7875
            return [
7876
                COURSEMANAGER,
7877
                SESSIONADMIN,
7878
            ];
7879
        }
7880
    }
7881
7882
    /**
7883
     * Count users in courses and if they have certificate.
7884
     * This function is resource intensive.
7885
     *
7886
     * @return array
7887
     */
7888
    public static function countUsersWhoFinishedCourses()
7889
    {
7890
        $courses = [];
7891
        $currentAccessUrlId = api_get_current_access_url_id();
7892
        $sql = "SELECT course.code, cru.user_id
7893
                FROM course_rel_user cru
7894
                    JOIN course ON cru.c_id = course.id
7895
                    JOIN access_url_rel_user auru on cru.user_id = auru.user_id
7896
                    JOIN access_url_rel_course ON course.id = access_url_rel_course.c_id
7897
                WHERE access_url_rel_course.access_url_id = $currentAccessUrlId
7898
                ORDER BY course.code
7899
        ";
7900
7901
        $res = Database::query($sql);
7902
        if (Database::num_rows($res) > 0) {
7903
            while ($row = Database::fetch_array($res)) {
7904
                if (!isset($courses[$row['code']])) {
7905
                    $courses[$row['code']] = [
7906
                        'subscribed' => 0,
7907
                        'finished' => 0,
7908
                    ];
7909
                }
7910
7911
                $courses[$row['code']]['subscribed']++;
7912
                $entityManager = Database::getManager();
7913
                $repository = $entityManager->getRepository('ChamiloCoreBundle:GradebookCategory');
7914
                //todo check when have more than 1 gradebook
7915
                /** @var \Chamilo\CoreBundle\Entity\GradebookCategory $gradebook */
7916
                $gradebook = $repository->findOneBy(['courseCode' => $row['code']]);
7917
7918
                if (!empty($gradebook)) {
7919
                    $finished = 0;
7920
                    $gb = Category::createCategoryObjectFromEntity($gradebook);
7921
                    $finished = $gb->is_certificate_available($row['user_id']);
7922
                    if (!empty($finished)) {
7923
                        $courses[$row['code']]['finished']++;
7924
                    }
7925
                }
7926
            }
7927
        }
7928
7929
        return $courses;
7930
    }
7931
7932
    /**
7933
     * Count users in sessions and if they have certificate.
7934
     * This function is resource intensive.
7935
     *
7936
     * @return array
7937
     */
7938
    public static function countUsersWhoFinishedCoursesInSessions()
7939
    {
7940
        $coursesInSessions = [];
7941
        $currentAccessUrlId = api_get_current_access_url_id();
7942
        $sql = "SELECT course.code, srcru.session_id, srcru.user_id, session.name
7943
                FROM session_rel_course_rel_user srcru
7944
                    JOIN course ON srcru.c_id = course.id
7945
                    JOIN access_url_rel_session aurs on srcru.session_id = aurs.session_id
7946
                    JOIN session ON srcru.session_id = session.id
7947
                WHERE aurs.access_url_id = $currentAccessUrlId
7948
                ORDER BY course.code, session.name
7949
        ";
7950
7951
        $res = Database::query($sql);
7952
        if (Database::num_rows($res) > 0) {
7953
            while ($row = Database::fetch_array($res)) {
7954
                $index = $row['code'].' ('.$row['name'].')';
7955
                if (!isset($coursesInSessions[$index])) {
7956
                    $coursesInSessions[$index] = [
7957
                        'subscribed' => 0,
7958
                        'finished' => 0,
7959
                    ];
7960
                }
7961
7962
                $coursesInSessions[$index]['subscribed']++;
7963
                $entityManager = Database::getManager();
7964
                $repository = $entityManager->getRepository('ChamiloCoreBundle:GradebookCategory');
7965
                /** @var \Chamilo\CoreBundle\Entity\GradebookCategory $gradebook */
7966
                $gradebook = $repository->findOneBy(
7967
                    [
7968
                        'courseCode' => $row['code'],
7969
                        'sessionId' => $row['session_id'],
7970
                    ]
7971
                );
7972
7973
                if (!empty($gradebook)) {
7974
                    $finished = 0;
7975
                    $gb = Category::createCategoryObjectFromEntity($gradebook);
7976
                    $finished = $gb->is_certificate_available($row['user_id']);
7977
                    if (!empty($finished)) {
7978
                        $coursesInSessions[$index]['finished']++;
7979
                    }
7980
                }
7981
            }
7982
        }
7983
7984
        return $coursesInSessions;
7985
    }
7986
7987
    /**
7988
     * Build the active-column of the table to lock or unlock a certain user
7989
     * lock = the user can no longer use this account.
7990
     *
7991
     * @author Patrick Cool <[email protected]>, Ghent University
7992
     *
7993
     * @return string Some HTML-code with the lock/unlock button
7994
     */
7995
    public static function getActiveFilterForTable(string $active, string $params, array $row): string
7996
    {
7997
        if ('1' == $active) {
7998
            $action = 'Lock';
7999
            $image = 'accept';
8000
        } elseif ('-1' == $active) {
8001
            $action = 'edit';
8002
            $image = 'warning';
8003
        } elseif ('0' == $active) {
8004
            $action = 'Unlock';
8005
            $image = 'error';
8006
        }
8007
8008
        if ('edit' === $action) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $action does not seem to be defined for all execution paths leading up to this point.
Loading history...
8009
            $langAccountExpired = get_lang('AccountExpired');
8010
8011
            return Display::return_icon(
8012
                $image.'.png',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $image does not seem to be defined for all execution paths leading up to this point.
Loading history...
8013
                    $langAccountExpired,
8014
                [],
8015
                ICON_SIZE_TINY
8016
            ).'<span class="sr-only" aria-hidden="true">'.$langAccountExpired.'</span>';
8017
        }
8018
8019
        if ($row['0'] != api_get_user_id()) {
8020
            $langAction = get_lang(ucfirst($action));
8021
            // you cannot lock yourself out otherwise you could disable all the
8022
            // accounts including your own => everybody is locked out and nobody
8023
            // can change it anymore.
8024
            return Display::return_icon(
8025
                $image.'.png',
8026
                $langAction,
8027
                ['onclick' => 'active_user(this);', 'id' => 'img_'.$row['0'], 'style' => 'cursor: pointer;'],
8028
                ICON_SIZE_TINY
8029
                ).'<span class="sr-only" aria-hidden="true">'.$langAction.'</span>';
8030
        }
8031
8032
        return '';
8033
    }
8034
8035
    public static function getScriptFunctionForActiveFilter(): string
8036
    {
8037
        return 'function active_user(element_div) {
8038
            id_image = $(element_div).attr("id");
8039
            image_clicked = $(element_div).attr("src");
8040
            image_clicked_info = image_clicked.split("/");
8041
            image_real_clicked = image_clicked_info[image_clicked_info.length-1];
8042
            var status = 1;
8043
            if (image_real_clicked == "accept.png") {
8044
                status = 0;
8045
            }
8046
            user_id = id_image.split("_");
8047
            ident = "#img_"+user_id[1];
8048
            if (confirm("'.get_lang('AreYouSureToEditTheUserStatus', '').'")) {
8049
                 $.ajax({
8050
                    contentType: "application/x-www-form-urlencoded",
8051
                    beforeSend: function(myObject) {
8052
                        $(ident).attr("src","'.Display::returnIconPath('loading1.gif').'"); //candy eye stuff
8053
                    },
8054
                    type: "GET",
8055
                    url: _p.web_ajax + "user_manager.ajax.php?a=active_user",
8056
                    data: "user_id=" + user_id[1] + "&status=" + status,
8057
                    success: function(data) {
8058
                        if (data == 1) {
8059
                            $(ident).attr("src", "'.Display::returnIconPath('accept.png', ICON_SIZE_TINY).'");
8060
                            $(ident).attr("title","'.get_lang('Lock').'");
8061
                        }
8062
                        if (data == 0) {
8063
                            $(ident).attr("src","'.Display::returnIconPath('error.png').'");
8064
                            $(ident).attr("title","'.get_lang('Unlock').'");
8065
                        }
8066
                        if (data == -1) {
8067
                            $(ident).attr("src", "'.Display::returnIconPath('warning.png').'");
8068
                            $(ident).attr("title","'.get_lang('ActionNotAllowed').'");
8069
                        }
8070
                    }
8071
                });
8072
            }
8073
        }';
8074
    }
8075
8076
    /**
8077
     * Get a list of users with the given e-mail address + their "active" field value (0 or 1).
8078
     *
8079
     * @param string $mail User id
8080
     *
8081
     * @return array List of users e-mails + active field
8082
     */
8083
    public static function getUsersByMail(string $mail): array
8084
    {
8085
        $resultData = Database::select(
8086
            'id, active',
8087
            Database::get_main_table(TABLE_MAIN_USER),
8088
            [
8089
                'where' => ['email = ?' => $mail],
8090
            ],
8091
            'all',
8092
            null
8093
        );
8094
8095
        if ($resultData === false) {
8096
            return [];
8097
        }
8098
8099
        return $resultData;
8100
    }
8101
8102
    /**
8103
     * Get whether we can send an e-mail or not.
8104
     * If the e-mail is not in the database, send the mail.
8105
     * If the e-mail is in the database but none of its occurences is active, don't send.
8106
     *
8107
     * @param string $mail The e-mail address to check
8108
     *
8109
     * @return bool Whether we can send an e-mail or not
8110
     */
8111
    public static function isEmailingAllowed(string $mail): bool
8112
    {
8113
        $list = self::getUsersByMail($mail);
8114
        if (empty($list)) {
8115
            // No e-mail matches, send the mail
8116
            return true;
8117
        }
8118
        $send = false;
8119
        foreach ($list as $id => $user) {
8120
            if ($user['active'] == 1) {
8121
                // as soon as we find at least one active user, send the mail
8122
                return true;
8123
            }
8124
        }
8125
8126
        return false;
8127
    }
8128
8129
    /**
8130
     * return user hash based on user_id and loggedin user's salt.
8131
     *
8132
     * @param int user_id id of the user for whom we need the hash
8133
     *
8134
     * @return string containing the hash
8135
     */
8136
    public static function generateUserHash(int $user_id): string
8137
    {
8138
        $currentUserId = api_get_user_id();
8139
        $userManager = self::getManager();
8140
        /** @var User $user */
8141
        $user = self::getRepository()->find($currentUserId);
8142
        if (empty($user)) {
8143
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the type-hinted return string.
Loading history...
8144
        }
8145
8146
        return rawurlencode(api_encrypt_hash($user_id, $user->getSalt()));
8147
    }
8148
8149
    /**
8150
     * return decrypted hash or false.
8151
     *
8152
     * @param string hash    hash that is to be decrypted
8153
     */
8154
    public static function decryptUserHash(string $hash): string
8155
    {
8156
        $currentUserId = api_get_user_id();
8157
        $userManager = self::getManager();
8158
        /** @var User $user */
8159
        $user = self::getRepository()->find($currentUserId);
8160
        if (empty($user)) {
8161
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the type-hinted return string.
Loading history...
8162
        }
8163
8164
        return api_decrypt_hash(rawurldecode($hash), $user->getSalt());
8165
    }
8166
8167
    /**
8168
     * Search for users based on given filters.
8169
     */
8170
    public static function searchUsers(array $filters = [], array $editableFields = []): array
8171
    {
8172
        $where = [];
8173
8174
        $accessUrlRelUserTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
8175
        $userGroupTable = Database::get_main_table(TABLE_USERGROUP_REL_USER);
8176
8177
        $isMultipleUrl = (api_is_platform_admin() || api_is_session_admin()) && api_get_multiple_access_url();
8178
        $urlId = api_get_current_access_url_id();
8179
8180
        if (!empty($filters['keywordFirstname'])) {
8181
            $where[] = "u.firstname LIKE '%".Database::escape_string($filters['keywordFirstname'])."%'";
8182
        }
8183
        if (!empty($filters['keywordLastname'])) {
8184
            $where[] = "u.lastname LIKE '%".Database::escape_string($filters['keywordLastname'])."%'";
8185
        }
8186
        if (!empty($filters['keywordUsername'])) {
8187
            $where[] = "u.username LIKE '%".Database::escape_string($filters['keywordUsername'])."%'";
8188
        }
8189
        if (!empty($filters['keywordEmail'])) {
8190
            $where[] = "u.email LIKE '%".Database::escape_string($filters['keywordEmail'])."%'";
8191
        }
8192
        if (!empty($filters['keywordOfficialCode'])) {
8193
            $where[] = "u.official_code LIKE '%".Database::escape_string($filters['keywordOfficialCode'])."%'";
8194
        }
8195
        if (!empty($filters['keywordStatus']) && $filters['keywordStatus'] !== '%') {
8196
            $where[] = "u.status = '".Database::escape_string($filters['keywordStatus'])."'";
8197
        }
8198
        if (!empty($filters['keywordActive']) && empty($filters['keywordInactive'])) {
8199
            $where[] = "u.active = 1";
8200
        } elseif (empty($filters['keywordActive']) && !empty($filters['keywordInactive'])) {
8201
            $where[] = "u.active = 0";
8202
        }
8203
8204
        if ($isMultipleUrl) {
8205
            $where[] = "u.id IN (SELECT user_id FROM $accessUrlRelUserTable WHERE access_url_id = $urlId)";
8206
        }
8207
8208
        if (!empty($filters['class_id'])) {
8209
            $where[] = "u.id IN (SELECT user_id FROM $userGroupTable WHERE usergroup_id = ".(int) $filters['class_id'].")";
8210
        }
8211
8212
        $extraField = new ExtraField('user');
8213
        $extraFieldResults = null;
8214
        $extraFieldHasData = false;
8215
8216
        foreach ($filters as $key => $value) {
8217
            if (str_starts_with($key, 'extra_')) {
8218
                if (is_array($value)) {
8219
                    $value = array_filter($value, function ($v) {
8220
                        return $v !== null && $v !== '';
8221
                    });
8222
                }
8223
8224
                if (empty($value)) {
8225
                    continue;
8226
                }
8227
8228
                $variable = substr($key, 6);
8229
                $fieldInfo = $extraField->get_handler_field_info_by_field_variable($variable);
8230
                if ($fieldInfo) {
8231
                    $extraFieldHasData = true;
8232
                    $values = is_array($value) ? $value : [$value];
8233
8234
                    $fieldResults = [];
8235
                    foreach ($values as $singleValue) {
8236
                        if (empty($singleValue)) {
8237
                            continue;
8238
                        }
8239
8240
                        if ($fieldInfo['field_type'] == ExtraField::FIELD_TYPE_TAG) {
8241
                            $result = $extraField->getAllUserPerTag($fieldInfo['id'], $singleValue);
8242
                            $result = empty($result) ? [] : array_column($result, 'user_id');
8243
                        } else {
8244
                            $result = UserManager::get_extra_user_data_by_value($variable, $singleValue, true);
8245
                        }
8246
8247
                        if (!empty($result)) {
8248
                            $fieldResults[] = $result;
8249
                        }
8250
                    }
8251
8252
                    if (!empty($values) && empty($fieldResults)) {
8253
                        $extraFieldResults = [];
8254
                        break;
8255
                    }
8256
8257
                    $mergedFieldResults = call_user_func_array('array_merge', $fieldResults);
8258
8259
                    if ($extraFieldResults === null) {
8260
                        $extraFieldResults = $mergedFieldResults;
8261
                    } else {
8262
                        $extraFieldResults = array_intersect($extraFieldResults, $mergedFieldResults);
8263
                    }
8264
8265
                    if (empty($extraFieldResults)) {
8266
                        break;
8267
                    }
8268
                }
8269
            }
8270
        }
8271
8272
        if ($extraFieldHasData && $extraFieldResults === null) {
8273
            $extraFieldResults = [];
8274
        }
8275
8276
        if ($extraFieldHasData) {
8277
            if (empty($extraFieldResults)) {
8278
                $where[] = "u.id IN ('-1')";
8279
            } else {
8280
                $where[] = "u.id IN ('".implode("','", array_unique($extraFieldResults))."')";
8281
            }
8282
        }
8283
8284
        $fields = ['u.id', 'u.username'];
8285
8286
        if (!empty($editableFields)) {
8287
            foreach ($editableFields as $field) {
8288
                $fields[] = "u.".Database::escapeField($field);
8289
            }
8290
        }
8291
8292
        $sortableFields = [
8293
            0 => 'u.id',
8294
            1 => 'u.username',
8295
        ];
8296
8297
        $columnIndex = $_GET['users_column'] ?? 0;
8298
        $direction = strtoupper($_GET['users_direction'] ?? 'ASC');
8299
8300
        if (!in_array($direction, ['ASC', 'DESC'])) {
8301
            $direction = 'ASC';
8302
        }
8303
8304
        $orderBy = $sortableFields[$columnIndex] ?? 'u.id';
8305
8306
        $sql = "SELECT ".implode(", ", $fields)." FROM ".Database::get_main_table(TABLE_MAIN_USER)." u";
8307
        if (!empty($where)) {
8308
            $sql .= " WHERE ".implode(" AND ", $where);
8309
        }
8310
        $sql .= " ORDER BY $orderBy $direction";
8311
8312
        return Database::store_result(Database::query($sql), 'ASSOC');
8313
    }
8314
8315
    /**
8316
     * @return EncoderFactory
8317
     */
8318
    private static function getEncoderFactory()
8319
    {
8320
        $encryption = self::getPasswordEncryption();
8321
        $encoders = [
8322
            'Chamilo\\UserBundle\\Entity\\User' => new \Chamilo\UserBundle\Security\Encoder($encryption),
8323
        ];
8324
8325
        return new EncoderFactory($encoders);
8326
    }
8327
8328
    /**
8329
     * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface
8330
     */
8331
    private static function getEncoder(User $user)
8332
    {
8333
        $encoderFactory = self::getEncoderFactory();
8334
8335
        return $encoderFactory->getEncoder($user);
8336
    }
8337
8338
    /**
8339
     * Disables or enables a user.
8340
     *
8341
     * @param int $user_id
8342
     * @param int $active  Enable or disable
8343
     *
8344
     * @return bool True on success, false on failure
8345
     * @assert (-1,0) === false
8346
     * @assert (1,1) === true
8347
     */
8348
    private static function change_active_state($user_id, $active)
8349
    {
8350
        $user_id = (int) $user_id;
8351
        $active = (int) $active;
8352
8353
        if (empty($user_id)) {
8354
            return false;
8355
        }
8356
8357
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
8358
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
8359
        $r = Database::query($sql);
8360
        $ev = LOG_USER_DISABLE;
8361
        if ($active == 1) {
8362
            $ev = LOG_USER_ENABLE;
8363
        }
8364
        if ($r !== false) {
8365
            Event::addEvent($ev, LOG_USER_ID, $user_id);
8366
        }
8367
8368
        return $r;
8369
    }
8370
8371
    /**
8372
     * Get either a Gravatar URL or complete image tag for a specified email address.
8373
     *
8374
     * @param string $email The email address
8375
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
8376
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
8377
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
8378
     * @param bool   $img   True to return a complete IMG tag False for just the URL
8379
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
8380
     *
8381
     * @return string containing either just a URL or a complete image tag
8382
     * @source http://gravatar.com/site/implement/images/php/
8383
     */
8384
    private static function getGravatar(
8385
        $email,
8386
        $s = 80,
8387
        $d = 'mm',
8388
        $r = 'g',
8389
        $img = false,
8390
        $atts = []
8391
    ) {
8392
        $url = 'http://www.gravatar.com/avatar/';
8393
        if (!empty($_SERVER['HTTPS'])) {
8394
            $url = 'https://secure.gravatar.com/avatar/';
8395
        }
8396
        $url .= md5(strtolower(trim($email)));
8397
        $url .= "?s=$s&d=$d&r=$r";
8398
        if ($img) {
8399
            $url = '<img src="'.$url.'"';
8400
            foreach ($atts as $key => $val) {
8401
                $url .= ' '.$key.'="'.$val.'"';
8402
            }
8403
            $url .= ' />';
8404
        }
8405
8406
        return $url;
8407
    }
8408
}
8409