Passed
Pull Request — 1.11.x (#4063)
by Renaud
10:27
created

UserManager::build_user_extra_file_list()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 44
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 27
nc 4
nop 4
dl 0
loc 44
rs 8.5546
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
6
use Chamilo\CoreBundle\Entity\Repository\AccessUrlRepository;
7
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
8
use Chamilo\CoreBundle\Entity\SkillRelUser;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, SkillRelUser. Consider defining an alias.

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

2615
                /** @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...
2616
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
2617
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
2618
                @rename($path.$old_file, $path.$prefix.$old_file);
2619
            } else {
2620
                @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

2620
                /** @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...
2621
                @unlink($path.'medium_'.$old_file);
2622
                @unlink($path.'big_'.$old_file);
2623
                @unlink($path.$old_file);
2624
            }
2625
        }
2626
2627
        // Exit if only deletion has been requested. Return an empty picture name.
2628
        if ($delete) {
2629
            return '';
2630
        }
2631
2632
        // Validation 2.
2633
        $allowed_types = api_get_supported_image_extensions();
2634
        $file = str_replace('\\', '/', $file);
2635
        $filename = (($pos = strrpos($file, '/')) !== false) ? substr($file, $pos + 1) : $file;
2636
        $extension = strtolower(substr(strrchr($filename, '.'), 1));
2637
        if (!in_array($extension, $allowed_types)) {
2638
            return false;
2639
        }
2640
2641
        // This is the common name for the new photos.
2642
        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...
2643
            $old_extension = strtolower(substr(strrchr($old_file, '.'), 1));
2644
            $filename = in_array($old_extension, $allowed_types) ? substr($old_file, 0, -strlen($old_extension)) : $old_file;
2645
            $filename = (substr($filename, -1) == '.') ? $filename.$extension : $filename.'.'.$extension;
2646
        } else {
2647
            $filename = api_replace_dangerous_char($filename);
2648
            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...
2649
                $filename = uniqid('').'_'.$filename;
2650
            }
2651
            // We always prefix user photos with user ids, so on setting
2652
            // api_get_setting('split_users_upload_directory') === 'true'
2653
            // the correspondent directories to be found successfully.
2654
            $filename = $user_id.'_'.$filename;
2655
        }
2656
2657
        if (!file_exists($source_file)) {
2658
            return false;
2659
        }
2660
2661
        $mimeContentType = mime_content_type($source_file);
2662
        if (false === strpos($mimeContentType, 'image')) {
2663
            return false;
2664
        }
2665
2666
        //Crop the image to adjust 1:1 ratio
2667
        $image = new Image($source_file);
2668
        $image->crop($cropParameters);
2669
2670
        // Storing the new photos in 4 versions with various sizes.
2671
        $userPath = self::getUserPathById($user_id, 'system');
2672
2673
        // If this path does not exist - we create it.
2674
        if (!file_exists($userPath)) {
2675
            mkdir($userPath, api_get_permissions_for_new_directories(), true);
2676
        }
2677
        $small = new Image($source_file);
2678
        $small->resize(32);
2679
        $small->send_image($userPath.'small_'.$filename);
2680
        $medium = new Image($source_file);
2681
        $medium->resize(85);
2682
        $medium->send_image($userPath.'medium_'.$filename);
2683
        $normal = new Image($source_file);
2684
        $normal->resize(200);
2685
        $normal->send_image($userPath.$filename);
2686
2687
        $big = new Image($source_file); // This is the original picture.
2688
        $big->send_image($userPath.'big_'.$filename);
2689
2690
        $result = $small && $medium && $normal && $big;
0 ignored issues
show
introduced by
$big is of type Image, thus it always evaluated to true.
Loading history...
introduced by
$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...
2691
2692
        return $result ? $filename : false;
2693
    }
2694
2695
    /**
2696
     * Update User extra field file type into {user_folder}/{$extra_field}.
2697
     *
2698
     * @param int    $user_id     The user internal identification number
2699
     * @param string $extra_field The $extra_field The extra field name
2700
     * @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...
2701
     * @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...
2702
     *
2703
     * @return bool|null return filename if success, but false
2704
     */
2705
    public static function update_user_extra_file(
2706
        $user_id,
2707
        $extra_field = '',
2708
        $file = null,
2709
        $source_file = null
2710
    ) {
2711
        // Add Filter
2712
        $source_file = Security::filter_filename($source_file);
2713
        $file = Security::filter_filename($file);
2714
2715
        if (empty($user_id)) {
2716
            return false;
2717
        }
2718
2719
        if (empty($source_file)) {
2720
            $source_file = $file;
2721
        }
2722
2723
        // User-reserved directory where extra file have to be placed.
2724
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2725
        $path = $path_info['dir'];
2726
        if (!empty($extra_field)) {
2727
            $path .= $extra_field.'/';
2728
        }
2729
        // If this directory does not exist - we create it.
2730
        if (!file_exists($path)) {
2731
            @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

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

6530
            Event::/** @scrutinizer ignore-call */ 
6531
                   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...
6531
6532
            // Logout the current user
6533
            self::loginDelete(api_get_user_id());
6534
6535
            Session::erase('_user');
6536
            Session::erase('is_platformAdmin');
6537
            Session::erase('is_allowedCreateCourse');
6538
            Session::erase('_uid');
6539
6540
            // Cleaning session variables
6541
            $_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...
6542
            $_user['lastName'] = $userInfo['lastname'];
6543
            $_user['mail'] = $userInfo['email'];
6544
            $_user['official_code'] = $userInfo['official_code'];
6545
            $_user['picture_uri'] = $userInfo['picture_uri'];
6546
            $_user['user_id'] = $userId;
6547
            $_user['id'] = $userId;
6548
            $_user['status'] = $userInfo['status'];
6549
6550
            // Filling session variables with new data
6551
            Session::write('_uid', $userId);
6552
            Session::write('_user', $userInfo);
6553
            Session::write('is_platformAdmin', (bool) self::is_admin($userId));
6554
            Session::write('is_allowedCreateCourse', $userInfo['status'] == 1);
6555
            // will be useful later to know if the user is actually an admin or not (example reporting)
6556
            Session::write('login_as', true);
6557
            $logInfo = [
6558
                'tool' => 'login',
6559
                'tool_id' => 0,
6560
                'tool_id_detail' => 0,
6561
                'info' => $userId,
6562
            ];
6563
            Event::registerLog($logInfo);
6564
6565
            return true;
6566
        }
6567
6568
        return false;
6569
    }
6570
6571
    /**
6572
     * Remove all login records from the track_e_online stats table,
6573
     * for the given user ID.
6574
     *
6575
     * @param int $userId User ID
6576
     */
6577
    public static function loginDelete($userId)
6578
    {
6579
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6580
        $userId = (int) $userId;
6581
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
6582
        Database::query($query);
6583
    }
6584
6585
    /**
6586
     * Login as first admin user registered in the platform.
6587
     *
6588
     * @return array
6589
     */
6590
    public static function logInAsFirstAdmin()
6591
    {
6592
        $adminList = self::get_all_administrators();
6593
6594
        if (!empty($adminList)) {
6595
            $userInfo = current($adminList);
6596
            if (!empty($userInfo)) {
6597
                $result = self::loginAsUser($userInfo['user_id'], false);
6598
                if ($result && api_is_platform_admin()) {
6599
                    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...
6600
                }
6601
            }
6602
        }
6603
6604
        return [];
6605
    }
6606
6607
    /**
6608
     * Check if user is teacher of a student based in their courses.
6609
     *
6610
     * @param $teacherId
6611
     * @param $studentId
6612
     *
6613
     * @return array
6614
     */
6615
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
6616
    {
6617
        $courses = CourseManager::getCoursesFollowedByUser(
6618
            $teacherId,
6619
            COURSEMANAGER
6620
        );
6621
        if (empty($courses)) {
6622
            return false;
6623
        }
6624
6625
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
6626
        if (empty($coursesFromUser)) {
6627
            return false;
6628
        }
6629
6630
        $coursesCodeList = array_column($courses, 'code');
6631
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
6632
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
6633
        $commonCourses = array_filter($commonCourses);
6634
6635
        if (!empty($commonCourses)) {
6636
            return $commonCourses;
6637
        }
6638
6639
        return [];
6640
    }
6641
6642
    /**
6643
     * @param int $teacherId
6644
     * @param int $studentId
6645
     *
6646
     * @return bool
6647
     */
6648
    public static function isTeacherOfStudent($teacherId, $studentId)
6649
    {
6650
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
6651
            $teacherId,
6652
            $studentId
6653
        );
6654
6655
        if (!empty($courses)) {
6656
            return true;
6657
        }
6658
6659
        return false;
6660
    }
6661
6662
    /**
6663
     * Send user confirmation mail.
6664
     *
6665
     * @throws Exception
6666
     */
6667
    public static function sendUserConfirmationMail(User $user)
6668
    {
6669
        $uniqueId = api_get_unique_id();
6670
        $user->setConfirmationToken($uniqueId);
6671
6672
        Database::getManager()->persist($user);
6673
        Database::getManager()->flush();
6674
6675
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
6676
6677
        // Check if the user was originally set for an automated subscription to a course or session
6678
        $courseCodeToRedirect = Session::read('course_redirect');
6679
        $sessionToRedirect = Session::read('session_redirect');
6680
        if (!empty($courseCodeToRedirect)) {
6681
            $url .= '&c='.$courseCodeToRedirect;
6682
        }
6683
        if (!empty($sessionToRedirect)) {
6684
            $url .= '&s='.$sessionToRedirect;
6685
        }
6686
        $mailSubject = get_lang('RegistrationConfirmation');
6687
        $mailBody = get_lang('RegistrationConfirmationEmailMessage')
6688
            .PHP_EOL
6689
            .Display::url($url, $url);
6690
6691
        api_mail_html(
6692
            self::formatUserFullName($user),
6693
            $user->getEmail(),
6694
            $mailSubject,
6695
            $mailBody
6696
        );
6697
        Display::addFlash(Display::return_message(get_lang('CheckYourEmailAndFollowInstructions')));
6698
    }
6699
6700
    /**
6701
     * Anonymize a user. Replace personal info by anonymous info.
6702
     *
6703
     * @param int  $userId   User id
6704
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
6705
     *
6706
     * @throws \Exception
6707
     *
6708
     * @return bool
6709
     * @assert (0) === false
6710
     */
6711
    public static function anonymize($userId, $deleteIP = true)
6712
    {
6713
        global $debug;
6714
6715
        $userId = (int) $userId;
6716
6717
        if (empty($userId)) {
6718
            return false;
6719
        }
6720
6721
        $em = Database::getManager();
6722
        $user = api_get_user_entity($userId);
6723
        $uniqueId = uniqid('anon', true);
6724
        $user
6725
            ->setFirstname($uniqueId)
6726
            ->setLastname($uniqueId)
6727
            ->setBiography('')
6728
            ->setAddress('')
6729
            ->setCurriculumItems(null)
6730
            ->setDateOfBirth(null)
6731
            ->setCompetences('')
6732
            ->setDiplomas('')
6733
            ->setOpenarea('')
6734
            ->setTeach('')
6735
            ->setProductions(null)
6736
            ->setOpenid('')
6737
            ->setEmailCanonical($uniqueId.'@example.com')
6738
            ->setEmail($uniqueId.'@example.com')
6739
            ->setUsername($uniqueId)
6740
            ->setUsernameCanonical($uniqueId)
6741
            ->setPhone('')
6742
            ->setOfficialCode('')
6743
        ;
6744
6745
        self::deleteUserPicture($userId);
6746
        self::cleanUserRequestsOfRemoval($userId);
6747
6748
        // The IP address is a border-case personal data, as it does
6749
        // not directly allow for personal identification (it is not
6750
        // a completely safe value in most countries - the IP could
6751
        // be used by neighbours and crackers)
6752
        if ($deleteIP) {
6753
            $substitute = '127.0.0.1';
6754
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
6755
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
6756
            $res = Database::query($sql);
6757
            if ($res === false && $debug > 0) {
6758
                error_log("Could not anonymize IP address for user $userId ($sql)");
6759
            }
6760
6761
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6762
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6763
            $res = Database::query($sql);
6764
            if ($res === false && $debug > 0) {
6765
                error_log("Could not anonymize IP address for user $userId ($sql)");
6766
            }
6767
6768
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6769
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE exe_user_id = $userId";
6770
            $res = Database::query($sql);
6771
            if ($res === false && $debug > 0) {
6772
                error_log("Could not anonymize IP address for user $userId ($sql)");
6773
            }
6774
6775
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6776
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6777
            $res = Database::query($sql);
6778
            if ($res === false && $debug > 0) {
6779
                error_log("Could not anonymize IP address for user $userId ($sql)");
6780
            }
6781
6782
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6783
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6784
            $res = Database::query($sql);
6785
            if ($res === false && $debug > 0) {
6786
                error_log("Could not anonymize IP address for user $userId ($sql)");
6787
            }
6788
6789
            $table = Database::get_course_table(TABLE_WIKI);
6790
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6791
            $res = Database::query($sql);
6792
            if ($res === false && $debug > 0) {
6793
                error_log("Could not anonymize IP address for user $userId ($sql)");
6794
            }
6795
6796
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
6797
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
6798
            $res = Database::query($sql);
6799
            if ($res === false && $debug > 0) {
6800
                error_log("Could not anonymize IP address for user $userId ($sql)");
6801
            }
6802
6803
            $table = Database::get_course_table(TABLE_WIKI);
6804
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6805
            $res = Database::query($sql);
6806
            if ($res === false && $debug > 0) {
6807
                error_log("Could not anonymize IP address for user $userId ($sql)");
6808
            }
6809
        }
6810
        $em->persist($user);
6811
        $em->flush($user);
6812
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
6813
6814
        return true;
6815
    }
6816
6817
    /**
6818
     * @param int $userId
6819
     *
6820
     * @throws Exception
6821
     *
6822
     * @return string
6823
     */
6824
    public static function anonymizeUserWithVerification($userId)
6825
    {
6826
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
6827
6828
        $message = '';
6829
        if (api_is_platform_admin() ||
6830
            ($allowDelete && api_is_session_admin())
6831
        ) {
6832
            $userToUpdateInfo = api_get_user_info($userId);
6833
            $currentUserId = api_get_user_id();
6834
6835
            if ($userToUpdateInfo &&
6836
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
6837
            ) {
6838
                if ($userId != $currentUserId &&
6839
                    self::anonymize($userId)
6840
                ) {
6841
                    $message = Display::return_message(
6842
                        sprintf(get_lang('UserXAnonymized'), $userToUpdateInfo['complete_name_with_username']),
6843
                        'confirmation'
6844
                    );
6845
                } else {
6846
                    $message = Display::return_message(
6847
                        sprintf(get_lang('CannotAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
6848
                        'error'
6849
                    );
6850
                }
6851
            } else {
6852
                $message = Display::return_message(
6853
                    sprintf(get_lang('NoPermissionToAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
6854
                    'error'
6855
                );
6856
            }
6857
        }
6858
6859
        return $message;
6860
    }
6861
6862
    /**
6863
     * @param int $userId
6864
     *
6865
     * @throws Exception
6866
     *
6867
     * @return string
6868
     */
6869
    public static function deleteUserWithVerification($userId)
6870
    {
6871
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
6872
        $message = Display::return_message(get_lang('CannotDeleteUser'), 'error');
6873
        $userToUpdateInfo = api_get_user_info($userId);
6874
6875
        // User must exist.
6876
        if (empty($userToUpdateInfo)) {
6877
            return $message;
6878
        }
6879
6880
        $currentUserId = api_get_user_id();
6881
6882
        // Cannot delete myself.
6883
        if ($userId == $currentUserId) {
6884
            return $message;
6885
        }
6886
6887
        if (api_is_platform_admin() ||
6888
            ($allowDelete && api_is_session_admin())
6889
        ) {
6890
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
6891
                if (self::delete_user($userId)) {
6892
                    $message = Display::return_message(
6893
                        get_lang('UserDeleted').': '.$userToUpdateInfo['complete_name_with_username'],
6894
                        'confirmation'
6895
                    );
6896
                } else {
6897
                    $message = Display::return_message(get_lang('CannotDeleteUserBecauseOwnsCourse'), 'error');
6898
                }
6899
            }
6900
        }
6901
6902
        return $message;
6903
    }
6904
6905
    /**
6906
     * @return array
6907
     */
6908
    public static function createDataPrivacyExtraFields()
6909
    {
6910
        self::create_extra_field(
6911
            'request_for_legal_agreement_consent_removal_justification',
6912
            1, //text
6913
            'Request for legal agreement consent removal justification	',
6914
            ''
6915
        );
6916
6917
        self::create_extra_field(
6918
            'request_for_delete_account_justification',
6919
            1, //text
6920
            'Request for delete account justification',
6921
            ''
6922
        );
6923
6924
        $extraFieldId = self::create_extra_field(
6925
            'request_for_legal_agreement_consent_removal',
6926
            1, //text
6927
            'Request for legal agreement consent removal',
6928
            ''
6929
        );
6930
6931
        $extraFieldIdDeleteAccount = self::create_extra_field(
6932
            'request_for_delete_account',
6933
            1, //text
6934
            'Request for delete user account',
6935
            ''
6936
        );
6937
6938
        return [
6939
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
6940
            'delete_legal' => $extraFieldId,
6941
        ];
6942
    }
6943
6944
    /**
6945
     * @param int $userId
6946
     */
6947
    public static function cleanUserRequestsOfRemoval($userId)
6948
    {
6949
        $userId = (int) $userId;
6950
6951
        $extraFieldValue = new ExtraFieldValue('user');
6952
        $extraFieldsToDelete = [
6953
            'legal_accept',
6954
            'request_for_legal_agreement_consent_removal',
6955
            'request_for_legal_agreement_consent_removal_justification',
6956
            'request_for_delete_account_justification', // just in case delete also this
6957
            'request_for_delete_account',
6958
        ];
6959
6960
        foreach ($extraFieldsToDelete as $variable) {
6961
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
6962
                $userId,
6963
                $variable
6964
            );
6965
            if ($value && isset($value['id'])) {
6966
                $extraFieldValue->delete($value['id']);
6967
            }
6968
        }
6969
    }
6970
6971
    /**
6972
     * @param int $searchYear
6973
     *
6974
     * @throws Exception
6975
     *
6976
     * @return array
6977
     */
6978
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
6979
    {
6980
        $timezone = new DateTimeZone(api_get_timezone());
6981
6982
        $sessions = [];
6983
        if (DRH == $userInfo['status']) {
6984
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
6985
        } elseif (api_is_platform_admin(true)) {
6986
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
6987
        } else {
6988
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
6989
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
6990
6991
            foreach ($sessionsByCategory as $sessionsInCategory) {
6992
                $sessions = array_merge($sessions, $sessionsInCategory);
6993
            }
6994
        }
6995
6996
        $sessions = array_map(
6997
            function ($sessionInfo) {
6998
                if (!isset($sessionInfo['session_id'])) {
6999
                    $sessionInfo['session_id'] = $sessionInfo['id'];
7000
                }
7001
                if (!isset($sessionInfo['session_name'])) {
7002
                    $sessionInfo['session_name'] = $sessionInfo['name'];
7003
                }
7004
7005
                return $sessionInfo;
7006
            },
7007
            $sessions
7008
        );
7009
7010
        $calendarSessions = [];
7011
7012
        foreach ($sessions as $sessionInfo) {
7013
            if (!empty($sessionInfo['duration'])) {
7014
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
7015
                    $sessionInfo['session_id'],
7016
                    $userInfo['id']
7017
                );
7018
7019
                if (empty($courseAccess)) {
7020
                    continue;
7021
                }
7022
7023
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
7024
                $lastAccessDate = clone $firstAcessDate;
7025
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
7026
7027
                $firstAccessYear = (int) $firstAcessDate->format('Y');
7028
                $lastAccessYear = (int) $lastAccessDate->format('Y');
7029
7030
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
7031
                    $calendarSessions[$sessionInfo['session_id']] = [
7032
                        'name' => $sessionInfo['session_name'],
7033
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
7034
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
7035
                    ];
7036
                }
7037
7038
                continue;
7039
            }
7040
7041
            $accessStartDate = !empty($sessionInfo['access_start_date'])
7042
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
7043
                : null;
7044
            $accessEndDate = !empty($sessionInfo['access_end_date'])
7045
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
7046
                : null;
7047
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
7048
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
7049
7050
            $isValid = false;
7051
7052
            if ($accessStartYear && $accessEndYear) {
7053
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
7054
                    $isValid = true;
7055
                }
7056
            }
7057
7058
            if ($accessStartYear && !$accessEndYear) {
7059
                if ($accessStartYear == $searchYear) {
7060
                    $isValid = true;
7061
                }
7062
            }
7063
7064
            if (!$accessStartYear && $accessEndYear) {
7065
                if ($accessEndYear == $searchYear) {
7066
                    $isValid = true;
7067
                }
7068
            }
7069
7070
            if ($isValid) {
7071
                $calendarSessions[$sessionInfo['session_id']] = [
7072
                    'name' => $sessionInfo['session_name'],
7073
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
7074
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
7075
                ];
7076
            }
7077
        }
7078
7079
        return $calendarSessions;
7080
    }
7081
7082
    /**
7083
     * Get sessions info for planification calendar.
7084
     *
7085
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
7086
     * @param int   $searchYear
7087
     *
7088
     * @throws Exception
7089
     *
7090
     * @return array
7091
     */
7092
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
7093
    {
7094
        $timezone = new DateTimeZone(api_get_timezone());
7095
        $calendar = [];
7096
7097
        foreach ($sessionsList as $sessionId => $sessionInfo) {
7098
            $startDate = $sessionInfo['access_start_date']
7099
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
7100
                : null;
7101
            $endDate = $sessionInfo['access_end_date']
7102
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
7103
                : null;
7104
7105
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
7106
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
7107
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
7108
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
7109
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
7110
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
7111
7112
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
7113
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
7114
7115
            $calendar[] = [
7116
                'id' => $sessionId,
7117
                'name' => $sessionInfo['name'],
7118
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
7119
                'start_in_last_year' => $startYear < $searchYear,
7120
                'end_in_next_year' => $endYear > $searchYear,
7121
                'no_start' => !$startWeek,
7122
                'no_end' => !$endWeek,
7123
                'start' => $start,
7124
                'duration' => $duration > 0 ? $duration : 1,
7125
            ];
7126
        }
7127
7128
        usort(
7129
            $calendar,
7130
            function ($sA, $sB) {
7131
                if ($sA['start'] == $sB['start']) {
7132
                    return 0;
7133
                }
7134
7135
                if ($sA['start'] < $sB['start']) {
7136
                    return -1;
7137
                }
7138
7139
                return 1;
7140
            }
7141
        );
7142
7143
        return $calendar;
7144
    }
7145
7146
    /**
7147
     * Return the user's full name. Optionally with the username.
7148
     *
7149
     * @param bool $includeUsername Optional. By default username is not included.
7150
     *
7151
     * @return string
7152
     */
7153
    public static function formatUserFullName(User $user, $includeUsername = false)
7154
    {
7155
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
7156
7157
        if ($includeUsername && api_get_configuration_value('hide_username_with_complete_name') !== true) {
7158
            $username = $user->getUsername();
7159
7160
            return "$fullName ($username)";
7161
        }
7162
7163
        return $fullName;
7164
    }
7165
7166
    /**
7167
     * @param int $userId
7168
     *
7169
     * @return array
7170
     */
7171
    public static function getUserCareers($userId)
7172
    {
7173
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7174
        $tableCareer = Database::get_main_table(TABLE_CAREER);
7175
        $userId = (int) $userId;
7176
7177
        $sql = "SELECT c.id, c.name
7178
                FROM $table uc
7179
                INNER JOIN $tableCareer c
7180
                ON uc.career_id = c.id
7181
                WHERE user_id = $userId
7182
                ORDER BY uc.created_at
7183
                ";
7184
        $result = Database::query($sql);
7185
7186
        return Database::store_result($result, 'ASSOC');
7187
    }
7188
7189
    /**
7190
     * @param int $userId
7191
     * @param int $careerId
7192
     */
7193
    public static function addUserCareer($userId, $careerId)
7194
    {
7195
        if (!api_get_configuration_value('allow_career_users')) {
7196
            return false;
7197
        }
7198
7199
        if (self::userHasCareer($userId, $careerId) === false) {
7200
            $params = [
7201
                'user_id' => $userId,
7202
                'career_id' => $careerId,
7203
                'created_at' => api_get_utc_datetime(),
7204
                'updated_at' => api_get_utc_datetime(),
7205
            ];
7206
            $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7207
            Database::insert($table, $params);
7208
        }
7209
7210
        return true;
7211
    }
7212
7213
    /**
7214
     * @param int   $userCareerId
7215
     * @param array $data
7216
     *
7217
     * @return bool
7218
     */
7219
    public static function updateUserCareer($userCareerId, $data)
7220
    {
7221
        if (!api_get_configuration_value('allow_career_users')) {
7222
            return false;
7223
        }
7224
7225
        $params = ['extra_data' => $data, 'updated_at' => api_get_utc_datetime()];
7226
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7227
        Database::update(
7228
            $table,
7229
            $params,
7230
            ['id = ?' => (int) $userCareerId]
7231
        );
7232
7233
        return true;
7234
    }
7235
7236
    /**
7237
     * @param int $userId
7238
     * @param int $careerId
7239
     *
7240
     * @return array
7241
     */
7242
    public static function getUserCareer($userId, $careerId)
7243
    {
7244
        $userId = (int) $userId;
7245
        $careerId = (int) $careerId;
7246
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7247
7248
        $sql = "SELECT * FROM $table WHERE user_id = $userId AND career_id = $careerId";
7249
        $result = Database::query($sql);
7250
7251
        return Database::fetch_array($result, 'ASSOC');
7252
    }
7253
7254
    /**
7255
     * @param int $userId
7256
     * @param int $careerId
7257
     *
7258
     * @return bool
7259
     */
7260
    public static function userHasCareer($userId, $careerId)
7261
    {
7262
        $userId = (int) $userId;
7263
        $careerId = (int) $careerId;
7264
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7265
7266
        $sql = "SELECT id FROM $table WHERE user_id = $userId AND career_id = $careerId";
7267
        $result = Database::query($sql);
7268
7269
        return Database::num_rows($result) > 0;
7270
    }
7271
7272
    /**
7273
     * @param int $userId
7274
     *
7275
     * @throws Exception
7276
     */
7277
    public static function deleteUserFiles($userId)
7278
    {
7279
        $path = self::getUserPathById($userId, 'system');
7280
7281
        $fs = new Filesystem();
7282
        $fs->remove($path);
7283
    }
7284
7285
    public static function redirectToResetPassword($userId)
7286
    {
7287
        if (!api_get_configuration_value('force_renew_password_at_first_login')) {
7288
            return;
7289
        }
7290
7291
        $askPassword = self::get_extra_user_data_by_field(
7292
            $userId,
7293
            'ask_new_password'
7294
        );
7295
7296
        if (!empty($askPassword) && isset($askPassword['ask_new_password']) &&
7297
            1 === (int) $askPassword['ask_new_password']
7298
        ) {
7299
            $uniqueId = api_get_unique_id();
7300
            $userObj = api_get_user_entity($userId);
7301
7302
            $userObj->setConfirmationToken($uniqueId);
7303
            $userObj->setPasswordRequestedAt(new \DateTime());
7304
7305
            Database::getManager()->persist($userObj);
7306
            Database::getManager()->flush();
7307
7308
            $url = api_get_path(WEB_CODE_PATH).'auth/reset.php?token='.$uniqueId;
7309
            api_location($url);
7310
        }
7311
    }
7312
7313
    /**
7314
     * @return EncoderFactory
7315
     */
7316
    private static function getEncoderFactory()
7317
    {
7318
        $encryption = self::getPasswordEncryption();
7319
        $encoders = [
7320
            'Chamilo\\UserBundle\\Entity\\User' => new \Chamilo\UserBundle\Security\Encoder($encryption),
7321
        ];
7322
7323
        return new EncoderFactory($encoders);
7324
    }
7325
7326
    /**
7327
     * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface
7328
     */
7329
    private static function getEncoder(User $user)
7330
    {
7331
        $encoderFactory = self::getEncoderFactory();
7332
7333
        return $encoderFactory->getEncoder($user);
7334
    }
7335
7336
    /**
7337
     * Disables or enables a user.
7338
     *
7339
     * @param int $user_id
7340
     * @param int $active  Enable or disable
7341
     *
7342
     * @return bool True on success, false on failure
7343
     * @assert (-1,0) === false
7344
     * @assert (1,1) === true
7345
     */
7346
    private static function change_active_state($user_id, $active)
7347
    {
7348
        $user_id = (int) $user_id;
7349
        $active = (int) $active;
7350
7351
        if (empty($user_id)) {
7352
            return false;
7353
        }
7354
7355
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
7356
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
7357
        $r = Database::query($sql);
7358
        $ev = LOG_USER_DISABLE;
7359
        if ($active == 1) {
7360
            $ev = LOG_USER_ENABLE;
7361
        }
7362
        if ($r !== false) {
7363
            Event::addEvent($ev, LOG_USER_ID, $user_id);
7364
        }
7365
7366
        return $r;
7367
    }
7368
7369
    /**
7370
     * Get either a Gravatar URL or complete image tag for a specified email address.
7371
     *
7372
     * @param string $email The email address
7373
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
7374
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
7375
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
7376
     * @param bool   $img   True to return a complete IMG tag False for just the URL
7377
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
7378
     *
7379
     * @return string containing either just a URL or a complete image tag
7380
     * @source http://gravatar.com/site/implement/images/php/
7381
     */
7382
    private static function getGravatar(
7383
        $email,
7384
        $s = 80,
7385
        $d = 'mm',
7386
        $r = 'g',
7387
        $img = false,
7388
        $atts = []
7389
    ) {
7390
        $url = 'http://www.gravatar.com/avatar/';
7391
        if (!empty($_SERVER['HTTPS'])) {
7392
            $url = 'https://secure.gravatar.com/avatar/';
7393
        }
7394
        $url .= md5(strtolower(trim($email)));
7395
        $url .= "?s=$s&d=$d&r=$r";
7396
        if ($img) {
7397
            $url = '<img src="'.$url.'"';
7398
            foreach ($atts as $key => $val) {
7399
                $url .= ' '.$key.'="'.$val.'"';
7400
            }
7401
            $url .= ' />';
7402
        }
7403
7404
        return $url;
7405
    }
7406
}
7407