UserManager::updateCourseRelationTypeExLearner()   F
last analyzed

Complexity

Conditions 28
Paths 506

Size

Total Lines 125
Code Lines 88

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
6
use Chamilo\CoreBundle\Entity\Repository\AccessUrlRepository;
7
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
8
use Chamilo\CoreBundle\Entity\SkillRelUser;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, SkillRelUser. Consider defining an alias.

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

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

Loading history...
226
    }
227
228
    /**
229
     * Updates user expiration date.
230
     *
231
     * @param int    $userId
232
     * @param string $expirationDate
233
     */
234
    public static function updateExpirationDate($userId, $expirationDate)
235
    {
236
        $repository = self::getRepository();
237
        /** @var User $user */
238
        $user = $repository->find($userId);
239
        $userManager = self::getManager();
240
        $expirationDate = api_get_utc_datetime($expirationDate, false, true);
241
        $user->setExpirationDate($expirationDate);
242
        $userManager->updateUser($user, true);
243
    }
244
245
    /**
246
     * Creates a new user for the platform.
247
     *
248
     * @author Hugues Peeters <[email protected]>,
249
     * @author Roan Embrechts <[email protected]>
250
     *
251
     * @param string        $firstName
252
     * @param string        $lastName
253
     * @param int           $status                  (1 for course tutor, 5 for student, 6 for anonymous)
254
     * @param string        $email
255
     * @param string        $loginName
256
     * @param string        $password
257
     * @param string        $official_code           Any official code (optional)
258
     * @param string        $language                User language    (optional)
259
     * @param string        $phone                   Phone number    (optional)
260
     * @param string        $picture_uri             Picture URI        (optional)
261
     * @param string        $authSource              Authentication source (defaults to 'platform', dependind on constant)
262
     * @param string        $expirationDate          Account expiration date (optional, defaults to null)
263
     * @param int           $active                  Whether the account is enabled or disabled by default
264
     * @param int           $hr_dept_id              The department of HR in which the user is registered (defaults to 0)
265
     * @param array         $extra                   Extra fields (prefix labels with "extra_")
266
     * @param string        $encrypt_method          Used if password is given encrypted. Set to an empty string by default
267
     * @param bool          $send_mail
268
     * @param bool          $isAdmin
269
     * @param string        $address
270
     * @param bool          $sendEmailToAllAdmins
271
     * @param FormValidator $form
272
     * @param int           $creatorId
273
     * @param array         $emailTemplate
274
     * @param string        $redirectToURLAfterLogin
275
     *
276
     * @return mixed new user id - if the new user creation succeeds, false otherwise
277
     * @desc The function tries to retrieve user id from the session.
278
     * If it exists, the current user id is the creator id. If a problem arises,
279
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
280
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
281
     */
282
    public static function create_user(
283
        $firstName,
284
        $lastName,
285
        $status,
286
        $email,
287
        $loginName,
288
        $password,
289
        $official_code = '',
290
        $language = '',
291
        $phone = '',
292
        $picture_uri = '',
293
        $authSource = PLATFORM_AUTH_SOURCE,
294
        $expirationDate = null,
295
        $active = 1,
296
        $hr_dept_id = 0,
297
        $extra = [],
298
        $encrypt_method = '',
299
        $send_mail = false,
300
        $isAdmin = false,
301
        $address = '',
302
        $sendEmailToAllAdmins = false,
303
        $form = null,
304
        $creatorId = 0,
305
        $emailTemplate = [],
306
        $redirectToURLAfterLogin = ''
307
    ) {
308
        $creatorId = empty($creatorId) ? api_get_user_id() : 0;
309
        $creatorInfo = api_get_user_info($creatorId);
310
        $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
311
312
        $hook = HookCreateUser::create();
313
        if (!empty($hook)) {
314
            $hook->notifyCreateUser(HOOK_EVENT_TYPE_PRE);
315
        }
316
317
        if ('true' === api_get_setting('registration', 'email')) {
318
            // Force email validation.
319
            if (false === api_valid_email($email)) {
320
                Display::addFlash(
321
                   Display::return_message(get_lang('PleaseEnterValidEmail').' - '.$email, 'warning')
322
               );
323
324
                return false;
325
            }
326
        } else {
327
            // Allow empty email. If email is set, check if is valid.
328
            if (!empty($email) && false === api_valid_email($email)) {
329
                Display::addFlash(
330
                    Display::return_message(get_lang('PleaseEnterValidEmail').' - '.$email, 'warning')
331
                );
332
333
                return false;
334
            }
335
        }
336
337
        if ('true' === api_get_setting('login_is_email')) {
338
            if (false === api_valid_email($loginName)) {
339
                Display::addFlash(
340
                    Display::return_message(get_lang('PleaseEnterValidEmail').' - '.$loginName, 'warning')
341
                );
342
343
                return false;
344
            }
345
        } else {
346
            if (false === self::is_username_valid($loginName)) {
347
                Display::addFlash(
348
                    Display::return_message(get_lang('UsernameWrong').' - '.$loginName, 'warning')
349
                );
350
351
                return false;
352
            }
353
        }
354
355
        // First check wether the login already exists
356
        if (!self::is_username_available($loginName)) {
357
            Display::addFlash(
358
                Display::return_message(get_lang('LoginAlreadyTaken').' - '.$loginName, 'warning')
359
            );
360
361
            return false;
362
        }
363
364
        global $_configuration;
365
        $original_password = $password;
366
367
        $access_url_id = 1;
368
        if (api_get_multiple_access_url()) {
369
            $access_url_id = api_get_current_access_url_id();
370
        } else {
371
            // In some cases, the first access_url ID might be different from 1
372
            // for example when using a DB cluster or hacking the DB manually.
373
            // In this case, we want the first row, not necessarily "1".
374
            $dbm = Database::getManager();
375
            /** @var AccessUrlRepository $accessUrlRepository */
376
            $accessUrlRepository = $dbm->getRepository('ChamiloCoreBundle:AccessUrl');
377
            $accessUrl = $accessUrlRepository->getFirstId();
378
            if (!empty($accessUrl[0]) && !empty($accessUrl[0][1])) {
379
                $access_url_id = $accessUrl[0][1];
380
            }
381
        }
382
383
        if (isset($_configuration[$access_url_id]) &&
384
            is_array($_configuration[$access_url_id]) &&
385
            isset($_configuration[$access_url_id]['hosting_limit_users']) &&
386
            $_configuration[$access_url_id]['hosting_limit_users'] > 0) {
387
            $num = self::get_number_of_users(null, $access_url_id);
388
            if ($num >= $_configuration[$access_url_id]['hosting_limit_users']) {
389
                api_warn_hosting_contact('hosting_limit_users');
390
                Display::addFlash(
391
                    Display::return_message(
392
                        get_lang('PortalUsersLimitReached'),
393
                        'warning'
394
                    )
395
                );
396
397
                return false;
398
            }
399
        }
400
401
        if ($status === 1 &&
402
            isset($_configuration[$access_url_id]) &&
403
            is_array($_configuration[$access_url_id]) &&
404
            isset($_configuration[$access_url_id]['hosting_limit_teachers']) &&
405
            $_configuration[$access_url_id]['hosting_limit_teachers'] > 0
406
        ) {
407
            $num = self::get_number_of_users(1, $access_url_id);
408
            if ($num >= $_configuration[$access_url_id]['hosting_limit_teachers']) {
409
                Display::addFlash(
410
                    Display::return_message(
411
                        get_lang('PortalTeachersLimitReached'),
412
                        'warning'
413
                    )
414
                );
415
                api_warn_hosting_contact('hosting_limit_teachers');
416
417
                return false;
418
            }
419
        }
420
421
        if (empty($password)) {
422
            if ($authSource === PLATFORM_AUTH_SOURCE) {
423
                Display::addFlash(
424
                    Display::return_message(
425
                        get_lang('ThisFieldIsRequired').': '.get_lang(
426
                            'Password'
427
                        ),
428
                        'warning'
429
                    )
430
                );
431
432
                return false;
433
            }
434
435
            // We use the authSource as password.
436
            // The real validation will be by processed by the auth
437
            // source not Chamilo
438
            $password = $authSource;
439
        }
440
441
        // database table definition
442
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
443
444
        // Checking the user language
445
        $languages = api_get_languages();
446
        $language = strtolower($language);
447
448
        if (isset($languages['folder'])) {
449
            if (!in_array($language, $languages['folder'])) {
450
                $language = api_get_setting('platformLanguage');
451
            }
452
        }
453
454
        $currentDate = api_get_utc_datetime();
455
        $now = new DateTime();
456
457
        if (empty($expirationDate) || $expirationDate == '0000-00-00 00:00:00') {
458
            // Default expiration date
459
            // if there is a default duration of a valid account then
460
            // 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
            $andActive = "";
2411
            if (isset($conditions['active'])) {
2412
                $andActive = " AND active = ".(int) $conditions['active'];
2413
                unset($conditions['active']);
2414
            }
2415
2416
            $temp_conditions = [];
2417
            foreach ($conditions as $field => $value) {
2418
                $field = Database::escape_string($field);
2419
                $value = Database::escape_string($value);
2420
                if ($simple_like) {
2421
                    $temp_conditions[] = $field." LIKE '$value%'";
2422
                } else {
2423
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
2424
                }
2425
            }
2426
            if (!empty($temp_conditions)) {
2427
                $sql_query .= ' AND ('.implode(' '.$condition.' ', $temp_conditions).') ';
2428
            }
2429
2430
            if (api_is_multiple_url_enabled()) {
2431
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
2432
            }
2433
            $sql_query .= $andActive;
2434
        } else {
2435
            if (api_is_multiple_url_enabled()) {
2436
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
2437
            }
2438
        }
2439
2440
        if (api_is_session_admin() && (api_get_setting('prevent_session_admins_to_manage_all_users') === 'true')) {
2441
            $sql_query .= ' AND user.creator_id = '.api_get_user_id();
2442
        }
2443
2444
        if (!empty($onlyThisUserList)) {
2445
            $onlyThisUserListToString = implode("','", $onlyThisUserList);
2446
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
2447
        }
2448
2449
        if (count($order_by) > 0) {
2450
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
2451
        }
2452
2453
        $sql_result = Database::query($sql_query);
2454
        while ($result = Database::fetch_array($sql_result)) {
2455
            $userInfo = api_get_user_info($result['id']);
2456
            $return_array[] = $userInfo;
2457
        }
2458
2459
        return $return_array;
2460
    }
2461
2462
    /**
2463
     * Get user picture URL or path from user ID (returns an array).
2464
     * The return format is a complete path, enabling recovery of the directory
2465
     * with dirname() or the file with basename(). This also works for the
2466
     * functions dealing with the user's productions, as they are located in
2467
     * the same directory.
2468
     *
2469
     * @param int    $id       User ID
2470
     * @param string $type     Type of path to return (can be 'system', 'web')
2471
     * @param array  $userInfo user information to avoid query the DB
2472
     *                         returns the /main/img/unknown.jpg image set it at true
2473
     *
2474
     * @return array Array of 2 elements: 'dir' and 'file' which contain
2475
     *               the dir and file as the name implies if image does not exist it will
2476
     *               return the unknow image if anonymous parameter is true if not it returns an empty array
2477
     */
2478
    public static function get_user_picture_path_by_id(
2479
        $id,
2480
        $type = 'web',
2481
        $userInfo = []
2482
    ) {
2483
        switch ($type) {
2484
            case 'system': // Base: absolute system path.
2485
                $base = api_get_path(SYS_CODE_PATH);
2486
                break;
2487
            case 'web': // Base: absolute web path.
2488
            default:
2489
                $base = api_get_path(WEB_CODE_PATH);
2490
                break;
2491
        }
2492
2493
        $anonymousPath = [
2494
            'dir' => $base.'img/',
2495
            'file' => 'unknown.jpg',
2496
            'email' => '',
2497
        ];
2498
2499
        if (empty($id) || empty($type)) {
2500
            return $anonymousPath;
2501
        }
2502
2503
        $id = (int) $id;
2504
        if (empty($userInfo)) {
2505
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
2506
            $sql = "SELECT email, picture_uri FROM $user_table
2507
                    WHERE id = ".$id;
2508
            $res = Database::query($sql);
2509
2510
            if (!Database::num_rows($res)) {
2511
                return $anonymousPath;
2512
            }
2513
            $user = Database::fetch_array($res);
2514
            if (empty($user['picture_uri'])) {
2515
                return $anonymousPath;
2516
            }
2517
        } else {
2518
            $user = $userInfo;
2519
        }
2520
2521
        $pictureFilename = trim($user['picture_uri']);
2522
2523
        $dir = self::getUserPathById($id, $type);
2524
2525
        return [
2526
            'dir' => $dir,
2527
            'file' => $pictureFilename,
2528
            'email' => $user['email'],
2529
        ];
2530
    }
2531
2532
    /**
2533
     * *** READ BEFORE REVIEW THIS FUNCTION ***
2534
     * This function is a exact copy from get_user_picture_path_by_id() and it was create it to avoid
2535
     * a recursive calls for get_user_picture_path_by_id() in another functions when you update a user picture
2536
     * in same script, so you can find this function usage in update_user_picture() function.
2537
     *
2538
     * @param int    $id       User ID
2539
     * @param string $type     Type of path to return (can be 'system', 'web')
2540
     * @param array  $userInfo user information to avoid query the DB
2541
     *                         returns the /main/img/unknown.jpg image set it at true
2542
     *
2543
     * @return array Array of 2 elements: 'dir' and 'file' which contain
2544
     *               the dir and file as the name implies if image does not exist it will
2545
     *               return the unknown image if anonymous parameter is true if not it returns an empty array
2546
     */
2547
    public static function getUserPicturePathById($id, $type = 'web', $userInfo = [])
2548
    {
2549
        switch ($type) {
2550
            case 'system': // Base: absolute system path.
2551
                $base = api_get_path(SYS_CODE_PATH);
2552
                break;
2553
            case 'web': // Base: absolute web path.
2554
            default:
2555
                $base = api_get_path(WEB_CODE_PATH);
2556
                break;
2557
        }
2558
2559
        $anonymousPath = [
2560
            'dir' => $base.'img/',
2561
            'file' => 'unknown.jpg',
2562
            'email' => '',
2563
        ];
2564
2565
        if (empty($id) || empty($type)) {
2566
            return $anonymousPath;
2567
        }
2568
2569
        $id = (int) $id;
2570
        if (empty($userInfo)) {
2571
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
2572
            $sql = "SELECT email, picture_uri FROM $user_table WHERE id = $id";
2573
            $res = Database::query($sql);
2574
2575
            if (!Database::num_rows($res)) {
2576
                return $anonymousPath;
2577
            }
2578
            $user = Database::fetch_array($res);
2579
2580
            if (empty($user['picture_uri'])) {
2581
                return $anonymousPath;
2582
            }
2583
        } else {
2584
            $user = $userInfo;
2585
        }
2586
2587
        $pictureFilename = trim($user['picture_uri']);
2588
        $dir = self::getUserPathById($id, $type);
2589
2590
        return [
2591
            'dir' => $dir,
2592
            'file' => $pictureFilename,
2593
            'email' => $user['email'],
2594
        ];
2595
    }
2596
2597
    /**
2598
     * Get user path from user ID (returns an array).
2599
     * The return format is a complete path to a folder ending with "/"
2600
     * In case the first level of subdirectory of users/ does not exist, the
2601
     * function will attempt to create it. Probably not the right place to do it
2602
     * but at least it avoids headaches in many other places.
2603
     *
2604
     * @param int    $id   User ID
2605
     * @param string $type Type of path to return (can be 'system', 'web', 'last')
2606
     *
2607
     * @return string User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
2608
     */
2609
    public static function getUserPathById($id, $type)
2610
    {
2611
        $id = (int) $id;
2612
        if (!$id) {
2613
            return null;
2614
        }
2615
2616
        $userPath = "users/$id/";
2617
        if (api_get_setting('split_users_upload_directory') === 'true') {
2618
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
2619
            // In exceptional cases, on some portals, the intermediate base user
2620
            // directory might not have been created. Make sure it is before
2621
            // going further.
2622
2623
            $rootPath = api_get_path(SYS_UPLOAD_PATH).'users/'.substr((string) $id, 0, 1);
2624
            if (!is_dir($rootPath)) {
2625
                $perm = api_get_permissions_for_new_directories();
2626
                try {
2627
                    mkdir($rootPath, $perm);
2628
                } catch (Exception $e) {
2629
                    error_log($e->getMessage());
2630
                }
2631
            }
2632
        }
2633
        switch ($type) {
2634
            case 'system': // Base: absolute system path.
2635
                $userPath = api_get_path(SYS_UPLOAD_PATH).$userPath;
2636
                break;
2637
            case 'web': // Base: absolute web path.
2638
                $userPath = api_get_path(WEB_UPLOAD_PATH).$userPath;
2639
                break;
2640
            case 'last': // Only the last part starting with users/
2641
                break;
2642
        }
2643
2644
        return $userPath;
2645
    }
2646
2647
    /**
2648
     * Gets the current user image.
2649
     *
2650
     * @param string $user_id
2651
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
2652
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
2653
     * @param bool   $addRandomId
2654
     * @param array  $userInfo    to avoid query the DB
2655
     *
2656
     * @return string
2657
     */
2658
    public static function getUserPicture(
2659
        $user_id,
2660
        $size = USER_IMAGE_SIZE_MEDIUM,
2661
        $addRandomId = true,
2662
        $userInfo = []
2663
    ) {
2664
        // Make sure userInfo is defined. Otherwise, define it!
2665
        if (empty($userInfo) || !is_array($userInfo) || count($userInfo) == 0) {
2666
            if (empty($user_id)) {
2667
                return '';
2668
            } else {
2669
                $userInfo = api_get_user_info($user_id);
2670
            }
2671
        }
2672
2673
        $imageWebPath = self::get_user_picture_path_by_id(
2674
            $user_id,
2675
            'web',
2676
            $userInfo
2677
        );
2678
        $pictureWebFile = $imageWebPath['file'];
2679
        $pictureWebDir = $imageWebPath['dir'];
2680
2681
        $pictureAnonymousSize = '128';
2682
        $gravatarSize = 22;
2683
        $realSizeName = 'small_';
2684
2685
        switch ($size) {
2686
            case USER_IMAGE_SIZE_SMALL:
2687
                $pictureAnonymousSize = '32';
2688
                $realSizeName = 'small_';
2689
                $gravatarSize = 32;
2690
                break;
2691
            case USER_IMAGE_SIZE_MEDIUM:
2692
                $pictureAnonymousSize = '64';
2693
                $realSizeName = 'medium_';
2694
                $gravatarSize = 64;
2695
                break;
2696
            case USER_IMAGE_SIZE_ORIGINAL:
2697
                $pictureAnonymousSize = '128';
2698
                $realSizeName = '';
2699
                $gravatarSize = 128;
2700
                break;
2701
            case USER_IMAGE_SIZE_BIG:
2702
                $pictureAnonymousSize = '128';
2703
                $realSizeName = 'big_';
2704
                $gravatarSize = 128;
2705
                break;
2706
        }
2707
2708
        $gravatarEnabled = api_get_setting('gravatar_enabled');
2709
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
2710
        if ($pictureWebFile == 'unknown.jpg' || empty($pictureWebFile)) {
2711
            if ($gravatarEnabled === 'true') {
2712
                $file = self::getGravatar(
2713
                    $imageWebPath['email'],
2714
                    $gravatarSize,
2715
                    api_get_setting('gravatar_type')
2716
                );
2717
2718
                if ($addRandomId) {
2719
                    $file .= '&rand='.uniqid();
2720
                }
2721
2722
                return $file;
2723
            }
2724
2725
            return $anonymousPath;
2726
        }
2727
2728
        $pictureSysPath = self::get_user_picture_path_by_id($user_id, 'system');
2729
        $file = $pictureSysPath['dir'].$realSizeName.$pictureWebFile;
2730
        $picture = '';
2731
        if (file_exists($file)) {
2732
            $picture = $pictureWebDir.$realSizeName.$pictureWebFile;
2733
        } else {
2734
            $file = $pictureSysPath['dir'].$pictureWebFile;
2735
            if (file_exists($file) && !is_dir($file)) {
2736
                $picture = $pictureWebFile['dir'].$pictureWebFile;
2737
            }
2738
        }
2739
2740
        if (empty($picture)) {
2741
            return $anonymousPath;
2742
        }
2743
2744
        if ($addRandomId) {
2745
            $picture .= '?rand='.uniqid();
2746
        }
2747
2748
        return $picture;
2749
    }
2750
2751
    /**
2752
     * Creates new user photos in various sizes of a user, or deletes user photos.
2753
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2754
     *
2755
     * @param int    $user_id the user internal identification number
2756
     * @param string $file    The common file name for the newly created photos.
2757
     *                        It will be checked and modified for compatibility with the file system.
2758
     *                        If full name is provided, path component is ignored.
2759
     *                        If an empty name is provided, then old user photos are deleted only,
2760
     *
2761
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
2762
     *
2763
     * @param string $source_file    the full system name of the image from which user photos will be created
2764
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
2765
     *
2766
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
2767
     *              When deletion is requested returns empty string.
2768
     *              In case of internal error or negative validation returns FALSE.
2769
     */
2770
    public static function update_user_picture(
2771
        $user_id,
2772
        $file = null,
2773
        $source_file = null,
2774
        $cropParameters = ''
2775
    ) {
2776
        if (empty($user_id)) {
2777
            return false;
2778
        }
2779
        $delete = empty($file);
2780
        if (empty($source_file)) {
2781
            $source_file = $file;
2782
        }
2783
2784
        // User-reserved directory where photos have to be placed.
2785
        $path_info = self::getUserPicturePathById($user_id, 'system');
2786
        $path = $path_info['dir'];
2787
2788
        // If this directory does not exist - we create it.
2789
        if (!file_exists($path)) {
2790
            mkdir($path, api_get_permissions_for_new_directories(), true);
2791
        }
2792
2793
        // The old photos (if any).
2794
        $old_file = $path_info['file'];
2795
2796
        // Let us delete them.
2797
        if ($old_file != 'unknown.jpg') {
2798
            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...
2799
                $prefix = 'saved_'.date('Y_m_d_H_i_s').'_'.uniqid('').'_';
2800
                @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

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

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

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

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