Passed
Push — 1.11.x ( 3851b5...3f1029 )
by Julito
10:38 queued 33s
created

UserManager::is_extra_field_available()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
5
use Chamilo\CoreBundle\Entity\Repository\AccessUrlRepository;
6
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...
7
use Chamilo\CoreBundle\Entity\SkillRelUserComment;
8
use Chamilo\UserBundle\Entity\User;
9
use Chamilo\UserBundle\Repository\UserRepository;
10
use ChamiloSession as Session;
11
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
12
13
/**
14
 * Class UserManager.
15
 *
16
 * This library provides functions for user management.
17
 * Include/require it in your code to use its functionality.
18
 *
19
 * @package chamilo.library
20
 *
21
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
22
 */
23
class UserManager
24
{
25
    // This constants are deprecated use the constants located in ExtraField
26
    const USER_FIELD_TYPE_TEXT = 1;
27
    const USER_FIELD_TYPE_TEXTAREA = 2;
28
    const USER_FIELD_TYPE_RADIO = 3;
29
    const USER_FIELD_TYPE_SELECT = 4;
30
    const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
31
    const USER_FIELD_TYPE_DATE = 6;
32
    const USER_FIELD_TYPE_DATETIME = 7;
33
    const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
34
    const USER_FIELD_TYPE_DIVIDER = 9;
35
    const USER_FIELD_TYPE_TAG = 10;
36
    const USER_FIELD_TYPE_TIMEZONE = 11;
37
    const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
38
    const USER_FIELD_TYPE_FILE = 13;
39
    const USER_FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
40
41
    private static $encryptionMethod;
42
43
    /**
44
     * Constructor.
45
     *
46
     * @assert () === null
47
     */
48
    public function __construct()
49
    {
50
    }
51
52
    /**
53
     * Repository is use to query the DB, selects, etc.
54
     *
55
     * @return UserRepository
56
     */
57
    public static function getRepository()
58
    {
59
        /** @var UserRepository $userRepository */
60
        $userRepository = Database::getManager()->getRepository('ChamiloUserBundle:User');
61
62
        return $userRepository;
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
            $userManager = new Chamilo\UserBundle\Entity\Manager\UserManager(
78
                $encoderFactory,
79
                new \FOS\UserBundle\Util\Canonicalizer(),
80
                new \FOS\UserBundle\Util\Canonicalizer(),
81
                Database::getManager(),
82
                'Chamilo\\UserBundle\\Entity\\User'
83
            );
84
        }
85
86
        return $userManager;
87
    }
88
89
    /**
90
     * @param string $encryptionMethod
91
     */
92
    public static function setPasswordEncryption($encryptionMethod)
93
    {
94
        self::$encryptionMethod = $encryptionMethod;
95
    }
96
97
    /**
98
     * @return bool|mixed
99
     */
100
    public static function getPasswordEncryption()
101
    {
102
        $encryptionMethod = self::$encryptionMethod;
103
        if (empty($encryptionMethod)) {
104
            $encryptionMethod = api_get_configuration_value('password_encryption');
105
        }
106
107
        return $encryptionMethod;
108
    }
109
110
    /**
111
     * Validates the password.
112
     *
113
     * @param $encoded
114
     * @param $raw
115
     * @param $salt
116
     *
117
     * @return bool
118
     */
119
    public static function isPasswordValid($encoded, $raw, $salt)
120
    {
121
        $encoder = new \Chamilo\UserBundle\Security\Encoder(self::getPasswordEncryption());
122
        $validPassword = $encoder->isPasswordValid($encoded, $raw, $salt);
123
124
        return $validPassword;
125
    }
126
127
    /**
128
     * @param string $raw
129
     * @param User   $user
130
     *
131
     * @return string
132
     */
133
    public static function encryptPassword($raw, User $user)
134
    {
135
        $encoder = self::getEncoder($user);
136
        $encodedPassword = $encoder->encodePassword(
137
            $raw,
138
            $user->getSalt()
139
        );
140
141
        return $encodedPassword;
142
    }
143
144
    /**
145
     * @param int    $userId
146
     * @param string $password
147
     */
148
    public static function updatePassword($userId, $password)
149
    {
150
        $repository = self::getRepository();
151
        /** @var User $user */
152
        $user = $repository->find($userId);
153
        $userManager = self::getManager();
154
        $user->setPlainPassword($password);
155
        $userManager->updateUser($user, true);
156
    }
157
158
    /**
159
     * Creates a new user for the platform.
160
     *
161
     * @author Hugues Peeters <[email protected]>,
162
     * @author Roan Embrechts <[email protected]>
163
     *
164
     * @param string        $firstName
165
     * @param string        $lastName
166
     * @param int           $status                  (1 for course tutor, 5 for student, 6 for anonymous)
167
     * @param string        $email
168
     * @param string        $loginName
169
     * @param string        $password
170
     * @param string        $official_code           Any official code (optional)
171
     * @param string        $language                User language    (optional)
172
     * @param string        $phone                   Phone number    (optional)
173
     * @param string        $picture_uri             Picture URI        (optional)
174
     * @param string        $authSource              Authentication source (defaults to 'platform', dependind on constant)
175
     * @param string        $expirationDate          Account expiration date (optional, defaults to null)
176
     * @param int           $active                  Whether the account is enabled or disabled by default
177
     * @param int           $hr_dept_id              The department of HR in which the user is registered (defaults to 0)
178
     * @param array         $extra                   Extra fields
179
     * @param string        $encrypt_method          Used if password is given encrypted. Set to an empty string by default
180
     * @param bool          $send_mail
181
     * @param bool          $isAdmin
182
     * @param string        $address
183
     * @param bool          $sendEmailToAllAdmins
184
     * @param FormValidator $form
185
     * @param int           $creatorId
186
     * @param array         $emailTemplate
187
     * @param string        $redirectToURLAfterLogin
188
     *
189
     * @return mixed new user id - if the new user creation succeeds, false otherwise
190
     * @desc The function tries to retrieve user id from the session.
191
     * If it exists, the current user id is the creator id. If a problem arises,
192
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
193
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
194
     */
195
    public static function create_user(
196
        $firstName,
197
        $lastName,
198
        $status,
199
        $email,
200
        $loginName,
201
        $password,
202
        $official_code = '',
203
        $language = '',
204
        $phone = '',
205
        $picture_uri = '',
206
        $authSource = PLATFORM_AUTH_SOURCE,
207
        $expirationDate = null,
208
        $active = 1,
209
        $hr_dept_id = 0,
210
        $extra = [],
211
        $encrypt_method = '',
0 ignored issues
show
Unused Code introduced by
The parameter $encrypt_method is not used and could be removed. ( Ignorable by Annotation )

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

211
        /** @scrutinizer ignore-unused */ $encrypt_method = '',

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
212
        $send_mail = false,
213
        $isAdmin = false,
214
        $address = '',
215
        $sendEmailToAllAdmins = false,
216
        $form = null,
217
        $creatorId = 0,
218
        $emailTemplate = [],
219
        $redirectToURLAfterLogin = ''
220
    ) {
221
        $creatorId = empty($creatorId) ? api_get_user_id() : 0;
222
        $creatorInfo = api_get_user_info($creatorId);
223
        $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
224
225
        $hook = HookCreateUser::create();
226
        if (!empty($hook)) {
227
            $hook->notifyCreateUser(HOOK_EVENT_TYPE_PRE);
228
        }
229
230
        // First check wether the login already exists
231
        if (!self::is_username_available($loginName)) {
232
            Display::addFlash(
233
                Display::return_message(get_lang('LoginAlreadyTaken'))
234
            );
235
236
            return false;
237
        }
238
239
        global $_configuration;
240
        $original_password = $password;
241
242
        $access_url_id = 1;
243
        if (api_get_multiple_access_url()) {
244
            $access_url_id = api_get_current_access_url_id();
245
        } else {
246
            // In some cases, the first access_url ID might be different from 1
247
            // for example when using a DB cluster or hacking the DB manually.
248
            // In this case, we want the first row, not necessarily "1".
249
            $dbm = Database::getManager();
250
            /** @var AccessUrlRepository $accessUrlRepository */
251
            $accessUrlRepository = $dbm->getRepository('ChamiloCoreBundle:AccessUrl');
252
            $accessUrl = $accessUrlRepository->getFirstId();
253
            if (!empty($accessUrl[0]) && !empty($accessUrl[0][1])) {
254
                $access_url_id = $accessUrl[0][1];
255
            }
256
        }
257
258
        if (isset($_configuration[$access_url_id]) &&
259
            is_array($_configuration[$access_url_id]) &&
260
            isset($_configuration[$access_url_id]['hosting_limit_users']) &&
261
            $_configuration[$access_url_id]['hosting_limit_users'] > 0) {
262
            $num = self::get_number_of_users();
263
            if ($num >= $_configuration[$access_url_id]['hosting_limit_users']) {
264
                api_warn_hosting_contact('hosting_limit_users');
265
                Display::addFlash(
266
                    Display::return_message(
267
                        get_lang('PortalUsersLimitReached'),
268
                        'warning'
269
                    )
270
                );
271
272
                return false;
273
            }
274
        }
275
276
        if ($status === 1 &&
277
            isset($_configuration[$access_url_id]) &&
278
            is_array($_configuration[$access_url_id]) &&
279
            isset($_configuration[$access_url_id]['hosting_limit_teachers']) &&
280
            $_configuration[$access_url_id]['hosting_limit_teachers'] > 0
281
        ) {
282
            $num = self::get_number_of_users(1);
283
            if ($num >= $_configuration[$access_url_id]['hosting_limit_teachers']) {
284
                Display::addFlash(
285
                    Display::return_message(
286
                        get_lang('PortalTeachersLimitReached'),
287
                        'warning'
288
                    )
289
                );
290
                api_warn_hosting_contact('hosting_limit_teachers');
291
292
                return false;
293
            }
294
        }
295
296
        if (empty($password)) {
297
            if ($authSource === PLATFORM_AUTH_SOURCE) {
298
                Display::addFlash(
299
                    Display::return_message(
300
                        get_lang('ThisFieldIsRequired').': '.get_lang(
301
                            'Password'
302
                        ),
303
                        'warning'
304
                    )
305
                );
306
307
                return false;
308
            }
309
310
            // We use the authSource as password.
311
            // The real validation will be by processed by the auth
312
            // source not Chamilo
313
            $password = $authSource;
314
        }
315
316
        // database table definition
317
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
318
319
        // Checking the user language
320
        $languages = api_get_languages();
321
        $language = strtolower($language);
322
323
        if (isset($languages['folder'])) {
324
            if (!in_array($language, $languages['folder'])) {
325
                $language = api_get_setting('platformLanguage');
326
            }
327
        }
328
329
        $currentDate = api_get_utc_datetime();
330
        $now = new DateTime();
331
332
        if (empty($expirationDate) || $expirationDate == '0000-00-00 00:00:00') {
333
            // Default expiration date
334
            // if there is a default duration of a valid account then
335
            // we have to change the expiration_date accordingly
336
            // Accept 0000-00-00 00:00:00 as a null value to avoid issues with
337
            // third party code using this method with the previous (pre-1.10)
338
            // value of 0000...
339
            if (api_get_setting('account_valid_duration') != '') {
340
                $expirationDate = new DateTime($currentDate);
341
                $days = (int) api_get_setting('account_valid_duration');
342
                $expirationDate->modify('+'.$days.' day');
343
            }
344
        } else {
345
            $expirationDate = api_get_utc_datetime($expirationDate);
346
            $expirationDate = new \DateTime($expirationDate, new DateTimeZone('UTC'));
347
        }
348
349
        $userManager = self::getManager();
350
351
        /** @var User $user */
352
        $user = $userManager->createUser();
353
354
        $user
355
            ->setLastname($lastName)
356
            ->setFirstname($firstName)
357
            ->setUsername($loginName)
358
            ->setStatus($status)
359
            ->setPlainPassword($password)
360
            ->setEmail($email)
361
            ->setOfficialCode($official_code)
362
            ->setPictureUri($picture_uri)
363
            ->setCreatorId($creatorId)
364
            ->setAuthSource($authSource)
365
            ->setPhone($phone)
366
            ->setAddress($address)
367
            ->setLanguage($language)
368
            ->setRegistrationDate($now)
369
            ->setHrDeptId($hr_dept_id)
370
            ->setActive($active)
371
            ->setEnabled($active)
372
        ;
373
374
        if (!empty($expirationDate)) {
375
            $user->setExpirationDate($expirationDate);
0 ignored issues
show
Bug introduced by
It seems like $expirationDate can also be of type string; however, parameter $expirationDate of Chamilo\UserBundle\Entit...er::setExpirationDate() does only seem to accept DateTime, maybe add an additional type check? ( Ignorable by Annotation )

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

375
            $user->setExpirationDate(/** @scrutinizer ignore-type */ $expirationDate);
Loading history...
376
        }
377
378
        $userManager->updateUser($user);
379
        $userId = $user->getId();
380
381
        if (!empty($userId)) {
382
            $return = $userId;
383
            $sql = "UPDATE $table_user SET user_id = $return WHERE id = $return";
384
            Database::query($sql);
385
386
            if ($isAdmin) {
387
                self::add_user_as_admin($user);
388
            }
389
390
            if (api_get_multiple_access_url()) {
391
                UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
392
            } else {
393
                //we are adding by default the access_url_user table with access_url_id = 1
394
                UrlManager::add_user_to_url($userId, 1);
395
            }
396
397
            $extra['item_id'] = $userId;
398
399
            if (is_array($extra) && count($extra) > 0) {
400
                $courseFieldValue = new ExtraFieldValue('user');
401
                $courseFieldValue->saveFieldValues($extra);
402
            } else {
403
                // Create notify settings by default
404
                self::update_extra_field_value(
405
                    $userId,
406
                    'mail_notify_invitation',
407
                    '1'
408
                );
409
                self::update_extra_field_value(
410
                    $userId,
411
                    'mail_notify_message',
412
                    '1'
413
                );
414
                self::update_extra_field_value(
415
                    $userId,
416
                    'mail_notify_group_message',
417
                    '1'
418
                );
419
            }
420
421
            self::update_extra_field_value(
422
                $userId,
423
                'already_logged_in',
424
                'false'
425
            );
426
427
            if (!empty($redirectToURLAfterLogin) && api_get_configuration_value('plugin_redirection_enabled')) {
428
                RedirectionPlugin::insert($userId, $redirectToURLAfterLogin);
429
            }
430
431
            if (!empty($email) && $send_mail) {
432
                $recipient_name = api_get_person_name(
433
                    $firstName,
434
                    $lastName,
435
                    null,
436
                    PERSON_NAME_EMAIL_ADDRESS
437
                );
438
                $tplSubject = new Template(
439
                    null,
440
                    false,
441
                    false,
442
                    false,
443
                    false,
444
                    false
445
                );
446
                $layoutSubject = $tplSubject->get_template('mail/subject_registration_platform.tpl');
447
                $emailSubject = $tplSubject->fetch($layoutSubject);
448
                $sender_name = api_get_person_name(
449
                    api_get_setting('administratorName'),
450
                    api_get_setting('administratorSurname'),
451
                    null,
452
                    PERSON_NAME_EMAIL_ADDRESS
453
                );
454
                $email_admin = api_get_setting('emailAdministrator');
455
456
                $url = api_get_path(WEB_PATH);
457
                if (api_is_multiple_url_enabled()) {
458
                    $access_url_id = api_get_current_access_url_id();
459
                    if ($access_url_id != -1) {
460
                        $urlInfo = api_get_access_url($access_url_id);
461
                        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...
462
                            $url = $urlInfo['url'];
463
                        }
464
                    }
465
                }
466
467
                $tplContent = new Template(
468
                    null,
469
                    false,
470
                    false,
471
                    false,
472
                    false,
473
                    false
474
                );
475
                // variables for the default template
476
                $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
477
                $tplContent->assign('login_name', $loginName);
478
                $tplContent->assign('original_password', stripslashes($original_password));
479
                $tplContent->assign('mailWebPath', $url);
480
                $tplContent->assign('new_user', $user);
481
482
                $layoutContent = $tplContent->get_template('mail/content_registration_platform.tpl');
483
                $emailBody = $tplContent->fetch($layoutContent);
484
485
                $userInfo = api_get_user_info($userId);
486
                $mailTemplateManager = new MailTemplateManager();
487
488
                /* MANAGE EVENT WITH MAIL */
489
                if (EventsMail::check_if_using_class('user_registration')) {
490
                    $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...
491
                    $values["password"] = $original_password;
492
                    $values["send_to"] = [$return];
493
                    $values["prior_lang"] = null;
494
                    EventsDispatcher::events('user_registration', $values);
495
                } else {
496
                    $phoneNumber = isset($extra['mobile_phone_number']) ? $extra['mobile_phone_number'] : null;
497
                    $additionalParameters = [
498
                        'smsType' => SmsPlugin::WELCOME_LOGIN_PASSWORD,
499
                        'userId' => $return,
500
                        'mobilePhoneNumber' => $phoneNumber,
501
                        'password' => $original_password,
502
                    ];
503
504
                    $emailBodyTemplate = '';
505
                    if (!empty($emailTemplate)) {
506
                        if (isset($emailTemplate['content_registration_platform.tpl']) &&
507
                            !empty($emailTemplate['content_registration_platform.tpl'])
508
                        ) {
509
                            $emailBodyTemplate = $mailTemplateManager->parseTemplate(
510
                                $emailTemplate['content_registration_platform.tpl'],
511
                                $userInfo
0 ignored issues
show
Bug introduced by
It seems like $userInfo can also be of type false and mixed; however, parameter $userInfo of MailTemplateManager::parseTemplate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

511
                                /** @scrutinizer ignore-type */ $userInfo
Loading history...
512
                            );
513
                        }
514
                    }
515
516
                    $twoEmail = api_get_configuration_value('send_two_inscription_confirmation_mail');
517
                    if ($twoEmail === true) {
518
                        $layoutContent = $tplContent->get_template('mail/new_user_first_email_confirmation.tpl');
519
                        $emailBody = $tplContent->fetch($layoutContent);
520
521
                        if (!empty($emailBodyTemplate) &&
522
                            isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
523
                            !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
524
                        ) {
525
                            $emailBody = $mailTemplateManager->parseTemplate(
526
                                $emailTemplate['new_user_first_email_confirmation.tpl'],
527
                                $userInfo
528
                            );
529
                        }
530
531
                        api_mail_html(
532
                            $recipient_name,
533
                            $email,
534
                            $emailSubject,
535
                            $emailBody,
536
                            $sender_name,
537
                            $email_admin,
538
                            null,
539
                            null,
540
                            null,
541
                            $additionalParameters,
542
                            $creatorEmail
543
                        );
544
545
                        $layoutContent = $tplContent->get_template('mail/new_user_second_email_confirmation.tpl');
546
                        $emailBody = $tplContent->fetch($layoutContent);
547
548
                        if (!empty($emailBodyTemplate) &&
549
                            isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
550
                            !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
551
                        ) {
552
                            $emailBody = $mailTemplateManager->parseTemplate(
553
                                $emailTemplate['new_user_second_email_confirmation.tpl'],
554
                                $userInfo
555
                            );
556
                        }
557
558
                        api_mail_html(
559
                            $recipient_name,
560
                            $email,
561
                            $emailSubject,
562
                            $emailBody,
563
                            $sender_name,
564
                            $email_admin,
565
                            null,
566
                            null,
567
                            null,
568
                            $additionalParameters,
569
                            $creatorEmail
570
                        );
571
                    } else {
572
                        if (!empty($emailBodyTemplate)) {
573
                            $emailBody = $emailBodyTemplate;
574
                        }
575
                        $sendToInbox = api_get_configuration_value('send_inscription_msg_to_inbox');
576
                        if ($sendToInbox) {
577
                            $adminList = self::get_all_administrators();
578
                            $senderId = 1;
579
                            if (!empty($adminList)) {
580
                                $adminInfo = current($adminList);
581
                                $senderId = $adminInfo['user_id'];
582
                            }
583
584
                            MessageManager::send_message_simple(
585
                                $userId,
586
                                $emailSubject,
587
                                $emailBody,
588
                                $senderId
589
                            );
590
                        } else {
591
                            api_mail_html(
592
                                $recipient_name,
593
                                $email,
594
                                $emailSubject,
595
                                $emailBody,
596
                                $sender_name,
597
                                $email_admin,
598
                                null,
599
                                null,
600
                                null,
601
                                $additionalParameters,
602
                                $creatorEmail
603
                            );
604
                        }
605
                    }
606
607
                    $notification = api_get_configuration_value('send_notification_when_user_added');
608
                    if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
609
                        foreach ($notification['admins'] as $adminId) {
610
                            $emailSubjectToAdmin = get_lang('UserAdded').': '.api_get_person_name($firstName, $lastName);
611
                            MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
0 ignored issues
show
Bug introduced by
It seems like $emailBody can also be of type false; however, parameter $message of MessageManager::send_message_simple() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

611
                            MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, /** @scrutinizer ignore-type */ $emailBody, $userId);
Loading history...
612
                        }
613
                    }
614
                }
615
616
                if ($sendEmailToAllAdmins) {
617
                    $adminList = self::get_all_administrators();
618
619
                    $tplContent = new Template(
620
                        null,
621
                        false,
622
                        false,
623
                        false,
624
                        false,
625
                        false
626
                    );
627
                    // variables for the default template
628
                    $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
629
                    $tplContent->assign('user_added', $user);
630
                    $renderer = FormValidator::getDefaultRenderer();
631
                    // Form template
632
                    $elementTemplate = ' {label}: {element} <br />';
633
                    $renderer->setElementTemplate($elementTemplate);
634
                    /** @var FormValidator $form */
635
                    $form->freeze(null, $elementTemplate);
636
                    $form->removeElement('submit');
637
                    $formData = $form->returnForm();
638
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
639
                    $tplContent->assign('link', Display::url($url, $url));
640
                    $tplContent->assign('form', $formData);
641
642
                    $layoutContent = $tplContent->get_template('mail/content_registration_platform_to_admin.tpl');
643
                    $emailBody = $tplContent->fetch($layoutContent);
644
645
                    if (!empty($emailBodyTemplate) &&
646
                        isset($emailTemplate['content_registration_platform_to_admin.tpl']) &&
647
                        !empty($emailTemplate['content_registration_platform_to_admin.tpl'])
648
                    ) {
649
                        $emailBody = $mailTemplateManager->parseTemplate(
650
                            $emailTemplate['content_registration_platform_to_admin.tpl'],
651
                            $userInfo
652
                        );
653
                    }
654
655
                    $subject = get_lang('UserAdded');
656
657
                    foreach ($adminList as $adminId => $data) {
658
                        MessageManager::send_message_simple(
659
                            $adminId,
660
                            $subject,
661
                            $emailBody,
662
                            $userId
663
                        );
664
                    }
665
                }
666
                /* ENDS MANAGE EVENT WITH MAIL */
667
            }
668
669
            if (!empty($hook)) {
670
                $hook->setEventData([
671
                    'return' => $userId,
672
                    'originalPassword' => $original_password,
673
                ]);
674
                $hook->notifyCreateUser(HOOK_EVENT_TYPE_POST);
675
            }
676
            Event::addEvent(LOG_USER_CREATE, 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

676
            Event::/** @scrutinizer ignore-call */ 
677
                   addEvent(LOG_USER_CREATE, 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...
677
        } else {
678
            Display::addFlash(
679
                Display::return_message(get_lang('ErrorContactPlatformAdmin'))
680
            );
681
682
            return false;
683
        }
684
685
        return $return;
686
    }
687
688
    /**
689
     * Can user be deleted? This function checks whether there's a course
690
     * in which the given user is the
691
     * only course administrator. If that is the case, the user can't be
692
     * deleted because the course would remain without a course admin.
693
     *
694
     * @param int $user_id The user id
695
     *
696
     * @return bool true if user can be deleted
697
     *
698
     * @assert (null) === false
699
     * @assert (-1) === false
700
     * @assert ('abc') === false
701
     */
702
    public static function canDeleteUser($user_id)
703
    {
704
        $deny = api_get_configuration_value('deny_delete_users');
705
706
        if ($deny) {
707
            return false;
708
        }
709
710
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
711
        $user_id = (int) $user_id;
712
713
        if (empty($user_id)) {
714
            return false;
715
        }
716
717
        $sql = "SELECT * FROM $table_course_user
718
                WHERE status = 1 AND user_id = ".$user_id;
719
        $res = Database::query($sql);
720
        while ($course = Database::fetch_object($res)) {
721
            $sql = "SELECT id FROM $table_course_user
722
                    WHERE status=1 AND c_id = ".intval($course->c_id);
723
            $res2 = Database::query($sql);
724
            if (Database::num_rows($res2) == 1) {
725
                return false;
726
            }
727
        }
728
729
        return true;
730
    }
731
732
    /**
733
     * Delete a user from the platform, and all its belongings. This is a
734
     * very dangerous function that should only be accessible by
735
     * super-admins. Other roles should only be able to disable a user,
736
     * which removes access to the platform but doesn't delete anything.
737
     *
738
     * @param int The ID of th user to be deleted
739
     *
740
     * @throws Exception
741
     *
742
     * @return bool true if user is successfully deleted, false otherwise
743
     * @assert (null) === false
744
     * @assert ('abc') === false
745
     */
746
    public static function delete_user($user_id)
747
    {
748
        $user_id = (int) $user_id;
749
750
        if (empty($user_id)) {
751
            return false;
752
        }
753
754
        if (!self::canDeleteUser($user_id)) {
755
            return false;
756
        }
757
758
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
759
        $usergroup_rel_user = Database::get_main_table(TABLE_USERGROUP_REL_USER);
760
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
761
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
762
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
763
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
764
        $table_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
765
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
766
        $table_group = Database::get_course_table(TABLE_GROUP_USER);
767
        $table_work = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
768
769
        // Unsubscribe the user from all groups in all his courses
770
        $sql = "SELECT c.id
771
                FROM $table_course c
772
                INNER JOIN $table_course_user cu
773
                ON (c.id = cu.c_id)
774
                WHERE
775
                    cu.user_id = '".$user_id."' AND
776
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
777
                ";
778
779
        $res = Database::query($sql);
780
        while ($course = Database::fetch_object($res)) {
781
            $sql = "DELETE FROM $table_group
782
                    WHERE c_id = {$course->id} AND user_id = $user_id";
783
            Database::query($sql);
784
        }
785
786
        // Unsubscribe user from usergroup_rel_user
787
        $sql = "DELETE FROM $usergroup_rel_user WHERE user_id = '".$user_id."'";
788
        Database::query($sql);
789
790
        // Unsubscribe user from all courses
791
        $sql = "DELETE FROM $table_course_user WHERE user_id = '".$user_id."'";
792
        Database::query($sql);
793
794
        // Unsubscribe user from all courses in sessions
795
        $sql = "DELETE FROM $table_session_course_user WHERE user_id = '".$user_id."'";
796
        Database::query($sql);
797
798
        // If the user was added as a id_coach then set the current admin as coach see BT#
799
        $currentUserId = api_get_user_id();
800
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
801
                WHERE id_coach = '".$user_id."'";
802
        Database::query($sql);
803
804
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
805
                WHERE session_admin_id = '".$user_id."'";
806
        Database::query($sql);
807
808
        // Unsubscribe user from all sessions
809
        $sql = "DELETE FROM $table_session_user
810
                WHERE user_id = '".$user_id."'";
811
        Database::query($sql);
812
813
        if (api_get_configuration_value('plugin_redirection_enabled')) {
814
            RedirectionPlugin::deleteUserRedirection($user_id);
815
        }
816
817
        // Delete user picture
818
        /* TODO: Logic about api_get_setting('split_users_upload_directory') == 'true'
819
        a user has 4 different sized photos to be deleted. */
820
        $user_info = api_get_user_info($user_id);
821
822
        if (strlen($user_info['picture_uri']) > 0) {
823
            $path = self::getUserPathById($user_id, 'system');
824
            $img_path = $path.$user_info['picture_uri'];
825
            if (file_exists($img_path)) {
826
                unlink($img_path);
827
            }
828
        }
829
830
        // Delete the personal course categories
831
        $course_cat_table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
832
        $sql = "DELETE FROM $course_cat_table WHERE user_id = '".$user_id."'";
833
        Database::query($sql);
834
835
        // Delete user from the admin table
836
        $sql = "DELETE FROM $table_admin WHERE user_id = '".$user_id."'";
837
        Database::query($sql);
838
839
        // Delete the personal agenda-items from this user
840
        $agenda_table = Database::get_main_table(TABLE_PERSONAL_AGENDA);
841
        $sql = "DELETE FROM $agenda_table WHERE user = '".$user_id."'";
842
        Database::query($sql);
843
844
        $gradebook_results_table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_RESULT);
845
        $sql = 'DELETE FROM '.$gradebook_results_table.' WHERE user_id = '.$user_id;
846
        Database::query($sql);
847
848
        $extraFieldValue = new ExtraFieldValue('user');
849
        $extraFieldValue->deleteValuesByItem($user_id);
850
851
        UrlManager::deleteUserFromAllUrls($user_id);
852
853
        if (api_get_setting('allow_social_tool') === 'true') {
854
            $userGroup = new UserGroup();
855
            //Delete user from portal groups
856
            $group_list = $userGroup->get_groups_by_user($user_id);
857
            if (!empty($group_list)) {
858
                foreach ($group_list as $group_id => $data) {
859
                    $userGroup->delete_user_rel_group($user_id, $group_id);
860
                }
861
            }
862
863
            // Delete user from friend lists
864
            SocialManager::remove_user_rel_user($user_id, true);
865
        }
866
867
        // Removing survey invitation
868
        SurveyManager::delete_all_survey_invitations_by_user($user_id);
869
870
        // Delete students works
871
        $sql = "DELETE FROM $table_work WHERE user_id = $user_id AND c_id <> 0";
872
        Database::query($sql);
873
874
        $sql = "UPDATE c_item_property SET to_user_id = NULL
875
                WHERE to_user_id = '".$user_id."'";
876
        Database::query($sql);
877
878
        $sql = "UPDATE c_item_property SET insert_user_id = NULL
879
                WHERE insert_user_id = '".$user_id."'";
880
        Database::query($sql);
881
882
        $sql = "UPDATE c_item_property SET lastedit_user_id = NULL
883
                WHERE lastedit_user_id = '".$user_id."'";
884
        Database::query($sql);
885
886
        // Skills
887
        $em = Database::getManager();
888
889
        $criteria = ['user' => $user_id];
890
        $skills = $em->getRepository('ChamiloCoreBundle:SkillRelUser')->findBy($criteria);
891
        if ($skills) {
892
            /** @var SkillRelUser $skill */
893
            foreach ($skills as $skill) {
894
                $comments = $skill->getComments();
895
                if ($comments) {
896
                    /** @var SkillRelUserComment $comment */
897
                    foreach ($comments as $comment) {
898
                        $em->remove($comment);
899
                    }
900
                }
901
                $em->remove($skill);
902
            }
903
            $em->flush();
904
        }
905
906
        // ExtraFieldSavedSearch
907
        $criteria = ['user' => $user_id];
908
        $searchList = $em->getRepository('ChamiloCoreBundle:ExtraFieldSavedSearch')->findBy($criteria);
909
        if ($searchList) {
910
            foreach ($searchList as $search) {
911
                $em->remove($search);
912
            }
913
            $em->flush();
914
        }
915
916
        $connection = Database::getManager()->getConnection();
917
        $tableExists = $connection->getSchemaManager()->tablesExist(['plugin_bbb_room']);
918
        if ($tableExists) {
919
            // Delete user from database
920
            $sql = "DELETE FROM plugin_bbb_room WHERE participant_id = $user_id";
921
            Database::query($sql);
922
        }
923
924
        // Delete user/ticket relationships :(
925
        $tableExists = $connection->getSchemaManager()->tablesExist(['ticket_ticket']);
926
        if ($tableExists) {
927
            TicketManager::deleteUserFromTicketSystem($user_id);
928
        }
929
930
        $tableExists = $connection->getSchemaManager()->tablesExist(['c_lp_category_user']);
931
        if ($tableExists) {
932
            $sql = "DELETE FROM c_lp_category_user WHERE user_id = $user_id";
933
            Database::query($sql);
934
        }
935
936
        $app_plugin = new AppPlugin();
937
        $app_plugin->performActionsWhenDeletingItem('user', $user_id);
938
939
        // Delete user from database
940
        $sql = "DELETE FROM $table_user WHERE id = '".$user_id."'";
941
        Database::query($sql);
942
943
        // Add event to system log
944
        $user_id_manager = api_get_user_id();
945
946
        Event::addEvent(
947
            LOG_USER_DELETE,
948
            LOG_USER_ID,
949
            $user_id,
950
            api_get_utc_datetime(),
951
            $user_id_manager
952
        );
953
954
        Event::addEvent(
955
            LOG_USER_DELETE,
956
            LOG_USER_OBJECT,
957
            $user_info,
958
            api_get_utc_datetime(),
959
            $user_id_manager
960
        );
961
        $cacheAvailable = api_get_configuration_value('apc');
962
        if ($cacheAvailable === true) {
963
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
964
            if (apcu_exists($apcVar)) {
965
                apcu_delete($apcVar);
966
            }
967
        }
968
969
        return true;
970
    }
971
972
    /**
973
     * Deletes users completely. Can be called either as:
974
     * - UserManager::delete_users(1, 2, 3); or
975
     * - UserManager::delete_users(array(1, 2, 3));.
976
     *
977
     * @param array|int $ids
978
     *
979
     * @return bool True if at least one user was successfuly deleted. False otherwise.
980
     *
981
     * @author Laurent Opprecht
982
     *
983
     * @uses \UserManager::delete_user() to actually delete each user
984
     * @assert (null) === false
985
     * @assert (-1) === false
986
     * @assert (array(-1)) === false
987
     */
988
    public static function delete_users($ids = [])
989
    {
990
        $result = false;
991
        $ids = is_array($ids) ? $ids : func_get_args();
992
        if (!is_array($ids) || count($ids) == 0) {
993
            return false;
994
        }
995
        $ids = array_map('intval', $ids);
996
        foreach ($ids as $id) {
997
            if (empty($id) || $id < 1) {
998
                continue;
999
            }
1000
            $deleted = self::delete_user($id);
1001
            $result = $deleted || $result;
1002
        }
1003
1004
        return $result;
1005
    }
1006
1007
    /**
1008
     * Disable users. Can be called either as:
1009
     * - UserManager::deactivate_users(1, 2, 3);
1010
     * - UserManager::deactivate_users(array(1, 2, 3));.
1011
     *
1012
     * @param array|int $ids
1013
     *
1014
     * @return bool
1015
     *
1016
     * @author Laurent Opprecht
1017
     * @assert (null) === false
1018
     * @assert (array(-1)) === false
1019
     */
1020
    public static function deactivate_users($ids = [])
1021
    {
1022
        if (empty($ids)) {
1023
            return false;
1024
        }
1025
1026
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1027
1028
        $ids = is_array($ids) ? $ids : func_get_args();
1029
        $ids = array_map('intval', $ids);
1030
        $ids = implode(',', $ids);
1031
1032
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
1033
        $r = Database::query($sql);
1034
        if ($r !== false) {
1035
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
1036
1037
            return true;
1038
        }
1039
1040
        return false;
1041
    }
1042
1043
    /**
1044
     * Enable users. Can be called either as:
1045
     * - UserManager::activate_users(1, 2, 3);
1046
     * - UserManager::activate_users(array(1, 2, 3));.
1047
     *
1048
     * @param array|int IDs of the users to enable
1049
     *
1050
     * @return bool
1051
     *
1052
     * @author Laurent Opprecht
1053
     * @assert (null) === false
1054
     * @assert (array(-1)) === false
1055
     */
1056
    public static function activate_users($ids = [])
1057
    {
1058
        if (empty($ids)) {
1059
            return false;
1060
        }
1061
1062
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1063
1064
        $ids = is_array($ids) ? $ids : func_get_args();
1065
        $ids = array_map('intval', $ids);
1066
        $ids = implode(',', $ids);
1067
1068
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
1069
        $r = Database::query($sql);
1070
        if ($r !== false) {
1071
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
1072
1073
            return true;
1074
        }
1075
1076
        return false;
1077
    }
1078
1079
    /**
1080
     * Update user information with new openid.
1081
     *
1082
     * @param int    $user_id
1083
     * @param string $openid
1084
     *
1085
     * @return bool true if the user information was updated
1086
     * @assert (false,'') === false
1087
     * @assert (-1,'') === false
1088
     */
1089
    public static function update_openid($user_id, $openid)
1090
    {
1091
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1092
        if ($user_id != strval(intval($user_id))) {
1093
            return false;
1094
        }
1095
        if ($user_id === false) {
1096
            return false;
1097
        }
1098
        $sql = "UPDATE $table_user SET
1099
                openid='".Database::escape_string($openid)."'";
1100
        $sql .= " WHERE id= $user_id";
1101
1102
        if (Database::query($sql) !== false) {
1103
            return true;
1104
        }
1105
1106
        return false;
1107
    }
1108
1109
    /**
1110
     * Update user information with all the parameters passed to this function.
1111
     *
1112
     * @param int    $user_id         The ID of the user to be updated
1113
     * @param string $firstname       The user's firstname
1114
     * @param string $lastname        The user's lastname
1115
     * @param string $username        The user's username (login)
1116
     * @param string $password        The user's password
1117
     * @param string $auth_source     The authentication source (default: "platform")
1118
     * @param string $email           The user's e-mail address
1119
     * @param int    $status          The user's status
1120
     * @param string $official_code   The user's official code (usually just an internal institutional code)
1121
     * @param string $phone           The user's phone number
1122
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
1123
     * @param string $expiration_date The date at which this user will be automatically disabled
1124
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
1125
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
1126
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
1127
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
1128
     * @param string $language        The language to which the user account will be set
1129
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
1130
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
1131
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
1132
     * @param string $address
1133
     * @param array  $emailTemplate
1134
     *
1135
     * @return bool|int False on error, or the user ID if the user information was updated
1136
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
1137
     */
1138
    public static function update_user(
1139
        $user_id,
1140
        $firstname,
1141
        $lastname,
1142
        $username,
1143
        $password = null,
1144
        $auth_source = null,
1145
        $email,
1146
        $status,
1147
        $official_code,
1148
        $phone,
1149
        $picture_uri,
1150
        $expiration_date,
1151
        $active,
1152
        $creator_id = null,
1153
        $hr_dept_id = 0,
1154
        $extra = null,
1155
        $language = 'english',
1156
        $encrypt_method = '',
0 ignored issues
show
Unused Code introduced by
The parameter $encrypt_method is not used and could be removed. ( Ignorable by Annotation )

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

1156
        /** @scrutinizer ignore-unused */ $encrypt_method = '',

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1157
        $send_email = false,
1158
        $reset_password = 0,
1159
        $address = null,
1160
        $emailTemplate = []
1161
    ) {
1162
        $hook = HookUpdateUser::create();
1163
        if (!empty($hook)) {
1164
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_PRE);
1165
        }
1166
        $original_password = $password;
1167
        $user_id = (int) $user_id;
1168
        $creator_id = (int) $creator_id;
1169
1170
        if (empty($user_id)) {
1171
            return false;
1172
        }
1173
1174
        $userManager = self::getManager();
1175
        /** @var User $user */
1176
        $user = self::getRepository()->find($user_id);
1177
1178
        if (empty($user)) {
1179
            return false;
1180
        }
1181
1182
        if ($reset_password == 0) {
1183
            $password = null;
1184
            $auth_source = $user->getAuthSource();
1185
        } elseif ($reset_password == 1) {
1186
            $original_password = $password = api_generate_password();
1187
            $auth_source = PLATFORM_AUTH_SOURCE;
1188
        } elseif ($reset_password == 2) {
1189
            //$password = $password;
1190
            $auth_source = PLATFORM_AUTH_SOURCE;
1191
        } elseif ($reset_password == 3) {
1192
            //$password = $password;
1193
            //$auth_source = $auth_source;
1194
        }
1195
1196
        // Checking the user language
1197
        $languages = api_get_languages();
1198
        if (!in_array($language, $languages['folder'])) {
1199
            $language = api_get_setting('platformLanguage');
1200
        }
1201
1202
        $change_active = 0;
1203
        $isUserActive = $user->getActive();
1204
        if ($isUserActive != $active) {
1205
            $change_active = 1;
1206
        }
1207
1208
        $originalUsername = $user->getUsername();
1209
1210
        // If username is different from original then check if it exists.
1211
        if ($originalUsername !== $username) {
1212
            $available = self::is_username_available($username);
1213
            if ($available === false) {
1214
                return false;
1215
            }
1216
        }
1217
1218
        if (!empty($expiration_date)) {
1219
            $expiration_date = api_get_utc_datetime($expiration_date);
1220
            $expiration_date = new \DateTime(
1221
                $expiration_date,
1222
                new DateTimeZone('UTC')
1223
            );
1224
        }
1225
1226
        $user
1227
            ->setLastname($lastname)
1228
            ->setFirstname($firstname)
1229
            ->setUsername($username)
1230
            ->setStatus($status)
1231
            ->setAuthSource($auth_source)
1232
            ->setLanguage($language)
1233
            ->setEmail($email)
1234
            ->setOfficialCode($official_code)
1235
            ->setPhone($phone)
1236
            ->setAddress($address)
1237
            ->setPictureUri($picture_uri)
1238
            ->setExpirationDate($expiration_date)
1239
            ->setActive($active)
1240
            ->setEnabled($active)
1241
            ->setHrDeptId($hr_dept_id)
1242
        ;
1243
1244
        if (!is_null($password)) {
1245
            $user->setPlainPassword($password);
1246
        }
1247
1248
        $userManager->updateUser($user, true);
1249
1250
        if ($change_active == 1) {
1251
            if ($active == 1) {
1252
                $event_title = LOG_USER_ENABLE;
1253
            } else {
1254
                $event_title = LOG_USER_DISABLE;
1255
            }
1256
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
1257
        }
1258
1259
        if (is_array($extra) && count($extra) > 0) {
1260
            $res = true;
1261
            foreach ($extra as $fname => $fvalue) {
1262
                $res = $res && self::update_extra_field_value(
1263
                    $user_id,
1264
                    $fname,
1265
                    $fvalue
1266
                );
1267
            }
1268
        }
1269
1270
        if (!empty($email) && $send_email) {
1271
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
1272
            $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('YourReg').' '.api_get_setting('siteName');
1273
            $sender_name = api_get_person_name(
1274
                api_get_setting('administratorName'),
1275
                api_get_setting('administratorSurname'),
1276
                null,
1277
                PERSON_NAME_EMAIL_ADDRESS
1278
            );
1279
            $email_admin = api_get_setting('emailAdministrator');
1280
            $url = api_get_path(WEB_PATH);
1281
            if (api_is_multiple_url_enabled()) {
1282
                $access_url_id = api_get_current_access_url_id();
1283
                if ($access_url_id != -1) {
1284
                    $url = api_get_access_url($access_url_id);
1285
                    $url = $url['url'];
1286
                }
1287
            }
1288
1289
            $tplContent = new Template(
1290
                null,
1291
                false,
1292
                false,
1293
                false,
1294
                false,
1295
                false
1296
            );
1297
            // variables for the default template
1298
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
1299
            $tplContent->assign('login_name', $username);
1300
1301
            $originalPassword = '';
1302
            if ($reset_password > 0) {
1303
                $originalPassword = stripslashes($original_password);
1304
            }
1305
            $tplContent->assign('original_password', $originalPassword);
1306
            $tplContent->assign('portal_url', $url);
1307
1308
            $layoutContent = $tplContent->get_template('mail/user_edit_content.tpl');
1309
            $emailBody = $tplContent->fetch($layoutContent);
1310
1311
            $mailTemplateManager = new MailTemplateManager();
1312
1313
            if (!empty($emailTemplate) &&
1314
                isset($emailTemplate['user_edit_content.tpl']) &&
1315
                !empty($emailTemplate['user_edit_content.tpl'])
1316
            ) {
1317
                $userInfo = api_get_user_info($user_id);
1318
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], $userInfo);
0 ignored issues
show
Bug introduced by
It seems like $userInfo can also be of type false and mixed; however, parameter $userInfo of MailTemplateManager::parseTemplate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

1318
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], /** @scrutinizer ignore-type */ $userInfo);
Loading history...
1319
            }
1320
1321
            $creatorInfo = api_get_user_info($creator_id);
1322
            $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
1323
1324
            api_mail_html(
1325
                $recipient_name,
1326
                $email,
1327
                $emailsubject,
1328
                $emailBody,
1329
                $sender_name,
1330
                $email_admin,
1331
                null,
1332
                null,
1333
                null,
1334
                null,
1335
                $creatorEmail
1336
            );
1337
        }
1338
1339
        if (!empty($hook)) {
1340
            $hook->setEventData(['user' => $user]);
1341
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_POST);
1342
        }
1343
1344
        $cacheAvailable = api_get_configuration_value('apc');
1345
        if ($cacheAvailable === true) {
1346
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1347
            if (apcu_exists($apcVar)) {
1348
                apcu_delete($apcVar);
1349
            }
1350
        }
1351
1352
        return $user->getId();
1353
    }
1354
1355
    /**
1356
     * Disables a user.
1357
     *
1358
     * @param int User id
1359
     *
1360
     * @return bool
1361
     *
1362
     * @uses \UserManager::change_active_state() to actually disable the user
1363
     * @assert (0) === false
1364
     */
1365
    public static function disable($user_id)
1366
    {
1367
        if (empty($user_id)) {
1368
            return false;
1369
        }
1370
        self::change_active_state($user_id, 0);
1371
1372
        return true;
1373
    }
1374
1375
    /**
1376
     * Enable a user.
1377
     *
1378
     * @param int User id
1379
     *
1380
     * @return bool
1381
     *
1382
     * @uses \UserManager::change_active_state() to actually disable the user
1383
     * @assert (0) === false
1384
     */
1385
    public static function enable($user_id)
1386
    {
1387
        if (empty($user_id)) {
1388
            return false;
1389
        }
1390
        self::change_active_state($user_id, 1);
1391
1392
        return true;
1393
    }
1394
1395
    /**
1396
     * Returns the user's id based on the original id and field name in
1397
     * the extra fields. Returns 0 if no user was found. This function is
1398
     * mostly useful in the context of a web services-based sinchronization.
1399
     *
1400
     * @param string Original user id
1401
     * @param string Original field name
1402
     *
1403
     * @return int User id
1404
     * @assert ('0','---') === 0
1405
     */
1406
    public static function get_user_id_from_original_id(
1407
        $original_user_id_value,
1408
        $original_user_id_name
1409
    ) {
1410
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1411
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1412
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1413
1414
        $original_user_id_name = Database::escape_string($original_user_id_name);
1415
        $original_user_id_value = Database::escape_string($original_user_id_value);
1416
1417
        $sql = "SELECT item_id as user_id
1418
                FROM $t_uf uf
1419
                INNER JOIN $t_ufv ufv
1420
                ON ufv.field_id = uf.id
1421
                WHERE
1422
                    variable = '$original_user_id_name' AND
1423
                    value = '$original_user_id_value' AND
1424
                    extra_field_type = $extraFieldType
1425
                ";
1426
        $res = Database::query($sql);
1427
        $row = Database::fetch_object($res);
1428
        if ($row) {
1429
            return $row->user_id;
1430
        }
1431
1432
        return 0;
1433
    }
1434
1435
    /**
1436
     * Check if a username is available.
1437
     *
1438
     * @param string $username the wanted username
1439
     *
1440
     * @return bool true if the wanted username is available
1441
     * @assert ('') === false
1442
     * @assert ('xyzxyzxyz') === true
1443
     */
1444
    public static function is_username_available($username)
1445
    {
1446
        if (empty($username)) {
1447
            return false;
1448
        }
1449
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1450
        $sql = "SELECT username FROM $table_user
1451
                WHERE username = '".Database::escape_string($username)."'";
1452
        $res = Database::query($sql);
1453
1454
        return Database::num_rows($res) == 0;
1455
    }
1456
1457
    /**
1458
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1459
     *
1460
     * @param string $firstname the first name of the user
1461
     * @param string $lastname  the last name of the user
1462
     *
1463
     * @return string suggests a username that contains only ASCII-letters and digits,
1464
     *                without check for uniqueness within the system
1465
     *
1466
     * @author Julio Montoya Armas
1467
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1468
     * @assert ('','') === false
1469
     * @assert ('a','b') === 'ab'
1470
     */
1471
    public static function create_username($firstname, $lastname)
1472
    {
1473
        if (empty($firstname) && empty($lastname)) {
1474
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
1475
        }
1476
1477
        // The first letter only.
1478
        $firstname = api_substr(
1479
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1480
            0,
1481
            1
1482
        );
1483
        //Looking for a space in the lastname
1484
        $pos = api_strpos($lastname, ' ');
1485
        if ($pos !== false) {
1486
            $lastname = api_substr($lastname, 0, $pos);
1487
        }
1488
1489
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1490
        $username = $firstname.$lastname;
1491
        if (empty($username)) {
1492
            $username = 'user';
1493
        }
1494
1495
        $username = URLify::transliterate($username);
1496
1497
        return strtolower(substr($username, 0, USERNAME_MAX_LENGTH - 3));
1498
    }
1499
1500
    /**
1501
     * Creates a unique username, using:
1502
     * 1. the first name and the last name of a user;
1503
     * 2. an already created username but not checked for uniqueness yet.
1504
     *
1505
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1506
     *                          parameter is treated as username which is to be checked f
1507
     *                          or uniqueness and to be modified when it is necessary.
1508
     * @param string $lastname  the last name of the user
1509
     *
1510
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1511
     *                Note: When the method is called several times with same parameters,
1512
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1513
     *
1514
     * @author Ivan Tcholakov, 2009
1515
     */
1516
    public static function create_unique_username($firstname, $lastname = null)
1517
    {
1518
        if (is_null($lastname)) {
1519
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1520
            // For making this method tolerant of mistakes,
1521
            // let us transliterate and purify the suggested input username anyway.
1522
            // So, instead of the sentence $username = $firstname; we place the following:
1523
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1524
        } else {
1525
            $username = self::create_username($firstname, $lastname);
1526
        }
1527
        if (!self::is_username_available($username)) {
1528
            $i = 2;
1529
            $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1530
            while (!self::is_username_available($temp_username)) {
1531
                $i++;
1532
                $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1533
            }
1534
            $username = $temp_username;
1535
        }
1536
1537
        $username = URLify::transliterate($username);
1538
1539
        return $username;
1540
    }
1541
1542
    /**
1543
     * Modifies a given username accordingly to the specification for valid characters and length.
1544
     *
1545
     * @param $username string          The input username
1546
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1547
     *                     otherwise compliance may be partial. The default value is FALSE.
1548
     *
1549
     * @return string the resulting purified username
1550
     */
1551
    public static function purify_username($username, $strict = false)
1552
    {
1553
        if ($strict) {
1554
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1555
            // into ASCII letters in order they not to be totally removed.
1556
            // 2. Applying the strict purifier.
1557
            // 3. Length limitation.
1558
            $return = api_get_setting('login_is_email') === 'true' ? substr(preg_replace(USERNAME_PURIFIER_MAIL, '', $username), 0, USERNAME_MAX_LENGTH) : substr(preg_replace(USERNAME_PURIFIER, '', $username), 0, USERNAME_MAX_LENGTH);
1559
            $return = URLify::transliterate($return);
1560
1561
            return $return;
1562
        }
1563
1564
        // 1. Applying the shallow purifier.
1565
        // 2. Length limitation.
1566
        return substr(
1567
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1568
            0,
1569
            USERNAME_MAX_LENGTH
1570
        );
1571
    }
1572
1573
    /**
1574
     * Checks whether the user id exists in the database.
1575
     *
1576
     * @param int $userId User id
1577
     *
1578
     * @return bool True if user id was found, false otherwise
1579
     */
1580
    public static function is_user_id_valid($userId)
1581
    {
1582
        $resultData = Database::select(
1583
            'COUNT(1) AS count',
1584
            Database::get_main_table(TABLE_MAIN_USER),
1585
            [
1586
                'where' => ['id = ?' => (int) $userId],
1587
            ],
1588
            'first'
1589
        );
1590
1591
        if ($resultData === false) {
1592
            return false;
1593
        }
1594
1595
        return $resultData['count'] > 0;
1596
    }
1597
1598
    /**
1599
     * Checks whether a given username matches to the specification strictly.
1600
     * The empty username is assumed here as invalid.
1601
     * Mostly this function is to be used in the user interface built-in validation routines
1602
     * for providing feedback while usernames are enterd manually.
1603
     *
1604
     * @param string $username the input username
1605
     *
1606
     * @return bool returns TRUE if the username is valid, FALSE otherwise
1607
     */
1608
    public static function is_username_valid($username)
1609
    {
1610
        return !empty($username) && $username == self::purify_username($username, true);
1611
    }
1612
1613
    /**
1614
     * Checks whether a username is empty. If the username contains whitespace characters,
1615
     * such as spaces, tabulators, newlines, etc.,
1616
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
1617
     *
1618
     * @param string $username the given username
1619
     *
1620
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1621
     */
1622
    public static function is_username_empty($username)
1623
    {
1624
        return strlen(self::purify_username($username, false)) == 0;
1625
    }
1626
1627
    /**
1628
     * Checks whether a username is too long or not.
1629
     *
1630
     * @param string $username the given username, it should contain only ASCII-letters and digits
1631
     *
1632
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1633
     */
1634
    public static function is_username_too_long($username)
1635
    {
1636
        return strlen($username) > USERNAME_MAX_LENGTH;
1637
    }
1638
1639
    /**
1640
     * Get the users by ID.
1641
     *
1642
     * @param array  $ids    student ids
1643
     * @param string $active
1644
     * @param string $order
1645
     * @param string $limit
1646
     *
1647
     * @return array $result student information
1648
     */
1649
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
1650
    {
1651
        if (empty($ids)) {
1652
            return [];
1653
        }
1654
1655
        $ids = is_array($ids) ? $ids : [$ids];
1656
        $ids = array_map('intval', $ids);
1657
        $ids = implode(',', $ids);
1658
1659
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
1660
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
1661
        if (!is_null($active)) {
1662
            $sql .= ' AND active='.($active ? '1' : '0');
1663
        }
1664
1665
        if (!is_null($order)) {
1666
            $order = Database::escape_string($order);
1667
            $sql .= ' ORDER BY '.$order;
1668
        }
1669
1670
        if (!is_null($limit)) {
1671
            $limit = Database::escape_string($limit);
1672
            $sql .= ' LIMIT '.$limit;
1673
        }
1674
1675
        $rs = Database::query($sql);
1676
        $result = [];
1677
        while ($row = Database::fetch_array($rs)) {
1678
            $result[] = $row;
1679
        }
1680
1681
        return $result;
1682
    }
1683
1684
    /**
1685
     * Get a list of users of which the given conditions match with an = 'cond'.
1686
     *
1687
     * @param array $conditions a list of condition (example : status=>STUDENT)
1688
     * @param array $order_by   a list of fields on which sort
1689
     *
1690
     * @return array an array with all users of the platform
1691
     *
1692
     * @todo security filter order by
1693
     */
1694
    public static function get_user_list(
1695
        $conditions = [],
1696
        $order_by = [],
1697
        $limit_from = false,
1698
        $limit_to = false,
1699
        $idCampus = null
1700
    ) {
1701
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1702
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1703
        $return_array = [];
1704
        $sql = "SELECT user.* FROM $user_table user ";
1705
1706
        if (api_is_multiple_url_enabled()) {
1707
            if ($idCampus) {
1708
                $urlId = $idCampus;
1709
            } else {
1710
                $urlId = api_get_current_access_url_id();
1711
            }
1712
            $sql .= " INNER JOIN $userUrlTable url_user
1713
                      ON (user.user_id = url_user.user_id)
1714
                      WHERE url_user.access_url_id = $urlId";
1715
        } else {
1716
            $sql .= " WHERE 1=1 ";
1717
        }
1718
1719
        if (count($conditions) > 0) {
1720
            foreach ($conditions as $field => $value) {
1721
                $field = Database::escape_string($field);
1722
                $value = Database::escape_string($value);
1723
                $sql .= " AND $field = '$value'";
1724
            }
1725
        }
1726
1727
        if (count($order_by) > 0) {
1728
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1729
        }
1730
1731
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1732
            $limit_from = (int) $limit_from;
1733
            $limit_to = (int) $limit_to;
1734
            $sql .= " LIMIT $limit_from, $limit_to";
1735
        }
1736
        $sql_result = Database::query($sql);
1737
        while ($result = Database::fetch_array($sql_result)) {
1738
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1739
            $return_array[] = $result;
1740
        }
1741
1742
        return $return_array;
1743
    }
1744
1745
    public static function getUserListExtraConditions(
1746
        $conditions = [],
1747
        $order_by = [],
1748
        $limit_from = false,
1749
        $limit_to = false,
1750
        $idCampus = null,
1751
        $extraConditions = '',
1752
        $getCount = false
1753
    ) {
1754
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1755
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1756
        $return_array = [];
1757
        $sql = "SELECT user.* FROM $user_table user ";
1758
1759
        if ($getCount) {
1760
            $sql = "SELECT count(user.id) count FROM $user_table user ";
1761
        }
1762
1763
        if (api_is_multiple_url_enabled()) {
1764
            if ($idCampus) {
1765
                $urlId = $idCampus;
1766
            } else {
1767
                $urlId = api_get_current_access_url_id();
1768
            }
1769
            $sql .= " INNER JOIN $userUrlTable url_user
1770
                      ON (user.user_id = url_user.user_id)
1771
                      WHERE url_user.access_url_id = $urlId";
1772
        } else {
1773
            $sql .= " WHERE 1=1 ";
1774
        }
1775
1776
        $sql .= " AND status <> ".ANONYMOUS." ";
1777
1778
        if (count($conditions) > 0) {
1779
            foreach ($conditions as $field => $value) {
1780
                $field = Database::escape_string($field);
1781
                $value = Database::escape_string($value);
1782
                $sql .= " AND $field = '$value'";
1783
            }
1784
        }
1785
1786
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
1787
1788
        if (!empty($order_by) && count($order_by) > 0) {
1789
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1790
        }
1791
1792
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1793
            $limit_from = (int) $limit_from;
1794
            $limit_to = (int) $limit_to;
1795
            $sql .= " LIMIT $limit_from, $limit_to";
1796
        }
1797
1798
        $sql_result = Database::query($sql);
1799
1800
        if ($getCount) {
1801
1802
            $result = Database::fetch_array($sql_result);
1803
1804
            return $result['count'];
1805
        }
1806
1807
        while ($result = Database::fetch_array($sql_result)) {
1808
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1809
            $return_array[] = $result;
1810
        }
1811
1812
        return $return_array;
1813
    }
1814
1815
1816
    /**
1817
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
1818
     *
1819
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
1820
     * @param array  $order_by         a list of fields on which sort
1821
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
1822
     * @param string $condition        Whether we want the filters to be combined by AND or OR
1823
     * @param array  $onlyThisUserList
1824
     *
1825
     * @return array an array with all users of the platform
1826
     *
1827
     * @todo optional course code parameter, optional sorting parameters...
1828
     * @todo security filter order_by
1829
     */
1830
    public static function getUserListLike(
1831
        $conditions = [],
1832
        $order_by = [],
1833
        $simple_like = false,
1834
        $condition = 'AND',
1835
        $onlyThisUserList = []
1836
    ) {
1837
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1838
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1839
        $return_array = [];
1840
        $sql_query = "SELECT user.id FROM $user_table user ";
1841
1842
        if (api_is_multiple_url_enabled()) {
1843
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
1844
        }
1845
1846
        $sql_query .= ' WHERE 1 = 1 ';
1847
        if (count($conditions) > 0) {
1848
            $temp_conditions = [];
1849
            foreach ($conditions as $field => $value) {
1850
                $field = Database::escape_string($field);
1851
                $value = Database::escape_string($value);
1852
                if ($simple_like) {
1853
                    $temp_conditions[] = $field." LIKE '$value%'";
1854
                } else {
1855
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
1856
                }
1857
            }
1858
            if (!empty($temp_conditions)) {
1859
                $sql_query .= ' AND '.implode(' '.$condition.' ', $temp_conditions);
1860
            }
1861
1862
            if (api_is_multiple_url_enabled()) {
1863
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1864
            }
1865
        } else {
1866
            if (api_is_multiple_url_enabled()) {
1867
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1868
            }
1869
        }
1870
1871
        if (!empty($onlyThisUserList)) {
1872
            $onlyThisUserListToString = implode("','", $onlyThisUserList);
1873
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
1874
        }
1875
1876
        if (count($order_by) > 0) {
1877
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1878
        }
1879
1880
        $sql_result = Database::query($sql_query);
1881
        while ($result = Database::fetch_array($sql_result)) {
1882
            $userInfo = api_get_user_info($result['id']);
1883
            $return_array[] = $userInfo;
1884
        }
1885
1886
        return $return_array;
1887
    }
1888
1889
    /**
1890
     * Get user picture URL or path from user ID (returns an array).
1891
     * The return format is a complete path, enabling recovery of the directory
1892
     * with dirname() or the file with basename(). This also works for the
1893
     * functions dealing with the user's productions, as they are located in
1894
     * the same directory.
1895
     *
1896
     * @param int    $id       User ID
1897
     * @param string $type     Type of path to return (can be 'system', 'web')
1898
     * @param array  $userInfo user information to avoid query the DB
1899
     *                         returns the /main/img/unknown.jpg image set it at true
1900
     *
1901
     * @return array Array of 2 elements: 'dir' and 'file' which contain
1902
     *               the dir and file as the name implies if image does not exist it will
1903
     *               return the unknow image if anonymous parameter is true if not it returns an empty array
1904
     */
1905
    public static function get_user_picture_path_by_id(
1906
        $id,
1907
        $type = 'web',
1908
        $userInfo = []
1909
    ) {
1910
        switch ($type) {
1911
            case 'system': // Base: absolute system path.
1912
                $base = api_get_path(SYS_CODE_PATH);
1913
                break;
1914
            case 'web': // Base: absolute web path.
1915
            default:
1916
                $base = api_get_path(WEB_CODE_PATH);
1917
                break;
1918
        }
1919
1920
        $anonymousPath = [
1921
            'dir' => $base.'img/',
1922
            'file' => 'unknown.jpg',
1923
            'email' => '',
1924
        ];
1925
1926
        if (empty($id) || empty($type)) {
1927
            return $anonymousPath;
1928
        }
1929
1930
        $id = (int) $id;
1931
        if (empty($userInfo)) {
1932
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
1933
            $sql = "SELECT email, picture_uri FROM $user_table
1934
                    WHERE id = ".$id;
1935
            $res = Database::query($sql);
1936
1937
            if (!Database::num_rows($res)) {
1938
                return $anonymousPath;
1939
            }
1940
            $user = Database::fetch_array($res);
1941
            if (empty($user['picture_uri'])) {
1942
                return $anonymousPath;
1943
            }
1944
        } else {
1945
            $user = $userInfo;
1946
        }
1947
1948
        $pictureFilename = trim($user['picture_uri']);
1949
1950
        $dir = self::getUserPathById($id, $type);
1951
1952
        return [
1953
            'dir' => $dir,
1954
            'file' => $pictureFilename,
1955
            'email' => $user['email'],
1956
        ];
1957
    }
1958
1959
    /**
1960
     * *** READ BEFORE REVIEW THIS FUNCTION ***
1961
     * This function is a exact copy from get_user_picture_path_by_id() and it was create it to avoid
1962
     * a recursive calls for get_user_picture_path_by_id() in another functions when you update a user picture
1963
     * in same script, so you can find this function usage in update_user_picture() function.
1964
     *
1965
     * @param int    $id       User ID
1966
     * @param string $type     Type of path to return (can be 'system', 'web')
1967
     * @param array  $userInfo user information to avoid query the DB
1968
     *                         returns the /main/img/unknown.jpg image set it at true
1969
     *
1970
     * @return array Array of 2 elements: 'dir' and 'file' which contain
1971
     *               the dir and file as the name implies if image does not exist it will
1972
     *               return the unknown image if anonymous parameter is true if not it returns an empty array
1973
     */
1974
    public static function getUserPicturePathById($id, $type = 'web', $userInfo = [])
1975
    {
1976
        switch ($type) {
1977
            case 'system': // Base: absolute system path.
1978
                $base = api_get_path(SYS_CODE_PATH);
1979
                break;
1980
            case 'web': // Base: absolute web path.
1981
            default:
1982
                $base = api_get_path(WEB_CODE_PATH);
1983
                break;
1984
        }
1985
1986
        $anonymousPath = [
1987
            'dir' => $base.'img/',
1988
            'file' => 'unknown.jpg',
1989
            'email' => '',
1990
        ];
1991
1992
        if (empty($id) || empty($type)) {
1993
            return $anonymousPath;
1994
        }
1995
1996
        $id = (int) $id;
1997
        if (empty($userInfo)) {
1998
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
1999
            $sql = "SELECT email, picture_uri FROM $user_table WHERE id = $id";
2000
            $res = Database::query($sql);
2001
2002
            if (!Database::num_rows($res)) {
2003
                return $anonymousPath;
2004
            }
2005
            $user = Database::fetch_array($res);
2006
2007
            if (empty($user['picture_uri'])) {
2008
                return $anonymousPath;
2009
            }
2010
        } else {
2011
            $user = $userInfo;
2012
        }
2013
2014
        $pictureFilename = trim($user['picture_uri']);
2015
        $dir = self::getUserPathById($id, $type);
2016
2017
        return [
2018
            'dir' => $dir,
2019
            'file' => $pictureFilename,
2020
            'email' => $user['email'],
2021
        ];
2022
    }
2023
2024
    /**
2025
     * Get user path from user ID (returns an array).
2026
     * The return format is a complete path to a folder ending with "/"
2027
     * In case the first level of subdirectory of users/ does not exist, the
2028
     * function will attempt to create it. Probably not the right place to do it
2029
     * but at least it avoids headaches in many other places.
2030
     *
2031
     * @param int    $id   User ID
2032
     * @param string $type Type of path to return (can be 'system', 'web', 'last')
2033
     *
2034
     * @return string User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
2035
     */
2036
    public static function getUserPathById($id, $type)
2037
    {
2038
        $id = (int) $id;
2039
        if (!$id) {
2040
            return null;
2041
        }
2042
2043
        $userPath = "users/$id/";
2044
        if (api_get_setting('split_users_upload_directory') === 'true') {
2045
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
2046
            // In exceptional cases, on some portals, the intermediate base user
2047
            // directory might not have been created. Make sure it is before
2048
            // going further.
2049
2050
            $rootPath = api_get_path(SYS_UPLOAD_PATH).'users/'.substr((string) $id, 0, 1);
2051
            if (!is_dir($rootPath)) {
2052
                $perm = api_get_permissions_for_new_directories();
2053
                try {
2054
                    mkdir($rootPath, $perm);
2055
                } catch (Exception $e) {
2056
                    error_log($e->getMessage());
2057
                }
2058
            }
2059
        }
2060
        switch ($type) {
2061
            case 'system': // Base: absolute system path.
2062
                $userPath = api_get_path(SYS_UPLOAD_PATH).$userPath;
2063
                break;
2064
            case 'web': // Base: absolute web path.
2065
                $userPath = api_get_path(WEB_UPLOAD_PATH).$userPath;
2066
                break;
2067
            case 'last': // Only the last part starting with users/
2068
                break;
2069
        }
2070
2071
        return $userPath;
2072
    }
2073
2074
    /**
2075
     * Gets the current user image.
2076
     *
2077
     * @param string $user_id
2078
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
2079
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
2080
     * @param bool   $addRandomId
2081
     * @param array  $userInfo    to avoid query the DB
2082
     *
2083
     * @return string
2084
     */
2085
    public static function getUserPicture(
2086
        $user_id,
2087
        $size = USER_IMAGE_SIZE_MEDIUM,
2088
        $addRandomId = true,
2089
        $userInfo = []
2090
    ) {
2091
        // Make sure userInfo is defined. Otherwise, define it!
2092
        if (empty($userInfo) || !is_array($userInfo) || count($userInfo) == 0) {
2093
            if (empty($user_id)) {
2094
                return '';
2095
            } else {
2096
                $userInfo = api_get_user_info($user_id);
2097
            }
2098
        }
2099
2100
        $imageWebPath = self::get_user_picture_path_by_id(
2101
            $user_id,
2102
            'web',
2103
            $userInfo
0 ignored issues
show
Bug introduced by
It seems like $userInfo can also be of type false; however, parameter $userInfo of UserManager::get_user_picture_path_by_id() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

2103
            /** @scrutinizer ignore-type */ $userInfo
Loading history...
2104
        );
2105
        $pictureWebFile = $imageWebPath['file'];
2106
        $pictureWebDir = $imageWebPath['dir'];
2107
2108
        $pictureAnonymousSize = '128';
2109
        $gravatarSize = 22;
2110
        $realSizeName = 'small_';
2111
2112
        switch ($size) {
2113
            case USER_IMAGE_SIZE_SMALL:
2114
                $pictureAnonymousSize = '32';
2115
                $realSizeName = 'small_';
2116
                $gravatarSize = 32;
2117
                break;
2118
            case USER_IMAGE_SIZE_MEDIUM:
2119
                $pictureAnonymousSize = '64';
2120
                $realSizeName = 'medium_';
2121
                $gravatarSize = 64;
2122
                break;
2123
            case USER_IMAGE_SIZE_ORIGINAL:
2124
                $pictureAnonymousSize = '128';
2125
                $realSizeName = '';
2126
                $gravatarSize = 128;
2127
                break;
2128
            case USER_IMAGE_SIZE_BIG:
2129
                $pictureAnonymousSize = '128';
2130
                $realSizeName = 'big_';
2131
                $gravatarSize = 128;
2132
                break;
2133
        }
2134
2135
        $gravatarEnabled = api_get_setting('gravatar_enabled');
2136
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
2137
        if ($pictureWebFile == 'unknown.jpg' || empty($pictureWebFile)) {
2138
            if ($gravatarEnabled === 'true') {
2139
                $file = self::getGravatar(
2140
                    $imageWebPath['email'],
2141
                    $gravatarSize,
2142
                    api_get_setting('gravatar_type')
2143
                );
2144
2145
                if ($addRandomId) {
2146
                    $file .= '&rand='.uniqid();
2147
                }
2148
2149
                return $file;
2150
            }
2151
2152
            return $anonymousPath;
2153
        }
2154
2155
        $pictureSysPath = self::get_user_picture_path_by_id($user_id, 'system');
2156
        $file = $pictureSysPath['dir'].$realSizeName.$pictureWebFile;
2157
        $picture = '';
2158
        if (file_exists($file)) {
2159
            $picture = $pictureWebDir.$realSizeName.$pictureWebFile;
2160
        } else {
2161
            $file = $pictureSysPath['dir'].$pictureWebFile;
2162
            if (file_exists($file) && !is_dir($file)) {
2163
                $picture = $pictureWebFile['dir'].$pictureWebFile;
2164
            }
2165
        }
2166
2167
        if (empty($picture)) {
2168
            return $anonymousPath;
2169
        }
2170
2171
        if ($addRandomId) {
2172
            $picture .= '?rand='.uniqid();
2173
        }
2174
2175
        return $picture;
2176
    }
2177
2178
    /**
2179
     * Creates new user photos in various sizes of a user, or deletes user photos.
2180
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2181
     *
2182
     * @param int    $user_id the user internal identification number
2183
     * @param string $file    The common file name for the newly created photos.
2184
     *                        It will be checked and modified for compatibility with the file system.
2185
     *                        If full name is provided, path component is ignored.
2186
     *                        If an empty name is provided, then old user photos are deleted only,
2187
     *
2188
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
2189
     *
2190
     * @param string $source_file    the full system name of the image from which user photos will be created
2191
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
2192
     *
2193
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
2194
     *              When deletion is requested returns empty string.
2195
     *              In case of internal error or negative validation returns FALSE.
2196
     */
2197
    public static function update_user_picture(
2198
        $user_id,
2199
        $file = null,
2200
        $source_file = null,
2201
        $cropParameters = ''
2202
    ) {
2203
        if (empty($user_id)) {
2204
            return false;
2205
        }
2206
        $delete = empty($file);
2207
        if (empty($source_file)) {
2208
            $source_file = $file;
2209
        }
2210
2211
        // User-reserved directory where photos have to be placed.
2212
        $path_info = self::getUserPicturePathById($user_id, 'system');
2213
        $path = $path_info['dir'];
2214
2215
        // If this directory does not exist - we create it.
2216
        if (!file_exists($path)) {
2217
            mkdir($path, api_get_permissions_for_new_directories(), true);
2218
        }
2219
2220
        // The old photos (if any).
2221
        $old_file = $path_info['file'];
2222
2223
        // Let us delete them.
2224
        if ($old_file != 'unknown.jpg') {
2225
            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...
2226
                $prefix = 'saved_'.date('Y_m_d_H_i_s').'_'.uniqid('').'_';
2227
                @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

2227
                /** @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...
2228
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
2229
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
2230
                @rename($path.$old_file, $path.$prefix.$old_file);
2231
            } else {
2232
                @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

2232
                /** @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...
2233
                @unlink($path.'medium_'.$old_file);
2234
                @unlink($path.'big_'.$old_file);
2235
                @unlink($path.$old_file);
2236
            }
2237
        }
2238
2239
        // Exit if only deletion has been requested. Return an empty picture name.
2240
        if ($delete) {
2241
            return '';
0 ignored issues
show
Bug Best Practice introduced by
The expression return '' returns the type string which is incompatible with the documented return type boolean.
Loading history...
2242
        }
2243
2244
        // Validation 2.
2245
        $allowed_types = api_get_supported_image_extensions();
2246
        $file = str_replace('\\', '/', $file);
2247
        $filename = (($pos = strrpos($file, '/')) !== false) ? substr($file, $pos + 1) : $file;
2248
        $extension = strtolower(substr(strrchr($filename, '.'), 1));
2249
        if (!in_array($extension, $allowed_types)) {
2250
            return false;
2251
        }
2252
2253
        // This is the common name for the new photos.
2254
        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...
2255
            $old_extension = strtolower(substr(strrchr($old_file, '.'), 1));
2256
            $filename = in_array($old_extension, $allowed_types) ? substr($old_file, 0, -strlen($old_extension)) : $old_file;
2257
            $filename = (substr($filename, -1) == '.') ? $filename.$extension : $filename.'.'.$extension;
2258
        } else {
2259
            $filename = api_replace_dangerous_char($filename);
2260
            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...
2261
                $filename = uniqid('').'_'.$filename;
2262
            }
2263
            // We always prefix user photos with user ids, so on setting
2264
            // api_get_setting('split_users_upload_directory') === 'true'
2265
            // the correspondent directories to be found successfully.
2266
            $filename = $user_id.'_'.$filename;
2267
        }
2268
2269
        //Crop the image to adjust 1:1 ratio
2270
        $image = new Image($source_file);
2271
        $image->crop($cropParameters);
2272
2273
        // Storing the new photos in 4 versions with various sizes.
2274
        $userPath = self::getUserPathById($user_id, 'system');
2275
2276
        // If this path does not exist - we create it.
2277
        if (!file_exists($userPath)) {
2278
            mkdir($userPath, api_get_permissions_for_new_directories(), true);
2279
        }
2280
        $small = new Image($source_file);
2281
        $small->resize(32);
2282
        $small->send_image($userPath.'small_'.$filename);
2283
        $medium = new Image($source_file);
2284
        $medium->resize(85);
2285
        $medium->send_image($userPath.'medium_'.$filename);
2286
        $normal = new Image($source_file);
2287
        $normal->resize(200);
2288
        $normal->send_image($userPath.$filename);
2289
2290
        $big = new Image($source_file); // This is the original picture.
2291
        $big->send_image($userPath.'big_'.$filename);
2292
2293
        $result = $small && $medium && $normal && $big;
0 ignored issues
show
introduced by
$normal is of type Image, thus it always evaluated to true.
Loading history...
introduced by
$medium is of type Image, thus it always evaluated to true.
Loading history...
introduced by
$big is of type Image, thus it always evaluated to true.
Loading history...
2294
2295
        return $result ? $filename : false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result ? $filename : false returns the type string which is incompatible with the documented return type boolean.
Loading history...
2296
    }
2297
2298
    /**
2299
     * Update User extra field file type into {user_folder}/{$extra_field}.
2300
     *
2301
     * @param int    $user_id     The user internal identification number
2302
     * @param string $extra_field The $extra_field The extra field name
2303
     * @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...
2304
     * @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...
2305
     *
2306
     * @return bool|null return filename if success, but false
2307
     */
2308
    public static function update_user_extra_file(
2309
        $user_id,
2310
        $extra_field = '',
2311
        $file = null,
2312
        $source_file = null
2313
    ) {
2314
        // Add Filter
2315
        $source_file = Security::filter_filename($source_file);
2316
        $file = Security::filter_filename($file);
2317
2318
        if (empty($user_id)) {
2319
            return false;
2320
        }
2321
2322
        if (empty($source_file)) {
2323
            $source_file = $file;
2324
        }
2325
2326
        // User-reserved directory where extra file have to be placed.
2327
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2328
        $path = $path_info['dir'];
2329
        if (!empty($extra_field)) {
2330
            $path .= $extra_field.'/';
2331
        }
2332
        // If this directory does not exist - we create it.
2333
        if (!file_exists($path)) {
2334
            @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

2334
            /** @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...
2335
        }
2336
2337
        if (filter_extension($file)) {
2338
            if (@move_uploaded_file($source_file, $path.$file)) {
2339
                if ($extra_field) {
2340
                    return $extra_field.'/'.$file;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extra_field . '/' . $file returns the type string which is incompatible with the documented return type boolean|null.
Loading history...
2341
                } else {
2342
                    return $file;
2343
                }
2344
            }
2345
        }
2346
2347
        return false; // this should be returned if anything went wrong with the upload
2348
    }
2349
2350
    /**
2351
     * Deletes user photos.
2352
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2353
     *
2354
     * @param int $userId the user internal identification number
2355
     *
2356
     * @return mixed returns empty string on success, FALSE on error
2357
     */
2358
    public static function deleteUserPicture($userId)
2359
    {
2360
        return self::update_user_picture($userId);
2361
    }
2362
2363
    /**
2364
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
2365
     * doesn't have any.
2366
     *
2367
     * If there has been a request to remove a production, the function will return
2368
     * without building the list unless forced to do so by the optional second
2369
     * parameter. This increases performance by avoiding to read through the
2370
     * productions on the filesystem before the removal request has been carried
2371
     * out because they'll have to be re-read afterwards anyway.
2372
     *
2373
     * @param int  $user_id    User id
2374
     * @param bool $force      Optional parameter to force building after a removal request
2375
     * @param bool $showDelete
2376
     *
2377
     * @return string A string containing the XHTML code to display the production list, or FALSE
2378
     */
2379
    public static function build_production_list($user_id, $force = false, $showDelete = false)
2380
    {
2381
        if (!$force && !empty($_POST['remove_production'])) {
2382
            return true; // postpone reading from the filesystem
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type string.
Loading history...
2383
        }
2384
2385
        $productions = self::get_user_productions($user_id);
2386
2387
        if (empty($productions)) {
2388
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
2389
        }
2390
2391
        $production_dir = self::getUserPathById($user_id, 'web');
2392
        $del_image = Display::returnIconPath('delete.png');
2393
        $add_image = Display::returnIconPath('archive.png');
2394
        $del_text = get_lang('Delete');
2395
        $production_list = '';
2396
        if (count($productions) > 0) {
2397
            $production_list = '<div class="files-production"><ul id="productions">';
2398
            foreach ($productions as $file) {
2399
                $production_list .= '<li>
2400
                    <img src="'.$add_image.'" />
2401
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2402
                        '.htmlentities($file).'
2403
                    </a>';
2404
                if ($showDelete) {
2405
                    $production_list .= '&nbsp;&nbsp;
2406
                        <input
2407
                            style="width:16px;"
2408
                            type="image"
2409
                            name="remove_production['.urlencode($file).']"
2410
                            src="'.$del_image.'"
2411
                            alt="'.$del_text.'"
2412
                            title="'.$del_text.' '.htmlentities($file).'"
2413
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2414
                }
2415
            }
2416
            $production_list .= '</ul></div>';
2417
        }
2418
2419
        return $production_list;
2420
    }
2421
2422
    /**
2423
     * Returns an array with the user's productions.
2424
     *
2425
     * @param int $user_id User id
2426
     *
2427
     * @return array An array containing the user's productions
2428
     */
2429
    public static function get_user_productions($user_id)
2430
    {
2431
        $production_repository = self::getUserPathById($user_id, 'system');
2432
        $productions = [];
2433
2434
        if (is_dir($production_repository)) {
2435
            $handle = opendir($production_repository);
2436
            while ($file = readdir($handle)) {
0 ignored issues
show
Bug introduced by
It seems like $handle can also be of type false; however, parameter $dir_handle of readdir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

2436
            while ($file = readdir(/** @scrutinizer ignore-type */ $handle)) {
Loading history...
2437
                if ($file == '.' ||
2438
                    $file == '..' ||
2439
                    $file == '.htaccess' ||
2440
                    is_dir($production_repository.$file)
2441
                ) {
2442
                    // skip current/parent directory and .htaccess
2443
                    continue;
2444
                }
2445
2446
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2447
                    // User's photos should not be listed as productions.
2448
                    continue;
2449
                }
2450
                $productions[] = $file;
2451
            }
2452
        }
2453
2454
        return $productions;
2455
    }
2456
2457
    /**
2458
     * Remove a user production.
2459
     *
2460
     * @param int    $user_id    User id
2461
     * @param string $production The production to remove
2462
     *
2463
     * @return bool
2464
     */
2465
    public static function remove_user_production($user_id, $production)
2466
    {
2467
        $production_path = self::get_user_picture_path_by_id($user_id, 'system');
2468
        $production_file = $production_path['dir'].$production;
2469
        if (is_file($production_file)) {
2470
            unlink($production_file);
2471
2472
            return true;
2473
        }
2474
2475
        return false;
2476
    }
2477
2478
    /**
2479
     * Update an extra field value for a given user.
2480
     *
2481
     * @param int    $userId   User ID
2482
     * @param string $variable Field variable name
2483
     * @param string $value    Field value
2484
     *
2485
     * @return bool true if field updated, false otherwise
2486
     */
2487
    public static function update_extra_field_value($userId, $variable, $value = '')
2488
    {
2489
        $extraFieldValue = new ExtraFieldValue('user');
2490
        $params = [
2491
            'item_id' => $userId,
2492
            'variable' => $variable,
2493
            'value' => $value,
2494
        ];
2495
2496
        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...
2497
    }
2498
2499
    /**
2500
     * Get an array of extra fields with field details (type, default value and options).
2501
     *
2502
     * @param    int    Offset (from which row)
2503
     * @param    int    Number of items
2504
     * @param    int    Column on which sorting is made
2505
     * @param    string    Sorting direction
2506
     * @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...
2507
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2508
     *
2509
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2510
     */
2511
    public static function get_extra_fields(
2512
        $from = 0,
2513
        $number_of_items = 0,
2514
        $column = 5,
2515
        $direction = 'ASC',
2516
        $all_visibility = true,
2517
        $field_filter = null
2518
    ) {
2519
        $fields = [];
2520
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2521
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2522
        $columns = [
2523
            'id',
2524
            'variable',
2525
            'field_type',
2526
            'display_text',
2527
            'default_value',
2528
            'field_order',
2529
            'filter',
2530
        ];
2531
        $column = (int) $column;
2532
        $sort_direction = '';
2533
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2534
            $sort_direction = strtoupper($direction);
2535
        }
2536
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2537
        $sqlf = "SELECT * FROM $t_uf WHERE extra_field_type = $extraFieldType ";
2538
        if (!$all_visibility) {
2539
            $sqlf .= " AND visible_to_self = 1 ";
2540
        }
2541
        if (!is_null($field_filter)) {
2542
            $field_filter = (int) $field_filter;
2543
            $sqlf .= " AND filter = $field_filter ";
2544
        }
2545
        $sqlf .= " ORDER BY ".$columns[$column]." $sort_direction ";
2546
        if ($number_of_items != 0) {
2547
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2548
        }
2549
        $resf = Database::query($sqlf);
2550
        if (Database::num_rows($resf) > 0) {
2551
            while ($rowf = Database::fetch_array($resf)) {
2552
                $fields[$rowf['id']] = [
2553
                    0 => $rowf['id'],
2554
                    1 => $rowf['variable'],
2555
                    2 => $rowf['field_type'],
2556
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2557
                    4 => $rowf['default_value'],
2558
                    5 => $rowf['field_order'],
2559
                    6 => $rowf['visible_to_self'],
2560
                    7 => $rowf['changeable'],
2561
                    8 => $rowf['filter'],
2562
                    9 => [],
2563
                    10 => '<a name="'.$rowf['id'].'"></a>',
2564
                ];
2565
2566
                $sqlo = "SELECT * FROM $t_ufo
2567
                         WHERE field_id = ".$rowf['id']."
2568
                         ORDER BY option_order ASC";
2569
                $reso = Database::query($sqlo);
2570
                if (Database::num_rows($reso) > 0) {
2571
                    while ($rowo = Database::fetch_array($reso)) {
2572
                        $fields[$rowf['id']][9][$rowo['id']] = [
2573
                            0 => $rowo['id'],
2574
                            1 => $rowo['option_value'],
2575
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2576
                            3 => $rowo['option_order'],
2577
                        ];
2578
                    }
2579
                }
2580
            }
2581
        }
2582
2583
        return $fields;
2584
    }
2585
2586
    /**
2587
     * Build a list of extra file already uploaded in $user_folder/{$extra_field}/.
2588
     *
2589
     * @param $user_id
2590
     * @param $extra_field
2591
     * @param bool $force
2592
     * @param bool $showDelete
2593
     *
2594
     * @return bool|string
2595
     */
2596
    public static function build_user_extra_file_list(
2597
        $user_id,
2598
        $extra_field,
2599
        $force = false,
2600
        $showDelete = false
2601
    ) {
2602
        if (!$force && !empty($_POST['remove_'.$extra_field])) {
2603
            return true; // postpone reading from the filesystem
2604
        }
2605
2606
        $extra_files = self::get_user_extra_files($user_id, $extra_field);
2607
        if (empty($extra_files)) {
2608
            return false;
2609
        }
2610
2611
        $path_info = self::get_user_picture_path_by_id($user_id, 'web');
2612
        $path = $path_info['dir'];
2613
        $del_image = Display::returnIconPath('delete.png');
2614
2615
        $del_text = get_lang('Delete');
2616
        $extra_file_list = '';
2617
        if (count($extra_files) > 0) {
2618
            $extra_file_list = '<div class="files-production"><ul id="productions">';
2619
            foreach ($extra_files as $file) {
2620
                $filename = substr($file, strlen($extra_field) + 1);
2621
                $extra_file_list .= '<li>'.Display::return_icon('archive.png').
2622
                    '<a href="'.$path.$extra_field.'/'.urlencode($filename).'" target="_blank">
2623
                        '.htmlentities($filename).
2624
                    '</a> ';
2625
                if ($showDelete) {
2626
                    $extra_file_list .= '<input
2627
                        style="width:16px;"
2628
                        type="image"
2629
                        name="remove_extra_'.$extra_field.'['.urlencode($file).']"
2630
                        src="'.$del_image.'"
2631
                        alt="'.$del_text.'"
2632
                        title="'.$del_text.' '.htmlentities($filename).'"
2633
                        onclick="javascript: return confirmation(\''.htmlentities($filename).'\');" /></li>';
2634
                }
2635
            }
2636
            $extra_file_list .= '</ul></div>';
2637
        }
2638
2639
        return $extra_file_list;
2640
    }
2641
2642
    /**
2643
     * Get valid filenames in $user_folder/{$extra_field}/.
2644
     *
2645
     * @param $user_id
2646
     * @param $extra_field
2647
     * @param bool $full_path
2648
     *
2649
     * @return array
2650
     */
2651
    public static function get_user_extra_files($user_id, $extra_field, $full_path = false)
2652
    {
2653
        if (!$full_path) {
2654
            // Nothing to do
2655
        } else {
2656
            $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2657
            $path = $path_info['dir'];
2658
        }
2659
        $extra_data = self::get_extra_user_data_by_field($user_id, $extra_field);
2660
        $extra_files = $extra_data[$extra_field];
2661
2662
        $files = [];
2663
        if (is_array($extra_files)) {
2664
            foreach ($extra_files as $key => $value) {
2665
                if (!$full_path) {
2666
                    // Relative path from user folder
2667
                    $files[] = $value;
2668
                } else {
2669
                    $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...
2670
                }
2671
            }
2672
        } elseif (!empty($extra_files)) {
2673
            if (!$full_path) {
2674
                // Relative path from user folder
2675
                $files[] = $extra_files;
2676
            } else {
2677
                $files[] = $path.$extra_files;
2678
            }
2679
        }
2680
2681
        return $files; // can be an empty array
2682
    }
2683
2684
    /**
2685
     * Remove an {$extra_file} from the user folder $user_folder/{$extra_field}/.
2686
     *
2687
     * @param int    $user_id
2688
     * @param string $extra_field
2689
     * @param string $extra_file
2690
     *
2691
     * @return bool
2692
     */
2693
    public static function remove_user_extra_file($user_id, $extra_field, $extra_file)
2694
    {
2695
        $extra_file = Security::filter_filename($extra_file);
2696
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2697
        if (strpos($extra_file, $extra_field) !== false) {
2698
            $path_extra_file = $path_info['dir'].$extra_file;
2699
        } else {
2700
            $path_extra_file = $path_info['dir'].$extra_field.'/'.$extra_file;
2701
        }
2702
        if (is_file($path_extra_file)) {
2703
            unlink($path_extra_file);
2704
2705
            return true;
2706
        }
2707
2708
        return false;
2709
    }
2710
2711
    /**
2712
     * Creates a new extra field.
2713
     *
2714
     * @param string $variable    Field's internal variable name
2715
     * @param int    $fieldType   Field's type
2716
     * @param string $displayText Field's language var name
2717
     * @param string $default     Field's default value
2718
     *
2719
     * @return int
2720
     */
2721
    public static function create_extra_field(
2722
        $variable,
2723
        $fieldType,
2724
        $displayText,
2725
        $default
2726
    ) {
2727
        $extraField = new ExtraField('user');
2728
        $params = [
2729
            'variable' => $variable,
2730
            'field_type' => $fieldType,
2731
            'display_text' => $displayText,
2732
            'default_value' => $default,
2733
        ];
2734
2735
        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...
2736
    }
2737
2738
    /**
2739
     * Check if a field is available.
2740
     *
2741
     * @param string $variable
2742
     *
2743
     * @return bool
2744
     */
2745
    public static function is_extra_field_available($variable)
2746
    {
2747
        $extraField = new ExtraField('user');
2748
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2749
2750
        return !empty($data) ? true : false;
2751
    }
2752
2753
    /**
2754
     * Gets user extra fields data.
2755
     *
2756
     * @param    int    User ID
2757
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2758
     * @param    bool    Whether to return invisible fields as well
2759
     * @param    bool    Whether to split multiple-selection fields or not
2760
     *
2761
     * @return array Array of fields => value for the given user
2762
     */
2763
    public static function get_extra_user_data(
2764
        $user_id,
2765
        $prefix = false,
2766
        $allVisibility = true,
2767
        $splitMultiple = false,
0 ignored issues
show
Unused Code introduced by
The parameter $splitMultiple is not used and could be removed. ( Ignorable by Annotation )

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

2767
        /** @scrutinizer ignore-unused */ $splitMultiple = false,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2768
        $fieldFilter = null
2769
    ) {
2770
        $user_id = (int) $user_id;
2771
2772
        if (empty($user_id)) {
2773
            return [];
2774
        }
2775
2776
        $extra_data = [];
2777
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2778
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2779
        $user_id = (int) $user_id;
2780
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2781
                FROM $t_uf f
2782
                WHERE
2783
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
2784
                ";
2785
        $filter_cond = '';
2786
2787
        if (!$allVisibility) {
2788
            if (isset($fieldFilter)) {
2789
                $fieldFilter = (int) $fieldFilter;
2790
                $filter_cond .= " AND filter = $fieldFilter ";
2791
            }
2792
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2793
        } else {
2794
            if (isset($fieldFilter)) {
2795
                $fieldFilter = (int) $fieldFilter;
2796
                $sql .= " AND filter = $fieldFilter ";
2797
            }
2798
        }
2799
2800
        $sql .= ' ORDER BY f.field_order';
2801
2802
        $res = Database::query($sql);
2803
        if (Database::num_rows($res) > 0) {
2804
            while ($row = Database::fetch_array($res)) {
2805
                if ($row['type'] == self::USER_FIELD_TYPE_TAG) {
2806
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2807
                    $extra_data['extra_'.$row['fvar']] = $tags;
2808
                } else {
2809
                    $sqlu = "SELECT value as fval
2810
                            FROM $t_ufv
2811
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2812
                    $resu = Database::query($sqlu);
2813
                    // get default value
2814
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2815
                               WHERE id=".$row['id'];
2816
                    $res_df = Database::query($sql_df);
2817
2818
                    if (Database::num_rows($resu) > 0) {
2819
                        $rowu = Database::fetch_array($resu);
2820
                        $fval = $rowu['fval'];
2821
                        if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
2822
                            $fval = explode(';', $rowu['fval']);
2823
                        }
2824
                    } else {
2825
                        $row_df = Database::fetch_array($res_df);
2826
                        $fval = $row_df['fval_df'];
2827
                    }
2828
                    // We get here (and fill the $extra_data array) even if there
2829
                    // is no user with data (we fill it with default values)
2830
                    if ($prefix) {
2831
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
2832
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2833
                        } else {
2834
                            $extra_data['extra_'.$row['fvar']] = $fval;
2835
                        }
2836
                    } else {
2837
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
2838
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2839
                        } else {
2840
                            $extra_data[$row['fvar']] = $fval;
2841
                        }
2842
                    }
2843
                }
2844
            }
2845
        }
2846
2847
        return $extra_data;
2848
    }
2849
2850
    /** Get extra user data by field
2851
     * @param int    user ID
2852
     * @param string the internal variable name of the field
2853
     *
2854
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2855
     */
2856
    public static function get_extra_user_data_by_field(
2857
        $user_id,
2858
        $field_variable,
2859
        $prefix = false,
2860
        $all_visibility = true,
2861
        $splitmultiple = false
0 ignored issues
show
Unused Code introduced by
The parameter $splitmultiple is not used and could be removed. ( Ignorable by Annotation )

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

2861
        /** @scrutinizer ignore-unused */ $splitmultiple = false

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2862
    ) {
2863
        $user_id = (int) $user_id;
2864
2865
        if (empty($user_id)) {
2866
            return [];
2867
        }
2868
2869
        $extra_data = [];
2870
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2871
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2872
2873
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2874
                FROM $t_uf f
2875
                WHERE f.variable = '$field_variable' ";
2876
2877
        if (!$all_visibility) {
2878
            $sql .= " AND f.visible_to_self = 1 ";
2879
        }
2880
2881
        $sql .= " AND extra_field_type = ".EntityExtraField::USER_FIELD_TYPE;
2882
        $sql .= " ORDER BY f.field_order ";
2883
2884
        $res = Database::query($sql);
2885
        if (Database::num_rows($res) > 0) {
2886
            while ($row = Database::fetch_array($res)) {
2887
                $sqlu = "SELECT value as fval FROM $t_ufv v
2888
                         INNER JOIN $t_uf f
2889
                         ON (v.field_id = f.id)
2890
                         WHERE
2891
                            extra_field_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2892
                            field_id = ".$row['id']." AND
2893
                            item_id = ".$user_id;
2894
                $resu = Database::query($sqlu);
2895
                $fval = '';
2896
                if (Database::num_rows($resu) > 0) {
2897
                    $rowu = Database::fetch_array($resu);
2898
                    $fval = $rowu['fval'];
2899
                    if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
2900
                        $fval = explode(';', $rowu['fval']);
2901
                    }
2902
                }
2903
                if ($prefix) {
2904
                    $extra_data['extra_'.$row['fvar']] = $fval;
2905
                } else {
2906
                    $extra_data[$row['fvar']] = $fval;
2907
                }
2908
            }
2909
        }
2910
2911
        return $extra_data;
2912
    }
2913
2914
    /**
2915
     * Get the extra field information for a certain field (the options as well).
2916
     *
2917
     * @param int $variable The name of the field we want to know everything about
2918
     *
2919
     * @return array Array containing all the information about the extra profile field
2920
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2921
     *               as returned by the database)
2922
     *
2923
     * @author Julio Montoya
2924
     *
2925
     * @since v1.8.6
2926
     */
2927
    public static function get_extra_field_information_by_name($variable)
2928
    {
2929
        $extraField = new ExtraField('user');
2930
2931
        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...
2932
    }
2933
2934
    /**
2935
     * Get the extra field information for user tag (the options as well).
2936
     *
2937
     * @param int $variable The name of the field we want to know everything about
2938
     *
2939
     * @return array Array containing all the information about the extra profile field
2940
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2941
     *               as returned by the database)
2942
     *
2943
     * @author José Loguercio
2944
     *
2945
     * @since v1.11.0
2946
     */
2947
    public static function get_extra_field_tags_information_by_name($variable)
2948
    {
2949
        $extraField = new ExtraField('user');
2950
2951
        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...
2952
    }
2953
2954
    /**
2955
     * @param string $type
2956
     *
2957
     * @return array
2958
     */
2959
    public static function get_all_extra_field_by_type($type)
2960
    {
2961
        $extraField = new ExtraField('user');
2962
2963
        return $extraField->get_all_extra_field_by_type($type);
2964
    }
2965
2966
    /**
2967
     * Get all the extra field information of a certain field (also the options).
2968
     *
2969
     * @param int $fieldId the ID of the field we want to know everything of
2970
     *
2971
     * @return array $return containing all th information about the extra profile field
2972
     *
2973
     * @author Julio Montoya
2974
     *
2975
     * @deprecated
2976
     * @since v1.8.6
2977
     */
2978
    public static function get_extra_field_information($fieldId)
2979
    {
2980
        $extraField = new ExtraField('user');
2981
2982
        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...
2983
    }
2984
2985
    /**
2986
     * Get extra user data by value.
2987
     *
2988
     * @param string $variable       the internal variable name of the field
2989
     * @param string $value          the internal value of the field
2990
     * @param bool   $all_visibility
2991
     *
2992
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2993
     */
2994
    public static function get_extra_user_data_by_value($variable, $value, $all_visibility = true)
0 ignored issues
show
Unused Code introduced by
The parameter $all_visibility is not used and could be removed. ( Ignorable by Annotation )

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

2994
    public static function get_extra_user_data_by_value($variable, $value, /** @scrutinizer ignore-unused */ $all_visibility = true)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2995
    {
2996
        $extraFieldValue = new ExtraFieldValue('user');
2997
        $extraField = new ExtraField('user');
2998
2999
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
3000
3001
        if (false === $info) {
3002
            return [];
3003
        }
3004
3005
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
3006
            $variable,
3007
            $value,
3008
            false,
3009
            false,
3010
            true
3011
        );
3012
3013
        $result = [];
3014
        if (!empty($data)) {
3015
            foreach ($data as $item) {
3016
                $result[] = $item['item_id'];
3017
            }
3018
        }
3019
3020
        return $result;
3021
    }
3022
3023
    /**
3024
     * Get extra user data by tags value.
3025
     *
3026
     * @param int    $fieldId the ID of the field we want to know everything of
3027
     * @param string $tag     the tag name for search
3028
     *
3029
     * @return array with extra data info of a user
3030
     *
3031
     * @author José Loguercio
3032
     *
3033
     * @since v1.11.0
3034
     */
3035
    public static function get_extra_user_data_by_tags($fieldId, $tag)
3036
    {
3037
        $extraField = new ExtraField('user');
3038
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
3039
        $array = [];
3040
        foreach ($result as $index => $user) {
3041
            $array[] = $user['user_id'];
3042
        }
3043
3044
        return $array;
3045
    }
3046
3047
    /**
3048
     * Get extra user data by field variable.
3049
     *
3050
     * @param string $variable field variable
3051
     *
3052
     * @return array data
3053
     */
3054
    public static function get_extra_user_data_by_field_variable($variable)
3055
    {
3056
        $extraInfo = self::get_extra_field_information_by_name($variable);
3057
        $field_id = (int) $extraInfo['id'];
3058
3059
        $extraField = new ExtraFieldValue('user');
3060
        $data = $extraField->getValuesByFieldId($field_id);
3061
3062
        if (!empty($data)) {
3063
            foreach ($data as $row) {
3064
                $user_id = $row['item_id'];
3065
                $data[$user_id] = $row;
3066
            }
3067
        }
3068
3069
        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...
3070
    }
3071
3072
    /**
3073
     * Get extra user data tags by field variable.
3074
     *
3075
     * @param string $variable field variable
3076
     *
3077
     * @return array
3078
     */
3079
    public static function get_extra_user_data_for_tags($variable)
3080
    {
3081
        $data = self::get_extra_field_tags_information_by_name($variable);
3082
3083
        return $data;
3084
    }
3085
3086
    /**
3087
     * Gives a list of [session_category][session_id] for the current user.
3088
     *
3089
     * @param int  $user_id
3090
     * @param bool $is_time_over                 whether to fill the first element or not
3091
     *                                           (to give space for courses out of categories)
3092
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
3093
     * @param bool $ignoreTimeLimit              ignore time start/end
3094
     * @param bool $getCount
3095
     *
3096
     * @return array list of statuses [session_category][session_id]
3097
     *
3098
     * @todo ensure multiple access urls are managed correctly
3099
     */
3100
    public static function get_sessions_by_category(
3101
        $user_id,
3102
        $is_time_over = true,
3103
        $ignore_visibility_for_admins = false,
3104
        $ignoreTimeLimit = false,
3105
        $getCount = false
3106
    ) {
3107
        $user_id = (int) $user_id;
3108
3109
        if (empty($user_id)) {
3110
            return [];
3111
        }
3112
3113
        $allowOrder = api_get_configuration_value('session_list_order');
3114
        $position = '';
3115
        if ($allowOrder) {
3116
            $position = ', s.position AS position ';
3117
        }
3118
3119
        // Get the list of sessions per user
3120
        $now = new DateTime('now', new DateTimeZone('UTC'));
3121
3122
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
3123
        // join would not catch session-courses where the user is general
3124
        // session coach but which do not have students nor coaches registered
3125
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
3126
3127
        if (!$getCount) {
3128
            $dqlSelect = " DISTINCT
3129
                s.id,
3130
                s.name,
3131
                s.accessStartDate AS access_start_date,
3132
                s.accessEndDate AS access_end_date,
3133
                s.duration,
3134
                sc.id AS session_category_id,
3135
                sc.name AS session_category_name,
3136
                sc.dateStart AS session_category_date_start,
3137
                sc.dateEnd AS session_category_date_end,
3138
                s.coachAccessStartDate AS coach_access_start_date,
3139
                s.coachAccessEndDate AS coach_access_end_date,
3140
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
3141
                $position
3142
            ";
3143
        }
3144
3145
        $dql = "SELECT $dqlSelect
3146
                FROM ChamiloCoreBundle:Session AS s
3147
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
3148
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.sessionId = s.id
3149
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc ";
3150
3151
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
3152
        // is awfully inefficient for large sets of data (1m25s for 58K
3153
        // sessions, BT#14115) but executing a similar query twice and grouping
3154
        // the results afterwards in PHP takes about 1/1000th of the time
3155
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
3156
        $dqlStudent = $dql.' WHERE scu.user = :user AND url.accessUrlId = :url ';
3157
        $dqlCoach = $dql.' WHERE s.generalCoach = :user AND url.accessUrlId = :url ';
3158
3159
        // Default order
3160
        $order = 'ORDER BY sc.name, s.name';
3161
3162
        // Order by date if showing all sessions
3163
        $showAllSessions = api_get_configuration_value('show_all_sessions_on_my_course_page') === true;
3164
        if ($showAllSessions) {
3165
            $order = 'ORDER BY s.accessStartDate';
3166
        }
3167
3168
        // Order by position
3169
        if ($allowOrder) {
3170
            $order = 'ORDER BY s.position';
3171
        }
3172
3173
        // Order by dates according to settings
3174
        $orderBySettings = api_get_configuration_value('my_courses_session_order');
3175
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
3176
            $field = $orderBySettings['field'];
3177
            $orderSetting = $orderBySettings['order'];
3178
            switch ($field) {
3179
                case 'start_date':
3180
                    $order = " ORDER BY s.accessStartDate $orderSetting";
3181
                    break;
3182
                case 'end_date':
3183
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
3184
                    if ($orderSetting == 'asc') {
3185
                        // Put null values at the end
3186
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
3187
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
3188
                    }
3189
                    break;
3190
                case 'name':
3191
                    $order = " ORDER BY s.name $orderSetting ";
3192
                    break;
3193
            }
3194
        }
3195
3196
        $dqlStudent .= $order;
3197
        $dqlCoach .= $order;
3198
3199
        $accessUrlId = api_get_current_access_url_id();
3200
        $dqlStudent = Database::getManager()
3201
            ->createQuery($dqlStudent)
3202
            ->setParameters(
3203
                ['user' => $user_id, 'url' => $accessUrlId]
3204
            )
3205
        ;
3206
        $dqlCoach = Database::getManager()
3207
            ->createQuery($dqlCoach)
3208
            ->setParameters(
3209
                ['user' => $user_id, 'url' => $accessUrlId]
3210
            )
3211
        ;
3212
3213
        if ($getCount) {
3214
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
3215
        }
3216
3217
        $sessionDataStudent = $dqlStudent->getResult();
3218
        $sessionDataCoach = $dqlCoach->getResult();
3219
3220
        $sessionData = [];
3221
        // First fill $sessionData with student sessions
3222
        foreach ($sessionDataStudent as $row) {
3223
            $sessionData[$row['id']] = $row;
3224
        }
3225
        // Overwrite session data of the user as a student with session data
3226
        // of the user as a coach.
3227
        // There shouldn't be such duplicate rows, but just in case...
3228
        foreach ($sessionDataCoach as $row) {
3229
            $sessionData[$row['id']] = $row;
3230
        }
3231
3232
        $collapsable = api_get_configuration_value('allow_user_session_collapsable');
3233
        $extraField = new ExtraFieldValue('session');
3234
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
3235
3236
        $categories = [];
3237
        foreach ($sessionData as $row) {
3238
            $session_id = $row['id'];
3239
            $coachList = SessionManager::getCoachesBySession($session_id);
3240
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
3241
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
3242
            $courseList = self::get_courses_list_by_session(
3243
                $user_id,
3244
                $session_id
3245
            );
3246
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
3247
3248
            // User portal filters:
3249
            if ($ignoreTimeLimit === false) {
3250
                if ($is_time_over) {
3251
                    // History
3252
                    if ($row['duration']) {
3253
                        if ($daysLeft >= 0) {
3254
                            continue;
3255
                        }
3256
                    } else {
3257
                        if (empty($row['access_end_date'])) {
3258
                            continue;
3259
                        } else {
3260
                            if ($row['access_end_date'] > $now) {
3261
                                continue;
3262
                            }
3263
                        }
3264
                    }
3265
                } else {
3266
                    // Current user portal
3267
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
3268
                    $isCoachOfCourse = in_array($user_id, $coachList);
3269
3270
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
3271
                        // Teachers can access the session depending in the access_coach date
3272
                    } else {
3273
                        if ($row['duration']) {
3274
                            if ($daysLeft <= 0) {
3275
                                continue;
3276
                            }
3277
                        } else {
3278
                            if (isset($row['access_end_date']) &&
3279
                                !empty($row['access_end_date'])
3280
                            ) {
3281
                                if ($row['access_end_date'] <= $now) {
3282
                                    continue;
3283
                                }
3284
                            }
3285
                        }
3286
                    }
3287
                }
3288
            }
3289
3290
            $categories[$row['session_category_id']]['session_category'] = [
3291
                'id' => $row['session_category_id'],
3292
                'name' => $row['session_category_name'],
3293
                'date_start' => $categoryStart,
3294
                'date_end' => $categoryEnd,
3295
            ];
3296
3297
            $visibility = api_get_session_visibility(
3298
                $session_id,
3299
                null,
3300
                $ignore_visibility_for_admins
3301
            );
3302
3303
            if ($visibility != SESSION_VISIBLE) {
3304
                // Course Coach session visibility.
3305
                $blockedCourseCount = 0;
3306
                $closedVisibilityList = [
3307
                    COURSE_VISIBILITY_CLOSED,
3308
                    COURSE_VISIBILITY_HIDDEN,
3309
                ];
3310
3311
                foreach ($courseList as $course) {
3312
                    // Checking session visibility
3313
                    $sessionCourseVisibility = api_get_session_visibility(
3314
                        $session_id,
3315
                        $course['real_id'],
3316
                        $ignore_visibility_for_admins
3317
                    );
3318
3319
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
3320
                    if ($courseIsVisible === false || $sessionCourseVisibility == SESSION_INVISIBLE) {
3321
                        $blockedCourseCount++;
3322
                    }
3323
                }
3324
3325
                // If all courses are blocked then no show in the list.
3326
                if ($blockedCourseCount === count($courseList)) {
3327
                    $visibility = SESSION_INVISIBLE;
3328
                } else {
3329
                    $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...
3330
                }
3331
            }
3332
3333
            switch ($visibility) {
3334
                case SESSION_VISIBLE_READ_ONLY:
3335
                case SESSION_VISIBLE:
3336
                case SESSION_AVAILABLE:
3337
                    break;
3338
                case SESSION_INVISIBLE:
3339
                    if ($ignore_visibility_for_admins === false) {
3340
                        continue 2;
3341
                    }
3342
            }
3343
3344
            $collapsed = '';
3345
            $collapsedAction = '';
3346
            if ($collapsable) {
3347
                $collapsableData = SessionManager::getCollapsableData(
3348
                    $user_id,
3349
                    $session_id,
3350
                    $extraField,
3351
                    $collapsableLink
3352
                );
3353
                $collapsed = $collapsableData['collapsed'];
3354
                $collapsedAction = $collapsableData['collapsable_link'];
3355
            }
3356
3357
            $categories[$row['session_category_id']]['sessions'][] = [
3358
                'session_name' => $row['name'],
3359
                'session_id' => $row['id'],
3360
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
3361
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
3362
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
3363
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
3364
                'courses' => $courseList,
3365
                'collapsed' => $collapsed,
3366
                'collapsable_link' => $collapsedAction,
3367
                'duration' => $row['duration'],
3368
            ];
3369
        }
3370
3371
        return $categories;
3372
    }
3373
3374
    /**
3375
     * Gives a list of [session_id-course_code] => [status] for the current user.
3376
     *
3377
     * @param int $user_id
3378
     * @param int $sessionLimit
3379
     *
3380
     * @return array list of statuses (session_id-course_code => status)
3381
     */
3382
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
3383
    {
3384
        // Database Table Definitions
3385
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3386
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
3387
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3388
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3389
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3390
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3391
3392
        $user_id = (int) $user_id;
3393
3394
        if (empty($user_id)) {
3395
            return [];
3396
        }
3397
3398
        // We filter the courses from the URL
3399
        $join_access_url = $where_access_url = '';
3400
        if (api_get_multiple_access_url()) {
3401
            $access_url_id = api_get_current_access_url_id();
3402
            if ($access_url_id != -1) {
3403
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3404
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
3405
                $where_access_url = " AND access_url_id = $access_url_id ";
3406
            }
3407
        }
3408
3409
        // Courses in which we subscribed out of any session
3410
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3411
3412
        $sql = "SELECT
3413
                    course.code,
3414
                    course_rel_user.status course_rel_status,
3415
                    course_rel_user.sort sort,
3416
                    course_rel_user.user_course_cat user_course_cat
3417
                 FROM $tbl_course_user course_rel_user
3418
                 LEFT JOIN $tbl_course course
3419
                 ON course.id = course_rel_user.c_id
3420
                 LEFT JOIN $tbl_user_course_category user_course_category
3421
                 ON course_rel_user.user_course_cat = user_course_category.id
3422
                 $join_access_url
3423
                 WHERE
3424
                    course_rel_user.user_id = '".$user_id."' AND
3425
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
3426
                    $where_access_url
3427
                 ORDER BY user_course_category.sort, course_rel_user.sort, course.title ASC";
3428
3429
        $course_list_sql_result = Database::query($sql);
3430
3431
        $personal_course_list = [];
3432
        if (Database::num_rows($course_list_sql_result) > 0) {
3433
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3434
                $course_info = api_get_course_info($result_row['code']);
3435
                $result_row['course_info'] = $course_info;
3436
                $personal_course_list[] = $result_row;
3437
            }
3438
        }
3439
3440
        $coachCourseConditions = '';
3441
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
3442
        if (api_is_allowed_to_create_course()) {
3443
            $sessionListFromCourseCoach = [];
3444
            $sql = " SELECT DISTINCT session_id
3445
                    FROM $tbl_session_course_user
3446
                    WHERE user_id = $user_id AND status = 2 ";
3447
3448
            $result = Database::query($sql);
3449
            if (Database::num_rows($result)) {
3450
                $result = Database::store_result($result);
3451
                foreach ($result as $session) {
3452
                    $sessionListFromCourseCoach[] = $session['session_id'];
3453
                }
3454
            }
3455
            if (!empty($sessionListFromCourseCoach)) {
3456
                $condition = implode("','", $sessionListFromCourseCoach);
3457
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
3458
            }
3459
        }
3460
3461
        // Get the list of sessions where the user is subscribed
3462
        // This is divided into two different queries
3463
        $sessions = [];
3464
        $sessionLimitRestriction = '';
3465
        if (!empty($sessionLimit)) {
3466
            $sessionLimit = (int) $sessionLimit;
3467
            $sessionLimitRestriction = "LIMIT $sessionLimit";
3468
        }
3469
3470
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
3471
                FROM $tbl_session_user su INNER JOIN $tbl_session s
3472
                ON (s.id = su.session_id)
3473
                WHERE (
3474
                    su.user_id = $user_id AND
3475
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
3476
                )
3477
                $coachCourseConditions
3478
                ORDER BY access_start_date, access_end_date, name
3479
                $sessionLimitRestriction
3480
        ";
3481
3482
        $result = Database::query($sql);
3483
        if (Database::num_rows($result) > 0) {
3484
            while ($row = Database::fetch_assoc($result)) {
3485
                $sessions[$row['id']] = $row;
3486
            }
3487
        }
3488
3489
        $sql = "SELECT DISTINCT
3490
                id, name, access_start_date, access_end_date
3491
                FROM $tbl_session s
3492
                WHERE (
3493
                    id_coach = $user_id
3494
                )
3495
                $coachCourseConditions
3496
                ORDER BY access_start_date, access_end_date, name";
3497
3498
        $result = Database::query($sql);
3499
        if (Database::num_rows($result) > 0) {
3500
            while ($row = Database::fetch_assoc($result)) {
3501
                if (empty($sessions[$row['id']])) {
3502
                    $sessions[$row['id']] = $row;
3503
                }
3504
            }
3505
        }
3506
3507
        if (api_is_allowed_to_create_course()) {
3508
            foreach ($sessions as $enreg) {
3509
                $session_id = $enreg['id'];
3510
                $session_visibility = api_get_session_visibility($session_id);
3511
3512
                if ($session_visibility == SESSION_INVISIBLE) {
3513
                    continue;
3514
                }
3515
3516
                // This query is horribly slow when more than a few thousand
3517
                // users and just a few sessions to which they are subscribed
3518
                $sql = "SELECT DISTINCT
3519
                        course.code code,
3520
                        course.title i,
3521
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
3522
                        email, course.course_language l,
3523
                        1 sort,
3524
                        category_code user_course_cat,
3525
                        access_start_date,
3526
                        access_end_date,
3527
                        session.id as session_id,
3528
                        session.name as session_name
3529
                    FROM $tbl_session_course_user as session_course_user
3530
                    INNER JOIN $tbl_course AS course
3531
                        ON course.id = session_course_user.c_id
3532
                    INNER JOIN $tbl_session as session
3533
                        ON session.id = session_course_user.session_id
3534
                    LEFT JOIN $tbl_user as user
3535
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
3536
                    WHERE
3537
                        session_course_user.session_id = $session_id AND (
3538
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
3539
                            OR session.id_coach = $user_id
3540
                        )
3541
                    ORDER BY i";
3542
                $course_list_sql_result = Database::query($sql);
3543
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3544
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3545
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3546
                    $personal_course_list[$key] = $result_row;
3547
                }
3548
            }
3549
        }
3550
3551
        foreach ($sessions as $enreg) {
3552
            $session_id = $enreg['id'];
3553
            $session_visibility = api_get_session_visibility($session_id);
3554
            if ($session_visibility == SESSION_INVISIBLE) {
3555
                continue;
3556
            }
3557
3558
            /* This query is very similar to the above query,
3559
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3560
            $sql = "SELECT DISTINCT
3561
                course.code code,
3562
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
3563
                email,
3564
                course.course_language l,
3565
                1 sort,
3566
                category_code user_course_cat,
3567
                access_start_date,
3568
                access_end_date,
3569
                session.id as session_id,
3570
                session.name as session_name,
3571
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
3572
            FROM $tbl_session_course_user as session_course_user
3573
            INNER JOIN $tbl_course AS course
3574
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
3575
            INNER JOIN $tbl_session as session
3576
            ON session_course_user.session_id = session.id
3577
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
3578
            WHERE session_course_user.user_id = $user_id
3579
            ORDER BY i";
3580
3581
            $course_list_sql_result = Database::query($sql);
3582
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3583
                $result_row['course_info'] = api_get_course_info($result_row['code']);
3584
                $key = $result_row['session_id'].' - '.$result_row['code'];
3585
                if (!isset($personal_course_list[$key])) {
3586
                    $personal_course_list[$key] = $result_row;
3587
                }
3588
            }
3589
        }
3590
3591
        return $personal_course_list;
3592
    }
3593
3594
    /**
3595
     * Gives a list of courses for the given user in the given session.
3596
     *
3597
     * @param int $user_id
3598
     * @param int $session_id
3599
     *
3600
     * @return array list of statuses (session_id-course_code => status)
3601
     */
3602
    public static function get_courses_list_by_session($user_id, $session_id)
3603
    {
3604
        // Database Table Definitions
3605
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3606
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
3607
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3608
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3609
3610
        $user_id = (int) $user_id;
3611
        $session_id = (int) $session_id;
3612
        // We filter the courses from the URL
3613
        $join_access_url = $where_access_url = '';
3614
        if (api_get_multiple_access_url()) {
3615
            $urlId = api_get_current_access_url_id();
3616
            if ($urlId != -1) {
3617
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3618
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
3619
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
3620
            }
3621
        }
3622
3623
        /* This query is very similar to the query below, but it will check the
3624
        session_rel_course_user table if there are courses registered
3625
        to our user or not */
3626
        $sql = "SELECT DISTINCT
3627
                    c.visibility,
3628
                    c.id as real_id,
3629
                    c.code as course_code,
3630
                    sc.position
3631
                FROM $tbl_session_course_user as scu
3632
                INNER JOIN $tbl_session_course sc
3633
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3634
                INNER JOIN $tableCourse as c
3635
                ON (scu.c_id = c.id)
3636
                $join_access_url
3637
                WHERE
3638
                    scu.user_id = $user_id AND
3639
                    scu.session_id = $session_id
3640
                    $where_access_url
3641
                ORDER BY sc.position ASC";
3642
3643
        $myCourseList = [];
3644
        $courses = [];
3645
        $result = Database::query($sql);
3646
        if (Database::num_rows($result) > 0) {
3647
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3648
                $result_row['status'] = 5;
3649
                if (!in_array($result_row['real_id'], $courses)) {
3650
                    $position = $result_row['position'];
3651
                    if (!isset($myCourseList[$position])) {
3652
                        $myCourseList[$position] = $result_row;
3653
                    } else {
3654
                        $myCourseList[] = $result_row;
3655
                    }
3656
                    $courses[] = $result_row['real_id'];
3657
                }
3658
            }
3659
        }
3660
3661
        if (api_is_allowed_to_create_course()) {
3662
            $sql = "SELECT DISTINCT
3663
                        c.visibility,
3664
                        c.id as real_id,
3665
                        c.code as course_code,
3666
                        sc.position
3667
                    FROM $tbl_session_course_user as scu
3668
                    INNER JOIN $tbl_session as s
3669
                    ON (scu.session_id = s.id)
3670
                    INNER JOIN $tbl_session_course sc
3671
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3672
                    INNER JOIN $tableCourse as c
3673
                    ON (scu.c_id = c.id)
3674
                    $join_access_url
3675
                    WHERE
3676
                      s.id = $session_id AND
3677
                      (
3678
                        (scu.user_id = $user_id AND scu.status = 2) OR
3679
                        s.id_coach = $user_id
3680
                      )
3681
                    $where_access_url
3682
                    ORDER BY sc.position ASC";
3683
            $result = Database::query($sql);
3684
3685
            if (Database::num_rows($result) > 0) {
3686
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3687
                    $result_row['status'] = 2;
3688
                    if (!in_array($result_row['real_id'], $courses)) {
3689
                        $position = $result_row['position'];
3690
                        if (!isset($myCourseList[$position])) {
3691
                            $myCourseList[$position] = $result_row;
3692
                        } else {
3693
                            $myCourseList[] = $result_row;
3694
                        }
3695
                        $courses[] = $result_row['real_id'];
3696
                    }
3697
                }
3698
            }
3699
        }
3700
3701
        if (api_is_drh()) {
3702
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3703
            $sessionList = array_keys($sessionList);
3704
            if (in_array($session_id, $sessionList)) {
3705
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3706
                if (!empty($courseList)) {
3707
                    foreach ($courseList as $course) {
3708
                        if (!in_array($course['id'], $courses)) {
3709
                            $position = $course['position'];
3710
                            if (!isset($myCourseList[$position])) {
3711
                                $myCourseList[$position] = $course;
3712
                            } else {
3713
                                $myCourseList[] = $course;
3714
                            }
3715
                        }
3716
                    }
3717
                }
3718
            }
3719
        } else {
3720
            //check if user is general coach for this session
3721
            $sessionInfo = api_get_session_info($session_id);
3722
            if ($sessionInfo['id_coach'] == $user_id) {
3723
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3724
                if (!empty($courseList)) {
3725
                    foreach ($courseList as $course) {
3726
                        if (!in_array($course['id'], $courses)) {
3727
                            $position = $course['position'];
3728
                            if (!isset($myCourseList[$position])) {
3729
                                $myCourseList[$position] = $course;
3730
                            } else {
3731
                                $myCourseList[] = $course;
3732
                            }
3733
                        }
3734
                    }
3735
                }
3736
            }
3737
        }
3738
3739
        if (!empty($myCourseList)) {
3740
            ksort($myCourseList);
3741
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
3742
            if (empty($checkPosition)) {
3743
                // The session course list doesn't have any position,
3744
                // then order the course list by course code
3745
                $list = array_column($myCourseList, 'course_code');
3746
                array_multisort($myCourseList, SORT_ASC, $list);
3747
            }
3748
        }
3749
3750
        return $myCourseList;
3751
    }
3752
3753
    /**
3754
     * Get user id from a username.
3755
     *
3756
     * @param string $username
3757
     *
3758
     * @return int User ID (or false if not found)
3759
     */
3760
    public static function get_user_id_from_username($username)
3761
    {
3762
        if (empty($username)) {
3763
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3764
        }
3765
        $username = trim($username);
3766
        $username = Database::escape_string($username);
3767
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3768
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3769
        $res = Database::query($sql);
3770
3771
        if ($res === false) {
3772
            return false;
3773
        }
3774
        if (Database::num_rows($res) !== 1) {
3775
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3776
        }
3777
        $row = Database::fetch_array($res);
3778
3779
        return $row['id'];
3780
    }
3781
3782
    /**
3783
     * Get the users files upload from his share_folder.
3784
     *
3785
     * @param string $user_id      User ID
3786
     * @param string $course       course directory
3787
     * @param string $resourceType resource type: images, all
3788
     *
3789
     * @return string
3790
     */
3791
    public static function get_user_upload_files_by_course(
3792
        $user_id,
3793
        $course,
3794
        $resourceType = 'all'
3795
    ) {
3796
        $return = '';
3797
        $user_id = (int) $user_id;
3798
3799
        if (!empty($user_id) && !empty($course)) {
3800
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3801
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3802
            $file_list = [];
3803
3804
            if (is_dir($path)) {
3805
                $handle = opendir($path);
3806
                while ($file = readdir($handle)) {
0 ignored issues
show
Bug introduced by
It seems like $handle can also be of type false; however, parameter $dir_handle of readdir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

3806
                while ($file = readdir(/** @scrutinizer ignore-type */ $handle)) {
Loading history...
3807
                    if ($file == '.' || $file == '..' || $file == '.htaccess' || is_dir($path.$file)) {
3808
                        continue; // skip current/parent directory and .htaccess
3809
                    }
3810
                    $file_list[] = $file;
3811
                }
3812
                if (count($file_list) > 0) {
3813
                    $return = "<h4>$course</h4>";
3814
                    $return .= '<ul class="thumbnails">';
3815
                }
3816
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
3817
                foreach ($file_list as $file) {
3818
                    if ($resourceType == 'all') {
3819
                        $return .= '<li>
3820
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
3821
                    } elseif ($resourceType == 'images') {
3822
                        //get extension
3823
                        $ext = explode('.', $file);
3824
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
3825
                            $return .= '<li class="span2">
3826
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
3827
                                                <img src="'.$web_path.urlencode($file).'" >
3828
                                            </a>
3829
                                        </li>';
3830
                        }
3831
                    }
3832
                }
3833
                if (count($file_list) > 0) {
3834
                    $return .= '</ul>';
3835
                }
3836
            }
3837
        }
3838
3839
        return $return;
3840
    }
3841
3842
    /**
3843
     * Gets the API key (or keys) and return them into an array.
3844
     *
3845
     * @param int     Optional user id (defaults to the result of api_get_user_id())
3846
     * @param string $api_service
3847
     *
3848
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
3849
     */
3850
    public static function get_api_keys($user_id = null, $api_service = 'dokeos')
3851
    {
3852
        if ($user_id != strval(intval($user_id))) {
3853
            return false;
3854
        }
3855
        if (empty($user_id)) {
3856
            $user_id = api_get_user_id();
3857
        }
3858
        if ($user_id === false) {
3859
            return false;
3860
        }
3861
        $service_name = Database::escape_string($api_service);
3862
        if (is_string($service_name) === false) {
3863
            return false;
3864
        }
3865
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3866
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
3867
        $res = Database::query($sql);
3868
        if ($res === false) {
3869
            return false;
3870
        } //error during query
3871
        $num = Database::num_rows($res);
3872
        if ($num == 0) {
3873
            return false;
3874
        }
3875
        $list = [];
3876
        while ($row = Database::fetch_array($res)) {
3877
            $list[$row['id']] = $row['api_key'];
3878
        }
3879
3880
        return $list;
3881
    }
3882
3883
    /**
3884
     * Adds a new API key to the users' account.
3885
     *
3886
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
3887
     * @param string $api_service
3888
     *
3889
     * @return bool True on success, false on failure
3890
     */
3891
    public static function add_api_key($user_id = null, $api_service = 'dokeos')
3892
    {
3893
        if ($user_id != strval(intval($user_id))) {
3894
            return false;
3895
        }
3896
        if (empty($user_id)) {
3897
            $user_id = api_get_user_id();
3898
        }
3899
        if ($user_id === false) {
3900
            return false;
3901
        }
3902
        $service_name = Database::escape_string($api_service);
3903
        if (is_string($service_name) === false) {
3904
            return false;
3905
        }
3906
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3907
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
3908
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
3909
        $res = Database::query($sql);
3910
        if ($res === false) {
3911
            return false;
3912
        } //error during query
3913
        $num = Database::insert_id();
3914
3915
        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...
3916
    }
3917
3918
    /**
3919
     * Deletes an API key from the user's account.
3920
     *
3921
     * @param   int     API key's internal ID
3922
     *
3923
     * @return bool True on success, false on failure
3924
     */
3925
    public static function delete_api_key($key_id)
3926
    {
3927
        if ($key_id != strval(intval($key_id))) {
3928
            return false;
3929
        }
3930
        if ($key_id === false) {
3931
            return false;
3932
        }
3933
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3934
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
3935
        $res = Database::query($sql);
3936
        if ($res === false) {
3937
            return false;
3938
        } //error during query
3939
        $num = Database::num_rows($res);
3940
        if ($num !== 1) {
3941
            return false;
3942
        }
3943
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
3944
        $res = Database::query($sql);
3945
        if ($res === false) {
3946
            return false;
3947
        } //error during query
3948
3949
        return true;
3950
    }
3951
3952
    /**
3953
     * Regenerate an API key from the user's account.
3954
     *
3955
     * @param   int     user ID (defaults to the results of api_get_user_id())
3956
     * @param   string  API key's internal ID
3957
     *
3958
     * @return int num
3959
     */
3960
    public static function update_api_key($user_id, $api_service)
3961
    {
3962
        if ($user_id != strval(intval($user_id))) {
3963
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3964
        }
3965
        if ($user_id === false) {
3966
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3967
        }
3968
        $service_name = Database::escape_string($api_service);
3969
        if (is_string($service_name) === false) {
3970
            return false;
3971
        }
3972
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3973
        $sql = "SELECT id FROM $t_api
3974
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3975
        $res = Database::query($sql);
3976
        $num = Database::num_rows($res);
3977
        if ($num == 1) {
3978
            $id_key = Database::fetch_array($res, 'ASSOC');
3979
            self::delete_api_key($id_key['id']);
3980
            $num = self::add_api_key($user_id, $api_service);
3981
        } elseif ($num == 0) {
3982
            $num = self::add_api_key($user_id, $api_service);
3983
        }
3984
3985
        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...
3986
    }
3987
3988
    /**
3989
     * @param   int     user ID (defaults to the results of api_get_user_id())
3990
     * @param   string    API key's internal ID
3991
     *
3992
     * @return int row ID, or return false if not found
3993
     */
3994
    public static function get_api_key_id($user_id, $api_service)
3995
    {
3996
        if ($user_id != strval(intval($user_id))) {
3997
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3998
        }
3999
        if ($user_id === false) {
4000
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
4001
        }
4002
        if (empty($api_service)) {
4003
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
4004
        }
4005
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4006
        $api_service = Database::escape_string($api_service);
4007
        $sql = "SELECT id FROM $t_api
4008
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
4009
        $res = Database::query($sql);
4010
        if (Database::num_rows($res) < 1) {
4011
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
4012
        }
4013
        $row = Database::fetch_array($res, 'ASSOC');
4014
4015
        return $row['id'];
4016
    }
4017
4018
    /**
4019
     * Checks if a user_id is platform admin.
4020
     *
4021
     * @param   int user ID
4022
     *
4023
     * @return bool True if is admin, false otherwise
4024
     *
4025
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
4026
     */
4027
    public static function is_admin($user_id)
4028
    {
4029
        $user_id = (int) $user_id;
4030
        if (empty($user_id)) {
4031
            return false;
4032
        }
4033
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
4034
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
4035
        $res = Database::query($sql);
4036
4037
        return Database::num_rows($res) === 1;
4038
    }
4039
4040
    /**
4041
     * Get the total count of users.
4042
     *
4043
     * @param int $status        Status of users to be counted
4044
     * @param int $access_url_id Access URL ID (optional)
4045
     * @param int $active
4046
     *
4047
     * @return mixed Number of users or false on error
4048
     */
4049
    public static function get_number_of_users($status = 0, $access_url_id = 1, $active = null)
4050
    {
4051
        $t_u = Database::get_main_table(TABLE_MAIN_USER);
4052
        $t_a = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4053
4054
        if (api_is_multiple_url_enabled()) {
4055
            $sql = "SELECT count(u.id)
4056
                    FROM $t_u u
4057
                    INNER JOIN $t_a url_user
4058
                    ON (u.id = url_user.user_id)
4059
                    WHERE url_user.access_url_id = $access_url_id
4060
            ";
4061
        } else {
4062
            $sql = "SELECT count(u.id)
4063
                    FROM $t_u u
4064
                    WHERE 1 = 1 ";
4065
        }
4066
4067
        if (is_int($status) && $status > 0) {
4068
            $status = (int) $status;
4069
            $sql .= " AND u.status = $status ";
4070
        }
4071
4072
        if ($active !== null) {
4073
            $active = (int) $active;
4074
            $sql .= " AND u.active = $active ";
4075
        }
4076
4077
        $res = Database::query($sql);
4078
        if (Database::num_rows($res) === 1) {
4079
            return (int) Database::result($res, 0, 0);
4080
        }
4081
4082
        return false;
4083
    }
4084
4085
    /**
4086
     * Gets the tags of a specific field_id
4087
     * USER TAGS.
4088
     *
4089
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
4090
     *
4091
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
4092
     *    Called it "books" for example.
4093
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
4094
     * 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
4095
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
4096
     * 5. Test and enjoy.
4097
     *
4098
     * @param string $tag
4099
     * @param int    $field_id      field_id
4100
     * @param string $return_format how we are going to result value in array or in a string (json)
4101
     * @param $limit
4102
     *
4103
     * @return mixed
4104
     */
4105
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
4106
    {
4107
        // database table definition
4108
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4109
        $field_id = (int) $field_id;
4110
        $limit = (int) $limit;
4111
        $tag = trim(Database::escape_string($tag));
4112
4113
        // all the information of the field
4114
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
4115
                WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag LIMIT $limit";
4116
        $result = Database::query($sql);
4117
        $return = [];
4118
        if (Database::num_rows($result) > 0) {
4119
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4120
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
4121
            }
4122
        }
4123
        if ($return_format === 'json') {
4124
            $return = json_encode($return);
4125
        }
4126
4127
        return $return;
4128
    }
4129
4130
    /**
4131
     * @param int $field_id
4132
     * @param int $limit
4133
     *
4134
     * @return array
4135
     */
4136
    public static function get_top_tags($field_id, $limit = 100)
4137
    {
4138
        // database table definition
4139
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4140
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4141
        $field_id = (int) $field_id;
4142
        $limit = (int) $limit;
4143
        // all the information of the field
4144
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
4145
                INNER JOIN $table_user_tag ut
4146
                ON (ut.id = uv.tag_id)
4147
                WHERE field_id = $field_id
4148
                GROUP BY tag_id
4149
                ORDER BY count DESC
4150
                LIMIT $limit";
4151
        $result = Database::query($sql);
4152
        $return = [];
4153
        if (Database::num_rows($result) > 0) {
4154
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4155
                $return[] = $row;
4156
            }
4157
        }
4158
4159
        return $return;
4160
    }
4161
4162
    /**
4163
     * Get user's tags.
4164
     *
4165
     * @param int $user_id
4166
     * @param int $field_id
4167
     *
4168
     * @return array
4169
     */
4170
    public static function get_user_tags($user_id, $field_id)
4171
    {
4172
        // database table definition
4173
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4174
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4175
        $field_id = (int) $field_id;
4176
        $user_id = (int) $user_id;
4177
4178
        // all the information of the field
4179
        $sql = "SELECT ut.id, tag, count
4180
                FROM $table_user_tag ut
4181
                INNER JOIN $table_user_tag_values uv
4182
                ON (uv.tag_id=ut.ID)
4183
                WHERE field_id = $field_id AND user_id = $user_id
4184
                ORDER BY tag";
4185
        $result = Database::query($sql);
4186
        $return = [];
4187
        if (Database::num_rows($result) > 0) {
4188
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4189
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
4190
            }
4191
        }
4192
4193
        return $return;
4194
    }
4195
4196
    /**
4197
     * Get user's tags.
4198
     *
4199
     * @param int  $user_id
4200
     * @param int  $field_id
4201
     * @param bool $show_links show links or not
4202
     *
4203
     * @return string
4204
     */
4205
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
4206
    {
4207
        // database table definition
4208
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4209
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4210
        $field_id = (int) $field_id;
4211
        $user_id = (int) $user_id;
4212
4213
        // all the information of the field
4214
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
4215
                INNER JOIN $table_user_tag_values uv
4216
                ON (uv.tag_id = ut.id)
4217
                WHERE field_id = $field_id AND user_id = $user_id
4218
                ORDER BY tag";
4219
4220
        $result = Database::query($sql);
4221
        $return = [];
4222
        if (Database::num_rows($result) > 0) {
4223
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4224
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
4225
            }
4226
        }
4227
        $user_tags = $return;
4228
        $tag_tmp = [];
4229
        foreach ($user_tags as $tag) {
4230
            if ($show_links) {
4231
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
4232
                    $tag['tag'].
4233
                '</a>';
4234
            } else {
4235
                $tag_tmp[] = $tag['tag'];
4236
            }
4237
        }
4238
4239
        if (is_array($user_tags) && count($user_tags) > 0) {
4240
            return implode(', ', $tag_tmp);
4241
        } else {
4242
            return '';
4243
        }
4244
    }
4245
4246
    /**
4247
     * Get the tag id.
4248
     *
4249
     * @param int $tag
4250
     * @param int $field_id
4251
     *
4252
     * @return int returns 0 if fails otherwise the tag id
4253
     */
4254
    public static function get_tag_id($tag, $field_id)
4255
    {
4256
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4257
        $tag = Database::escape_string($tag);
4258
        $field_id = (int) $field_id;
4259
        //with COLLATE latin1_bin to select query in a case sensitive mode
4260
        $sql = "SELECT id FROM $table_user_tag
4261
                WHERE tag LIKE '$tag' AND field_id = $field_id";
4262
        $result = Database::query($sql);
4263
        if (Database::num_rows($result) > 0) {
4264
            $row = Database::fetch_array($result, 'ASSOC');
4265
4266
            return $row['id'];
4267
        } else {
4268
            return 0;
4269
        }
4270
    }
4271
4272
    /**
4273
     * Get the tag id.
4274
     *
4275
     * @param int $tag_id
4276
     * @param int $field_id
4277
     *
4278
     * @return int 0 if fails otherwise the tag id
4279
     */
4280
    public static function get_tag_id_from_id($tag_id, $field_id)
4281
    {
4282
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4283
        $tag_id = (int) $tag_id;
4284
        $field_id = (int) $field_id;
4285
        $sql = "SELECT id FROM $table_user_tag
4286
                WHERE id = '$tag_id' AND field_id = $field_id";
4287
        $result = Database::query($sql);
4288
        if (Database::num_rows($result) > 0) {
4289
            $row = Database::fetch_array($result, 'ASSOC');
4290
4291
            return $row['id'];
4292
        } else {
4293
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
4294
        }
4295
    }
4296
4297
    /**
4298
     * Adds a user-tag value.
4299
     *
4300
     * @param mixed $tag
4301
     * @param int   $user_id
4302
     * @param int   $field_id field id of the tag
4303
     *
4304
     * @return bool True if the tag was inserted or updated. False otherwise.
4305
     *              The return value doesn't take into account *values* added to the tag.
4306
     *              Only the creation/update of the tag field itself.
4307
     */
4308
    public static function add_tag($tag, $user_id, $field_id)
4309
    {
4310
        // database table definition
4311
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4312
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4313
        $tag = trim(Database::escape_string($tag));
4314
        $user_id = (int) $user_id;
4315
        $field_id = (int) $field_id;
4316
4317
        $tag_id = self::get_tag_id($tag, $field_id);
4318
4319
        /* IMPORTANT
4320
         *  @todo we don't create tags with numbers
4321
         *
4322
         */
4323
        if (is_numeric($tag)) {
4324
            //the form is sending an id this means that the user select it from the list so it MUST exists
4325
            /* $new_tag_id = self::get_tag_id_from_id($tag,$field_id);
4326
              if ($new_tag_id !== false) {
4327
              $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $new_tag_id";
4328
              $result = Database::query($sql);
4329
              $last_insert_id = $new_tag_id;
4330
              } else {
4331
              $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
4332
              $result = Database::query($sql);
4333
              $last_insert_id = Database::insert_id();
4334
              } */
4335
        }
4336
4337
        //this is a new tag
4338
        if ($tag_id == 0) {
4339
            //the tag doesn't exist
4340
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
4341
            Database::query($sql);
4342
            $last_insert_id = Database::insert_id();
4343
        } else {
4344
            //the tag exists we update it
4345
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
4346
            Database::query($sql);
4347
            $last_insert_id = $tag_id;
4348
        }
4349
4350
        if (!empty($last_insert_id) && ($last_insert_id != 0)) {
4351
            //we insert the relationship user-tag
4352
            $sql = "SELECT tag_id FROM $table_user_tag_values
4353
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
4354
            $result = Database::query($sql);
4355
            //if the relationship does not exist we create it
4356
            if (Database::num_rows($result) == 0) {
4357
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
4358
                Database::query($sql);
4359
            }
4360
4361
            return true;
4362
        }
4363
4364
        return false;
4365
    }
4366
4367
    /**
4368
     * Deletes an user tag.
4369
     *
4370
     * @param int $user_id
4371
     * @param int $field_id
4372
     */
4373
    public static function delete_user_tags($user_id, $field_id)
4374
    {
4375
        // database table definition
4376
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4377
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4378
        $user_id = (int) $user_id;
4379
4380
        $tags = self::get_user_tags($user_id, $field_id);
4381
        if (is_array($tags) && count($tags) > 0) {
4382
            foreach ($tags as $key => $tag) {
4383
                if ($tag['count'] > '0') {
4384
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
4385
                    Database::query($sql);
4386
                }
4387
                $sql = "DELETE FROM $table_user_tag_values
4388
                        WHERE user_id = $user_id AND tag_id = $key";
4389
                Database::query($sql);
4390
            }
4391
        }
4392
    }
4393
4394
    /**
4395
     * Process the tag list comes from the UserManager::update_extra_field_value() function.
4396
     *
4397
     * @param array $tags     the tag list that will be added
4398
     * @param int   $user_id
4399
     * @param int   $field_id
4400
     *
4401
     * @return bool
4402
     */
4403
    public static function process_tags($tags, $user_id, $field_id)
4404
    {
4405
        // We loop the tags and add it to the DB
4406
        if (is_array($tags)) {
4407
            foreach ($tags as $tag) {
4408
                self::add_tag($tag, $user_id, $field_id);
4409
            }
4410
        } else {
4411
            self::add_tag($tags, $user_id, $field_id);
4412
        }
4413
4414
        return true;
4415
    }
4416
4417
    /**
4418
     * Returns a list of all administrators.
4419
     *
4420
     * @return array
4421
     */
4422
    public static function get_all_administrators()
4423
    {
4424
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4425
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
4426
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4427
        $access_url_id = api_get_current_access_url_id();
4428
        if (api_get_multiple_access_url()) {
4429
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
4430
                    FROM $tbl_url_rel_user as url
4431
                    INNER JOIN $table_admin as admin
4432
                    ON (admin.user_id=url.user_id)
4433
                    INNER JOIN $table_user u
4434
                    ON (u.id=admin.user_id)
4435
                    WHERE access_url_id ='".$access_url_id."'";
4436
        } else {
4437
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
4438
                    FROM $table_admin as admin
4439
                    INNER JOIN $table_user u
4440
                    ON (u.id=admin.user_id)";
4441
        }
4442
        $result = Database::query($sql);
4443
        $return = [];
4444
        if (Database::num_rows($result) > 0) {
4445
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4446
                $return[$row['user_id']] = $row;
4447
            }
4448
        }
4449
4450
        return $return;
4451
    }
4452
4453
    /**
4454
     * Search an user (tags, first name, last name and email ).
4455
     *
4456
     * @param string $tag
4457
     * @param int    $field_id        field id of the tag
4458
     * @param int    $from            where to start in the query
4459
     * @param int    $number_of_items
4460
     * @param bool   $getCount        get count or not
4461
     *
4462
     * @return array
4463
     */
4464
    public static function get_all_user_tags(
4465
        $tag,
4466
        $field_id = 0,
4467
        $from = 0,
4468
        $number_of_items = 10,
4469
        $getCount = false
4470
    ) {
4471
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
4472
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4473
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4474
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4475
4476
        $field_id = intval($field_id);
4477
        $from = intval($from);
4478
        $number_of_items = intval($number_of_items);
4479
4480
        $where_field = "";
4481
        $where_extra_fields = self::get_search_form_where_extra_fields();
4482
        if ($field_id != 0) {
4483
            $where_field = " field_id = $field_id AND ";
4484
        }
4485
4486
        // all the information of the field
4487
        if ($getCount) {
4488
            $select = "SELECT count(DISTINCT u.id) count";
4489
        } else {
4490
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, tag, picture_uri";
4491
        }
4492
4493
        $sql = " $select
4494
                FROM $user_table u
4495
                INNER JOIN $access_url_rel_user_table url_rel_user
4496
                ON (u.id = url_rel_user.user_id)
4497
                LEFT JOIN $table_user_tag_values uv
4498
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
4499
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
4500
                WHERE
4501
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
4502
                    (
4503
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
4504
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
4505
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
4506
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
4507
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
4508
                     )
4509
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
4510
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
4511
4512
        $keyword_active = true;
4513
        // only active users
4514
        if ($keyword_active) {
4515
            $sql .= " AND u.active='1'";
4516
        }
4517
        // avoid anonymous
4518
        $sql .= " AND u.status <> 6 ";
4519
        $sql .= " ORDER BY username";
4520
        $sql .= " LIMIT $from , $number_of_items";
4521
4522
        $result = Database::query($sql);
4523
        $return = [];
4524
4525
        if (Database::num_rows($result) > 0) {
4526
            if ($getCount) {
4527
                $row = Database::fetch_array($result, 'ASSOC');
4528
4529
                return $row['count'];
4530
            }
4531
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4532
                if (isset($return[$row['id']]) &&
4533
                    !empty($return[$row['id']]['tag'])
4534
                ) {
4535
                    $url = Display::url(
4536
                        $row['tag'],
4537
                        api_get_path(WEB_PATH).'main/social/search.php?q='.$row['tag'],
4538
                        ['class' => 'tag']
4539
                    );
4540
                    $row['tag'] = $url;
4541
                }
4542
                $return[$row['id']] = $row;
4543
            }
4544
        }
4545
4546
        return $return;
4547
    }
4548
4549
    /**
4550
     * Get extra filterable user fields (only type select).
4551
     *
4552
     * @return array Array of extra fields as [int => ['name' => ..., 'variable' => ..., 'data' => ...]] (
4553
     *               or empty array if no extra field)
4554
     */
4555
    public static function getExtraFilterableFields()
4556
    {
4557
        $extraFieldList = self::get_extra_fields();
4558
        $fields = [];
4559
        if (is_array($extraFieldList)) {
4560
            foreach ($extraFieldList as $extraField) {
4561
                // If is enabled to filter and is a "<select>" field type
4562
                if ($extraField[8] == 1 && $extraField[2] == 4) {
4563
                    $fields[] = [
4564
                        'name' => $extraField[3],
4565
                        'variable' => $extraField[1],
4566
                        'data' => $extraField[9],
4567
                    ];
4568
                }
4569
            }
4570
        }
4571
4572
        return $fields;
4573
    }
4574
4575
    /**
4576
     * Get extra where clauses for finding users based on extra filterable user fields (type select).
4577
     *
4578
     * @return string With AND clauses based on user's ID which have the values to search in extra user fields
4579
     *                (or empty if no extra field exists)
4580
     */
4581
    public static function get_search_form_where_extra_fields()
4582
    {
4583
        $useExtraFields = false;
4584
        $extraFields = self::getExtraFilterableFields();
4585
        $extraFieldResult = [];
4586
        if (is_array($extraFields) && count($extraFields) > 0) {
4587
            foreach ($extraFields as $extraField) {
4588
                $varName = 'field_'.$extraField['variable'];
4589
                if (self::is_extra_field_available($extraField['variable'])) {
4590
                    if (isset($_GET[$varName]) && $_GET[$varName] != '0') {
4591
                        $useExtraFields = true;
4592
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
4593
                            $extraField['variable'],
4594
                            $_GET[$varName]
4595
                        );
4596
                    }
4597
                }
4598
            }
4599
        }
4600
4601
        if ($useExtraFields) {
4602
            $finalResult = [];
4603
            if (count($extraFieldResult) > 1) {
4604
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
4605
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
4606
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
4607
                    }
4608
                }
4609
            } else {
4610
                $finalResult = $extraFieldResult[0];
4611
            }
4612
4613
            if (is_array($finalResult) && count($finalResult) > 0) {
4614
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
4615
            } else {
4616
                //no results
4617
                $whereFilter = " AND u.id  = -1 ";
4618
            }
4619
4620
            return $whereFilter;
4621
        }
4622
4623
        return '';
4624
    }
4625
4626
    /**
4627
     * Show the search form.
4628
     *
4629
     * @param string $query the value of the search box
4630
     *
4631
     * @throws Exception
4632
     *
4633
     * @return string HTML form
4634
     */
4635
    public static function get_search_form($query, $defaultParams = [])
4636
    {
4637
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
4638
        $form = new FormValidator(
4639
            'search_user',
4640
            'get',
4641
            api_get_path(WEB_PATH).'main/social/search.php',
4642
            '',
4643
            [],
4644
            FormValidator::LAYOUT_HORIZONTAL
4645
        );
4646
4647
        $query = Security::remove_XSS($query);
4648
4649
        if (!empty($query)) {
4650
            $form->addHeader(get_lang('Results').' "'.$query.'"');
4651
        }
4652
4653
        $form->addText(
4654
            'q',
4655
            get_lang('UsersGroups'),
4656
            false,
4657
            [
4658
                'id' => 'q',
4659
            ]
4660
        );
4661
        $options = [
4662
            0 => get_lang('Select'),
4663
            1 => get_lang('User'),
4664
            2 => get_lang('Group'),
4665
        ];
4666
        $form->addSelect(
4667
            'search_type',
4668
            get_lang('Type'),
4669
            $options,
4670
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
4671
        );
4672
4673
        // Extra fields
4674
        $extraFields = self::getExtraFilterableFields();
4675
        $defaults = [];
4676
        if (is_array($extraFields) && count($extraFields) > 0) {
4677
            foreach ($extraFields as $extraField) {
4678
                $varName = 'field_'.$extraField['variable'];
4679
                $options = [
4680
                    0 => get_lang('Select'),
4681
                ];
4682
                foreach ($extraField['data'] as $option) {
4683
                    if (isset($_GET[$varName])) {
4684
                        if ($_GET[$varName] == $option[1]) {
4685
                            $defaults[$option[1]] = true;
4686
                        }
4687
                    }
4688
4689
                    $options[$option[1]] = $option[1];
4690
                }
4691
                $form->addSelect($varName, $extraField['name'], $options);
4692
            }
4693
        }
4694
4695
        $defaults['search_type'] = (int) $searchType;
4696
        $defaults['q'] = $query;
4697
4698
        if (!empty($defaultParams)) {
4699
            $defaults = array_merge($defaults, $defaultParams);
4700
        }
4701
        $form->setDefaults($defaults);
4702
        $form->addButtonSearch(get_lang('Search'));
4703
4704
        $js = '<script>
4705
        extra_field_toogle();
4706
        function extra_field_toogle() {
4707
            if (jQuery("select[name=search_type]").val() != "1") {
4708
                jQuery(".extra_field").hide();
4709
            } else {
4710
                jQuery(".extra_field").show();
4711
            }
4712
        }
4713
        </script>';
4714
4715
        return $js.$form->returnForm();
4716
    }
4717
4718
    /**
4719
     * Shows the user menu.
4720
     */
4721
    public static function show_menu()
4722
    {
4723
        echo '<div class="actions">';
4724
        echo '<a href="/main/auth/profile.php">'.
4725
            Display::return_icon('profile.png').' '.get_lang('PersonalData').'</a>';
4726
        echo '<a href="/main/messages/inbox.php">'.
4727
            Display::return_icon('inbox.png').' '.get_lang('Inbox').'</a>';
4728
        echo '<a href="/main/messages/outbox.php">'.
4729
            Display::return_icon('outbox.png').' '.get_lang('Outbox').'</a>';
4730
        echo '<span style="float:right; padding-top:7px;">'.
4731
        '<a href="/main/auth/profile.php?show=1">'.
4732
            Display::return_icon('edit.gif').' '.get_lang('Configuration').'</a>';
4733
        echo '</span>';
4734
        echo '</div>';
4735
    }
4736
4737
    /**
4738
     * Allow to register contact to social network.
4739
     *
4740
     * @param int $friend_id     user friend id
4741
     * @param int $my_user_id    user id
4742
     * @param int $relation_type relation between users see constants definition
4743
     *
4744
     * @return bool
4745
     */
4746
    public static function relate_users($friend_id, $my_user_id, $relation_type)
4747
    {
4748
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4749
4750
        $friend_id = (int) $friend_id;
4751
        $my_user_id = (int) $my_user_id;
4752
        $relation_type = (int) $relation_type;
4753
4754
        $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
4755
                WHERE
4756
                    friend_user_id='.$friend_id.' AND
4757
                    user_id='.$my_user_id.' AND
4758
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
4759
        $result = Database::query($sql);
4760
        $row = Database::fetch_array($result, 'ASSOC');
4761
        $current_date = api_get_utc_datetime();
4762
4763
        if ($row['count'] == 0) {
4764
            $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
4765
                    VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
4766
            Database::query($sql);
4767
4768
            return true;
4769
        }
4770
4771
        $sql = 'SELECT COUNT(*) as count, relation_type  FROM '.$tbl_my_friend.'
4772
                WHERE
4773
                    friend_user_id='.$friend_id.' AND
4774
                    user_id='.$my_user_id.' AND
4775
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
4776
        $result = Database::query($sql);
4777
        $row = Database::fetch_array($result, 'ASSOC');
4778
4779
        if ($row['count'] == 1) {
4780
            //only for the case of a RRHH or a Student BOSS
4781
            if ($row['relation_type'] != $relation_type &&
4782
                ($relation_type == USER_RELATION_TYPE_RRHH || $relation_type == USER_RELATION_TYPE_BOSS)
4783
            ) {
4784
                $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
4785
                        VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
4786
            } else {
4787
                $sql = 'UPDATE '.$tbl_my_friend.' SET relation_type='.$relation_type.'
4788
                        WHERE friend_user_id='.$friend_id.' AND user_id='.$my_user_id;
4789
            }
4790
            Database::query($sql);
4791
4792
            return true;
4793
        }
4794
4795
        return false;
4796
    }
4797
4798
    /**
4799
     * Deletes a contact.
4800
     *
4801
     * @param bool   $friend_id
4802
     * @param bool   $real_removed          true will delete ALL friends relationship
4803
     * @param string $with_status_condition
4804
     *
4805
     * @author isaac flores paz <[email protected]>
4806
     * @author Julio Montoya <[email protected]> Cleaning code
4807
     */
4808
    public static function remove_user_rel_user(
4809
        $friend_id,
4810
        $real_removed = false,
4811
        $with_status_condition = ''
4812
    ) {
4813
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4814
        $tbl_my_message = Database::get_main_table(TABLE_MESSAGE);
4815
        $friend_id = (int) $friend_id;
4816
        $user_id = api_get_user_id();
4817
4818
        if ($real_removed) {
4819
            $extra_condition = '';
4820
            if ($with_status_condition != '') {
4821
                $extra_condition = ' AND relation_type = '.intval($with_status_condition);
4822
            }
4823
            $sql = 'DELETE FROM '.$tbl_my_friend.'
4824
                    WHERE
4825
                        relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
4826
                        friend_user_id='.$friend_id.' '.$extra_condition;
4827
            Database::query($sql);
4828
            $sql = 'DELETE FROM '.$tbl_my_friend.'
4829
                   WHERE
4830
                    relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
4831
                    user_id='.$friend_id.' '.$extra_condition;
4832
            Database::query($sql);
4833
        } else {
4834
            $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
4835
                    WHERE
4836
                        user_id='.$user_id.' AND
4837
                        relation_type NOT IN('.USER_RELATION_TYPE_DELETED.', '.USER_RELATION_TYPE_RRHH.') AND
4838
                        friend_user_id='.$friend_id;
4839
            $result = Database::query($sql);
4840
            $row = Database::fetch_array($result, 'ASSOC');
4841
            if ($row['count'] == 1) {
4842
                //Delete user rel user
4843
                $sql_i = 'UPDATE '.$tbl_my_friend.' SET relation_type='.USER_RELATION_TYPE_DELETED.'
4844
                          WHERE user_id='.$user_id.' AND friend_user_id='.$friend_id;
4845
4846
                $sql_j = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
4847
                          WHERE
4848
                                user_receiver_id='.$user_id.' AND
4849
                                user_sender_id='.$friend_id.' AND update_date="0000-00-00 00:00:00" ';
4850
                // Delete user
4851
                $sql_ij = 'UPDATE '.$tbl_my_friend.'  SET relation_type='.USER_RELATION_TYPE_DELETED.'
4852
                           WHERE user_id='.$friend_id.' AND friend_user_id='.$user_id;
4853
                $sql_ji = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
4854
                           WHERE
4855
                                user_receiver_id='.$friend_id.' AND
4856
                                user_sender_id='.$user_id.' AND
4857
                                update_date="0000-00-00 00:00:00" ';
4858
                Database::query($sql_i);
4859
                Database::query($sql_j);
4860
                Database::query($sql_ij);
4861
                Database::query($sql_ji);
4862
            }
4863
        }
4864
4865
        // Delete accepted invitations
4866
        $sql = "DELETE FROM $tbl_my_message
4867
                WHERE
4868
                    msg_status = ".MESSAGE_STATUS_INVITATION_ACCEPTED." AND
4869
                    (
4870
                        user_receiver_id = $user_id AND
4871
                        user_sender_id = $friend_id
4872
                    ) OR
4873
                    (
4874
                        user_sender_id = $user_id AND
4875
                        user_receiver_id = $friend_id
4876
                    )
4877
        ";
4878
        Database::query($sql);
4879
    }
4880
4881
    /**
4882
     * @param int $userId
4883
     *
4884
     * @return array
4885
     */
4886
    public static function getDrhListFromUser($userId)
4887
    {
4888
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4889
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4890
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4891
        $userId = (int) $userId;
4892
4893
        $orderBy = null;
4894
        if (api_is_western_name_order()) {
4895
            $orderBy .= ' ORDER BY firstname, lastname ';
4896
        } else {
4897
            $orderBy .= ' ORDER BY lastname, firstname ';
4898
        }
4899
4900
        $sql = "SELECT u.id, username, u.firstname, u.lastname
4901
                FROM $tblUser u
4902
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
4903
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
4904
                WHERE
4905
                    access_url_id = ".api_get_current_access_url_id()." AND
4906
                    uru.user_id = '$userId' AND
4907
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4908
                $orderBy
4909
                ";
4910
        $result = Database::query($sql);
4911
4912
        return Database::store_result($result);
4913
    }
4914
4915
    /**
4916
     * get users followed by human resource manager.
4917
     *
4918
     * @param int    $userId
4919
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
4920
     * @param bool   $getOnlyUserId
4921
     * @param bool   $getSql
4922
     * @param bool   $getCount
4923
     * @param int    $from
4924
     * @param int    $numberItems
4925
     * @param int    $column
4926
     * @param string $direction
4927
     * @param int    $active
4928
     * @param string $lastConnectionDate
4929
     *
4930
     * @return array users
4931
     */
4932
    public static function get_users_followed_by_drh(
4933
        $userId,
4934
        $userStatus = 0,
4935
        $getOnlyUserId = false,
4936
        $getSql = false,
4937
        $getCount = false,
4938
        $from = null,
4939
        $numberItems = null,
4940
        $column = null,
4941
        $direction = null,
4942
        $active = null,
4943
        $lastConnectionDate = null
4944
    ) {
4945
        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...
4946
            $userId,
4947
            $userStatus,
4948
            $getOnlyUserId,
4949
            $getSql,
4950
            $getCount,
4951
            $from,
4952
            $numberItems,
4953
            $column,
4954
            $direction,
4955
            $active,
4956
            $lastConnectionDate,
4957
            DRH
4958
        );
4959
    }
4960
4961
    /**
4962
     * Get users followed by human resource manager.
4963
     *
4964
     * @param int    $userId
4965
     * @param int    $userStatus         Filter users by status (STUDENT, COURSEMANAGER, etc)
4966
     * @param bool   $getOnlyUserId
4967
     * @param bool   $getSql
4968
     * @param bool   $getCount
4969
     * @param int    $from
4970
     * @param int    $numberItems
4971
     * @param int    $column
4972
     * @param string $direction
4973
     * @param int    $active
4974
     * @param string $lastConnectionDate
4975
     * @param int    $status             the function is called by who? COURSEMANAGER, DRH?
4976
     * @param string $keyword
4977
     *
4978
     * @return mixed Users list (array) or the SQL query if $getSQL was set to true
4979
     */
4980
    public static function getUsersFollowedByUser(
4981
        $userId,
4982
        $userStatus = null,
4983
        $getOnlyUserId = false,
4984
        $getSql = false,
4985
        $getCount = false,
4986
        $from = null,
4987
        $numberItems = null,
4988
        $column = null,
4989
        $direction = null,
4990
        $active = null,
4991
        $lastConnectionDate = null,
4992
        $status = null,
4993
        $keyword = null
4994
    ) {
4995
        // Database Table Definitions
4996
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4997
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4998
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4999
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
5000
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5001
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5002
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
5003
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
5004
5005
        $userId = (int) $userId;
5006
        $limitCondition = '';
5007
5008
        if (isset($from) && isset($numberItems)) {
5009
            $from = (int) $from;
5010
            $numberItems = (int) $numberItems;
5011
            $limitCondition = "LIMIT $from, $numberItems";
5012
        }
5013
5014
        $column = Database::escape_string($column);
5015
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
5016
5017
        $userConditions = '';
5018
        if (!empty($userStatus)) {
5019
            $userConditions .= ' AND u.status = '.intval($userStatus);
5020
        }
5021
5022
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
5023
        if ($getOnlyUserId) {
5024
            $select = " SELECT DISTINCT u.id user_id";
5025
        }
5026
5027
        $masterSelect = "SELECT DISTINCT * FROM ";
5028
5029
        if ($getCount) {
5030
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
5031
            $select = " SELECT DISTINCT(u.id) user_id";
5032
        }
5033
5034
        if (!is_null($active)) {
5035
            $active = intval($active);
5036
            $userConditions .= " AND u.active = $active ";
5037
        }
5038
5039
        if (!empty($keyword)) {
5040
            $keyword = Database::escape_string($keyword);
5041
            $userConditions .= " AND (
5042
                u.username LIKE '%$keyword%' OR
5043
                u.firstname LIKE '%$keyword%' OR
5044
                u.lastname LIKE '%$keyword%' OR
5045
                u.official_code LIKE '%$keyword%' OR
5046
                u.email LIKE '%$keyword%'
5047
            )";
5048
        }
5049
5050
        if (!empty($lastConnectionDate)) {
5051
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
5052
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
5053
        }
5054
5055
        $sessionConditionsCoach = null;
5056
        $sessionConditionsTeacher = null;
5057
        $drhConditions = null;
5058
        $teacherSelect = null;
5059
5060
        switch ($status) {
5061
            case DRH:
5062
                $drhConditions .= " AND
5063
                    friend_user_id = '$userId' AND
5064
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
5065
                ";
5066
                break;
5067
            case COURSEMANAGER:
5068
                $drhConditions .= " AND
5069
                    friend_user_id = '$userId' AND
5070
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
5071
                ";
5072
5073
                $sessionConditionsCoach .= " AND
5074
                    (s.id_coach = '$userId')
5075
                ";
5076
5077
                $sessionConditionsTeacher .= " AND
5078
                    (scu.status = 2 AND scu.user_id = '$userId')
5079
                ";
5080
5081
                $teacherSelect =
5082
                "UNION ALL (
5083
                        $select
5084
                        FROM $tbl_user u
5085
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
5086
                        WHERE
5087
                            (
5088
                                sru.session_id IN (
5089
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
5090
                                    $tbl_session_rel_access_url session_rel_access_rel_user
5091
                                    ON session_rel_access_rel_user.session_id = s.id
5092
                                    WHERE access_url_id = ".api_get_current_access_url_id()."
5093
                                    $sessionConditionsCoach
5094
                                ) OR sru.session_id IN (
5095
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
5096
                                    INNER JOIN $tbl_session_rel_access_url url
5097
                                    ON (url.session_id = s.id)
5098
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
5099
                                    ON (scu.session_id = s.id)
5100
                                    WHERE access_url_id = ".api_get_current_access_url_id()."
5101
                                    $sessionConditionsTeacher
5102
                                )
5103
                            )
5104
                            $userConditions
5105
                    )
5106
                    UNION ALL(
5107
                        $select
5108
                        FROM $tbl_user u
5109
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
5110
                        WHERE cu.c_id IN (
5111
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
5112
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
5113
                        )
5114
                        $userConditions
5115
                    )"
5116
                ;
5117
                break;
5118
            case STUDENT_BOSS:
5119
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
5120
                break;
5121
            case HRM_REQUEST:
5122
                $drhConditions .= " AND
5123
                    friend_user_id = '$userId' AND
5124
                    relation_type = '".USER_RELATION_TYPE_HRM_REQUEST."'
5125
                ";
5126
                break;
5127
        }
5128
5129
        $join = null;
5130
        $sql = " $masterSelect
5131
                (
5132
                    (
5133
                        $select
5134
                        FROM $tbl_user u
5135
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
5136
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
5137
                        $join
5138
                        WHERE
5139
                            access_url_id = ".api_get_current_access_url_id()."
5140
                            $drhConditions
5141
                            $userConditions
5142
                    )
5143
                    $teacherSelect
5144
5145
                ) as t1";
5146
5147
        if ($getSql) {
5148
            return $sql;
5149
        }
5150
        if ($getCount) {
5151
            $result = Database::query($sql);
5152
            $row = Database::fetch_array($result);
5153
5154
            return $row['count'];
5155
        }
5156
5157
        $orderBy = null;
5158
        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...
5159
            if (api_is_western_name_order()) {
5160
                $orderBy .= " ORDER BY firstname, lastname ";
5161
            } else {
5162
                $orderBy .= " ORDER BY lastname, firstname ";
5163
            }
5164
5165
            if (!empty($column) && !empty($direction)) {
5166
                // Fixing order due the UNIONs
5167
                $column = str_replace('u.', '', $column);
5168
                $orderBy = " ORDER BY $column $direction ";
5169
            }
5170
        }
5171
5172
        $sql .= $orderBy;
5173
        $sql .= $limitCondition;
5174
5175
        $result = Database::query($sql);
5176
        $users = [];
5177
        if (Database::num_rows($result) > 0) {
5178
            while ($row = Database::fetch_array($result)) {
5179
                $users[$row['user_id']] = $row;
5180
            }
5181
        }
5182
5183
        return $users;
5184
    }
5185
5186
    /**
5187
     * Subscribes users to human resource manager (Dashboard feature).
5188
     *
5189
     * @param int   $hr_dept_id
5190
     * @param array $users_id
5191
     * @param bool  $deleteOtherAssignedUsers
5192
     *
5193
     * @return int
5194
     */
5195
    public static function subscribeUsersToHRManager(
5196
        $hr_dept_id,
5197
        $users_id,
5198
        $deleteOtherAssignedUsers = true
5199
    ) {
5200
        return self::subscribeUsersToUser(
5201
            $hr_dept_id,
5202
            $users_id,
5203
            USER_RELATION_TYPE_RRHH,
5204
            false,
5205
            $deleteOtherAssignedUsers
5206
        );
5207
    }
5208
5209
    /**
5210
     * Register request to assign users to HRM.
5211
     *
5212
     * @param int   $hrmId   The HRM ID
5213
     * @param array $usersId The users IDs
5214
     *
5215
     * @return int
5216
     */
5217
    public static function requestUsersToHRManager($hrmId, $usersId)
5218
    {
5219
        return self::subscribeUsersToUser(
5220
            $hrmId,
5221
            $usersId,
5222
            USER_RELATION_TYPE_HRM_REQUEST,
5223
            false,
5224
            false
5225
        );
5226
    }
5227
5228
    /**
5229
     * Remove the requests for assign a user to a HRM.
5230
     *
5231
     * @param User  $hrmId
5232
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
5233
     */
5234
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
5235
    {
5236
        $users = implode(', ', $usersId);
5237
        Database::getManager()
5238
            ->createQuery('
5239
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
5240
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
5241
            ')
5242
            ->execute(['hrm_id' => $hrmId, 'relation_type' => USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
5243
    }
5244
5245
    /**
5246
     * Add subscribed users to a user by relation type.
5247
     *
5248
     * @param int    $userId                   The user id
5249
     * @param array  $subscribedUsersId        The id of subscribed users
5250
     * @param string $relationType             The relation type
5251
     * @param bool   $deleteUsersBeforeInsert
5252
     * @param bool   $deleteOtherAssignedUsers
5253
     *
5254
     * @return int
5255
     */
5256
    public static function subscribeUsersToUser(
5257
        $userId,
5258
        $subscribedUsersId,
5259
        $relationType,
5260
        $deleteUsersBeforeInsert = false,
5261
        $deleteOtherAssignedUsers = true
5262
    ) {
5263
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5264
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5265
5266
        $userId = (int) $userId;
5267
        $relationType = (int) $relationType;
5268
        $affectedRows = 0;
5269
5270
        if ($deleteOtherAssignedUsers) {
5271
            if (api_get_multiple_access_url()) {
5272
                // Deleting assigned users to hrm_id
5273
                $sql = "SELECT s.user_id
5274
                        FROM $userRelUserTable s
5275
                        INNER JOIN $userRelAccessUrlTable a
5276
                        ON (a.user_id = s.user_id)
5277
                        WHERE
5278
                            friend_user_id = $userId AND
5279
                            relation_type = $relationType AND
5280
                            access_url_id = ".api_get_current_access_url_id();
5281
            } else {
5282
                $sql = "SELECT user_id
5283
                        FROM $userRelUserTable
5284
                        WHERE
5285
                            friend_user_id = $userId AND
5286
                            relation_type = $relationType";
5287
            }
5288
            $result = Database::query($sql);
5289
5290
            if (Database::num_rows($result) > 0) {
5291
                while ($row = Database::fetch_array($result)) {
5292
                    $sql = "DELETE FROM $userRelUserTable
5293
                            WHERE
5294
                                user_id = {$row['user_id']} AND
5295
                                friend_user_id = $userId AND
5296
                                relation_type = $relationType";
5297
                    Database::query($sql);
5298
                }
5299
            }
5300
        }
5301
5302
        if ($deleteUsersBeforeInsert) {
5303
            $sql = "DELETE FROM $userRelUserTable
5304
                    WHERE
5305
                        user_id = $userId AND
5306
                        relation_type = $relationType";
5307
            Database::query($sql);
5308
        }
5309
5310
        // Inserting new user list
5311
        if (is_array($subscribedUsersId)) {
5312
            foreach ($subscribedUsersId as $subscribedUserId) {
5313
                $subscribedUserId = (int) $subscribedUserId;
5314
                $sql = "SELECT id
5315
                        FROM $userRelUserTable
5316
                        WHERE
5317
                            user_id = $subscribedUserId AND
5318
                            friend_user_id = $userId AND
5319
                            relation_type = $relationType";
5320
5321
                $result = Database::query($sql);
5322
                $num = Database::num_rows($result);
5323
                if ($num === 0) {
5324
                    $date = api_get_utc_datetime();
5325
                    $sql = "INSERT INTO $userRelUserTable (user_id, friend_user_id, relation_type, last_edit)
5326
                            VALUES ($subscribedUserId, $userId, $relationType, '$date')";
5327
                    $result = Database::query($sql);
5328
                    $affectedRows += Database::affected_rows($result);
5329
                }
5330
            }
5331
        }
5332
5333
        return $affectedRows;
5334
    }
5335
5336
    /**
5337
     * This function check if an user is followed by human resources manager.
5338
     *
5339
     * @param int $user_id
5340
     * @param int $hr_dept_id Human resources manager
5341
     *
5342
     * @return bool
5343
     */
5344
    public static function is_user_followed_by_drh($user_id, $hr_dept_id)
5345
    {
5346
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5347
        $user_id = (int) $user_id;
5348
        $hr_dept_id = (int) $hr_dept_id;
5349
        $result = false;
5350
5351
        $sql = "SELECT user_id FROM $tbl_user_rel_user
5352
                WHERE
5353
                    user_id = $user_id AND
5354
                    friend_user_id = $hr_dept_id AND
5355
                    relation_type = ".USER_RELATION_TYPE_RRHH;
5356
        $rs = Database::query($sql);
5357
        if (Database::num_rows($rs) > 0) {
5358
            $result = true;
5359
        }
5360
5361
        return $result;
5362
    }
5363
5364
    /**
5365
     * Return the user id of teacher or session administrator.
5366
     *
5367
     * @param array $courseInfo
5368
     *
5369
     * @return mixed The user id, or false if the session ID was negative
5370
     */
5371
    public static function get_user_id_of_course_admin_or_session_admin($courseInfo)
5372
    {
5373
        $session = api_get_session_id();
5374
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5375
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5376
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5377
5378
        if (empty($courseInfo)) {
5379
            return false;
5380
        }
5381
5382
        $courseId = $courseInfo['real_id'];
5383
5384
        if ($session == 0 || is_null($session)) {
5385
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
5386
                    INNER JOIN '.$table_course_user.' ru
5387
                    ON ru.user_id = u.id
5388
                    WHERE
5389
                        ru.status = 1 AND
5390
                        ru.c_id = "'.$courseId.'" ';
5391
            $rs = Database::query($sql);
5392
            $num_rows = Database::num_rows($rs);
5393
            if ($num_rows == 1) {
5394
                $row = Database::fetch_array($rs);
5395
5396
                return $row['uid'];
5397
            } else {
5398
                $my_num_rows = $num_rows;
5399
                $my_user_id = Database::result($rs, $my_num_rows - 1, 'uid');
5400
5401
                return $my_user_id;
5402
            }
5403
        } elseif ($session > 0) {
5404
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
5405
                    INNER JOIN '.$table_session_course_user.' sru
5406
                    ON sru.user_id=u.id
5407
                    WHERE
5408
                        sru.c_id="'.$courseId.'" AND
5409
                        sru.status=2';
5410
            $rs = Database::query($sql);
5411
            $row = Database::fetch_array($rs);
5412
5413
            return $row['uid'];
5414
        }
5415
5416
        return false;
5417
    }
5418
5419
    /**
5420
     * Determines if a user is a gradebook certified.
5421
     *
5422
     * @param int $cat_id  The category id of gradebook
5423
     * @param int $user_id The user id
5424
     *
5425
     * @return bool
5426
     */
5427
    public static function is_user_certified($cat_id, $user_id)
5428
    {
5429
        $cat_id = (int) $cat_id;
5430
        $user_id = (int) $user_id;
5431
5432
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
5433
        $sql = 'SELECT path_certificate
5434
                FROM '.$table.'
5435
                WHERE
5436
                    cat_id = "'.$cat_id.'" AND
5437
                    user_id = "'.$user_id.'"';
5438
        $rs = Database::query($sql);
5439
        $row = Database::fetch_array($rs);
5440
5441
        if ($row['path_certificate'] == '' || is_null($row['path_certificate'])) {
5442
            return false;
5443
        }
5444
5445
        return true;
5446
    }
5447
5448
    /**
5449
     * Gets the info about a gradebook certificate for a user by course.
5450
     *
5451
     * @param string $course_code The course code
5452
     * @param int    $user_id     The user id
5453
     *
5454
     * @return array if there is not information return false
5455
     */
5456
    public static function get_info_gradebook_certificate($course_code, $user_id)
5457
    {
5458
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
5459
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
5460
        $session_id = api_get_session_id();
5461
5462
        if (empty($session_id)) {
5463
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
5464
        } else {
5465
            $session_condition = " AND session_id = $session_id";
5466
        }
5467
5468
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.'
5469
                WHERE cat_id = (
5470
                    SELECT id FROM '.$tbl_grade_category.'
5471
                    WHERE
5472
                        course_code = "'.Database::escape_string($course_code).'" '.$session_condition.'
5473
                    LIMIT 1
5474
                ) AND user_id='.intval($user_id);
5475
5476
        $rs = Database::query($sql);
5477
        if (Database::num_rows($rs) > 0) {
5478
            $row = Database::fetch_array($rs, 'ASSOC');
5479
            $score = $row['score_certificate'];
5480
            $category_id = $row['cat_id'];
5481
            $cat = Category::load($category_id);
5482
            $displayscore = ScoreDisplay::instance();
5483
            if (isset($cat) && $displayscore->is_custom()) {
5484
                $grade = $displayscore->display_score(
5485
                    [$score, $cat[0]->get_weight()],
5486
                    SCORE_DIV_PERCENT_WITH_CUSTOM
5487
                );
5488
            } else {
5489
                $grade = $displayscore->display_score(
5490
                    [$score, $cat[0]->get_weight()]
5491
                );
5492
            }
5493
            $row['grade'] = $grade;
5494
5495
            return $row;
5496
        }
5497
5498
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
5499
    }
5500
5501
    /**
5502
     * This function check if the user is a coach inside session course.
5503
     *
5504
     * @param int $user_id    User id
5505
     * @param int $courseId
5506
     * @param int $session_id
5507
     *
5508
     * @return bool True if the user is a coach
5509
     */
5510
    public static function is_session_course_coach($user_id, $courseId, $session_id)
5511
    {
5512
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5513
        // Protect data
5514
        $user_id = intval($user_id);
5515
        $courseId = intval($courseId);
5516
        $session_id = intval($session_id);
5517
        $result = false;
5518
5519
        $sql = "SELECT session_id FROM $table
5520
                WHERE
5521
                  session_id = $session_id AND
5522
                  c_id = $courseId AND
5523
                  user_id = $user_id AND
5524
                  status = 2 ";
5525
        $res = Database::query($sql);
5526
5527
        if (Database::num_rows($res) > 0) {
5528
            $result = true;
5529
        }
5530
5531
        return $result;
5532
    }
5533
5534
    /**
5535
     * This function returns an icon path that represents the favicon of the website of which the url given.
5536
     * Defaults to the current Chamilo favicon.
5537
     *
5538
     * @param string $url1 URL of website where to look for favicon.ico
5539
     * @param string $url2 Optional second URL of website where to look for favicon.ico
5540
     *
5541
     * @return string Path of icon to load
5542
     */
5543
    public static function get_favicon_from_url($url1, $url2 = null)
5544
    {
5545
        $icon_link = '';
5546
        $url = $url1;
5547
        if (empty($url1)) {
5548
            $url = $url2;
5549
            if (empty($url)) {
5550
                $url = api_get_access_url(api_get_current_access_url_id());
5551
                $url = $url[0];
5552
            }
5553
        }
5554
        if (!empty($url)) {
5555
            $pieces = parse_url($url);
5556
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
5557
        }
5558
5559
        return $icon_link;
5560
    }
5561
5562
    /**
5563
     * @param User $user
5564
     */
5565
    public static function add_user_as_admin(User $user)
5566
    {
5567
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
5568
        if ($user) {
0 ignored issues
show
introduced by
$user is of type Chamilo\UserBundle\Entity\User, thus it always evaluated to true.
Loading history...
5569
            $userId = $user->getId();
5570
5571
            if (!self::is_admin($userId)) {
5572
                $sql = "INSERT INTO $table_admin SET user_id = $userId";
5573
                Database::query($sql);
5574
            }
5575
5576
            $user->addRole('ROLE_SUPER_ADMIN');
5577
            self::getManager()->updateUser($user, true);
5578
        }
5579
    }
5580
5581
    /**
5582
     * @param int $userId
5583
     */
5584
    public static function remove_user_admin($userId)
5585
    {
5586
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
5587
        $userId = (int) $userId;
5588
        if (self::is_admin($userId)) {
5589
            $sql = "DELETE FROM $table_admin WHERE user_id = $userId";
5590
            Database::query($sql);
5591
        }
5592
    }
5593
5594
    /**
5595
     * @param string $from
5596
     * @param string $to
5597
     */
5598
    public static function update_all_user_languages($from, $to)
5599
    {
5600
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5601
        $from = Database::escape_string($from);
5602
        $to = Database::escape_string($to);
5603
5604
        if (!empty($to) && !empty($from)) {
5605
            $sql = "UPDATE $table_user SET language = '$to'
5606
                    WHERE language = '$from'";
5607
            Database::query($sql);
5608
        }
5609
    }
5610
5611
    /**
5612
     * Subscribe boss to students.
5613
     *
5614
     * @param int   $bossId  The boss id
5615
     * @param array $usersId The users array
5616
     * @param bool  $deleteOtherAssignedUsers
5617
     *
5618
     * @return int Affected rows
5619
     */
5620
    public static function subscribeBossToUsers($bossId, $usersId, $deleteOtherAssignedUsers = true)
5621
    {
5622
        return self::subscribeUsersToUser(
5623
            $bossId,
5624
            $usersId,
5625
            USER_RELATION_TYPE_BOSS,
5626
            false,
5627
            $deleteOtherAssignedUsers
5628
        );
5629
    }
5630
5631
    /**
5632
     * @param int $userId
5633
     *
5634
     * @return bool
5635
     */
5636
    public static function removeAllBossFromStudent($userId)
5637
    {
5638
        $userId = (int) $userId;
5639
5640
        if (empty($userId)) {
5641
            return false;
5642
        }
5643
5644
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5645
        $sql = "DELETE FROM $userRelUserTable
5646
                WHERE user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
5647
        Database::query($sql);
5648
5649
        return true;
5650
    }
5651
5652
    /**
5653
     * Subscribe boss to students, if $bossList is empty then the boss list will be empty too.
5654
     *
5655
     * @param int   $studentId
5656
     * @param array $bossList
5657
     * @param bool  $sendNotification
5658
     *
5659
     * @return mixed Affected rows or false on failure
5660
     */
5661
    public static function subscribeUserToBossList(
5662
        $studentId,
5663
        $bossList,
5664
        $sendNotification = false
5665
    ) {
5666
        $inserted = 0;
5667
        if (!empty($bossList)) {
5668
            sort($bossList);
5669
            $studentId = (int) $studentId;
5670
            $studentInfo = api_get_user_info($studentId);
5671
5672
            if (empty($studentInfo)) {
5673
                return false;
5674
            }
5675
5676
            $previousBossList = self::getStudentBossList($studentId);
5677
            $previousBossList = !empty($previousBossList) ? array_column($previousBossList, 'boss_id') : [];
5678
            sort($previousBossList);
5679
5680
            // Boss list is the same, nothing changed.
5681
            if ($bossList == $previousBossList) {
5682
                return false;
5683
            }
5684
5685
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5686
            self::removeAllBossFromStudent($studentId);
5687
5688
            foreach ($bossList as $bossId) {
5689
                $bossId = (int) $bossId;
5690
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
5691
                        VALUES ($studentId, $bossId, ".USER_RELATION_TYPE_BOSS.")";
5692
                $insertId = Database::query($sql);
5693
5694
                if ($insertId) {
5695
                    if ($sendNotification) {
5696
                        $name = $studentInfo['complete_name'];
5697
                        $url = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?student='.$studentId;
5698
                        $url = Display::url($url, $url);
5699
                        $subject = sprintf(get_lang('UserXHasBeenAssignedToBoss'), $name);
5700
                        $message = sprintf(get_lang('UserXHasBeenAssignedToBossWithUrlX'), $name, $url);
5701
                        MessageManager::send_message_simple(
5702
                            $bossId,
5703
                            $subject,
5704
                            $message
5705
                        );
5706
                    }
5707
                    $inserted++;
5708
                }
5709
            }
5710
        } else {
5711
            self::removeAllBossFromStudent($studentId);
5712
        }
5713
5714
        return $inserted;
5715
    }
5716
5717
    /**
5718
     * Get users followed by student boss.
5719
     *
5720
     * @param int    $userId
5721
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
5722
     * @param bool   $getOnlyUserId
5723
     * @param bool   $getSql
5724
     * @param bool   $getCount
5725
     * @param int    $from
5726
     * @param int    $numberItems
5727
     * @param int    $column
5728
     * @param string $direction
5729
     * @param int    $active
5730
     * @param string $lastConnectionDate
5731
     *
5732
     * @return array users
5733
     */
5734
    public static function getUsersFollowedByStudentBoss(
5735
        $userId,
5736
        $userStatus = 0,
5737
        $getOnlyUserId = false,
5738
        $getSql = false,
5739
        $getCount = false,
5740
        $from = null,
5741
        $numberItems = null,
5742
        $column = null,
5743
        $direction = null,
5744
        $active = null,
5745
        $lastConnectionDate = null
5746
    ) {
5747
        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...
5748
            $userId,
5749
            $userStatus,
5750
            $getOnlyUserId,
5751
            $getSql,
5752
            $getCount,
5753
            $from,
5754
            $numberItems,
5755
            $column,
5756
            $direction,
5757
            $active,
5758
            $lastConnectionDate,
5759
            STUDENT_BOSS
5760
        );
5761
    }
5762
5763
    /**
5764
     * @return array
5765
     */
5766
    public static function getOfficialCodeGrouped()
5767
    {
5768
        $user = Database::get_main_table(TABLE_MAIN_USER);
5769
        $sql = "SELECT DISTINCT official_code
5770
                FROM $user
5771
                GROUP BY official_code";
5772
        $result = Database::query($sql);
5773
        $values = Database::store_result($result, 'ASSOC');
5774
        $result = [];
5775
        foreach ($values as $value) {
5776
            $result[$value['official_code']] = $value['official_code'];
5777
        }
5778
5779
        return $result;
5780
    }
5781
5782
    /**
5783
     * @param string $officialCode
5784
     *
5785
     * @return array
5786
     */
5787
    public static function getUsersByOfficialCode($officialCode)
5788
    {
5789
        $user = Database::get_main_table(TABLE_MAIN_USER);
5790
        $officialCode = Database::escape_string($officialCode);
5791
5792
        $sql = "SELECT DISTINCT id
5793
                FROM $user
5794
                WHERE official_code = '$officialCode'
5795
                ";
5796
        $result = Database::query($sql);
5797
5798
        $users = [];
5799
        while ($row = Database::fetch_array($result)) {
5800
            $users[] = $row['id'];
5801
        }
5802
5803
        return $users;
5804
    }
5805
5806
    /**
5807
     * Calc the expended time (in seconds) by a user in a course.
5808
     *
5809
     * @param int    $userId    The user id
5810
     * @param int    $courseId  The course id
5811
     * @param int    $sessionId Optional. The session id
5812
     * @param string $from      Optional. From date
5813
     * @param string $until     Optional. Until date
5814
     *
5815
     * @return int The time
5816
     */
5817
    public static function getTimeSpentInCourses(
5818
        $userId,
5819
        $courseId,
5820
        $sessionId = 0,
5821
        $from = '',
5822
        $until = ''
5823
    ) {
5824
        $userId = (int) $userId;
5825
        $sessionId = (int) $sessionId;
5826
5827
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5828
        $whereConditions = [
5829
            'user_id = ? ' => $userId,
5830
            'AND c_id = ? ' => $courseId,
5831
            'AND session_id = ? ' => $sessionId,
5832
        ];
5833
5834
        if (!empty($from) && !empty($until)) {
5835
            $whereConditions["AND (login_course_date >= '?' "] = $from;
5836
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
5837
        }
5838
5839
        $trackResult = Database::select(
5840
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
5841
            $trackCourseAccessTable,
5842
            [
5843
                'where' => $whereConditions,
5844
            ],
5845
            'first'
5846
        );
5847
5848
        if ($trackResult != false) {
5849
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
5850
        }
5851
5852
        return 0;
5853
    }
5854
5855
    /**
5856
     * Get the boss user ID from a followed user id.
5857
     *
5858
     * @param $userId
5859
     *
5860
     * @return bool
5861
     */
5862
    public static function getFirstStudentBoss($userId)
5863
    {
5864
        $userId = (int) $userId;
5865
        if ($userId > 0) {
5866
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5867
            $row = Database::select(
5868
                'DISTINCT friend_user_id AS boss_id',
5869
                $userRelTable,
5870
                [
5871
                    'where' => [
5872
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
5873
                            $userId,
5874
                            USER_RELATION_TYPE_BOSS,
5875
                        ],
5876
                    ],
5877
                ]
5878
            );
5879
            if (!empty($row)) {
5880
                return $row[0]['boss_id'];
5881
            }
5882
        }
5883
5884
        return false;
5885
    }
5886
5887
    /**
5888
     * Get the boss user ID from a followed user id.
5889
     *
5890
     * @param int $userId student id
5891
     *
5892
     * @return array
5893
     */
5894
    public static function getStudentBossList($userId)
5895
    {
5896
        $userId = (int) $userId;
5897
5898
        if ($userId > 0) {
5899
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5900
            $result = Database::select(
5901
                'DISTINCT friend_user_id AS boss_id',
5902
                $userRelTable,
5903
                [
5904
                    'where' => [
5905
                        'user_id = ? AND relation_type = ? ' => [
5906
                            $userId,
5907
                            USER_RELATION_TYPE_BOSS,
5908
                        ],
5909
                    ],
5910
                ]
5911
            );
5912
5913
            return $result;
5914
        }
5915
5916
        return [];
5917
    }
5918
5919
    /**
5920
     * @param int $bossId
5921
     * @param int $studentId
5922
     *
5923
     * @return bool
5924
     */
5925
    public static function userIsBossOfStudent($bossId, $studentId)
5926
    {
5927
        $result = false;
5928
        $bossList = self::getStudentBossList($studentId);
5929
        if (!empty($bossList)) {
5930
            $bossList = array_column($bossList, 'boss_id');
5931
            if (in_array($bossId, $bossList)) {
5932
                $result = true;
5933
            }
5934
        }
5935
5936
        return $result;
5937
    }
5938
5939
    /**
5940
     * Displays the name of the user and makes the link to the user profile.
5941
     *
5942
     * @param array $userInfo
5943
     *
5944
     * @return string
5945
     */
5946
    public static function getUserProfileLink($userInfo)
5947
    {
5948
        if (isset($userInfo) && isset($userInfo['user_id'])) {
5949
            return Display::url(
5950
                $userInfo['complete_name_with_username'],
5951
                $userInfo['profile_url']
5952
            );
5953
        }
5954
5955
        return get_lang('Anonymous');
5956
    }
5957
5958
    /**
5959
     * Get users whose name matches $firstname and $lastname.
5960
     *
5961
     * @param string $firstname Firstname to search
5962
     * @param string $lastname  Lastname to search
5963
     *
5964
     * @return array The user list
5965
     */
5966
    public static function getUsersByName($firstname, $lastname)
5967
    {
5968
        $firstname = Database::escape_string($firstname);
5969
        $lastname = Database::escape_string($lastname);
5970
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
5971
5972
        $sql = <<<SQL
5973
            SELECT id, username, lastname, firstname
5974
            FROM $userTable
5975
            WHERE
5976
                firstname LIKE '$firstname%' AND
5977
                lastname LIKE '$lastname%'
5978
SQL;
5979
        $result = Database::query($sql);
5980
        $users = [];
5981
        while ($resultData = Database::fetch_object($result)) {
5982
            $users[] = $resultData;
5983
        }
5984
5985
        return $users;
5986
    }
5987
5988
    /**
5989
     * @param int $optionSelected
5990
     *
5991
     * @return string
5992
     */
5993
    public static function getUserSubscriptionTab($optionSelected = 1)
5994
    {
5995
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
5996
        if (($allowAdmin === 'true' && api_is_allowed_to_edit()) ||
5997
            api_is_platform_admin()
5998
        ) {
5999
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
6000
6001
            $headers = [
6002
                [
6003
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
6004
                    'content' => get_lang('Students'),
6005
                ],
6006
                [
6007
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
6008
                    'content' => get_lang('Teachers'),
6009
                ],
6010
                /*[
6011
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
6012
                    'content' => get_lang('Students'),
6013
                ],
6014
                [
6015
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
6016
                    'content' => get_lang('Teachers'),
6017
                ],*/
6018
                [
6019
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
6020
                    'content' => get_lang('Groups'),
6021
                ],
6022
                [
6023
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
6024
                    'content' => get_lang('Classes'),
6025
                ],
6026
            ];
6027
6028
            return Display::tabsOnlyLink($headers, $optionSelected);
6029
        }
6030
6031
        return '';
6032
    }
6033
6034
    /**
6035
     * Make sure this function is protected because it does NOT check password!
6036
     *
6037
     * This function defines globals.
6038
     *
6039
     * @param int  $userId
6040
     * @param bool $checkIfUserCanLoginAs
6041
     *
6042
     * @return bool
6043
     *
6044
     * @author Evie Embrechts
6045
     * @author Yannick Warnier <[email protected]>
6046
     */
6047
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
6048
    {
6049
        $userId = (int) $userId;
6050
        $userInfo = api_get_user_info($userId);
6051
6052
        // Check if the user is allowed to 'login_as'
6053
        $canLoginAs = true;
6054
        if ($checkIfUserCanLoginAs) {
6055
            $canLoginAs = api_can_login_as($userId);
6056
        }
6057
6058
        if (!$canLoginAs || empty($userInfo)) {
6059
            return false;
6060
        }
6061
6062
        if ($userId) {
6063
            $logInfo = [
6064
                'tool' => 'logout',
6065
                'tool_id' => 0,
6066
                'tool_id_detail' => 0,
6067
                'action' => '',
6068
                'info' => 'Change user (login as)',
6069
            ];
6070
            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

6070
            Event::/** @scrutinizer ignore-call */ 
6071
                   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...
6071
6072
            // Logout the current user
6073
            self::loginDelete(api_get_user_id());
6074
6075
            Session::erase('_user');
6076
            Session::erase('is_platformAdmin');
6077
            Session::erase('is_allowedCreateCourse');
6078
            Session::erase('_uid');
6079
6080
            // Cleaning session variables
6081
            $_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...
6082
            $_user['lastName'] = $userInfo['lastname'];
6083
            $_user['mail'] = $userInfo['email'];
6084
            $_user['official_code'] = $userInfo['official_code'];
6085
            $_user['picture_uri'] = $userInfo['picture_uri'];
6086
            $_user['user_id'] = $userId;
6087
            $_user['id'] = $userId;
6088
            $_user['status'] = $userInfo['status'];
6089
6090
            // Filling session variables with new data
6091
            Session::write('_uid', $userId);
6092
            Session::write('_user', $userInfo);
6093
            Session::write('is_platformAdmin', (bool) self::is_admin($userId));
6094
            Session::write('is_allowedCreateCourse', $userInfo['status'] == 1);
6095
            // will be useful later to know if the user is actually an admin or not (example reporting)
6096
            Session::write('login_as', true);
6097
            $logInfo = [
6098
                'tool' => 'login',
6099
                'tool_id' => 0,
6100
                'tool_id_detail' => 0,
6101
                'info' => $userId,
6102
            ];
6103
            Event::registerLog($logInfo);
6104
6105
            return true;
6106
        }
6107
6108
        return false;
6109
    }
6110
6111
    /**
6112
     * Remove all login records from the track_e_online stats table,
6113
     * for the given user ID.
6114
     *
6115
     * @param int $userId User ID
6116
     */
6117
    public static function loginDelete($userId)
6118
    {
6119
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6120
        $userId = (int) $userId;
6121
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
6122
        Database::query($query);
6123
    }
6124
6125
    /**
6126
     * Login as first admin user registered in the platform.
6127
     *
6128
     * @return array
6129
     */
6130
    public static function logInAsFirstAdmin()
6131
    {
6132
        $adminList = self::get_all_administrators();
6133
6134
        if (!empty($adminList)) {
6135
            $userInfo = current($adminList);
6136
            if (!empty($userInfo)) {
6137
                $result = self::loginAsUser($userInfo['user_id'], false);
6138
                if ($result && api_is_platform_admin()) {
6139
                    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...
6140
                }
6141
            }
6142
        }
6143
6144
        return [];
6145
    }
6146
6147
    /**
6148
     * Check if user is teacher of a student based in their courses.
6149
     *
6150
     * @param $teacherId
6151
     * @param $studentId
6152
     *
6153
     * @return array
6154
     */
6155
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
6156
    {
6157
        $courses = CourseManager::getCoursesFollowedByUser(
6158
            $teacherId,
6159
            COURSEMANAGER
6160
        );
6161
        if (empty($courses)) {
6162
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
6163
        }
6164
6165
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
6166
        if (empty($coursesFromUser)) {
6167
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
6168
        }
6169
6170
        $coursesCodeList = array_column($courses, 'code');
6171
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
6172
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
6173
        $commonCourses = array_filter($commonCourses);
6174
6175
        if (!empty($commonCourses)) {
6176
            return $commonCourses;
6177
        }
6178
6179
        return [];
6180
    }
6181
6182
    /**
6183
     * @param int $teacherId
6184
     * @param int $studentId
6185
     *
6186
     * @return bool
6187
     */
6188
    public static function isTeacherOfStudent($teacherId, $studentId)
6189
    {
6190
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
6191
            $teacherId,
6192
            $studentId
6193
        );
6194
6195
        if (!empty($courses)) {
6196
            return true;
6197
        }
6198
6199
        return false;
6200
    }
6201
6202
    /**
6203
     * Send user confirmation mail.
6204
     *
6205
     * @param User $user
6206
     *
6207
     * @throws Exception
6208
     */
6209
    public static function sendUserConfirmationMail(User $user)
6210
    {
6211
        $uniqueId = api_get_unique_id();
6212
        $user->setConfirmationToken($uniqueId);
6213
6214
        Database::getManager()->persist($user);
6215
        Database::getManager()->flush();
6216
6217
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
6218
6219
        // Check if the user was originally set for an automated subscription to a course or session
6220
        $courseCodeToRedirect = Session::read('course_redirect');
6221
        $sessionToRedirect = Session::read('session_redirect');
6222
        if (!empty($courseCodeToRedirect)) {
6223
            $url .= '&c='.$courseCodeToRedirect;
6224
        }
6225
        if (!empty($sessionToRedirect)) {
6226
            $url .= '&s='.$sessionToRedirect;
6227
        }
6228
        $mailSubject = get_lang('RegistrationConfirmation');
6229
        $mailBody = get_lang('RegistrationConfirmationEmailMessage')
6230
            .PHP_EOL
6231
            .Display::url($url, $url);
6232
6233
        api_mail_html(
6234
            self::formatUserFullName($user),
6235
            $user->getEmail(),
6236
            $mailSubject,
6237
            $mailBody
6238
        );
6239
        Display::addFlash(Display::return_message(get_lang('CheckYourEmailAndFollowInstructions')));
6240
    }
6241
6242
    /**
6243
     * Anonymize a user. Replace personal info by anonymous info.
6244
     *
6245
     * @param int  $userId   User id
6246
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
6247
     *
6248
     * @throws \Exception
6249
     *
6250
     * @return bool
6251
     * @assert (0) === false
6252
     */
6253
    public static function anonymize($userId, $deleteIP = true)
6254
    {
6255
        global $debug;
6256
6257
        $userId = (int) $userId;
6258
6259
        if (empty($userId)) {
6260
            return false;
6261
        }
6262
6263
        $em = Database::getManager();
6264
        $user = api_get_user_entity($userId);
6265
        $uniqueId = uniqid('anon', true);
6266
        $user
6267
            ->setFirstname($uniqueId)
6268
            ->setLastname($uniqueId)
6269
            ->setBiography('')
6270
            ->setAddress('')
6271
            ->setCurriculumItems(null)
6272
            ->setDateOfBirth(null)
6273
            ->setCompetences('')
6274
            ->setDiplomas('')
6275
            ->setOpenarea('')
6276
            ->setTeach('')
6277
            ->setProductions(null)
6278
            ->setOpenid('')
6279
            ->setEmailCanonical($uniqueId.'@example.com')
6280
            ->setEmail($uniqueId.'@example.com')
6281
            ->setUsername($uniqueId)
6282
            ->setUsernameCanonical($uniqueId)
6283
            ->setPhone('')
6284
            ->setOfficialCode('')
6285
        ;
6286
6287
        self::deleteUserPicture($userId);
6288
        self::cleanUserRequestsOfRemoval($userId);
6289
6290
        // The IP address is a border-case personal data, as it does
6291
        // not directly allow for personal identification (it is not
6292
        // a completely safe value in most countries - the IP could
6293
        // be used by neighbours and crackers)
6294
        if ($deleteIP) {
6295
            $substitute = '127.0.0.1';
6296
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
6297
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
6298
            $res = Database::query($sql);
6299
            if ($res === false && $debug > 0) {
6300
                error_log("Could not anonymize IP address for user $userId ($sql)");
6301
            }
6302
6303
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6304
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6305
            $res = Database::query($sql);
6306
            if ($res === false && $debug > 0) {
6307
                error_log("Could not anonymize IP address for user $userId ($sql)");
6308
            }
6309
6310
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6311
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE exe_user_id = $userId";
6312
            $res = Database::query($sql);
6313
            if ($res === false && $debug > 0) {
6314
                error_log("Could not anonymize IP address for user $userId ($sql)");
6315
            }
6316
6317
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6318
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6319
            $res = Database::query($sql);
6320
            if ($res === false && $debug > 0) {
6321
                error_log("Could not anonymize IP address for user $userId ($sql)");
6322
            }
6323
6324
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6325
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6326
            $res = Database::query($sql);
6327
            if ($res === false && $debug > 0) {
6328
                error_log("Could not anonymize IP address for user $userId ($sql)");
6329
            }
6330
6331
            $table = Database::get_course_table(TABLE_WIKI);
6332
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6333
            $res = Database::query($sql);
6334
            if ($res === false && $debug > 0) {
6335
                error_log("Could not anonymize IP address for user $userId ($sql)");
6336
            }
6337
6338
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
6339
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
6340
            $res = Database::query($sql);
6341
            if ($res === false && $debug > 0) {
6342
                error_log("Could not anonymize IP address for user $userId ($sql)");
6343
            }
6344
6345
            $table = Database::get_course_table(TABLE_WIKI);
6346
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6347
            $res = Database::query($sql);
6348
            if ($res === false && $debug > 0) {
6349
                error_log("Could not anonymize IP address for user $userId ($sql)");
6350
            }
6351
        }
6352
        $em->persist($user);
6353
        $em->flush($user);
6354
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
6355
6356
        return true;
6357
    }
6358
6359
    /**
6360
     * @param int $userId
6361
     *
6362
     * @throws Exception
6363
     *
6364
     * @return string
6365
     */
6366
    public static function anonymizeUserWithVerification($userId)
6367
    {
6368
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
6369
6370
        $message = '';
6371
        if (api_is_platform_admin() ||
6372
            ($allowDelete && api_is_session_admin())
6373
        ) {
6374
            $userToUpdateInfo = api_get_user_info($userId);
6375
            $currentUserId = api_get_user_id();
6376
6377
            if ($userToUpdateInfo &&
6378
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
6379
            ) {
6380
                if ($userId != $currentUserId &&
6381
                    self::anonymize($userId)
6382
                ) {
6383
                    $message = Display::return_message(
6384
                        sprintf(get_lang('UserXAnonymized'), $userToUpdateInfo['complete_name_with_username']),
6385
                        'confirmation'
6386
                    );
6387
                } else {
6388
                    $message = Display::return_message(
6389
                        sprintf(get_lang('CannotAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
6390
                        'error'
6391
                    );
6392
                }
6393
            } else {
6394
                $message = Display::return_message(
6395
                    sprintf(get_lang('NoPermissionToAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
6396
                    'error'
6397
                );
6398
            }
6399
        }
6400
6401
        return $message;
6402
    }
6403
6404
    /**
6405
     * @param int $userId
6406
     *
6407
     * @throws Exception
6408
     *
6409
     * @return string
6410
     */
6411
    public static function deleteUserWithVerification($userId)
6412
    {
6413
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
6414
        $message = Display::return_message(get_lang('CannotDeleteUser'), 'error');
6415
        $userToUpdateInfo = api_get_user_info($userId);
6416
6417
        // User must exist.
6418
        if (empty($userToUpdateInfo)) {
6419
            return $message;
6420
        }
6421
6422
        $currentUserId = api_get_user_id();
6423
6424
        // Cannot delete myself.
6425
        if ($userId == $currentUserId) {
6426
            return $message;
6427
        }
6428
6429
        if (api_is_platform_admin() ||
6430
            ($allowDelete && api_is_session_admin())
6431
        ) {
6432
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
6433
                if (self::delete_user($userId)) {
6434
                    $message = Display::return_message(
6435
                        get_lang('UserDeleted').': '.$userToUpdateInfo['complete_name_with_username'],
6436
                        'confirmation'
6437
                    );
6438
                } else {
6439
                    $message = Display::return_message(get_lang('CannotDeleteUserBecauseOwnsCourse'), 'error');
6440
                }
6441
            }
6442
        }
6443
6444
        return $message;
6445
    }
6446
6447
    /**
6448
     * @return array
6449
     */
6450
    public static function createDataPrivacyExtraFields()
6451
    {
6452
        self::create_extra_field(
6453
            'request_for_legal_agreement_consent_removal_justification',
6454
            1, //text
6455
            'Request for legal agreement consent removal justification	',
6456
            ''
6457
        );
6458
6459
        self::create_extra_field(
6460
            'request_for_delete_account_justification',
6461
            1, //text
6462
            'Request for delete account justification',
6463
            ''
6464
        );
6465
6466
        $extraFieldId = self::create_extra_field(
6467
            'request_for_legal_agreement_consent_removal',
6468
            1, //text
6469
            'Request for legal agreement consent removal',
6470
            ''
6471
        );
6472
6473
        $extraFieldIdDeleteAccount = self::create_extra_field(
6474
            'request_for_delete_account',
6475
            1, //text
6476
            'Request for delete user account',
6477
            ''
6478
        );
6479
6480
        return [
6481
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
6482
            'delete_legal' => $extraFieldId,
6483
        ];
6484
    }
6485
6486
    /**
6487
     * @param int $userId
6488
     */
6489
    public static function cleanUserRequestsOfRemoval($userId)
6490
    {
6491
        $userId = (int) $userId;
6492
6493
        $extraFieldValue = new ExtraFieldValue('user');
6494
        $extraFieldsToDelete = [
6495
            'legal_accept',
6496
            'request_for_legal_agreement_consent_removal',
6497
            'request_for_legal_agreement_consent_removal_justification',
6498
            'request_for_delete_account_justification', // just in case delete also this
6499
            'request_for_delete_account',
6500
        ];
6501
6502
        foreach ($extraFieldsToDelete as $variable) {
6503
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
6504
                $userId,
6505
                $variable
6506
            );
6507
            if ($value && isset($value['id'])) {
6508
                $extraFieldValue->delete($value['id']);
6509
            }
6510
        }
6511
    }
6512
6513
    /**
6514
     * @param array $userInfo
6515
     * @param int   $searchYear
6516
     *
6517
     * @throws Exception
6518
     *
6519
     * @return array
6520
     */
6521
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
6522
    {
6523
        $timezone = new DateTimeZone(api_get_timezone());
6524
6525
        $sessions = [];
6526
        if (DRH == $userInfo['status']) {
6527
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
6528
        } elseif (api_is_platform_admin(true)) {
6529
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
6530
        } else {
6531
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
6532
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
6533
6534
            foreach ($sessionsByCategory as $sessionsInCategory) {
6535
                $sessions = array_merge($sessions, $sessionsInCategory);
6536
            }
6537
        }
6538
6539
        $sessions = array_map(
6540
            function ($sessionInfo) {
6541
                if (!isset($sessionInfo['session_id'])) {
6542
                    $sessionInfo['session_id'] = $sessionInfo['id'];
6543
                }
6544
                if (!isset($sessionInfo['session_name'])) {
6545
                    $sessionInfo['session_name'] = $sessionInfo['name'];
6546
                }
6547
6548
                return $sessionInfo;
6549
            },
6550
            $sessions
6551
        );
6552
6553
        $calendarSessions = [];
6554
6555
        foreach ($sessions as $sessionInfo) {
6556
            if (!empty($sessionInfo['duration'])) {
6557
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
6558
                    $sessionInfo['session_id'],
6559
                    $userInfo['id']
6560
                );
6561
6562
                if (empty($courseAccess)) {
6563
                    continue;
6564
                }
6565
6566
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
6567
                $lastAccessDate = clone $firstAcessDate;
6568
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
6569
6570
                $firstAccessYear = (int) $firstAcessDate->format('Y');
6571
                $lastAccessYear = (int) $lastAccessDate->format('Y');
6572
6573
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
6574
                    $calendarSessions[$sessionInfo['session_id']] = [
6575
                        'name' => $sessionInfo['session_name'],
6576
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
6577
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
6578
                    ];
6579
                }
6580
6581
                continue;
6582
            }
6583
6584
            $accessStartDate = !empty($sessionInfo['access_start_date'])
6585
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6586
                : null;
6587
            $accessEndDate = !empty($sessionInfo['access_end_date'])
6588
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6589
                : null;
6590
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
6591
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
6592
6593
            $isValid = false;
6594
6595
            if ($accessStartYear && $accessEndYear) {
6596
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
6597
                    $isValid = true;
6598
                }
6599
            }
6600
6601
            if ($accessStartYear && !$accessEndYear) {
6602
                if ($accessStartYear == $searchYear) {
6603
                    $isValid = true;
6604
                }
6605
            }
6606
6607
            if (!$accessStartYear && $accessEndYear) {
6608
                if ($accessEndYear == $searchYear) {
6609
                    $isValid = true;
6610
                }
6611
            }
6612
6613
            if ($isValid) {
6614
                $calendarSessions[$sessionInfo['session_id']] = [
6615
                    'name' => $sessionInfo['session_name'],
6616
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
6617
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
6618
                ];
6619
            }
6620
        }
6621
6622
        return $calendarSessions;
6623
    }
6624
6625
    /**
6626
     * Get sessions info for planification calendar.
6627
     *
6628
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
6629
     * @param int   $searchYear
6630
     *
6631
     * @throws Exception
6632
     *
6633
     * @return array
6634
     */
6635
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
6636
    {
6637
        $timezone = new DateTimeZone(api_get_timezone());
6638
        $calendar = [];
6639
6640
        foreach ($sessionsList as $sessionId => $sessionInfo) {
6641
            $startDate = $sessionInfo['access_start_date']
6642
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6643
                : null;
6644
            $endDate = $sessionInfo['access_end_date']
6645
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6646
                : null;
6647
6648
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
6649
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
6650
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
6651
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
6652
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
6653
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
6654
6655
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
6656
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
6657
6658
            $calendar[] = [
6659
                'id' => $sessionId,
6660
                'name' => $sessionInfo['name'],
6661
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
0 ignored issues
show
Bug introduced by
It seems like $startDate can also be of type DateTime; however, parameter $startDate of SessionManager::convertSessionDateToString() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

6661
                'human_date' => SessionManager::convertSessionDateToString(/** @scrutinizer ignore-type */ $startDate, $endDate, false, true),
Loading history...
Bug introduced by
It seems like $endDate can also be of type DateTime; however, parameter $endDate of SessionManager::convertSessionDateToString() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

6661
                'human_date' => SessionManager::convertSessionDateToString($startDate, /** @scrutinizer ignore-type */ $endDate, false, true),
Loading history...
6662
                'start_in_last_year' => $startYear < $searchYear,
6663
                'end_in_next_year' => $endYear > $searchYear,
6664
                'no_start' => !$startWeek,
6665
                'no_end' => !$endWeek,
6666
                'start' => $start,
6667
                'duration' => $duration > 0 ? $duration : 1,
6668
            ];
6669
        }
6670
6671
        usort(
6672
            $calendar,
6673
            function ($sA, $sB) {
6674
                if ($sA['start'] == $sB['start']) {
6675
                    return 0;
6676
                }
6677
6678
                if ($sA['start'] < $sB['start']) {
6679
                    return -1;
6680
                }
6681
6682
                return 1;
6683
            }
6684
        );
6685
6686
        return $calendar;
6687
    }
6688
6689
    /**
6690
     * Return the user's full name. Optionally with the username.
6691
     *
6692
     * @param User $user
6693
     * @param bool $includeUsername Optional. By default username is not included.
6694
     *
6695
     * @return string
6696
     */
6697
    public static function formatUserFullName(User $user, $includeUsername = false)
6698
    {
6699
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
6700
6701
        if ($includeUsername && api_get_configuration_value('hide_username_with_complete_name') !== true) {
6702
            $username = $user->getUsername();
6703
6704
            return "$fullName ($username)";
6705
        }
6706
6707
        return $fullName;
6708
    }
6709
6710
    /**
6711
     * @param int $userId
6712
     *
6713
     * @return array
6714
     */
6715
    public static function getUserCareers($userId)
6716
    {
6717
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6718
        $tableCareer = Database::get_main_table(TABLE_CAREER);
6719
        $userId = (int) $userId;
6720
6721
        $sql = "SELECT c.id, c.name
6722
                FROM $table uc
6723
                INNER JOIN $tableCareer c
6724
                ON uc.career_id = c.id
6725
                WHERE user_id = $userId
6726
                ORDER BY uc.created_at
6727
                ";
6728
        $result = Database::query($sql);
6729
6730
        return Database::store_result($result, 'ASSOC');
6731
    }
6732
6733
    /**
6734
     * @param int $userId
6735
     * @param int $careerId
6736
     */
6737
    public static function addUserCareer($userId, $careerId)
6738
    {
6739
        if (!api_get_configuration_value('allow_career_users')) {
6740
            return false;
6741
        }
6742
6743
        if (self::userHasCareer($userId, $careerId) === false) {
6744
            $params = ['user_id' => $userId, 'career_id' => $careerId, 'created_at' => api_get_utc_datetime(), 'updated_at' => api_get_utc_datetime()];
6745
            $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6746
            Database::insert($table, $params);
6747
        }
6748
6749
        return true;
6750
    }
6751
6752
    /**
6753
     * @param int   $userCareerId
6754
     * @param array $data
6755
     *
6756
     * @return bool
6757
     */
6758
    public static function updateUserCareer($userCareerId, $data)
6759
    {
6760
        if (!api_get_configuration_value('allow_career_users')) {
6761
            return false;
6762
        }
6763
6764
        $params = ['extra_data' => $data, 'updated_at' => api_get_utc_datetime()];
6765
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6766
        Database::update(
6767
            $table,
6768
            $params,
6769
            ['id = ?' => (int) $userCareerId]
6770
        );
6771
6772
        return true;
6773
    }
6774
6775
    /**
6776
     * @param int $userId
6777
     * @param int $careerId
6778
     *
6779
     * @return array
6780
     */
6781
    public static function getUserCareer($userId, $careerId)
6782
    {
6783
        $userId = (int) $userId;
6784
        $careerId = (int) $careerId;
6785
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6786
6787
        $sql = "SELECT * FROM $table WHERE user_id = $userId AND career_id = $careerId";
6788
        $result = Database::query($sql);
6789
6790
        return Database::fetch_array($result, 'ASSOC');
6791
    }
6792
6793
    /**
6794
     * @param int $userId
6795
     * @param int $careerId
6796
     *
6797
     * @return bool
6798
     */
6799
    public static function userHasCareer($userId, $careerId)
6800
    {
6801
        $userId = (int) $userId;
6802
        $careerId = (int) $careerId;
6803
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6804
6805
        $sql = "SELECT id FROM $table WHERE user_id = $userId AND career_id = $careerId";
6806
        $result = Database::query($sql);
6807
6808
        return Database::num_rows($result) > 0;
6809
    }
6810
6811
    /**
6812
     * @return EncoderFactory
6813
     */
6814
    private static function getEncoderFactory()
6815
    {
6816
        $encryption = self::getPasswordEncryption();
6817
        $encoders = [
6818
            'Chamilo\\UserBundle\\Entity\\User' => new \Chamilo\UserBundle\Security\Encoder($encryption),
6819
        ];
6820
6821
        $encoderFactory = new EncoderFactory($encoders);
6822
6823
        return $encoderFactory;
6824
    }
6825
6826
    /**
6827
     * @param User $user
6828
     *
6829
     * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface
6830
     */
6831
    private static function getEncoder(User $user)
6832
    {
6833
        $encoderFactory = self::getEncoderFactory();
6834
6835
        return $encoderFactory->getEncoder($user);
6836
    }
6837
6838
    /**
6839
     * Disables or enables a user.
6840
     *
6841
     * @param int $user_id
6842
     * @param int $active  Enable or disable
6843
     *
6844
     * @return bool True on success, false on failure
6845
     * @assert (-1,0) === false
6846
     * @assert (1,1) === true
6847
     */
6848
    private static function change_active_state($user_id, $active)
6849
    {
6850
        $user_id = (int) $user_id;
6851
        $active = (int) $active;
6852
6853
        if (empty($user_id)) {
6854
            return false;
6855
        }
6856
6857
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6858
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
6859
        $r = Database::query($sql);
6860
        $ev = LOG_USER_DISABLE;
6861
        if ($active == 1) {
6862
            $ev = LOG_USER_ENABLE;
6863
        }
6864
        if ($r !== false) {
6865
            Event::addEvent($ev, LOG_USER_ID, $user_id);
6866
        }
6867
6868
        return $r;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $r returns the type Doctrine\DBAL\Driver\Statement which is incompatible with the documented return type boolean.
Loading history...
6869
    }
6870
6871
    /**
6872
     * Get either a Gravatar URL or complete image tag for a specified email address.
6873
     *
6874
     * @param string $email The email address
6875
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
6876
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
6877
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
6878
     * @param bool   $img   True to return a complete IMG tag False for just the URL
6879
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
6880
     *
6881
     * @return string containing either just a URL or a complete image tag
6882
     * @source http://gravatar.com/site/implement/images/php/
6883
     */
6884
    private static function getGravatar(
6885
        $email,
6886
        $s = 80,
6887
        $d = 'mm',
6888
        $r = 'g',
6889
        $img = false,
6890
        $atts = []
6891
    ) {
6892
        $url = 'http://www.gravatar.com/avatar/';
6893
        if (!empty($_SERVER['HTTPS'])) {
6894
            $url = 'https://secure.gravatar.com/avatar/';
6895
        }
6896
        $url .= md5(strtolower(trim($email)));
6897
        $url .= "?s=$s&d=$d&r=$r";
6898
        if ($img) {
6899
            $url = '<img src="'.$url.'"';
6900
            foreach ($atts as $key => $val) {
6901
                $url .= ' '.$key.'="'.$val.'"';
6902
            }
6903
            $url .= ' />';
6904
        }
6905
6906
        return $url;
6907
    }
6908
}
6909