Passed
Push — majorel-21208 ( e73d31 )
by
unknown
09:33
created

UserManager::getUserListLike()   C

Complexity

Conditions 12
Paths 160

Size

Total Lines 65
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 38
c 1
b 0
f 1
dl 0
loc 65
rs 6.4666
cc 12
nc 160
nop 5

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

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

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

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

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

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