Completed
Push — 1.11.x ( 74dacc...deb3a7 )
by Julito
09:52 queued 10s
created

UserManager::requestUsersToHRManager()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 2
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
6
use Chamilo\CoreBundle\Entity\Repository\AccessUrlRepository;
7
use Chamilo\CoreBundle\Entity\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...
8
use Chamilo\CoreBundle\Entity\SkillRelUserComment;
9
use Chamilo\UserBundle\Entity\User;
10
use Chamilo\UserBundle\Repository\UserRepository;
11
use ChamiloSession as Session;
12
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
13
14
/**
15
 * Class UserManager.
16
 *
17
 * This library provides functions for user management.
18
 * Include/require it in your code to use its functionality.
19
 *
20
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
21
 */
22
class UserManager
23
{
24
    // This constants are deprecated use the constants located in ExtraField
25
    const USER_FIELD_TYPE_TEXT = 1;
26
    const USER_FIELD_TYPE_TEXTAREA = 2;
27
    const USER_FIELD_TYPE_RADIO = 3;
28
    const USER_FIELD_TYPE_SELECT = 4;
29
    const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
30
    const USER_FIELD_TYPE_DATE = 6;
31
    const USER_FIELD_TYPE_DATETIME = 7;
32
    const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
33
    const USER_FIELD_TYPE_DIVIDER = 9;
34
    const USER_FIELD_TYPE_TAG = 10;
35
    const USER_FIELD_TYPE_TIMEZONE = 11;
36
    const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
37
    const USER_FIELD_TYPE_FILE = 13;
38
    const USER_FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
39
40
    private static $encryptionMethod;
41
42
    /**
43
     * Constructor.
44
     *
45
     * @assert () === null
46
     */
47
    public function __construct()
48
    {
49
    }
50
51
    /**
52
     * Repository is use to query the DB, selects, etc.
53
     *
54
     * @return UserRepository
55
     */
56
    public static function getRepository()
57
    {
58
        /** @var UserRepository $userRepository */
59
        $userRepository = Database::getManager()->getRepository('ChamiloUserBundle:User');
60
61
        return $userRepository;
62
    }
63
64
    /**
65
     * Create/update/delete methods are available in the UserManager
66
     * (based in the Sonata\UserBundle\Entity\UserManager).
67
     *
68
     * @return Chamilo\UserBundle\Entity\Manager\UserManager
69
     */
70
    public static function getManager()
71
    {
72
        static $userManager;
73
74
        if (!isset($userManager)) {
75
            $encoderFactory = self::getEncoderFactory();
76
            $userManager = new Chamilo\UserBundle\Entity\Manager\UserManager(
77
                $encoderFactory,
78
                new \FOS\UserBundle\Util\Canonicalizer(),
79
                new \FOS\UserBundle\Util\Canonicalizer(),
80
                Database::getManager(),
81
                'Chamilo\\UserBundle\\Entity\\User'
82
            );
83
        }
84
85
        return $userManager;
86
    }
87
88
    /**
89
     * @param string $encryptionMethod
90
     */
91
    public static function setPasswordEncryption($encryptionMethod)
92
    {
93
        self::$encryptionMethod = $encryptionMethod;
94
    }
95
96
    /**
97
     * @return bool|mixed
98
     */
99
    public static function getPasswordEncryption()
100
    {
101
        $encryptionMethod = self::$encryptionMethod;
102
        if (empty($encryptionMethod)) {
103
            $encryptionMethod = api_get_configuration_value('password_encryption');
104
        }
105
106
        return $encryptionMethod;
107
    }
108
109
    /**
110
     * Validates the password.
111
     *
112
     * @param $encoded
113
     * @param $raw
114
     * @param $salt
115
     *
116
     * @return bool
117
     */
118
    public static function isPasswordValid($encoded, $raw, $salt)
119
    {
120
        $encoder = new \Chamilo\UserBundle\Security\Encoder(self::getPasswordEncryption());
121
        $validPassword = $encoder->isPasswordValid($encoded, $raw, $salt);
122
123
        return $validPassword;
124
    }
125
126
    /**
127
     * @param string $raw
128
     *
129
     * @return string
130
     */
131
    public static function encryptPassword($raw, User $user)
132
    {
133
        $encoder = self::getEncoder($user);
134
        $encodedPassword = $encoder->encodePassword(
135
            $raw,
136
            $user->getSalt()
137
        );
138
139
        return $encodedPassword;
140
    }
141
142
    /**
143
     * @param int    $userId
144
     * @param string $password
145
     */
146
    public static function updatePassword($userId, $password)
147
    {
148
        $repository = self::getRepository();
149
        /** @var User $user */
150
        $user = $repository->find($userId);
151
        $userManager = self::getManager();
152
        $user->setPlainPassword($password);
153
        $userManager->updateUser($user, true);
154
        Event::addEvent(LOG_USER_PASSWORD_UPDATE, LOG_USER_ID, $userId);
0 ignored issues
show
Bug introduced by
The method addEvent() does not exist on Event. ( Ignorable by Annotation )

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

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

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

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

Loading history...
155
    }
156
157
    /**
158
     * Creates a new user for the platform.
159
     *
160
     * @author Hugues Peeters <[email protected]>,
161
     * @author Roan Embrechts <[email protected]>
162
     *
163
     * @param string        $firstName
164
     * @param string        $lastName
165
     * @param int           $status                  (1 for course tutor, 5 for student, 6 for anonymous)
166
     * @param string        $email
167
     * @param string        $loginName
168
     * @param string        $password
169
     * @param string        $official_code           Any official code (optional)
170
     * @param string        $language                User language    (optional)
171
     * @param string        $phone                   Phone number    (optional)
172
     * @param string        $picture_uri             Picture URI        (optional)
173
     * @param string        $authSource              Authentication source (defaults to 'platform', dependind on constant)
174
     * @param string        $expirationDate          Account expiration date (optional, defaults to null)
175
     * @param int           $active                  Whether the account is enabled or disabled by default
176
     * @param int           $hr_dept_id              The department of HR in which the user is registered (defaults to 0)
177
     * @param array         $extra                   Extra fields
178
     * @param string        $encrypt_method          Used if password is given encrypted. Set to an empty string by default
179
     * @param bool          $send_mail
180
     * @param bool          $isAdmin
181
     * @param string        $address
182
     * @param bool          $sendEmailToAllAdmins
183
     * @param FormValidator $form
184
     * @param int           $creatorId
185
     * @param array         $emailTemplate
186
     * @param string        $redirectToURLAfterLogin
187
     *
188
     * @return mixed new user id - if the new user creation succeeds, false otherwise
189
     * @desc The function tries to retrieve user id from the session.
190
     * If it exists, the current user id is the creator id. If a problem arises,
191
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
192
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
193
     */
194
    public static function create_user(
195
        $firstName,
196
        $lastName,
197
        $status,
198
        $email,
199
        $loginName,
200
        $password,
201
        $official_code = '',
202
        $language = '',
203
        $phone = '',
204
        $picture_uri = '',
205
        $authSource = PLATFORM_AUTH_SOURCE,
206
        $expirationDate = null,
207
        $active = 1,
208
        $hr_dept_id = 0,
209
        $extra = [],
210
        $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

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

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

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

610
                            MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, /** @scrutinizer ignore-type */ $emailBody, $userId);
Loading history...
611
                        }
612
                    }
613
                }
614
615
                if ($sendEmailToAllAdmins) {
616
                    $adminList = self::get_all_administrators();
617
618
                    $tplContent = new Template(
619
                        null,
620
                        false,
621
                        false,
622
                        false,
623
                        false,
624
                        false
625
                    );
626
                    // variables for the default template
627
                    $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
628
                    $tplContent->assign('user_added', $user);
629
                    $renderer = FormValidator::getDefaultRenderer();
630
                    // Form template
631
                    $elementTemplate = ' {label}: {element} <br />';
632
                    $renderer->setElementTemplate($elementTemplate);
633
                    /** @var FormValidator $form */
634
                    $form->freeze(null, $elementTemplate);
635
                    $form->removeElement('submit');
636
                    $formData = $form->returnForm();
637
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
638
                    $tplContent->assign('link', Display::url($url, $url));
639
                    $tplContent->assign('form', $formData);
640
641
                    $layoutContent = $tplContent->get_template('mail/content_registration_platform_to_admin.tpl');
642
                    $emailBody = $tplContent->fetch($layoutContent);
643
644
                    if (!empty($emailBodyTemplate) &&
645
                        isset($emailTemplate['content_registration_platform_to_admin.tpl']) &&
646
                        !empty($emailTemplate['content_registration_platform_to_admin.tpl'])
647
                    ) {
648
                        $emailBody = $mailTemplateManager->parseTemplate(
649
                            $emailTemplate['content_registration_platform_to_admin.tpl'],
650
                            $userInfo
651
                        );
652
                    }
653
654
                    $subject = get_lang('UserAdded');
655
656
                    foreach ($adminList as $adminId => $data) {
657
                        MessageManager::send_message_simple(
658
                            $adminId,
659
                            $subject,
660
                            $emailBody,
661
                            $userId
662
                        );
663
                    }
664
                }
665
                /* ENDS MANAGE EVENT WITH MAIL */
666
            }
667
668
            if (!empty($hook)) {
669
                $hook->setEventData([
670
                    'return' => $userId,
671
                    'originalPassword' => $original_password,
672
                ]);
673
                $hook->notifyCreateUser(HOOK_EVENT_TYPE_POST);
674
            }
675
            Event::addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId);
676
        } else {
677
            Display::addFlash(
678
                Display::return_message(get_lang('ErrorContactPlatformAdmin'))
679
            );
680
681
            return false;
682
        }
683
684
        return $return;
685
    }
686
687
    /**
688
     * Ensure the CAS-authenticated user exists in the database.
689
     *
690
     * @param $casUser string the CAS user identifier
691
     *
692
     * @throws Exception if more than one user share the same CAS user identifier
693
     *
694
     * @return string|bool the recognised user login name or false if not found
695
     */
696
    public static function casUserLoginName($casUser)
697
    {
698
        $loginName = false;
699
700
        // look inside the casUser extra field
701
        if (UserManager::is_extra_field_available('cas_user')) {
702
            $valueModel = new ExtraFieldValue('user');
703
            $itemList = $valueModel->get_item_id_from_field_variable_and_field_value(
704
                'cas_user',
705
                $casUser,
706
                false,
707
                false,
708
                true
709
            );
710
            if (false !== $itemList) {
711
                // at least one user has $casUser in the 'cas_user' extra field
712
                // we attempt to load each candidate user because there might be deleted ones
713
                // (extra field values of a deleted user might remain)
714
                foreach ($itemList as $item) {
715
                    $userId = intval($item['item_id']);
716
                    $user = UserManager::getRepository()->find($userId);
717
                    if (!is_null($user)) {
718
                        if (false === $loginName) {
719
                            $loginName = $user->getUsername();
720
                        } else {
721
                            throw new Exception(get_lang('MoreThanOneUserMatched'));
722
                        }
723
                    }
724
                }
725
            }
726
        }
727
728
        if (false === $loginName) {
729
            // no matching 'cas_user' extra field value, or no such extra field
730
            // falling back to the old behaviour: $casUser must be the login name
731
            $userId = UserManager::get_user_id_from_username($casUser);
732
            if (false !== $userId) {
733
                $loginName = $casUser;
734
            }
735
        }
736
737
        return $loginName;
738
    }
739
740
    /**
741
     * Checks the availability of extra field 'cas_user'
742
     * and creates it if missing.
743
     *
744
     * @throws Exception on failure
745
     */
746
    public static function ensureCASUserExtraFieldExists()
747
    {
748
        if (!UserManager::is_extra_field_available('cas_user')) {
749
            $extraField = new ExtraField('user');
750
            if (false === $extraField->save(
751
                    [
752
                        'variable' => 'cas_user',
753
                        'field_type' => ExtraField::FIELD_TYPE_TEXT,
754
                        'display_text' => get_lang('CAS User Identifier'),
755
                        'visible_to_self' => true,
756
                        'filter' => true,
757
                    ]
758
                )) {
759
                throw new Exception(get_lang('FailedToCreateExtraFieldCasUser'));
760
            }
761
        }
762
    }
763
764
    /**
765
     * Create a CAS-authenticated user from scratch, from its CAS user identifier, with temporary default values.
766
     *
767
     * @param string $casUser the CAS user identifier
768
     *
769
     * @throws Exception on error
770
     *
771
     * @return string the login name of the new user
772
     */
773
    public static function createCASAuthenticatedUserFromScratch($casUser)
774
    {
775
        self::ensureCASUserExtraFieldExists();
776
777
        $loginName = 'cas_user_'.$casUser;
778
        $defaultValue = get_lang("EditInProfile");
779
        require_once __DIR__.'/../../auth/external_login/functions.inc.php';
780
        $userId = external_add_user(
781
            [
782
                'username' => $loginName,
783
                'auth_source' => CAS_AUTH_SOURCE,
784
                'firstname' => $defaultValue,
785
                'lastname' => $defaultValue,
786
                'email' => $defaultValue,
787
            ]
788
        );
789
        if (false === $userId) {
790
            throw new Exception(get_lang('FailedUserCreation'));
791
        }
792
        // Not checking function update_extra_field_value return value because not reliable
793
        self::update_extra_field_value($userId, 'cas_user', $casUser);
794
795
        return $loginName;
796
    }
797
798
    /**
799
     * Create a CAS-authenticated user from LDAP, from its CAS user identifier.
800
     *
801
     * @param $casUser
802
     *
803
     * @throws Exception
804
     *
805
     * @return string login name of the new user
806
     */
807
    public static function createCASAuthenticatedUserFromLDAP($casUser)
808
    {
809
        self::ensureCASUserExtraFieldExists();
810
811
        require_once __DIR__.'/../../auth/external_login/ldap.inc.php';
812
        $login = extldapCasUserLogin($casUser);
813
        if (false !== $login) {
814
            $ldapUser = extldap_authenticate($login, 'nopass', true);
815
            if (false !== $ldapUser) {
816
                require_once __DIR__.'/../../auth/external_login/functions.inc.php';
817
                $user = extldap_get_chamilo_user($ldapUser);
818
                $user['username'] = $login;
819
                $user['auth_source'] = CAS_AUTH_SOURCE;
820
                $userId = external_add_user($user);
821
                if (false !== $userId) {
822
                    // Not checking function update_extra_field_value return value because not reliable
823
                    self::update_extra_field_value($userId, 'cas_user', $casUser);
824
825
                    return $login;
826
                } else {
827
                    throw new Exception('Could not create the new user '.$login);
828
                }
829
            } else {
830
                throw new Exception('Could not load the new user from LDAP using its login '.$login);
0 ignored issues
show
Bug introduced by
Are you sure $login of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

830
                throw new Exception('Could not load the new user from LDAP using its login './** @scrutinizer ignore-type */ $login);
Loading history...
831
            }
832
        } else {
833
            throw new Exception('Could not find the new user from LDAP using its cas user identifier '.$casUser);
834
        }
835
    }
836
837
    /**
838
     * updates user record in database from its LDAP record
839
     * copies relevant LDAP attribute values : firstname, lastname and email.
840
     *
841
     * @param $login string the user login name
842
     *
843
     * @throws Exception when the user login name is not found in the LDAP or in the database
844
     */
845
    public static function updateUserFromLDAP($login)
846
    {
847
        require_once __DIR__.'/../../auth/external_login/ldap.inc.php';
848
849
        $ldapUser = extldap_authenticate($login, 'nopass', true);
850
        if (false === $ldapUser) {
851
            throw new Exception(get_lang('NoSuchUserInLDAP'));
852
        }
853
854
        $user = extldap_get_chamilo_user($ldapUser);
855
        $userInfo = api_get_user_info_from_username($login);
856
        if (false === $userInfo) {
857
            throw new Exception(get_lang('NoSuchUserInInternalDatabase'));
858
        }
859
860
        $userId = UserManager::update_user(
861
            $userInfo['user_id'],
862
            $user["firstname"],
863
            $user["lastname"],
864
            $login,
865
            null,
866
            null,
867
            $user["email"],
868
            $userInfo['status'],
869
            '',
870
            '',
871
            '',
872
            null,
873
            1,
874
            null,
875
            0,
876
            null,
877
            ''
878
        );
879
        if (false === $userId) {
880
            throw new Exception(get_lang('CouldNotUpdateUser'));
881
        }
882
    }
883
884
    /**
885
     * Can user be deleted? This function checks whether there's a course
886
     * in which the given user is the
887
     * only course administrator. If that is the case, the user can't be
888
     * deleted because the course would remain without a course admin.
889
     *
890
     * @param int $user_id The user id
891
     *
892
     * @return bool true if user can be deleted
893
     *
894
     * @assert (null) === false
895
     * @assert (-1) === false
896
     * @assert ('abc') === false
897
     */
898
    public static function canDeleteUser($user_id)
899
    {
900
        $deny = api_get_configuration_value('deny_delete_users');
901
902
        if ($deny) {
903
            return false;
904
        }
905
906
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
907
        $user_id = (int) $user_id;
908
909
        if (empty($user_id)) {
910
            return false;
911
        }
912
913
        $sql = "SELECT * FROM $table_course_user
914
                WHERE status = 1 AND user_id = ".$user_id;
915
        $res = Database::query($sql);
916
        while ($course = Database::fetch_object($res)) {
917
            $sql = "SELECT id FROM $table_course_user
918
                    WHERE status=1 AND c_id = ".intval($course->c_id);
919
            $res2 = Database::query($sql);
920
            if (Database::num_rows($res2) == 1) {
921
                return false;
922
            }
923
        }
924
925
        return true;
926
    }
927
928
    /**
929
     * Delete a user from the platform, and all its belongings. This is a
930
     * very dangerous function that should only be accessible by
931
     * super-admins. Other roles should only be able to disable a user,
932
     * which removes access to the platform but doesn't delete anything.
933
     *
934
     * @param int The ID of th user to be deleted
935
     *
936
     * @throws Exception
937
     *
938
     * @return bool true if user is successfully deleted, false otherwise
939
     * @assert (null) === false
940
     * @assert ('abc') === false
941
     */
942
    public static function delete_user($user_id)
943
    {
944
        $user_id = (int) $user_id;
945
946
        if (empty($user_id)) {
947
            return false;
948
        }
949
950
        if (!self::canDeleteUser($user_id)) {
951
            return false;
952
        }
953
954
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
955
        $usergroup_rel_user = Database::get_main_table(TABLE_USERGROUP_REL_USER);
956
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
957
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
958
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
959
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
960
        $table_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
961
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
962
        $table_group = Database::get_course_table(TABLE_GROUP_USER);
963
        $table_work = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
964
965
        // Unsubscribe the user from all groups in all his courses
966
        $sql = "SELECT c.id
967
                FROM $table_course c
968
                INNER JOIN $table_course_user cu
969
                ON (c.id = cu.c_id)
970
                WHERE
971
                    cu.user_id = '".$user_id."' AND
972
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
973
                ";
974
975
        $res = Database::query($sql);
976
        while ($course = Database::fetch_object($res)) {
977
            $sql = "DELETE FROM $table_group
978
                    WHERE c_id = {$course->id} AND user_id = $user_id";
979
            Database::query($sql);
980
        }
981
982
        // Unsubscribe user from usergroup_rel_user
983
        $sql = "DELETE FROM $usergroup_rel_user WHERE user_id = '".$user_id."'";
984
        Database::query($sql);
985
986
        // Unsubscribe user from all courses
987
        $sql = "DELETE FROM $table_course_user WHERE user_id = '".$user_id."'";
988
        Database::query($sql);
989
990
        // Unsubscribe user from all courses in sessions
991
        $sql = "DELETE FROM $table_session_course_user WHERE user_id = '".$user_id."'";
992
        Database::query($sql);
993
994
        // If the user was added as a id_coach then set the current admin as coach see BT#
995
        $currentUserId = api_get_user_id();
996
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
997
                WHERE id_coach = '".$user_id."'";
998
        Database::query($sql);
999
1000
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
1001
                WHERE session_admin_id = '".$user_id."'";
1002
        Database::query($sql);
1003
1004
        // Unsubscribe user from all sessions
1005
        $sql = "DELETE FROM $table_session_user
1006
                WHERE user_id = '".$user_id."'";
1007
        Database::query($sql);
1008
1009
        if (api_get_configuration_value('plugin_redirection_enabled')) {
1010
            RedirectionPlugin::deleteUserRedirection($user_id);
1011
        }
1012
1013
        // Delete user picture
1014
        /* TODO: Logic about api_get_setting('split_users_upload_directory') == 'true'
1015
        a user has 4 different sized photos to be deleted. */
1016
        $user_info = api_get_user_info($user_id);
1017
1018
        if (strlen($user_info['picture_uri']) > 0) {
1019
            $path = self::getUserPathById($user_id, 'system');
1020
            $img_path = $path.$user_info['picture_uri'];
1021
            if (file_exists($img_path)) {
1022
                unlink($img_path);
1023
            }
1024
        }
1025
1026
        // Delete the personal course categories
1027
        $course_cat_table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
1028
        $sql = "DELETE FROM $course_cat_table WHERE user_id = '".$user_id."'";
1029
        Database::query($sql);
1030
1031
        // Delete user from the admin table
1032
        $sql = "DELETE FROM $table_admin WHERE user_id = '".$user_id."'";
1033
        Database::query($sql);
1034
1035
        // Delete the personal agenda-items from this user
1036
        $agenda_table = Database::get_main_table(TABLE_PERSONAL_AGENDA);
1037
        $sql = "DELETE FROM $agenda_table WHERE user = '".$user_id."'";
1038
        Database::query($sql);
1039
1040
        $gradebook_results_table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_RESULT);
1041
        $sql = 'DELETE FROM '.$gradebook_results_table.' WHERE user_id = '.$user_id;
1042
        Database::query($sql);
1043
1044
        $extraFieldValue = new ExtraFieldValue('user');
1045
        $extraFieldValue->deleteValuesByItem($user_id);
1046
1047
        UrlManager::deleteUserFromAllUrls($user_id);
1048
1049
        if (api_get_setting('allow_social_tool') === 'true') {
1050
            $userGroup = new UserGroup();
1051
            //Delete user from portal groups
1052
            $group_list = $userGroup->get_groups_by_user($user_id);
1053
            if (!empty($group_list)) {
1054
                foreach ($group_list as $group_id => $data) {
1055
                    $userGroup->delete_user_rel_group($user_id, $group_id);
1056
                }
1057
            }
1058
1059
            // Delete user from friend lists
1060
            SocialManager::remove_user_rel_user($user_id, true);
1061
        }
1062
1063
        // Removing survey invitation
1064
        SurveyManager::delete_all_survey_invitations_by_user($user_id);
1065
1066
        // Delete students works
1067
        $sql = "DELETE FROM $table_work WHERE user_id = $user_id AND c_id <> 0";
1068
        Database::query($sql);
1069
1070
        $sql = "UPDATE c_item_property SET to_user_id = NULL
1071
                WHERE to_user_id = '".$user_id."'";
1072
        Database::query($sql);
1073
1074
        $sql = "UPDATE c_item_property SET insert_user_id = NULL
1075
                WHERE insert_user_id = '".$user_id."'";
1076
        Database::query($sql);
1077
1078
        $sql = "UPDATE c_item_property SET lastedit_user_id = NULL
1079
                WHERE lastedit_user_id = '".$user_id."'";
1080
        Database::query($sql);
1081
1082
        // Skills
1083
        $em = Database::getManager();
1084
1085
        $criteria = ['user' => $user_id];
1086
        $skills = $em->getRepository('ChamiloCoreBundle:SkillRelUser')->findBy($criteria);
1087
        if ($skills) {
1088
            /** @var SkillRelUser $skill */
1089
            foreach ($skills as $skill) {
1090
                $comments = $skill->getComments();
1091
                if ($comments) {
1092
                    /** @var SkillRelUserComment $comment */
1093
                    foreach ($comments as $comment) {
1094
                        $em->remove($comment);
1095
                    }
1096
                }
1097
                $em->remove($skill);
1098
            }
1099
            $em->flush();
1100
        }
1101
1102
        // ExtraFieldSavedSearch
1103
        $criteria = ['user' => $user_id];
1104
        $searchList = $em->getRepository('ChamiloCoreBundle:ExtraFieldSavedSearch')->findBy($criteria);
1105
        if ($searchList) {
1106
            foreach ($searchList as $search) {
1107
                $em->remove($search);
1108
            }
1109
            $em->flush();
1110
        }
1111
1112
        $connection = Database::getManager()->getConnection();
1113
        $tableExists = $connection->getSchemaManager()->tablesExist(['plugin_bbb_room']);
1114
        if ($tableExists) {
1115
            // Delete user from database
1116
            $sql = "DELETE FROM plugin_bbb_room WHERE participant_id = $user_id";
1117
            Database::query($sql);
1118
        }
1119
1120
        // Delete user/ticket relationships :(
1121
        $tableExists = $connection->getSchemaManager()->tablesExist(['ticket_ticket']);
1122
        if ($tableExists) {
1123
            TicketManager::deleteUserFromTicketSystem($user_id);
1124
        }
1125
1126
        $tableExists = $connection->getSchemaManager()->tablesExist(['c_lp_category_user']);
1127
        if ($tableExists) {
1128
            $sql = "DELETE FROM c_lp_category_user WHERE user_id = $user_id";
1129
            Database::query($sql);
1130
        }
1131
1132
        $app_plugin = new AppPlugin();
1133
        $app_plugin->performActionsWhenDeletingItem('user', $user_id);
1134
1135
        // Delete user from database
1136
        $sql = "DELETE FROM $table_user WHERE id = '".$user_id."'";
1137
        Database::query($sql);
1138
1139
        // Add event to system log
1140
        $user_id_manager = api_get_user_id();
1141
1142
        Event::addEvent(
1143
            LOG_USER_DELETE,
1144
            LOG_USER_ID,
1145
            $user_id,
1146
            api_get_utc_datetime(),
1147
            $user_id_manager
1148
        );
1149
1150
        Event::addEvent(
1151
            LOG_USER_DELETE,
1152
            LOG_USER_OBJECT,
1153
            $user_info,
1154
            api_get_utc_datetime(),
1155
            $user_id_manager
1156
        );
1157
        $cacheAvailable = api_get_configuration_value('apc');
1158
        if ($cacheAvailable === true) {
1159
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1160
            if (apcu_exists($apcVar)) {
1161
                apcu_delete($apcVar);
1162
            }
1163
        }
1164
1165
        return true;
1166
    }
1167
1168
    /**
1169
     * Deletes users completely. Can be called either as:
1170
     * - UserManager::delete_users(1, 2, 3); or
1171
     * - UserManager::delete_users(array(1, 2, 3));.
1172
     *
1173
     * @param array|int $ids
1174
     *
1175
     * @return bool True if at least one user was successfuly deleted. False otherwise.
1176
     *
1177
     * @author Laurent Opprecht
1178
     *
1179
     * @uses \UserManager::delete_user() to actually delete each user
1180
     * @assert (null) === false
1181
     * @assert (-1) === false
1182
     * @assert (array(-1)) === false
1183
     */
1184
    public static function delete_users($ids = [])
1185
    {
1186
        $result = false;
1187
        $ids = is_array($ids) ? $ids : func_get_args();
1188
        if (!is_array($ids) || count($ids) == 0) {
1189
            return false;
1190
        }
1191
        $ids = array_map('intval', $ids);
1192
        foreach ($ids as $id) {
1193
            if (empty($id) || $id < 1) {
1194
                continue;
1195
            }
1196
            $deleted = self::delete_user($id);
1197
            $result = $deleted || $result;
1198
        }
1199
1200
        return $result;
1201
    }
1202
1203
    /**
1204
     * Disable users. Can be called either as:
1205
     * - UserManager::deactivate_users(1, 2, 3);
1206
     * - UserManager::deactivate_users(array(1, 2, 3));.
1207
     *
1208
     * @param array|int $ids
1209
     *
1210
     * @return bool
1211
     *
1212
     * @author Laurent Opprecht
1213
     * @assert (null) === false
1214
     * @assert (array(-1)) === false
1215
     */
1216
    public static function deactivate_users($ids = [])
1217
    {
1218
        if (empty($ids)) {
1219
            return false;
1220
        }
1221
1222
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1223
1224
        $ids = is_array($ids) ? $ids : func_get_args();
1225
        $ids = array_map('intval', $ids);
1226
        $ids = implode(',', $ids);
1227
1228
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
1229
        $r = Database::query($sql);
1230
        if ($r !== false) {
1231
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
1232
1233
            return true;
1234
        }
1235
1236
        return false;
1237
    }
1238
1239
    /**
1240
     * Enable users. Can be called either as:
1241
     * - UserManager::activate_users(1, 2, 3);
1242
     * - UserManager::activate_users(array(1, 2, 3));.
1243
     *
1244
     * @param array|int IDs of the users to enable
1245
     *
1246
     * @return bool
1247
     *
1248
     * @author Laurent Opprecht
1249
     * @assert (null) === false
1250
     * @assert (array(-1)) === false
1251
     */
1252
    public static function activate_users($ids = [])
1253
    {
1254
        if (empty($ids)) {
1255
            return false;
1256
        }
1257
1258
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1259
1260
        $ids = is_array($ids) ? $ids : func_get_args();
1261
        $ids = array_map('intval', $ids);
1262
        $ids = implode(',', $ids);
1263
1264
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
1265
        $r = Database::query($sql);
1266
        if ($r !== false) {
1267
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
1268
1269
            return true;
1270
        }
1271
1272
        return false;
1273
    }
1274
1275
    /**
1276
     * Update user information with new openid.
1277
     *
1278
     * @param int    $user_id
1279
     * @param string $openid
1280
     *
1281
     * @return bool true if the user information was updated
1282
     * @assert (false,'') === false
1283
     * @assert (-1,'') === false
1284
     */
1285
    public static function update_openid($user_id, $openid)
1286
    {
1287
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1288
        if ($user_id != strval(intval($user_id))) {
1289
            return false;
1290
        }
1291
        if ($user_id === false) {
1292
            return false;
1293
        }
1294
        $sql = "UPDATE $table_user SET
1295
                openid='".Database::escape_string($openid)."'";
1296
        $sql .= " WHERE id= $user_id";
1297
1298
        if (Database::query($sql) !== false) {
1299
            return true;
1300
        }
1301
1302
        return false;
1303
    }
1304
1305
    /**
1306
     * Update user information with all the parameters passed to this function.
1307
     *
1308
     * @param int    $user_id         The ID of the user to be updated
1309
     * @param string $firstname       The user's firstname
1310
     * @param string $lastname        The user's lastname
1311
     * @param string $username        The user's username (login)
1312
     * @param string $password        The user's password
1313
     * @param string $auth_source     The authentication source (default: "platform")
1314
     * @param string $email           The user's e-mail address
1315
     * @param int    $status          The user's status
1316
     * @param string $official_code   The user's official code (usually just an internal institutional code)
1317
     * @param string $phone           The user's phone number
1318
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
1319
     * @param string $expiration_date The date at which this user will be automatically disabled
1320
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
1321
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
1322
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
1323
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
1324
     * @param string $language        The language to which the user account will be set
1325
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
1326
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
1327
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
1328
     * @param string $address
1329
     * @param array  $emailTemplate
1330
     *
1331
     * @return bool|int False on error, or the user ID if the user information was updated
1332
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
1333
     */
1334
    public static function update_user(
1335
        $user_id,
1336
        $firstname,
1337
        $lastname,
1338
        $username,
1339
        $password = null,
1340
        $auth_source = null,
1341
        $email,
1342
        $status,
1343
        $official_code,
1344
        $phone,
1345
        $picture_uri,
1346
        $expiration_date,
1347
        $active,
1348
        $creator_id = null,
1349
        $hr_dept_id = 0,
1350
        $extra = null,
1351
        $language = 'english',
1352
        $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

1352
        /** @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...
1353
        $send_email = false,
1354
        $reset_password = 0,
1355
        $address = null,
1356
        $emailTemplate = []
1357
    ) {
1358
        $hook = HookUpdateUser::create();
1359
        if (!empty($hook)) {
1360
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_PRE);
1361
        }
1362
        $original_password = $password;
1363
        $user_id = (int) $user_id;
1364
        $creator_id = (int) $creator_id;
1365
1366
        if (empty($user_id)) {
1367
            return false;
1368
        }
1369
1370
        $userManager = self::getManager();
1371
        /** @var User $user */
1372
        $user = self::getRepository()->find($user_id);
1373
1374
        if (empty($user)) {
1375
            return false;
1376
        }
1377
1378
        if ($reset_password == 0) {
1379
            $password = null;
1380
            $auth_source = $user->getAuthSource();
1381
        } elseif ($reset_password == 1) {
1382
            $original_password = $password = api_generate_password();
1383
            $auth_source = PLATFORM_AUTH_SOURCE;
1384
        } elseif ($reset_password == 2) {
1385
            //$password = $password;
1386
            $auth_source = PLATFORM_AUTH_SOURCE;
1387
        } elseif ($reset_password == 3) {
1388
            //$password = $password;
1389
            //$auth_source = $auth_source;
1390
        }
1391
1392
        // Checking the user language
1393
        $languages = api_get_languages();
1394
        if (!in_array($language, $languages['folder'])) {
1395
            $language = api_get_setting('platformLanguage');
1396
        }
1397
1398
        $change_active = 0;
1399
        $isUserActive = $user->getActive();
1400
        if ($isUserActive != $active) {
1401
            $change_active = 1;
1402
        }
1403
1404
        $originalUsername = $user->getUsername();
1405
1406
        // If username is different from original then check if it exists.
1407
        if ($originalUsername !== $username) {
1408
            $available = self::is_username_available($username);
1409
            if ($available === false) {
1410
                return false;
1411
            }
1412
        }
1413
1414
        if (!empty($expiration_date)) {
1415
            $expiration_date = api_get_utc_datetime($expiration_date);
1416
            $expiration_date = new \DateTime(
1417
                $expiration_date,
1418
                new DateTimeZone('UTC')
1419
            );
1420
        }
1421
1422
        $user
1423
            ->setLastname($lastname)
1424
            ->setFirstname($firstname)
1425
            ->setUsername($username)
1426
            ->setStatus($status)
1427
            ->setAuthSource($auth_source)
1428
            ->setLanguage($language)
1429
            ->setEmail($email)
1430
            ->setOfficialCode($official_code)
1431
            ->setPhone($phone)
1432
            ->setAddress($address)
1433
            ->setPictureUri($picture_uri)
1434
            ->setExpirationDate($expiration_date)
1435
            ->setActive($active)
1436
            ->setEnabled($active)
1437
            ->setHrDeptId($hr_dept_id)
1438
        ;
1439
1440
        if (!is_null($password)) {
1441
            $user->setPlainPassword($password);
1442
            Event::addEvent(LOG_USER_PASSWORD_UPDATE, LOG_USER_ID, $user_id);
1443
        }
1444
1445
        $userManager->updateUser($user, true);
1446
        Event::addEvent(LOG_USER_UPDATE, LOG_USER_ID, $user_id);
1447
1448
        if ($change_active == 1) {
1449
            if ($active == 1) {
1450
                $event_title = LOG_USER_ENABLE;
1451
            } else {
1452
                $event_title = LOG_USER_DISABLE;
1453
            }
1454
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
1455
        }
1456
1457
        if (is_array($extra) && count($extra) > 0) {
1458
            $res = true;
1459
            foreach ($extra as $fname => $fvalue) {
1460
                $res = $res && self::update_extra_field_value(
1461
                    $user_id,
1462
                    $fname,
1463
                    $fvalue
1464
                );
1465
            }
1466
        }
1467
1468
        if (!empty($email) && $send_email) {
1469
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
1470
            $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('YourReg').' '.api_get_setting('siteName');
1471
            $sender_name = api_get_person_name(
1472
                api_get_setting('administratorName'),
1473
                api_get_setting('administratorSurname'),
1474
                null,
1475
                PERSON_NAME_EMAIL_ADDRESS
1476
            );
1477
            $email_admin = api_get_setting('emailAdministrator');
1478
            $url = api_get_path(WEB_PATH);
1479
            if (api_is_multiple_url_enabled()) {
1480
                $access_url_id = api_get_current_access_url_id();
1481
                if ($access_url_id != -1) {
1482
                    $url = api_get_access_url($access_url_id);
1483
                    $url = $url['url'];
1484
                }
1485
            }
1486
1487
            $tplContent = new Template(
1488
                null,
1489
                false,
1490
                false,
1491
                false,
1492
                false,
1493
                false
1494
            );
1495
            // variables for the default template
1496
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
1497
            $tplContent->assign('login_name', $username);
1498
1499
            $originalPassword = '';
1500
            if ($reset_password > 0) {
1501
                $originalPassword = stripslashes($original_password);
1502
            }
1503
            $tplContent->assign('original_password', $originalPassword);
1504
            $tplContent->assign('portal_url', $url);
1505
1506
            $layoutContent = $tplContent->get_template('mail/user_edit_content.tpl');
1507
            $emailBody = $tplContent->fetch($layoutContent);
1508
1509
            $mailTemplateManager = new MailTemplateManager();
1510
1511
            if (!empty($emailTemplate) &&
1512
                isset($emailTemplate['user_edit_content.tpl']) &&
1513
                !empty($emailTemplate['user_edit_content.tpl'])
1514
            ) {
1515
                $userInfo = api_get_user_info($user_id);
1516
                $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

1516
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], /** @scrutinizer ignore-type */ $userInfo);
Loading history...
1517
            }
1518
1519
            $creatorInfo = api_get_user_info($creator_id);
1520
            $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
1521
1522
            api_mail_html(
1523
                $recipient_name,
1524
                $email,
1525
                $emailsubject,
1526
                $emailBody,
1527
                $sender_name,
1528
                $email_admin,
1529
                null,
1530
                null,
1531
                null,
1532
                null,
1533
                $creatorEmail
1534
            );
1535
        }
1536
1537
        if (!empty($hook)) {
1538
            $hook->setEventData(['user' => $user]);
1539
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_POST);
1540
        }
1541
1542
        $cacheAvailable = api_get_configuration_value('apc');
1543
        if ($cacheAvailable === true) {
1544
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1545
            if (apcu_exists($apcVar)) {
1546
                apcu_delete($apcVar);
1547
            }
1548
        }
1549
1550
        return $user->getId();
1551
    }
1552
1553
    /**
1554
     * Disables a user.
1555
     *
1556
     * @param int User id
1557
     *
1558
     * @return bool
1559
     *
1560
     * @uses \UserManager::change_active_state() to actually disable the user
1561
     * @assert (0) === false
1562
     */
1563
    public static function disable($user_id)
1564
    {
1565
        if (empty($user_id)) {
1566
            return false;
1567
        }
1568
        self::change_active_state($user_id, 0);
1569
1570
        return true;
1571
    }
1572
1573
    /**
1574
     * Enable a user.
1575
     *
1576
     * @param int User id
1577
     *
1578
     * @return bool
1579
     *
1580
     * @uses \UserManager::change_active_state() to actually disable the user
1581
     * @assert (0) === false
1582
     */
1583
    public static function enable($user_id)
1584
    {
1585
        if (empty($user_id)) {
1586
            return false;
1587
        }
1588
        self::change_active_state($user_id, 1);
1589
1590
        return true;
1591
    }
1592
1593
    /**
1594
     * Returns the user's id based on the original id and field name in
1595
     * the extra fields. Returns 0 if no user was found. This function is
1596
     * mostly useful in the context of a web services-based sinchronization.
1597
     *
1598
     * @param string Original user id
1599
     * @param string Original field name
1600
     *
1601
     * @return int User id
1602
     * @assert ('0','---') === 0
1603
     */
1604
    public static function get_user_id_from_original_id(
1605
        $original_user_id_value,
1606
        $original_user_id_name
1607
    ) {
1608
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1609
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1610
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1611
1612
        $original_user_id_name = Database::escape_string($original_user_id_name);
1613
        $original_user_id_value = Database::escape_string($original_user_id_value);
1614
1615
        $sql = "SELECT item_id as user_id
1616
                FROM $t_uf uf
1617
                INNER JOIN $t_ufv ufv
1618
                ON ufv.field_id = uf.id
1619
                WHERE
1620
                    variable = '$original_user_id_name' AND
1621
                    value = '$original_user_id_value' AND
1622
                    extra_field_type = $extraFieldType
1623
                ";
1624
        $res = Database::query($sql);
1625
        $row = Database::fetch_object($res);
1626
        if ($row) {
1627
            return $row->user_id;
1628
        }
1629
1630
        return 0;
1631
    }
1632
1633
    /**
1634
     * Check if a username is available.
1635
     *
1636
     * @param string $username the wanted username
1637
     *
1638
     * @return bool true if the wanted username is available
1639
     * @assert ('') === false
1640
     * @assert ('xyzxyzxyz') === true
1641
     */
1642
    public static function is_username_available($username)
1643
    {
1644
        if (empty($username)) {
1645
            return false;
1646
        }
1647
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1648
        $sql = "SELECT username FROM $table_user
1649
                WHERE username = '".Database::escape_string($username)."'";
1650
        $res = Database::query($sql);
1651
1652
        return Database::num_rows($res) == 0;
1653
    }
1654
1655
    /**
1656
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1657
     *
1658
     * @param string $firstname the first name of the user
1659
     * @param string $lastname  the last name of the user
1660
     *
1661
     * @return string suggests a username that contains only ASCII-letters and digits,
1662
     *                without check for uniqueness within the system
1663
     *
1664
     * @author Julio Montoya Armas
1665
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1666
     * @assert ('','') === false
1667
     * @assert ('a','b') === 'ab'
1668
     */
1669
    public static function create_username($firstname, $lastname)
1670
    {
1671
        if (empty($firstname) && empty($lastname)) {
1672
            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...
1673
        }
1674
1675
        // The first letter only.
1676
        $firstname = api_substr(
1677
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1678
            0,
1679
            1
1680
        );
1681
        //Looking for a space in the lastname
1682
        $pos = api_strpos($lastname, ' ');
1683
        if ($pos !== false) {
1684
            $lastname = api_substr($lastname, 0, $pos);
1685
        }
1686
1687
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1688
        $username = $firstname.$lastname;
1689
        if (empty($username)) {
1690
            $username = 'user';
1691
        }
1692
1693
        $username = URLify::transliterate($username);
1694
1695
        return strtolower(substr($username, 0, USERNAME_MAX_LENGTH - 3));
1696
    }
1697
1698
    /**
1699
     * Creates a unique username, using:
1700
     * 1. the first name and the last name of a user;
1701
     * 2. an already created username but not checked for uniqueness yet.
1702
     *
1703
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1704
     *                          parameter is treated as username which is to be checked f
1705
     *                          or uniqueness and to be modified when it is necessary.
1706
     * @param string $lastname  the last name of the user
1707
     *
1708
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1709
     *                Note: When the method is called several times with same parameters,
1710
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1711
     *
1712
     * @author Ivan Tcholakov, 2009
1713
     */
1714
    public static function create_unique_username($firstname, $lastname = null)
1715
    {
1716
        if (is_null($lastname)) {
1717
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1718
            // For making this method tolerant of mistakes,
1719
            // let us transliterate and purify the suggested input username anyway.
1720
            // So, instead of the sentence $username = $firstname; we place the following:
1721
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1722
        } else {
1723
            $username = self::create_username($firstname, $lastname);
1724
        }
1725
        if (!self::is_username_available($username)) {
1726
            $i = 2;
1727
            $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1728
            while (!self::is_username_available($temp_username)) {
1729
                $i++;
1730
                $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1731
            }
1732
            $username = $temp_username;
1733
        }
1734
1735
        $username = URLify::transliterate($username);
1736
1737
        return $username;
1738
    }
1739
1740
    /**
1741
     * Modifies a given username accordingly to the specification for valid characters and length.
1742
     *
1743
     * @param $username string          The input username
1744
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1745
     *                     otherwise compliance may be partial. The default value is FALSE.
1746
     *
1747
     * @return string the resulting purified username
1748
     */
1749
    public static function purify_username($username, $strict = false)
1750
    {
1751
        if ($strict) {
1752
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1753
            // into ASCII letters in order they not to be totally removed.
1754
            // 2. Applying the strict purifier.
1755
            // 3. Length limitation.
1756
            $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);
1757
            $return = URLify::transliterate($return);
1758
1759
            return $return;
1760
        }
1761
1762
        // 1. Applying the shallow purifier.
1763
        // 2. Length limitation.
1764
        return substr(
1765
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1766
            0,
1767
            USERNAME_MAX_LENGTH
1768
        );
1769
    }
1770
1771
    /**
1772
     * Checks whether the user id exists in the database.
1773
     *
1774
     * @param int $userId User id
1775
     *
1776
     * @return bool True if user id was found, false otherwise
1777
     */
1778
    public static function is_user_id_valid($userId)
1779
    {
1780
        $resultData = Database::select(
1781
            'COUNT(1) AS count',
1782
            Database::get_main_table(TABLE_MAIN_USER),
1783
            [
1784
                'where' => ['id = ?' => (int) $userId],
1785
            ],
1786
            'first'
1787
        );
1788
1789
        if ($resultData === false) {
1790
            return false;
1791
        }
1792
1793
        return $resultData['count'] > 0;
1794
    }
1795
1796
    /**
1797
     * Checks whether a given username matches to the specification strictly.
1798
     * The empty username is assumed here as invalid.
1799
     * Mostly this function is to be used in the user interface built-in validation routines
1800
     * for providing feedback while usernames are enterd manually.
1801
     *
1802
     * @param string $username the input username
1803
     *
1804
     * @return bool returns TRUE if the username is valid, FALSE otherwise
1805
     */
1806
    public static function is_username_valid($username)
1807
    {
1808
        return !empty($username) && $username == self::purify_username($username, true);
1809
    }
1810
1811
    /**
1812
     * Checks whether a username is empty. If the username contains whitespace characters,
1813
     * such as spaces, tabulators, newlines, etc.,
1814
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
1815
     *
1816
     * @param string $username the given username
1817
     *
1818
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1819
     */
1820
    public static function is_username_empty($username)
1821
    {
1822
        return strlen(self::purify_username($username, false)) == 0;
1823
    }
1824
1825
    /**
1826
     * Checks whether a username is too long or not.
1827
     *
1828
     * @param string $username the given username, it should contain only ASCII-letters and digits
1829
     *
1830
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1831
     */
1832
    public static function is_username_too_long($username)
1833
    {
1834
        return strlen($username) > USERNAME_MAX_LENGTH;
1835
    }
1836
1837
    /**
1838
     * Get the users by ID.
1839
     *
1840
     * @param array  $ids    student ids
1841
     * @param string $active
1842
     * @param string $order
1843
     * @param string $limit
1844
     *
1845
     * @return array $result student information
1846
     */
1847
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
1848
    {
1849
        if (empty($ids)) {
1850
            return [];
1851
        }
1852
1853
        $ids = is_array($ids) ? $ids : [$ids];
1854
        $ids = array_map('intval', $ids);
1855
        $ids = implode(',', $ids);
1856
1857
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
1858
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
1859
        if (!is_null($active)) {
1860
            $sql .= ' AND active='.($active ? '1' : '0');
1861
        }
1862
1863
        if (!is_null($order)) {
1864
            $order = Database::escape_string($order);
1865
            $sql .= ' ORDER BY '.$order;
1866
        }
1867
1868
        if (!is_null($limit)) {
1869
            $limit = Database::escape_string($limit);
1870
            $sql .= ' LIMIT '.$limit;
1871
        }
1872
1873
        $rs = Database::query($sql);
1874
        $result = [];
1875
        while ($row = Database::fetch_array($rs)) {
1876
            $result[] = $row;
1877
        }
1878
1879
        return $result;
1880
    }
1881
1882
    /**
1883
     * Get a list of users of which the given conditions match with an = 'cond'.
1884
     *
1885
     * @param array $conditions a list of condition (example : status=>STUDENT)
1886
     * @param array $order_by   a list of fields on which sort
1887
     *
1888
     * @return array an array with all users of the platform
1889
     *
1890
     * @todo security filter order by
1891
     */
1892
    public static function get_user_list(
1893
        $conditions = [],
1894
        $order_by = [],
1895
        $limit_from = false,
1896
        $limit_to = false,
1897
        $idCampus = null
1898
    ) {
1899
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1900
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1901
        $return_array = [];
1902
        $sql = "SELECT user.* FROM $user_table user ";
1903
1904
        if (api_is_multiple_url_enabled()) {
1905
            if ($idCampus) {
1906
                $urlId = $idCampus;
1907
            } else {
1908
                $urlId = api_get_current_access_url_id();
1909
            }
1910
            $sql .= " INNER JOIN $userUrlTable url_user
1911
                      ON (user.user_id = url_user.user_id)
1912
                      WHERE url_user.access_url_id = $urlId";
1913
        } else {
1914
            $sql .= " WHERE 1=1 ";
1915
        }
1916
1917
        if (count($conditions) > 0) {
1918
            foreach ($conditions as $field => $value) {
1919
                $field = Database::escape_string($field);
1920
                $value = Database::escape_string($value);
1921
                $sql .= " AND $field = '$value'";
1922
            }
1923
        }
1924
1925
        if (count($order_by) > 0) {
1926
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1927
        }
1928
1929
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1930
            $limit_from = (int) $limit_from;
1931
            $limit_to = (int) $limit_to;
1932
            $sql .= " LIMIT $limit_from, $limit_to";
1933
        }
1934
        $sql_result = Database::query($sql);
1935
        while ($result = Database::fetch_array($sql_result)) {
1936
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1937
            $return_array[] = $result;
1938
        }
1939
1940
        return $return_array;
1941
    }
1942
1943
    public static function getUserListExtraConditions(
1944
        $conditions = [],
1945
        $order_by = [],
1946
        $limit_from = false,
1947
        $limit_to = false,
1948
        $idCampus = null,
1949
        $extraConditions = '',
1950
        $getCount = false
1951
    ) {
1952
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1953
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1954
        $return_array = [];
1955
        $sql = "SELECT user.* FROM $user_table user ";
1956
1957
        if ($getCount) {
1958
            $sql = "SELECT count(user.id) count FROM $user_table user ";
1959
        }
1960
1961
        if (api_is_multiple_url_enabled()) {
1962
            if ($idCampus) {
1963
                $urlId = $idCampus;
1964
            } else {
1965
                $urlId = api_get_current_access_url_id();
1966
            }
1967
            $sql .= " INNER JOIN $userUrlTable url_user
1968
                      ON (user.user_id = url_user.user_id)
1969
                      WHERE url_user.access_url_id = $urlId";
1970
        } else {
1971
            $sql .= " WHERE 1=1 ";
1972
        }
1973
1974
        $sql .= " AND status <> ".ANONYMOUS." ";
1975
1976
        if (count($conditions) > 0) {
1977
            foreach ($conditions as $field => $value) {
1978
                $field = Database::escape_string($field);
1979
                $value = Database::escape_string($value);
1980
                $sql .= " AND $field = '$value'";
1981
            }
1982
        }
1983
1984
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
1985
1986
        if (!empty($order_by) && count($order_by) > 0) {
1987
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1988
        }
1989
1990
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1991
            $limit_from = (int) $limit_from;
1992
            $limit_to = (int) $limit_to;
1993
            $sql .= " LIMIT $limit_from, $limit_to";
1994
        }
1995
1996
        $sql_result = Database::query($sql);
1997
1998
        if ($getCount) {
1999
            $result = Database::fetch_array($sql_result);
2000
2001
            return $result['count'];
2002
        }
2003
2004
        while ($result = Database::fetch_array($sql_result)) {
2005
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
2006
            $return_array[] = $result;
2007
        }
2008
2009
        return $return_array;
2010
    }
2011
2012
    /**
2013
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
2014
     *
2015
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
2016
     * @param array  $order_by         a list of fields on which sort
2017
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
2018
     * @param string $condition        Whether we want the filters to be combined by AND or OR
2019
     * @param array  $onlyThisUserList
2020
     *
2021
     * @return array an array with all users of the platform
2022
     *
2023
     * @todo optional course code parameter, optional sorting parameters...
2024
     * @todo security filter order_by
2025
     */
2026
    public static function getUserListLike(
2027
        $conditions = [],
2028
        $order_by = [],
2029
        $simple_like = false,
2030
        $condition = 'AND',
2031
        $onlyThisUserList = []
2032
    ) {
2033
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
2034
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2035
        $return_array = [];
2036
        $sql_query = "SELECT user.id FROM $user_table user ";
2037
2038
        if (api_is_multiple_url_enabled()) {
2039
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
2040
        }
2041
2042
        $sql_query .= ' WHERE 1 = 1 ';
2043
        if (count($conditions) > 0) {
2044
            $temp_conditions = [];
2045
            foreach ($conditions as $field => $value) {
2046
                $field = Database::escape_string($field);
2047
                $value = Database::escape_string($value);
2048
                if ($simple_like) {
2049
                    $temp_conditions[] = $field." LIKE '$value%'";
2050
                } else {
2051
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
2052
                }
2053
            }
2054
            if (!empty($temp_conditions)) {
2055
                $sql_query .= ' AND '.implode(' '.$condition.' ', $temp_conditions);
2056
            }
2057
2058
            if (api_is_multiple_url_enabled()) {
2059
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
2060
            }
2061
        } else {
2062
            if (api_is_multiple_url_enabled()) {
2063
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
2064
            }
2065
        }
2066
2067
        if (!empty($onlyThisUserList)) {
2068
            $onlyThisUserListToString = implode("','", $onlyThisUserList);
2069
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
2070
        }
2071
2072
        if (count($order_by) > 0) {
2073
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
2074
        }
2075
2076
        $sql_result = Database::query($sql_query);
2077
        while ($result = Database::fetch_array($sql_result)) {
2078
            $userInfo = api_get_user_info($result['id']);
2079
            $return_array[] = $userInfo;
2080
        }
2081
2082
        return $return_array;
2083
    }
2084
2085
    /**
2086
     * Get user picture URL or path from user ID (returns an array).
2087
     * The return format is a complete path, enabling recovery of the directory
2088
     * with dirname() or the file with basename(). This also works for the
2089
     * functions dealing with the user's productions, as they are located in
2090
     * the same directory.
2091
     *
2092
     * @param int    $id       User ID
2093
     * @param string $type     Type of path to return (can be 'system', 'web')
2094
     * @param array  $userInfo user information to avoid query the DB
2095
     *                         returns the /main/img/unknown.jpg image set it at true
2096
     *
2097
     * @return array Array of 2 elements: 'dir' and 'file' which contain
2098
     *               the dir and file as the name implies if image does not exist it will
2099
     *               return the unknow image if anonymous parameter is true if not it returns an empty array
2100
     */
2101
    public static function get_user_picture_path_by_id(
2102
        $id,
2103
        $type = 'web',
2104
        $userInfo = []
2105
    ) {
2106
        switch ($type) {
2107
            case 'system': // Base: absolute system path.
2108
                $base = api_get_path(SYS_CODE_PATH);
2109
                break;
2110
            case 'web': // Base: absolute web path.
2111
            default:
2112
                $base = api_get_path(WEB_CODE_PATH);
2113
                break;
2114
        }
2115
2116
        $anonymousPath = [
2117
            'dir' => $base.'img/',
2118
            'file' => 'unknown.jpg',
2119
            'email' => '',
2120
        ];
2121
2122
        if (empty($id) || empty($type)) {
2123
            return $anonymousPath;
2124
        }
2125
2126
        $id = (int) $id;
2127
        if (empty($userInfo)) {
2128
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
2129
            $sql = "SELECT email, picture_uri FROM $user_table
2130
                    WHERE id = ".$id;
2131
            $res = Database::query($sql);
2132
2133
            if (!Database::num_rows($res)) {
2134
                return $anonymousPath;
2135
            }
2136
            $user = Database::fetch_array($res);
2137
            if (empty($user['picture_uri'])) {
2138
                return $anonymousPath;
2139
            }
2140
        } else {
2141
            $user = $userInfo;
2142
        }
2143
2144
        $pictureFilename = trim($user['picture_uri']);
2145
2146
        $dir = self::getUserPathById($id, $type);
2147
2148
        return [
2149
            'dir' => $dir,
2150
            'file' => $pictureFilename,
2151
            'email' => $user['email'],
2152
        ];
2153
    }
2154
2155
    /**
2156
     * *** READ BEFORE REVIEW THIS FUNCTION ***
2157
     * This function is a exact copy from get_user_picture_path_by_id() and it was create it to avoid
2158
     * a recursive calls for get_user_picture_path_by_id() in another functions when you update a user picture
2159
     * in same script, so you can find this function usage in update_user_picture() function.
2160
     *
2161
     * @param int    $id       User ID
2162
     * @param string $type     Type of path to return (can be 'system', 'web')
2163
     * @param array  $userInfo user information to avoid query the DB
2164
     *                         returns the /main/img/unknown.jpg image set it at true
2165
     *
2166
     * @return array Array of 2 elements: 'dir' and 'file' which contain
2167
     *               the dir and file as the name implies if image does not exist it will
2168
     *               return the unknown image if anonymous parameter is true if not it returns an empty array
2169
     */
2170
    public static function getUserPicturePathById($id, $type = 'web', $userInfo = [])
2171
    {
2172
        switch ($type) {
2173
            case 'system': // Base: absolute system path.
2174
                $base = api_get_path(SYS_CODE_PATH);
2175
                break;
2176
            case 'web': // Base: absolute web path.
2177
            default:
2178
                $base = api_get_path(WEB_CODE_PATH);
2179
                break;
2180
        }
2181
2182
        $anonymousPath = [
2183
            'dir' => $base.'img/',
2184
            'file' => 'unknown.jpg',
2185
            'email' => '',
2186
        ];
2187
2188
        if (empty($id) || empty($type)) {
2189
            return $anonymousPath;
2190
        }
2191
2192
        $id = (int) $id;
2193
        if (empty($userInfo)) {
2194
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
2195
            $sql = "SELECT email, picture_uri FROM $user_table WHERE id = $id";
2196
            $res = Database::query($sql);
2197
2198
            if (!Database::num_rows($res)) {
2199
                return $anonymousPath;
2200
            }
2201
            $user = Database::fetch_array($res);
2202
2203
            if (empty($user['picture_uri'])) {
2204
                return $anonymousPath;
2205
            }
2206
        } else {
2207
            $user = $userInfo;
2208
        }
2209
2210
        $pictureFilename = trim($user['picture_uri']);
2211
        $dir = self::getUserPathById($id, $type);
2212
2213
        return [
2214
            'dir' => $dir,
2215
            'file' => $pictureFilename,
2216
            'email' => $user['email'],
2217
        ];
2218
    }
2219
2220
    /**
2221
     * Get user path from user ID (returns an array).
2222
     * The return format is a complete path to a folder ending with "/"
2223
     * In case the first level of subdirectory of users/ does not exist, the
2224
     * function will attempt to create it. Probably not the right place to do it
2225
     * but at least it avoids headaches in many other places.
2226
     *
2227
     * @param int    $id   User ID
2228
     * @param string $type Type of path to return (can be 'system', 'web', 'last')
2229
     *
2230
     * @return string User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
2231
     */
2232
    public static function getUserPathById($id, $type)
2233
    {
2234
        $id = (int) $id;
2235
        if (!$id) {
2236
            return null;
2237
        }
2238
2239
        $userPath = "users/$id/";
2240
        if (api_get_setting('split_users_upload_directory') === 'true') {
2241
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
2242
            // In exceptional cases, on some portals, the intermediate base user
2243
            // directory might not have been created. Make sure it is before
2244
            // going further.
2245
2246
            $rootPath = api_get_path(SYS_UPLOAD_PATH).'users/'.substr((string) $id, 0, 1);
2247
            if (!is_dir($rootPath)) {
2248
                $perm = api_get_permissions_for_new_directories();
2249
                try {
2250
                    mkdir($rootPath, $perm);
2251
                } catch (Exception $e) {
2252
                    error_log($e->getMessage());
2253
                }
2254
            }
2255
        }
2256
        switch ($type) {
2257
            case 'system': // Base: absolute system path.
2258
                $userPath = api_get_path(SYS_UPLOAD_PATH).$userPath;
2259
                break;
2260
            case 'web': // Base: absolute web path.
2261
                $userPath = api_get_path(WEB_UPLOAD_PATH).$userPath;
2262
                break;
2263
            case 'last': // Only the last part starting with users/
2264
                break;
2265
        }
2266
2267
        return $userPath;
2268
    }
2269
2270
    /**
2271
     * Gets the current user image.
2272
     *
2273
     * @param string $user_id
2274
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
2275
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
2276
     * @param bool   $addRandomId
2277
     * @param array  $userInfo    to avoid query the DB
2278
     *
2279
     * @return string
2280
     */
2281
    public static function getUserPicture(
2282
        $user_id,
2283
        $size = USER_IMAGE_SIZE_MEDIUM,
2284
        $addRandomId = true,
2285
        $userInfo = []
2286
    ) {
2287
        // Make sure userInfo is defined. Otherwise, define it!
2288
        if (empty($userInfo) || !is_array($userInfo) || count($userInfo) == 0) {
2289
            if (empty($user_id)) {
2290
                return '';
2291
            } else {
2292
                $userInfo = api_get_user_info($user_id);
2293
            }
2294
        }
2295
2296
        $imageWebPath = self::get_user_picture_path_by_id(
2297
            $user_id,
2298
            'web',
2299
            $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

2299
            /** @scrutinizer ignore-type */ $userInfo
Loading history...
2300
        );
2301
        $pictureWebFile = $imageWebPath['file'];
2302
        $pictureWebDir = $imageWebPath['dir'];
2303
2304
        $pictureAnonymousSize = '128';
2305
        $gravatarSize = 22;
2306
        $realSizeName = 'small_';
2307
2308
        switch ($size) {
2309
            case USER_IMAGE_SIZE_SMALL:
2310
                $pictureAnonymousSize = '32';
2311
                $realSizeName = 'small_';
2312
                $gravatarSize = 32;
2313
                break;
2314
            case USER_IMAGE_SIZE_MEDIUM:
2315
                $pictureAnonymousSize = '64';
2316
                $realSizeName = 'medium_';
2317
                $gravatarSize = 64;
2318
                break;
2319
            case USER_IMAGE_SIZE_ORIGINAL:
2320
                $pictureAnonymousSize = '128';
2321
                $realSizeName = '';
2322
                $gravatarSize = 128;
2323
                break;
2324
            case USER_IMAGE_SIZE_BIG:
2325
                $pictureAnonymousSize = '128';
2326
                $realSizeName = 'big_';
2327
                $gravatarSize = 128;
2328
                break;
2329
        }
2330
2331
        $gravatarEnabled = api_get_setting('gravatar_enabled');
2332
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
2333
        if ($pictureWebFile == 'unknown.jpg' || empty($pictureWebFile)) {
2334
            if ($gravatarEnabled === 'true') {
2335
                $file = self::getGravatar(
2336
                    $imageWebPath['email'],
2337
                    $gravatarSize,
2338
                    api_get_setting('gravatar_type')
2339
                );
2340
2341
                if ($addRandomId) {
2342
                    $file .= '&rand='.uniqid();
2343
                }
2344
2345
                return $file;
2346
            }
2347
2348
            return $anonymousPath;
2349
        }
2350
2351
        $pictureSysPath = self::get_user_picture_path_by_id($user_id, 'system');
2352
        $file = $pictureSysPath['dir'].$realSizeName.$pictureWebFile;
2353
        $picture = '';
2354
        if (file_exists($file)) {
2355
            $picture = $pictureWebDir.$realSizeName.$pictureWebFile;
2356
        } else {
2357
            $file = $pictureSysPath['dir'].$pictureWebFile;
2358
            if (file_exists($file) && !is_dir($file)) {
2359
                $picture = $pictureWebFile['dir'].$pictureWebFile;
2360
            }
2361
        }
2362
2363
        if (empty($picture)) {
2364
            return $anonymousPath;
2365
        }
2366
2367
        if ($addRandomId) {
2368
            $picture .= '?rand='.uniqid();
2369
        }
2370
2371
        return $picture;
2372
    }
2373
2374
    /**
2375
     * Creates new user photos in various sizes of a user, or deletes user photos.
2376
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2377
     *
2378
     * @param int    $user_id the user internal identification number
2379
     * @param string $file    The common file name for the newly created photos.
2380
     *                        It will be checked and modified for compatibility with the file system.
2381
     *                        If full name is provided, path component is ignored.
2382
     *                        If an empty name is provided, then old user photos are deleted only,
2383
     *
2384
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
2385
     *
2386
     * @param string $source_file    the full system name of the image from which user photos will be created
2387
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
2388
     *
2389
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
2390
     *              When deletion is requested returns empty string.
2391
     *              In case of internal error or negative validation returns FALSE.
2392
     */
2393
    public static function update_user_picture(
2394
        $user_id,
2395
        $file = null,
2396
        $source_file = null,
2397
        $cropParameters = ''
2398
    ) {
2399
        if (empty($user_id)) {
2400
            return false;
2401
        }
2402
        $delete = empty($file);
2403
        if (empty($source_file)) {
2404
            $source_file = $file;
2405
        }
2406
2407
        // User-reserved directory where photos have to be placed.
2408
        $path_info = self::getUserPicturePathById($user_id, 'system');
2409
        $path = $path_info['dir'];
2410
2411
        // If this directory does not exist - we create it.
2412
        if (!file_exists($path)) {
2413
            mkdir($path, api_get_permissions_for_new_directories(), true);
2414
        }
2415
2416
        // The old photos (if any).
2417
        $old_file = $path_info['file'];
2418
2419
        // Let us delete them.
2420
        if ($old_file != 'unknown.jpg') {
2421
            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...
2422
                $prefix = 'saved_'.date('Y_m_d_H_i_s').'_'.uniqid('').'_';
2423
                @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

2423
                /** @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...
2424
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
2425
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
2426
                @rename($path.$old_file, $path.$prefix.$old_file);
2427
            } else {
2428
                @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

2428
                /** @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...
2429
                @unlink($path.'medium_'.$old_file);
2430
                @unlink($path.'big_'.$old_file);
2431
                @unlink($path.$old_file);
2432
            }
2433
        }
2434
2435
        // Exit if only deletion has been requested. Return an empty picture name.
2436
        if ($delete) {
2437
            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...
2438
        }
2439
2440
        // Validation 2.
2441
        $allowed_types = api_get_supported_image_extensions();
2442
        $file = str_replace('\\', '/', $file);
2443
        $filename = (($pos = strrpos($file, '/')) !== false) ? substr($file, $pos + 1) : $file;
2444
        $extension = strtolower(substr(strrchr($filename, '.'), 1));
2445
        if (!in_array($extension, $allowed_types)) {
2446
            return false;
2447
        }
2448
2449
        // This is the common name for the new photos.
2450
        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...
2451
            $old_extension = strtolower(substr(strrchr($old_file, '.'), 1));
2452
            $filename = in_array($old_extension, $allowed_types) ? substr($old_file, 0, -strlen($old_extension)) : $old_file;
2453
            $filename = (substr($filename, -1) == '.') ? $filename.$extension : $filename.'.'.$extension;
2454
        } else {
2455
            $filename = api_replace_dangerous_char($filename);
2456
            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...
2457
                $filename = uniqid('').'_'.$filename;
2458
            }
2459
            // We always prefix user photos with user ids, so on setting
2460
            // api_get_setting('split_users_upload_directory') === 'true'
2461
            // the correspondent directories to be found successfully.
2462
            $filename = $user_id.'_'.$filename;
2463
        }
2464
2465
        //Crop the image to adjust 1:1 ratio
2466
        $image = new Image($source_file);
2467
        $image->crop($cropParameters);
2468
2469
        // Storing the new photos in 4 versions with various sizes.
2470
        $userPath = self::getUserPathById($user_id, 'system');
2471
2472
        // If this path does not exist - we create it.
2473
        if (!file_exists($userPath)) {
2474
            mkdir($userPath, api_get_permissions_for_new_directories(), true);
2475
        }
2476
        $small = new Image($source_file);
2477
        $small->resize(32);
2478
        $small->send_image($userPath.'small_'.$filename);
2479
        $medium = new Image($source_file);
2480
        $medium->resize(85);
2481
        $medium->send_image($userPath.'medium_'.$filename);
2482
        $normal = new Image($source_file);
2483
        $normal->resize(200);
2484
        $normal->send_image($userPath.$filename);
2485
2486
        $big = new Image($source_file); // This is the original picture.
2487
        $big->send_image($userPath.'big_'.$filename);
2488
2489
        $result = $small && $medium && $normal && $big;
0 ignored issues
show
introduced by
$big is of type Image, thus it always evaluated to true.
Loading history...
introduced by
$normal is of type Image, thus it always evaluated to true.
Loading history...
introduced by
$medium is of type Image, thus it always evaluated to true.
Loading history...
2490
2491
        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...
2492
    }
2493
2494
    /**
2495
     * Update User extra field file type into {user_folder}/{$extra_field}.
2496
     *
2497
     * @param int    $user_id     The user internal identification number
2498
     * @param string $extra_field The $extra_field The extra field name
2499
     * @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...
2500
     * @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...
2501
     *
2502
     * @return bool|null return filename if success, but false
2503
     */
2504
    public static function update_user_extra_file(
2505
        $user_id,
2506
        $extra_field = '',
2507
        $file = null,
2508
        $source_file = null
2509
    ) {
2510
        // Add Filter
2511
        $source_file = Security::filter_filename($source_file);
2512
        $file = Security::filter_filename($file);
2513
2514
        if (empty($user_id)) {
2515
            return false;
2516
        }
2517
2518
        if (empty($source_file)) {
2519
            $source_file = $file;
2520
        }
2521
2522
        // User-reserved directory where extra file have to be placed.
2523
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2524
        $path = $path_info['dir'];
2525
        if (!empty($extra_field)) {
2526
            $path .= $extra_field.'/';
2527
        }
2528
        // If this directory does not exist - we create it.
2529
        if (!file_exists($path)) {
2530
            @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

2530
            /** @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...
2531
        }
2532
2533
        if (filter_extension($file)) {
2534
            if (@move_uploaded_file($source_file, $path.$file)) {
2535
                if ($extra_field) {
2536
                    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...
2537
                } else {
2538
                    return $file;
2539
                }
2540
            }
2541
        }
2542
2543
        return false; // this should be returned if anything went wrong with the upload
2544
    }
2545
2546
    /**
2547
     * Deletes user photos.
2548
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2549
     *
2550
     * @param int $userId the user internal identification number
2551
     *
2552
     * @return mixed returns empty string on success, FALSE on error
2553
     */
2554
    public static function deleteUserPicture($userId)
2555
    {
2556
        return self::update_user_picture($userId);
2557
    }
2558
2559
    /**
2560
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
2561
     * doesn't have any.
2562
     *
2563
     * If there has been a request to remove a production, the function will return
2564
     * without building the list unless forced to do so by the optional second
2565
     * parameter. This increases performance by avoiding to read through the
2566
     * productions on the filesystem before the removal request has been carried
2567
     * out because they'll have to be re-read afterwards anyway.
2568
     *
2569
     * @param int  $user_id    User id
2570
     * @param bool $force      Optional parameter to force building after a removal request
2571
     * @param bool $showDelete
2572
     *
2573
     * @return string A string containing the XHTML code to display the production list, or FALSE
2574
     */
2575
    public static function build_production_list($user_id, $force = false, $showDelete = false)
2576
    {
2577
        if (!$force && !empty($_POST['remove_production'])) {
2578
            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...
2579
        }
2580
2581
        $productions = self::get_user_productions($user_id);
2582
2583
        if (empty($productions)) {
2584
            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...
2585
        }
2586
2587
        $production_dir = self::getUserPathById($user_id, 'web');
2588
        $del_image = Display::returnIconPath('delete.png');
2589
        $add_image = Display::returnIconPath('archive.png');
2590
        $del_text = get_lang('Delete');
2591
        $production_list = '';
2592
        if (count($productions) > 0) {
2593
            $production_list = '<div class="files-production"><ul id="productions">';
2594
            foreach ($productions as $file) {
2595
                $production_list .= '<li>
2596
                    <img src="'.$add_image.'" />
2597
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2598
                        '.htmlentities($file).'
2599
                    </a>';
2600
                if ($showDelete) {
2601
                    $production_list .= '&nbsp;&nbsp;
2602
                        <input
2603
                            style="width:16px;"
2604
                            type="image"
2605
                            name="remove_production['.urlencode($file).']"
2606
                            src="'.$del_image.'"
2607
                            alt="'.$del_text.'"
2608
                            title="'.$del_text.' '.htmlentities($file).'"
2609
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2610
                }
2611
            }
2612
            $production_list .= '</ul></div>';
2613
        }
2614
2615
        return $production_list;
2616
    }
2617
2618
    /**
2619
     * Returns an array with the user's productions.
2620
     *
2621
     * @param int $user_id User id
2622
     *
2623
     * @return array An array containing the user's productions
2624
     */
2625
    public static function get_user_productions($user_id)
2626
    {
2627
        $production_repository = self::getUserPathById($user_id, 'system');
2628
        $productions = [];
2629
2630
        if (is_dir($production_repository)) {
2631
            $handle = opendir($production_repository);
2632
            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

2632
            while ($file = readdir(/** @scrutinizer ignore-type */ $handle)) {
Loading history...
2633
                if ($file == '.' ||
2634
                    $file == '..' ||
2635
                    $file == '.htaccess' ||
2636
                    is_dir($production_repository.$file)
2637
                ) {
2638
                    // skip current/parent directory and .htaccess
2639
                    continue;
2640
                }
2641
2642
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2643
                    // User's photos should not be listed as productions.
2644
                    continue;
2645
                }
2646
                $productions[] = $file;
2647
            }
2648
        }
2649
2650
        return $productions;
2651
    }
2652
2653
    /**
2654
     * Remove a user production.
2655
     *
2656
     * @param int    $user_id    User id
2657
     * @param string $production The production to remove
2658
     *
2659
     * @return bool
2660
     */
2661
    public static function remove_user_production($user_id, $production)
2662
    {
2663
        $production_path = self::get_user_picture_path_by_id($user_id, 'system');
2664
        $production_file = $production_path['dir'].$production;
2665
        if (is_file($production_file)) {
2666
            unlink($production_file);
2667
2668
            return true;
2669
        }
2670
2671
        return false;
2672
    }
2673
2674
    /**
2675
     * Update an extra field value for a given user.
2676
     *
2677
     * @param int    $userId   User ID
2678
     * @param string $variable Field variable name
2679
     * @param string $value    Field value
2680
     *
2681
     * @return bool true if field updated, false otherwise
2682
     */
2683
    public static function update_extra_field_value($userId, $variable, $value = '')
2684
    {
2685
        $extraFieldValue = new ExtraFieldValue('user');
2686
        $params = [
2687
            'item_id' => $userId,
2688
            'variable' => $variable,
2689
            'value' => $value,
2690
        ];
2691
2692
        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...
2693
    }
2694
2695
    /**
2696
     * Get an array of extra fields with field details (type, default value and options).
2697
     *
2698
     * @param    int    Offset (from which row)
2699
     * @param    int    Number of items
2700
     * @param    int    Column on which sorting is made
2701
     * @param    string    Sorting direction
2702
     * @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...
2703
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2704
     *
2705
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2706
     */
2707
    public static function get_extra_fields(
2708
        $from = 0,
2709
        $number_of_items = 0,
2710
        $column = 5,
2711
        $direction = 'ASC',
2712
        $all_visibility = true,
2713
        $field_filter = null
2714
    ) {
2715
        $fields = [];
2716
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2717
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2718
        $columns = [
2719
            'id',
2720
            'variable',
2721
            'field_type',
2722
            'display_text',
2723
            'default_value',
2724
            'field_order',
2725
            'filter',
2726
        ];
2727
        $column = (int) $column;
2728
        $sort_direction = '';
2729
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2730
            $sort_direction = strtoupper($direction);
2731
        }
2732
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2733
        $sqlf = "SELECT * FROM $t_uf WHERE extra_field_type = $extraFieldType ";
2734
        if (!$all_visibility) {
2735
            $sqlf .= " AND visible_to_self = 1 ";
2736
        }
2737
        if (!is_null($field_filter)) {
2738
            $field_filter = (int) $field_filter;
2739
            $sqlf .= " AND filter = $field_filter ";
2740
        }
2741
        $sqlf .= " ORDER BY ".$columns[$column]." $sort_direction ";
2742
        if ($number_of_items != 0) {
2743
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2744
        }
2745
        $resf = Database::query($sqlf);
2746
        if (Database::num_rows($resf) > 0) {
2747
            while ($rowf = Database::fetch_array($resf)) {
2748
                $fields[$rowf['id']] = [
2749
                    0 => $rowf['id'],
2750
                    1 => $rowf['variable'],
2751
                    2 => $rowf['field_type'],
2752
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2753
                    4 => $rowf['default_value'],
2754
                    5 => $rowf['field_order'],
2755
                    6 => $rowf['visible_to_self'],
2756
                    7 => $rowf['changeable'],
2757
                    8 => $rowf['filter'],
2758
                    9 => [],
2759
                    10 => '<a name="'.$rowf['id'].'"></a>',
2760
                ];
2761
2762
                $sqlo = "SELECT * FROM $t_ufo
2763
                         WHERE field_id = ".$rowf['id']."
2764
                         ORDER BY option_order ASC";
2765
                $reso = Database::query($sqlo);
2766
                if (Database::num_rows($reso) > 0) {
2767
                    while ($rowo = Database::fetch_array($reso)) {
2768
                        $fields[$rowf['id']][9][$rowo['id']] = [
2769
                            0 => $rowo['id'],
2770
                            1 => $rowo['option_value'],
2771
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2772
                            3 => $rowo['option_order'],
2773
                        ];
2774
                    }
2775
                }
2776
            }
2777
        }
2778
2779
        return $fields;
2780
    }
2781
2782
    /**
2783
     * Build a list of extra file already uploaded in $user_folder/{$extra_field}/.
2784
     *
2785
     * @param $user_id
2786
     * @param $extra_field
2787
     * @param bool $force
2788
     * @param bool $showDelete
2789
     *
2790
     * @return bool|string
2791
     */
2792
    public static function build_user_extra_file_list(
2793
        $user_id,
2794
        $extra_field,
2795
        $force = false,
2796
        $showDelete = false
2797
    ) {
2798
        if (!$force && !empty($_POST['remove_'.$extra_field])) {
2799
            return true; // postpone reading from the filesystem
2800
        }
2801
2802
        $extra_files = self::get_user_extra_files($user_id, $extra_field);
2803
        if (empty($extra_files)) {
2804
            return false;
2805
        }
2806
2807
        $path_info = self::get_user_picture_path_by_id($user_id, 'web');
2808
        $path = $path_info['dir'];
2809
        $del_image = Display::returnIconPath('delete.png');
2810
2811
        $del_text = get_lang('Delete');
2812
        $extra_file_list = '';
2813
        if (count($extra_files) > 0) {
2814
            $extra_file_list = '<div class="files-production"><ul id="productions">';
2815
            foreach ($extra_files as $file) {
2816
                $filename = substr($file, strlen($extra_field) + 1);
2817
                $extra_file_list .= '<li>'.Display::return_icon('archive.png').
2818
                    '<a href="'.$path.$extra_field.'/'.urlencode($filename).'" target="_blank">
2819
                        '.htmlentities($filename).
2820
                    '</a> ';
2821
                if ($showDelete) {
2822
                    $extra_file_list .= '<input
2823
                        style="width:16px;"
2824
                        type="image"
2825
                        name="remove_extra_'.$extra_field.'['.urlencode($file).']"
2826
                        src="'.$del_image.'"
2827
                        alt="'.$del_text.'"
2828
                        title="'.$del_text.' '.htmlentities($filename).'"
2829
                        onclick="javascript: return confirmation(\''.htmlentities($filename).'\');" /></li>';
2830
                }
2831
            }
2832
            $extra_file_list .= '</ul></div>';
2833
        }
2834
2835
        return $extra_file_list;
2836
    }
2837
2838
    /**
2839
     * Get valid filenames in $user_folder/{$extra_field}/.
2840
     *
2841
     * @param $user_id
2842
     * @param $extra_field
2843
     * @param bool $full_path
2844
     *
2845
     * @return array
2846
     */
2847
    public static function get_user_extra_files($user_id, $extra_field, $full_path = false)
2848
    {
2849
        if (!$full_path) {
2850
            // Nothing to do
2851
        } else {
2852
            $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2853
            $path = $path_info['dir'];
2854
        }
2855
        $extra_data = self::get_extra_user_data_by_field($user_id, $extra_field);
2856
        $extra_files = $extra_data[$extra_field];
2857
2858
        $files = [];
2859
        if (is_array($extra_files)) {
2860
            foreach ($extra_files as $key => $value) {
2861
                if (!$full_path) {
2862
                    // Relative path from user folder
2863
                    $files[] = $value;
2864
                } else {
2865
                    $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...
2866
                }
2867
            }
2868
        } elseif (!empty($extra_files)) {
2869
            if (!$full_path) {
2870
                // Relative path from user folder
2871
                $files[] = $extra_files;
2872
            } else {
2873
                $files[] = $path.$extra_files;
2874
            }
2875
        }
2876
2877
        return $files; // can be an empty array
2878
    }
2879
2880
    /**
2881
     * Remove an {$extra_file} from the user folder $user_folder/{$extra_field}/.
2882
     *
2883
     * @param int    $user_id
2884
     * @param string $extra_field
2885
     * @param string $extra_file
2886
     *
2887
     * @return bool
2888
     */
2889
    public static function remove_user_extra_file($user_id, $extra_field, $extra_file)
2890
    {
2891
        $extra_file = Security::filter_filename($extra_file);
2892
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2893
        if (strpos($extra_file, $extra_field) !== false) {
2894
            $path_extra_file = $path_info['dir'].$extra_file;
2895
        } else {
2896
            $path_extra_file = $path_info['dir'].$extra_field.'/'.$extra_file;
2897
        }
2898
        if (is_file($path_extra_file)) {
2899
            unlink($path_extra_file);
2900
2901
            return true;
2902
        }
2903
2904
        return false;
2905
    }
2906
2907
    /**
2908
     * Creates a new extra field.
2909
     *
2910
     * @param string $variable    Field's internal variable name
2911
     * @param int    $fieldType   Field's type
2912
     * @param string $displayText Field's language var name
2913
     * @param string $default     Field's default value
2914
     *
2915
     * @return int
2916
     */
2917
    public static function create_extra_field(
2918
        $variable,
2919
        $fieldType,
2920
        $displayText,
2921
        $default
2922
    ) {
2923
        $extraField = new ExtraField('user');
2924
        $params = [
2925
            'variable' => $variable,
2926
            'field_type' => $fieldType,
2927
            'display_text' => $displayText,
2928
            'default_value' => $default,
2929
        ];
2930
2931
        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...
2932
    }
2933
2934
    /**
2935
     * Check if a field is available.
2936
     *
2937
     * @param string $variable
2938
     *
2939
     * @return bool
2940
     */
2941
    public static function is_extra_field_available($variable)
2942
    {
2943
        $extraField = new ExtraField('user');
2944
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2945
2946
        return !empty($data) ? true : false;
2947
    }
2948
2949
    /**
2950
     * Gets user extra fields data.
2951
     *
2952
     * @param    int    User ID
2953
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2954
     * @param    bool    Whether to return invisible fields as well
2955
     * @param    bool    Whether to split multiple-selection fields or not
2956
     *
2957
     * @return array Array of fields => value for the given user
2958
     */
2959
    public static function get_extra_user_data(
2960
        $user_id,
2961
        $prefix = false,
2962
        $allVisibility = true,
2963
        $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

2963
        /** @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...
2964
        $fieldFilter = null
2965
    ) {
2966
        $user_id = (int) $user_id;
2967
2968
        if (empty($user_id)) {
2969
            return [];
2970
        }
2971
2972
        $extra_data = [];
2973
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2974
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2975
        $user_id = (int) $user_id;
2976
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2977
                FROM $t_uf f
2978
                WHERE
2979
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
2980
                ";
2981
        $filter_cond = '';
2982
2983
        if (!$allVisibility) {
2984
            if (isset($fieldFilter)) {
2985
                $fieldFilter = (int) $fieldFilter;
2986
                $filter_cond .= " AND filter = $fieldFilter ";
2987
            }
2988
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2989
        } else {
2990
            if (isset($fieldFilter)) {
2991
                $fieldFilter = (int) $fieldFilter;
2992
                $sql .= " AND filter = $fieldFilter ";
2993
            }
2994
        }
2995
2996
        $sql .= ' ORDER BY f.field_order';
2997
2998
        $res = Database::query($sql);
2999
        if (Database::num_rows($res) > 0) {
3000
            while ($row = Database::fetch_array($res)) {
3001
                if ($row['type'] == self::USER_FIELD_TYPE_TAG) {
3002
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
3003
                    $extra_data['extra_'.$row['fvar']] = $tags;
3004
                } else {
3005
                    $sqlu = "SELECT value as fval
3006
                            FROM $t_ufv
3007
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
3008
                    $resu = Database::query($sqlu);
3009
                    // get default value
3010
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
3011
                               WHERE id=".$row['id'];
3012
                    $res_df = Database::query($sql_df);
3013
3014
                    if (Database::num_rows($resu) > 0) {
3015
                        $rowu = Database::fetch_array($resu);
3016
                        $fval = $rowu['fval'];
3017
                        if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
3018
                            $fval = explode(';', $rowu['fval']);
3019
                        }
3020
                    } else {
3021
                        $row_df = Database::fetch_array($res_df);
3022
                        $fval = $row_df['fval_df'];
3023
                    }
3024
                    // We get here (and fill the $extra_data array) even if there
3025
                    // is no user with data (we fill it with default values)
3026
                    if ($prefix) {
3027
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
3028
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
3029
                        } else {
3030
                            $extra_data['extra_'.$row['fvar']] = $fval;
3031
                        }
3032
                    } else {
3033
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
3034
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
3035
                        } else {
3036
                            $extra_data[$row['fvar']] = $fval;
3037
                        }
3038
                    }
3039
                }
3040
            }
3041
        }
3042
3043
        return $extra_data;
3044
    }
3045
3046
    /** Get extra user data by field
3047
     * @param int    user ID
3048
     * @param string the internal variable name of the field
3049
     *
3050
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
3051
     */
3052
    public static function get_extra_user_data_by_field(
3053
        $user_id,
3054
        $field_variable,
3055
        $prefix = false,
3056
        $all_visibility = true,
3057
        $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

3057
        /** @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...
3058
    ) {
3059
        $user_id = (int) $user_id;
3060
3061
        if (empty($user_id)) {
3062
            return [];
3063
        }
3064
3065
        $extra_data = [];
3066
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
3067
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3068
3069
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
3070
                FROM $t_uf f
3071
                WHERE f.variable = '$field_variable' ";
3072
3073
        if (!$all_visibility) {
3074
            $sql .= " AND f.visible_to_self = 1 ";
3075
        }
3076
3077
        $sql .= " AND extra_field_type = ".EntityExtraField::USER_FIELD_TYPE;
3078
        $sql .= " ORDER BY f.field_order ";
3079
3080
        $res = Database::query($sql);
3081
        if (Database::num_rows($res) > 0) {
3082
            while ($row = Database::fetch_array($res)) {
3083
                $sqlu = "SELECT value as fval FROM $t_ufv v
3084
                         INNER JOIN $t_uf f
3085
                         ON (v.field_id = f.id)
3086
                         WHERE
3087
                            extra_field_type = ".EntityExtraField::USER_FIELD_TYPE." AND
3088
                            field_id = ".$row['id']." AND
3089
                            item_id = ".$user_id;
3090
                $resu = Database::query($sqlu);
3091
                $fval = '';
3092
                if (Database::num_rows($resu) > 0) {
3093
                    $rowu = Database::fetch_array($resu);
3094
                    $fval = $rowu['fval'];
3095
                    if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
3096
                        $fval = explode(';', $rowu['fval']);
3097
                    }
3098
                }
3099
                if ($prefix) {
3100
                    $extra_data['extra_'.$row['fvar']] = $fval;
3101
                } else {
3102
                    $extra_data[$row['fvar']] = $fval;
3103
                }
3104
            }
3105
        }
3106
3107
        return $extra_data;
3108
    }
3109
3110
    /**
3111
     * Get the extra field information for a certain field (the options as well).
3112
     *
3113
     * @param string $variable The name of the field we want to know everything about
3114
     *
3115
     * @return array Array containing all the information about the extra profile field
3116
     *               (first level of array contains field details, then 'options' sub-array contains options details,
3117
     *               as returned by the database)
3118
     *
3119
     * @author Julio Montoya
3120
     *
3121
     * @since v1.8.6
3122
     */
3123
    public static function get_extra_field_information_by_name($variable)
3124
    {
3125
        $extraField = new ExtraField('user');
3126
3127
        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...
3128
    }
3129
3130
    /**
3131
     * Get the extra field information for user tag (the options as well).
3132
     *
3133
     * @param int $variable The name of the field we want to know everything about
3134
     *
3135
     * @return array Array containing all the information about the extra profile field
3136
     *               (first level of array contains field details, then 'options' sub-array contains options details,
3137
     *               as returned by the database)
3138
     *
3139
     * @author José Loguercio
3140
     *
3141
     * @since v1.11.0
3142
     */
3143
    public static function get_extra_field_tags_information_by_name($variable)
3144
    {
3145
        $extraField = new ExtraField('user');
3146
3147
        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...
3148
    }
3149
3150
    /**
3151
     * @param string $type
3152
     *
3153
     * @return array
3154
     */
3155
    public static function get_all_extra_field_by_type($type)
3156
    {
3157
        $extraField = new ExtraField('user');
3158
3159
        return $extraField->get_all_extra_field_by_type($type);
3160
    }
3161
3162
    /**
3163
     * Get all the extra field information of a certain field (also the options).
3164
     *
3165
     * @param int $fieldId the ID of the field we want to know everything of
3166
     *
3167
     * @return array $return containing all th information about the extra profile field
3168
     *
3169
     * @author Julio Montoya
3170
     *
3171
     * @deprecated
3172
     * @since v1.8.6
3173
     */
3174
    public static function get_extra_field_information($fieldId)
3175
    {
3176
        $extraField = new ExtraField('user');
3177
3178
        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...
3179
    }
3180
3181
    /**
3182
     * Get extra user data by value.
3183
     *
3184
     * @param string $variable the internal variable name of the field
3185
     * @param string $value    the internal value of the field
3186
     * @param bool   $useLike
3187
     *
3188
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
3189
     */
3190
    public static function get_extra_user_data_by_value($variable, $value, $useLike = false)
3191
    {
3192
        $extraFieldValue = new ExtraFieldValue('user');
3193
        $extraField = new ExtraField('user');
3194
3195
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
3196
3197
        if (false === $info) {
3198
            return [];
3199
        }
3200
3201
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
3202
            $variable,
3203
            $value,
3204
            false,
3205
            false,
3206
            true,
3207
            $useLike
3208
        );
3209
3210
        $result = [];
3211
        if (!empty($data)) {
3212
            foreach ($data as $item) {
3213
                $result[] = $item['item_id'];
3214
            }
3215
        }
3216
3217
        return $result;
3218
    }
3219
3220
    /**
3221
     * Get extra user data by tags value.
3222
     *
3223
     * @param int    $fieldId the ID of the field we want to know everything of
3224
     * @param string $tag     the tag name for search
3225
     *
3226
     * @return array with extra data info of a user
3227
     *
3228
     * @author José Loguercio
3229
     *
3230
     * @since v1.11.0
3231
     */
3232
    public static function get_extra_user_data_by_tags($fieldId, $tag)
3233
    {
3234
        $extraField = new ExtraField('user');
3235
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
3236
        $array = [];
3237
        foreach ($result as $index => $user) {
3238
            $array[] = $user['user_id'];
3239
        }
3240
3241
        return $array;
3242
    }
3243
3244
    /**
3245
     * Get extra user data by field variable.
3246
     *
3247
     * @param string $variable field variable
3248
     *
3249
     * @return array data
3250
     */
3251
    public static function get_extra_user_data_by_field_variable($variable)
3252
    {
3253
        $extraInfo = self::get_extra_field_information_by_name($variable);
3254
        $field_id = (int) $extraInfo['id'];
3255
3256
        $extraField = new ExtraFieldValue('user');
3257
        $data = $extraField->getValuesByFieldId($field_id);
3258
3259
        if (!empty($data)) {
3260
            foreach ($data as $row) {
3261
                $user_id = $row['item_id'];
3262
                $data[$user_id] = $row;
3263
            }
3264
        }
3265
3266
        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...
3267
    }
3268
3269
    /**
3270
     * Get extra user data tags by field variable.
3271
     *
3272
     * @param string $variable field variable
3273
     *
3274
     * @return array
3275
     */
3276
    public static function get_extra_user_data_for_tags($variable)
3277
    {
3278
        $data = self::get_extra_field_tags_information_by_name($variable);
3279
3280
        return $data;
3281
    }
3282
3283
    /**
3284
     * Gives a list of [session_category][session_id] for the current user.
3285
     *
3286
     * @param int  $user_id
3287
     * @param bool $is_time_over                 whether to fill the first element or not
3288
     *                                           (to give space for courses out of categories)
3289
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
3290
     * @param bool $ignoreTimeLimit              ignore time start/end
3291
     * @param bool $getCount
3292
     *
3293
     * @return array list of statuses [session_category][session_id]
3294
     *
3295
     * @todo ensure multiple access urls are managed correctly
3296
     */
3297
    public static function get_sessions_by_category(
3298
        $user_id,
3299
        $is_time_over = true,
3300
        $ignore_visibility_for_admins = false,
3301
        $ignoreTimeLimit = false,
3302
        $getCount = false
3303
    ) {
3304
        $user_id = (int) $user_id;
3305
3306
        if (empty($user_id)) {
3307
            return [];
3308
        }
3309
3310
        $allowOrder = api_get_configuration_value('session_list_order');
3311
        $position = '';
3312
        if ($allowOrder) {
3313
            $position = ', s.position AS position ';
3314
        }
3315
3316
        // Get the list of sessions per user
3317
        $now = new DateTime('now', new DateTimeZone('UTC'));
3318
3319
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
3320
        // join would not catch session-courses where the user is general
3321
        // session coach but which do not have students nor coaches registered
3322
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
3323
3324
        if (!$getCount) {
3325
            $dqlSelect = " DISTINCT
3326
                s.id,
3327
                s.name,
3328
                s.accessStartDate AS access_start_date,
3329
                s.accessEndDate AS access_end_date,
3330
                s.duration,
3331
                sc.id AS session_category_id,
3332
                sc.name AS session_category_name,
3333
                sc.dateStart AS session_category_date_start,
3334
                sc.dateEnd AS session_category_date_end,
3335
                s.coachAccessStartDate AS coach_access_start_date,
3336
                s.coachAccessEndDate AS coach_access_end_date,
3337
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
3338
                $position
3339
            ";
3340
        }
3341
3342
        $dql = "SELECT $dqlSelect
3343
                FROM ChamiloCoreBundle:Session AS s
3344
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
3345
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.sessionId = s.id
3346
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc ";
3347
3348
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
3349
        // is awfully inefficient for large sets of data (1m25s for 58K
3350
        // sessions, BT#14115) but executing a similar query twice and grouping
3351
        // the results afterwards in PHP takes about 1/1000th of the time
3352
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
3353
        $dqlStudent = $dql.' WHERE scu.user = :user AND url.accessUrlId = :url ';
3354
        $dqlCoach = $dql.' WHERE s.generalCoach = :user AND url.accessUrlId = :url ';
3355
3356
        // Default order
3357
        $order = 'ORDER BY sc.name, s.name';
3358
3359
        // Order by date if showing all sessions
3360
        $showAllSessions = api_get_configuration_value('show_all_sessions_on_my_course_page') === true;
3361
        if ($showAllSessions) {
3362
            $order = 'ORDER BY s.accessStartDate';
3363
        }
3364
3365
        // Order by position
3366
        if ($allowOrder) {
3367
            $order = 'ORDER BY s.position';
3368
        }
3369
3370
        // Order by dates according to settings
3371
        $orderBySettings = api_get_configuration_value('my_courses_session_order');
3372
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
3373
            $field = $orderBySettings['field'];
3374
            $orderSetting = $orderBySettings['order'];
3375
            switch ($field) {
3376
                case 'start_date':
3377
                    $order = " ORDER BY s.accessStartDate $orderSetting";
3378
                    break;
3379
                case 'end_date':
3380
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
3381
                    if ($orderSetting === 'asc') {
3382
                        // Put null values at the end
3383
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
3384
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
3385
                    }
3386
                    break;
3387
                case 'name':
3388
                    $order = " ORDER BY s.name $orderSetting ";
3389
                    break;
3390
            }
3391
        }
3392
3393
        $dqlStudent .= $order;
3394
        $dqlCoach .= $order;
3395
3396
        $accessUrlId = api_get_current_access_url_id();
3397
        $dqlStudent = Database::getManager()
3398
            ->createQuery($dqlStudent)
3399
            ->setParameters(
3400
                ['user' => $user_id, 'url' => $accessUrlId]
3401
            )
3402
        ;
3403
        $dqlCoach = Database::getManager()
3404
            ->createQuery($dqlCoach)
3405
            ->setParameters(
3406
                ['user' => $user_id, 'url' => $accessUrlId]
3407
            )
3408
        ;
3409
3410
        if ($getCount) {
3411
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
3412
        }
3413
3414
        $sessionDataStudent = $dqlStudent->getResult();
3415
        $sessionDataCoach = $dqlCoach->getResult();
3416
3417
        $sessionData = [];
3418
        // First fill $sessionData with student sessions
3419
        foreach ($sessionDataStudent as $row) {
3420
            $sessionData[$row['id']] = $row;
3421
        }
3422
        // Overwrite session data of the user as a student with session data
3423
        // of the user as a coach.
3424
        // There shouldn't be such duplicate rows, but just in case...
3425
        foreach ($sessionDataCoach as $row) {
3426
            $sessionData[$row['id']] = $row;
3427
        }
3428
3429
        $collapsable = api_get_configuration_value('allow_user_session_collapsable');
3430
        $extraField = new ExtraFieldValue('session');
3431
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
3432
3433
        $categories = [];
3434
        foreach ($sessionData as $row) {
3435
            $session_id = $row['id'];
3436
            $coachList = SessionManager::getCoachesBySession($session_id);
3437
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
3438
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
3439
            $courseList = self::get_courses_list_by_session(
3440
                $user_id,
3441
                $session_id
3442
            );
3443
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
3444
3445
            // User portal filters:
3446
            if ($ignoreTimeLimit === false) {
3447
                if ($is_time_over) {
3448
                    // History
3449
                    if ($row['duration']) {
3450
                        if ($daysLeft >= 0) {
3451
                            continue;
3452
                        }
3453
                    } else {
3454
                        if (empty($row['access_end_date'])) {
3455
                            continue;
3456
                        } else {
3457
                            if ($row['access_end_date'] > $now) {
3458
                                continue;
3459
                            }
3460
                        }
3461
                    }
3462
                } else {
3463
                    // Current user portal
3464
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
3465
                    $isCoachOfCourse = in_array($user_id, $coachList);
3466
3467
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
3468
                        // Teachers can access the session depending in the access_coach date
3469
                    } else {
3470
                        if ($row['duration']) {
3471
                            if ($daysLeft <= 0) {
3472
                                continue;
3473
                            }
3474
                        } else {
3475
                            if (isset($row['access_end_date']) &&
3476
                                !empty($row['access_end_date'])
3477
                            ) {
3478
                                if ($row['access_end_date'] <= $now) {
3479
                                    continue;
3480
                                }
3481
                            }
3482
                        }
3483
                    }
3484
                }
3485
            }
3486
3487
            $categories[$row['session_category_id']]['session_category'] = [
3488
                'id' => $row['session_category_id'],
3489
                'name' => $row['session_category_name'],
3490
                'date_start' => $categoryStart,
3491
                'date_end' => $categoryEnd,
3492
            ];
3493
3494
            $visibility = api_get_session_visibility(
3495
                $session_id,
3496
                null,
3497
                $ignore_visibility_for_admins
3498
            );
3499
3500
            if ($visibility != SESSION_VISIBLE) {
3501
                // Course Coach session visibility.
3502
                $blockedCourseCount = 0;
3503
                $closedVisibilityList = [
3504
                    COURSE_VISIBILITY_CLOSED,
3505
                    COURSE_VISIBILITY_HIDDEN,
3506
                ];
3507
3508
                foreach ($courseList as $course) {
3509
                    // Checking session visibility
3510
                    $sessionCourseVisibility = api_get_session_visibility(
3511
                        $session_id,
3512
                        $course['real_id'],
3513
                        $ignore_visibility_for_admins
3514
                    );
3515
3516
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
3517
                    if ($courseIsVisible === false || $sessionCourseVisibility == SESSION_INVISIBLE) {
3518
                        $blockedCourseCount++;
3519
                    }
3520
                }
3521
3522
                // If all courses are blocked then no show in the list.
3523
                if ($blockedCourseCount === count($courseList)) {
3524
                    $visibility = SESSION_INVISIBLE;
3525
                } else {
3526
                    $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...
3527
                }
3528
            }
3529
3530
            switch ($visibility) {
3531
                case SESSION_VISIBLE_READ_ONLY:
3532
                case SESSION_VISIBLE:
3533
                case SESSION_AVAILABLE:
3534
                    break;
3535
                case SESSION_INVISIBLE:
3536
                    if ($ignore_visibility_for_admins === false) {
3537
                        continue 2;
3538
                    }
3539
            }
3540
3541
            $collapsed = '';
3542
            $collapsedAction = '';
3543
            if ($collapsable) {
3544
                $collapsableData = SessionManager::getCollapsableData(
3545
                    $user_id,
3546
                    $session_id,
3547
                    $extraField,
3548
                    $collapsableLink
3549
                );
3550
                $collapsed = $collapsableData['collapsed'];
3551
                $collapsedAction = $collapsableData['collapsable_link'];
3552
            }
3553
3554
            $categories[$row['session_category_id']]['sessions'][] = [
3555
                'session_name' => $row['name'],
3556
                'session_id' => $row['id'],
3557
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
3558
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
3559
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
3560
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
3561
                'courses' => $courseList,
3562
                'collapsed' => $collapsed,
3563
                'collapsable_link' => $collapsedAction,
3564
                'duration' => $row['duration'],
3565
            ];
3566
        }
3567
3568
        return $categories;
3569
    }
3570
3571
    /**
3572
     * Gives a list of [session_id-course_code] => [status] for the current user.
3573
     *
3574
     * @param int $user_id
3575
     * @param int $sessionLimit
3576
     *
3577
     * @return array list of statuses (session_id-course_code => status)
3578
     */
3579
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
3580
    {
3581
        // Database Table Definitions
3582
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3583
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
3584
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3585
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3586
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3587
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3588
3589
        $user_id = (int) $user_id;
3590
3591
        if (empty($user_id)) {
3592
            return [];
3593
        }
3594
3595
        // We filter the courses from the URL
3596
        $join_access_url = $where_access_url = '';
3597
        if (api_get_multiple_access_url()) {
3598
            $access_url_id = api_get_current_access_url_id();
3599
            if ($access_url_id != -1) {
3600
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3601
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
3602
                $where_access_url = " AND access_url_id = $access_url_id ";
3603
            }
3604
        }
3605
3606
        // Courses in which we subscribed out of any session
3607
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3608
3609
        $sql = "SELECT
3610
                    course.code,
3611
                    course_rel_user.status course_rel_status,
3612
                    course_rel_user.sort sort,
3613
                    course_rel_user.user_course_cat user_course_cat
3614
                 FROM $tbl_course_user course_rel_user
3615
                 LEFT JOIN $tbl_course course
3616
                 ON course.id = course_rel_user.c_id
3617
                 LEFT JOIN $tbl_user_course_category user_course_category
3618
                 ON course_rel_user.user_course_cat = user_course_category.id
3619
                 $join_access_url
3620
                 WHERE
3621
                    course_rel_user.user_id = '".$user_id."' AND
3622
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
3623
                    $where_access_url
3624
                 ORDER BY user_course_category.sort, course_rel_user.sort, course.title ASC";
3625
3626
        $course_list_sql_result = Database::query($sql);
3627
3628
        $personal_course_list = [];
3629
        if (Database::num_rows($course_list_sql_result) > 0) {
3630
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3631
                $course_info = api_get_course_info($result_row['code']);
3632
                $result_row['course_info'] = $course_info;
3633
                $personal_course_list[] = $result_row;
3634
            }
3635
        }
3636
3637
        $coachCourseConditions = '';
3638
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
3639
        if (api_is_allowed_to_create_course()) {
3640
            $sessionListFromCourseCoach = [];
3641
            $sql = " SELECT DISTINCT session_id
3642
                    FROM $tbl_session_course_user
3643
                    WHERE user_id = $user_id AND status = 2 ";
3644
3645
            $result = Database::query($sql);
3646
            if (Database::num_rows($result)) {
3647
                $result = Database::store_result($result);
3648
                foreach ($result as $session) {
3649
                    $sessionListFromCourseCoach[] = $session['session_id'];
3650
                }
3651
            }
3652
            if (!empty($sessionListFromCourseCoach)) {
3653
                $condition = implode("','", $sessionListFromCourseCoach);
3654
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
3655
            }
3656
        }
3657
3658
        // Get the list of sessions where the user is subscribed
3659
        // This is divided into two different queries
3660
        $sessions = [];
3661
        $sessionLimitRestriction = '';
3662
        if (!empty($sessionLimit)) {
3663
            $sessionLimit = (int) $sessionLimit;
3664
            $sessionLimitRestriction = "LIMIT $sessionLimit";
3665
        }
3666
3667
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
3668
                FROM $tbl_session_user su INNER JOIN $tbl_session s
3669
                ON (s.id = su.session_id)
3670
                WHERE (
3671
                    su.user_id = $user_id AND
3672
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
3673
                )
3674
                $coachCourseConditions
3675
                ORDER BY access_start_date, access_end_date, name
3676
                $sessionLimitRestriction
3677
        ";
3678
3679
        $result = Database::query($sql);
3680
        if (Database::num_rows($result) > 0) {
3681
            while ($row = Database::fetch_assoc($result)) {
3682
                $sessions[$row['id']] = $row;
3683
            }
3684
        }
3685
3686
        $sql = "SELECT DISTINCT
3687
                id, name, access_start_date, access_end_date
3688
                FROM $tbl_session s
3689
                WHERE (
3690
                    id_coach = $user_id
3691
                )
3692
                $coachCourseConditions
3693
                ORDER BY access_start_date, access_end_date, name";
3694
3695
        $result = Database::query($sql);
3696
        if (Database::num_rows($result) > 0) {
3697
            while ($row = Database::fetch_assoc($result)) {
3698
                if (empty($sessions[$row['id']])) {
3699
                    $sessions[$row['id']] = $row;
3700
                }
3701
            }
3702
        }
3703
3704
        if (api_is_allowed_to_create_course()) {
3705
            foreach ($sessions as $enreg) {
3706
                $session_id = $enreg['id'];
3707
                $session_visibility = api_get_session_visibility($session_id);
3708
3709
                if ($session_visibility == SESSION_INVISIBLE) {
3710
                    continue;
3711
                }
3712
3713
                // This query is horribly slow when more than a few thousand
3714
                // users and just a few sessions to which they are subscribed
3715
                $sql = "SELECT DISTINCT
3716
                        course.code code,
3717
                        course.title i,
3718
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
3719
                        email, course.course_language l,
3720
                        1 sort,
3721
                        category_code user_course_cat,
3722
                        access_start_date,
3723
                        access_end_date,
3724
                        session.id as session_id,
3725
                        session.name as session_name
3726
                    FROM $tbl_session_course_user as session_course_user
3727
                    INNER JOIN $tbl_course AS course
3728
                        ON course.id = session_course_user.c_id
3729
                    INNER JOIN $tbl_session as session
3730
                        ON session.id = session_course_user.session_id
3731
                    LEFT JOIN $tbl_user as user
3732
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
3733
                    WHERE
3734
                        session_course_user.session_id = $session_id AND (
3735
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
3736
                            OR session.id_coach = $user_id
3737
                        )
3738
                    ORDER BY i";
3739
                $course_list_sql_result = Database::query($sql);
3740
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3741
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3742
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3743
                    $personal_course_list[$key] = $result_row;
3744
                }
3745
            }
3746
        }
3747
3748
        foreach ($sessions as $enreg) {
3749
            $session_id = $enreg['id'];
3750
            $session_visibility = api_get_session_visibility($session_id);
3751
            if ($session_visibility == SESSION_INVISIBLE) {
3752
                continue;
3753
            }
3754
3755
            /* This query is very similar to the above query,
3756
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3757
            $sql = "SELECT DISTINCT
3758
                course.code code,
3759
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
3760
                email,
3761
                course.course_language l,
3762
                1 sort,
3763
                category_code user_course_cat,
3764
                access_start_date,
3765
                access_end_date,
3766
                session.id as session_id,
3767
                session.name as session_name,
3768
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
3769
            FROM $tbl_session_course_user as session_course_user
3770
            INNER JOIN $tbl_course AS course
3771
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
3772
            INNER JOIN $tbl_session as session
3773
            ON session_course_user.session_id = session.id
3774
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
3775
            WHERE session_course_user.user_id = $user_id
3776
            ORDER BY i";
3777
3778
            $course_list_sql_result = Database::query($sql);
3779
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3780
                $result_row['course_info'] = api_get_course_info($result_row['code']);
3781
                $key = $result_row['session_id'].' - '.$result_row['code'];
3782
                if (!isset($personal_course_list[$key])) {
3783
                    $personal_course_list[$key] = $result_row;
3784
                }
3785
            }
3786
        }
3787
3788
        return $personal_course_list;
3789
    }
3790
3791
    /**
3792
     * Gives a list of courses for the given user in the given session.
3793
     *
3794
     * @param int $user_id
3795
     * @param int $session_id
3796
     *
3797
     * @return array list of statuses (session_id-course_code => status)
3798
     */
3799
    public static function get_courses_list_by_session($user_id, $session_id)
3800
    {
3801
        // Database Table Definitions
3802
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3803
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
3804
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3805
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3806
3807
        $user_id = (int) $user_id;
3808
        $session_id = (int) $session_id;
3809
        // We filter the courses from the URL
3810
        $join_access_url = $where_access_url = '';
3811
        if (api_get_multiple_access_url()) {
3812
            $urlId = api_get_current_access_url_id();
3813
            if ($urlId != -1) {
3814
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3815
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
3816
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
3817
            }
3818
        }
3819
3820
        /* This query is very similar to the query below, but it will check the
3821
        session_rel_course_user table if there are courses registered
3822
        to our user or not */
3823
        $sql = "SELECT DISTINCT
3824
                    c.visibility,
3825
                    c.id as real_id,
3826
                    c.code as course_code,
3827
                    sc.position
3828
                FROM $tbl_session_course_user as scu
3829
                INNER JOIN $tbl_session_course sc
3830
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3831
                INNER JOIN $tableCourse as c
3832
                ON (scu.c_id = c.id)
3833
                $join_access_url
3834
                WHERE
3835
                    scu.user_id = $user_id AND
3836
                    scu.session_id = $session_id
3837
                    $where_access_url
3838
                ORDER BY sc.position ASC";
3839
3840
        $myCourseList = [];
3841
        $courses = [];
3842
        $result = Database::query($sql);
3843
        if (Database::num_rows($result) > 0) {
3844
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3845
                $result_row['status'] = 5;
3846
                if (!in_array($result_row['real_id'], $courses)) {
3847
                    $position = $result_row['position'];
3848
                    if (!isset($myCourseList[$position])) {
3849
                        $myCourseList[$position] = $result_row;
3850
                    } else {
3851
                        $myCourseList[] = $result_row;
3852
                    }
3853
                    $courses[] = $result_row['real_id'];
3854
                }
3855
            }
3856
        }
3857
3858
        if (api_is_allowed_to_create_course()) {
3859
            $sql = "SELECT DISTINCT
3860
                        c.visibility,
3861
                        c.id as real_id,
3862
                        c.code as course_code,
3863
                        sc.position
3864
                    FROM $tbl_session_course_user as scu
3865
                    INNER JOIN $tbl_session as s
3866
                    ON (scu.session_id = s.id)
3867
                    INNER JOIN $tbl_session_course sc
3868
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3869
                    INNER JOIN $tableCourse as c
3870
                    ON (scu.c_id = c.id)
3871
                    $join_access_url
3872
                    WHERE
3873
                      s.id = $session_id AND
3874
                      (
3875
                        (scu.user_id = $user_id AND scu.status = 2) OR
3876
                        s.id_coach = $user_id
3877
                      )
3878
                    $where_access_url
3879
                    ORDER BY sc.position ASC";
3880
            $result = Database::query($sql);
3881
3882
            if (Database::num_rows($result) > 0) {
3883
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3884
                    $result_row['status'] = 2;
3885
                    if (!in_array($result_row['real_id'], $courses)) {
3886
                        $position = $result_row['position'];
3887
                        if (!isset($myCourseList[$position])) {
3888
                            $myCourseList[$position] = $result_row;
3889
                        } else {
3890
                            $myCourseList[] = $result_row;
3891
                        }
3892
                        $courses[] = $result_row['real_id'];
3893
                    }
3894
                }
3895
            }
3896
        }
3897
3898
        if (api_is_drh()) {
3899
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3900
            $sessionList = array_keys($sessionList);
3901
            if (in_array($session_id, $sessionList)) {
3902
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3903
                if (!empty($courseList)) {
3904
                    foreach ($courseList as $course) {
3905
                        if (!in_array($course['id'], $courses)) {
3906
                            $position = $course['position'];
3907
                            if (!isset($myCourseList[$position])) {
3908
                                $myCourseList[$position] = $course;
3909
                            } else {
3910
                                $myCourseList[] = $course;
3911
                            }
3912
                        }
3913
                    }
3914
                }
3915
            }
3916
        } else {
3917
            //check if user is general coach for this session
3918
            $sessionInfo = api_get_session_info($session_id);
3919
            if ($sessionInfo['id_coach'] == $user_id) {
3920
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3921
                if (!empty($courseList)) {
3922
                    foreach ($courseList as $course) {
3923
                        if (!in_array($course['id'], $courses)) {
3924
                            $position = $course['position'];
3925
                            if (!isset($myCourseList[$position])) {
3926
                                $myCourseList[$position] = $course;
3927
                            } else {
3928
                                $myCourseList[] = $course;
3929
                            }
3930
                        }
3931
                    }
3932
                }
3933
            }
3934
        }
3935
3936
        if (!empty($myCourseList)) {
3937
            ksort($myCourseList);
3938
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
3939
            if (empty($checkPosition)) {
3940
                // The session course list doesn't have any position,
3941
                // then order the course list by course code
3942
                $list = array_column($myCourseList, 'course_code');
3943
                array_multisort($myCourseList, SORT_ASC, $list);
3944
            }
3945
        }
3946
3947
        return $myCourseList;
3948
    }
3949
3950
    /**
3951
     * Get user id from a username.
3952
     *
3953
     * @param string $username
3954
     *
3955
     * @return int User ID (or false if not found)
3956
     */
3957
    public static function get_user_id_from_username($username)
3958
    {
3959
        if (empty($username)) {
3960
            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...
3961
        }
3962
        $username = trim($username);
3963
        $username = Database::escape_string($username);
3964
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3965
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3966
        $res = Database::query($sql);
3967
3968
        if ($res === false) {
3969
            return false;
3970
        }
3971
        if (Database::num_rows($res) !== 1) {
3972
            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...
3973
        }
3974
        $row = Database::fetch_array($res);
3975
3976
        return $row['id'];
3977
    }
3978
3979
    /**
3980
     * Get the users files upload from his share_folder.
3981
     *
3982
     * @param string $user_id      User ID
3983
     * @param string $course       course directory
3984
     * @param string $resourceType resource type: images, all
3985
     *
3986
     * @return string
3987
     */
3988
    public static function get_user_upload_files_by_course(
3989
        $user_id,
3990
        $course,
3991
        $resourceType = 'all'
3992
    ) {
3993
        $return = '';
3994
        $user_id = (int) $user_id;
3995
3996
        if (!empty($user_id) && !empty($course)) {
3997
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3998
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3999
            $file_list = [];
4000
4001
            if (is_dir($path)) {
4002
                $handle = opendir($path);
4003
                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

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

6266
            Event::/** @scrutinizer ignore-call */ 
6267
                   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...
6267
6268
            // Logout the current user
6269
            self::loginDelete(api_get_user_id());
6270
6271
            Session::erase('_user');
6272
            Session::erase('is_platformAdmin');
6273
            Session::erase('is_allowedCreateCourse');
6274
            Session::erase('_uid');
6275
6276
            // Cleaning session variables
6277
            $_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...
6278
            $_user['lastName'] = $userInfo['lastname'];
6279
            $_user['mail'] = $userInfo['email'];
6280
            $_user['official_code'] = $userInfo['official_code'];
6281
            $_user['picture_uri'] = $userInfo['picture_uri'];
6282
            $_user['user_id'] = $userId;
6283
            $_user['id'] = $userId;
6284
            $_user['status'] = $userInfo['status'];
6285
6286
            // Filling session variables with new data
6287
            Session::write('_uid', $userId);
6288
            Session::write('_user', $userInfo);
6289
            Session::write('is_platformAdmin', (bool) self::is_admin($userId));
6290
            Session::write('is_allowedCreateCourse', $userInfo['status'] == 1);
6291
            // will be useful later to know if the user is actually an admin or not (example reporting)
6292
            Session::write('login_as', true);
6293
            $logInfo = [
6294
                'tool' => 'login',
6295
                'tool_id' => 0,
6296
                'tool_id_detail' => 0,
6297
                'info' => $userId,
6298
            ];
6299
            Event::registerLog($logInfo);
6300
6301
            return true;
6302
        }
6303
6304
        return false;
6305
    }
6306
6307
    /**
6308
     * Remove all login records from the track_e_online stats table,
6309
     * for the given user ID.
6310
     *
6311
     * @param int $userId User ID
6312
     */
6313
    public static function loginDelete($userId)
6314
    {
6315
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6316
        $userId = (int) $userId;
6317
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
6318
        Database::query($query);
6319
    }
6320
6321
    /**
6322
     * Login as first admin user registered in the platform.
6323
     *
6324
     * @return array
6325
     */
6326
    public static function logInAsFirstAdmin()
6327
    {
6328
        $adminList = self::get_all_administrators();
6329
6330
        if (!empty($adminList)) {
6331
            $userInfo = current($adminList);
6332
            if (!empty($userInfo)) {
6333
                $result = self::loginAsUser($userInfo['user_id'], false);
6334
                if ($result && api_is_platform_admin()) {
6335
                    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...
6336
                }
6337
            }
6338
        }
6339
6340
        return [];
6341
    }
6342
6343
    /**
6344
     * Check if user is teacher of a student based in their courses.
6345
     *
6346
     * @param $teacherId
6347
     * @param $studentId
6348
     *
6349
     * @return array
6350
     */
6351
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
6352
    {
6353
        $courses = CourseManager::getCoursesFollowedByUser(
6354
            $teacherId,
6355
            COURSEMANAGER
6356
        );
6357
        if (empty($courses)) {
6358
            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...
6359
        }
6360
6361
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
6362
        if (empty($coursesFromUser)) {
6363
            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...
6364
        }
6365
6366
        $coursesCodeList = array_column($courses, 'code');
6367
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
6368
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
6369
        $commonCourses = array_filter($commonCourses);
6370
6371
        if (!empty($commonCourses)) {
6372
            return $commonCourses;
6373
        }
6374
6375
        return [];
6376
    }
6377
6378
    /**
6379
     * @param int $teacherId
6380
     * @param int $studentId
6381
     *
6382
     * @return bool
6383
     */
6384
    public static function isTeacherOfStudent($teacherId, $studentId)
6385
    {
6386
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
6387
            $teacherId,
6388
            $studentId
6389
        );
6390
6391
        if (!empty($courses)) {
6392
            return true;
6393
        }
6394
6395
        return false;
6396
    }
6397
6398
    /**
6399
     * Send user confirmation mail.
6400
     *
6401
     * @throws Exception
6402
     */
6403
    public static function sendUserConfirmationMail(User $user)
6404
    {
6405
        $uniqueId = api_get_unique_id();
6406
        $user->setConfirmationToken($uniqueId);
6407
6408
        Database::getManager()->persist($user);
6409
        Database::getManager()->flush();
6410
6411
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
6412
6413
        // Check if the user was originally set for an automated subscription to a course or session
6414
        $courseCodeToRedirect = Session::read('course_redirect');
6415
        $sessionToRedirect = Session::read('session_redirect');
6416
        if (!empty($courseCodeToRedirect)) {
6417
            $url .= '&c='.$courseCodeToRedirect;
6418
        }
6419
        if (!empty($sessionToRedirect)) {
6420
            $url .= '&s='.$sessionToRedirect;
6421
        }
6422
        $mailSubject = get_lang('RegistrationConfirmation');
6423
        $mailBody = get_lang('RegistrationConfirmationEmailMessage')
6424
            .PHP_EOL
6425
            .Display::url($url, $url);
6426
6427
        api_mail_html(
6428
            self::formatUserFullName($user),
6429
            $user->getEmail(),
6430
            $mailSubject,
6431
            $mailBody
6432
        );
6433
        Display::addFlash(Display::return_message(get_lang('CheckYourEmailAndFollowInstructions')));
6434
    }
6435
6436
    /**
6437
     * Anonymize a user. Replace personal info by anonymous info.
6438
     *
6439
     * @param int  $userId   User id
6440
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
6441
     *
6442
     * @throws \Exception
6443
     *
6444
     * @return bool
6445
     * @assert (0) === false
6446
     */
6447
    public static function anonymize($userId, $deleteIP = true)
6448
    {
6449
        global $debug;
6450
6451
        $userId = (int) $userId;
6452
6453
        if (empty($userId)) {
6454
            return false;
6455
        }
6456
6457
        $em = Database::getManager();
6458
        $user = api_get_user_entity($userId);
6459
        $uniqueId = uniqid('anon', true);
6460
        $user
6461
            ->setFirstname($uniqueId)
6462
            ->setLastname($uniqueId)
6463
            ->setBiography('')
6464
            ->setAddress('')
6465
            ->setCurriculumItems(null)
6466
            ->setDateOfBirth(null)
6467
            ->setCompetences('')
6468
            ->setDiplomas('')
6469
            ->setOpenarea('')
6470
            ->setTeach('')
6471
            ->setProductions(null)
6472
            ->setOpenid('')
6473
            ->setEmailCanonical($uniqueId.'@example.com')
6474
            ->setEmail($uniqueId.'@example.com')
6475
            ->setUsername($uniqueId)
6476
            ->setUsernameCanonical($uniqueId)
6477
            ->setPhone('')
6478
            ->setOfficialCode('')
6479
        ;
6480
6481
        self::deleteUserPicture($userId);
6482
        self::cleanUserRequestsOfRemoval($userId);
6483
6484
        // The IP address is a border-case personal data, as it does
6485
        // not directly allow for personal identification (it is not
6486
        // a completely safe value in most countries - the IP could
6487
        // be used by neighbours and crackers)
6488
        if ($deleteIP) {
6489
            $substitute = '127.0.0.1';
6490
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
6491
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
6492
            $res = Database::query($sql);
6493
            if ($res === false && $debug > 0) {
6494
                error_log("Could not anonymize IP address for user $userId ($sql)");
6495
            }
6496
6497
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6498
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6499
            $res = Database::query($sql);
6500
            if ($res === false && $debug > 0) {
6501
                error_log("Could not anonymize IP address for user $userId ($sql)");
6502
            }
6503
6504
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6505
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE exe_user_id = $userId";
6506
            $res = Database::query($sql);
6507
            if ($res === false && $debug > 0) {
6508
                error_log("Could not anonymize IP address for user $userId ($sql)");
6509
            }
6510
6511
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6512
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6513
            $res = Database::query($sql);
6514
            if ($res === false && $debug > 0) {
6515
                error_log("Could not anonymize IP address for user $userId ($sql)");
6516
            }
6517
6518
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6519
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6520
            $res = Database::query($sql);
6521
            if ($res === false && $debug > 0) {
6522
                error_log("Could not anonymize IP address for user $userId ($sql)");
6523
            }
6524
6525
            $table = Database::get_course_table(TABLE_WIKI);
6526
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6527
            $res = Database::query($sql);
6528
            if ($res === false && $debug > 0) {
6529
                error_log("Could not anonymize IP address for user $userId ($sql)");
6530
            }
6531
6532
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
6533
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
6534
            $res = Database::query($sql);
6535
            if ($res === false && $debug > 0) {
6536
                error_log("Could not anonymize IP address for user $userId ($sql)");
6537
            }
6538
6539
            $table = Database::get_course_table(TABLE_WIKI);
6540
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6541
            $res = Database::query($sql);
6542
            if ($res === false && $debug > 0) {
6543
                error_log("Could not anonymize IP address for user $userId ($sql)");
6544
            }
6545
        }
6546
        $em->persist($user);
6547
        $em->flush($user);
6548
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
6549
6550
        return true;
6551
    }
6552
6553
    /**
6554
     * @param int $userId
6555
     *
6556
     * @throws Exception
6557
     *
6558
     * @return string
6559
     */
6560
    public static function anonymizeUserWithVerification($userId)
6561
    {
6562
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
6563
6564
        $message = '';
6565
        if (api_is_platform_admin() ||
6566
            ($allowDelete && api_is_session_admin())
6567
        ) {
6568
            $userToUpdateInfo = api_get_user_info($userId);
6569
            $currentUserId = api_get_user_id();
6570
6571
            if ($userToUpdateInfo &&
6572
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
6573
            ) {
6574
                if ($userId != $currentUserId &&
6575
                    self::anonymize($userId)
6576
                ) {
6577
                    $message = Display::return_message(
6578
                        sprintf(get_lang('UserXAnonymized'), $userToUpdateInfo['complete_name_with_username']),
6579
                        'confirmation'
6580
                    );
6581
                } else {
6582
                    $message = Display::return_message(
6583
                        sprintf(get_lang('CannotAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
6584
                        'error'
6585
                    );
6586
                }
6587
            } else {
6588
                $message = Display::return_message(
6589
                    sprintf(get_lang('NoPermissionToAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
6590
                    'error'
6591
                );
6592
            }
6593
        }
6594
6595
        return $message;
6596
    }
6597
6598
    /**
6599
     * @param int $userId
6600
     *
6601
     * @throws Exception
6602
     *
6603
     * @return string
6604
     */
6605
    public static function deleteUserWithVerification($userId)
6606
    {
6607
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
6608
        $message = Display::return_message(get_lang('CannotDeleteUser'), 'error');
6609
        $userToUpdateInfo = api_get_user_info($userId);
6610
6611
        // User must exist.
6612
        if (empty($userToUpdateInfo)) {
6613
            return $message;
6614
        }
6615
6616
        $currentUserId = api_get_user_id();
6617
6618
        // Cannot delete myself.
6619
        if ($userId == $currentUserId) {
6620
            return $message;
6621
        }
6622
6623
        if (api_is_platform_admin() ||
6624
            ($allowDelete && api_is_session_admin())
6625
        ) {
6626
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
6627
                if (self::delete_user($userId)) {
6628
                    $message = Display::return_message(
6629
                        get_lang('UserDeleted').': '.$userToUpdateInfo['complete_name_with_username'],
6630
                        'confirmation'
6631
                    );
6632
                } else {
6633
                    $message = Display::return_message(get_lang('CannotDeleteUserBecauseOwnsCourse'), 'error');
6634
                }
6635
            }
6636
        }
6637
6638
        return $message;
6639
    }
6640
6641
    /**
6642
     * @return array
6643
     */
6644
    public static function createDataPrivacyExtraFields()
6645
    {
6646
        self::create_extra_field(
6647
            'request_for_legal_agreement_consent_removal_justification',
6648
            1, //text
6649
            'Request for legal agreement consent removal justification	',
6650
            ''
6651
        );
6652
6653
        self::create_extra_field(
6654
            'request_for_delete_account_justification',
6655
            1, //text
6656
            'Request for delete account justification',
6657
            ''
6658
        );
6659
6660
        $extraFieldId = self::create_extra_field(
6661
            'request_for_legal_agreement_consent_removal',
6662
            1, //text
6663
            'Request for legal agreement consent removal',
6664
            ''
6665
        );
6666
6667
        $extraFieldIdDeleteAccount = self::create_extra_field(
6668
            'request_for_delete_account',
6669
            1, //text
6670
            'Request for delete user account',
6671
            ''
6672
        );
6673
6674
        return [
6675
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
6676
            'delete_legal' => $extraFieldId,
6677
        ];
6678
    }
6679
6680
    /**
6681
     * @param int $userId
6682
     */
6683
    public static function cleanUserRequestsOfRemoval($userId)
6684
    {
6685
        $userId = (int) $userId;
6686
6687
        $extraFieldValue = new ExtraFieldValue('user');
6688
        $extraFieldsToDelete = [
6689
            'legal_accept',
6690
            'request_for_legal_agreement_consent_removal',
6691
            'request_for_legal_agreement_consent_removal_justification',
6692
            'request_for_delete_account_justification', // just in case delete also this
6693
            'request_for_delete_account',
6694
        ];
6695
6696
        foreach ($extraFieldsToDelete as $variable) {
6697
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
6698
                $userId,
6699
                $variable
6700
            );
6701
            if ($value && isset($value['id'])) {
6702
                $extraFieldValue->delete($value['id']);
6703
            }
6704
        }
6705
    }
6706
6707
    /**
6708
     * @param int $searchYear
6709
     *
6710
     * @throws Exception
6711
     *
6712
     * @return array
6713
     */
6714
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
6715
    {
6716
        $timezone = new DateTimeZone(api_get_timezone());
6717
6718
        $sessions = [];
6719
        if (DRH == $userInfo['status']) {
6720
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
6721
        } elseif (api_is_platform_admin(true)) {
6722
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
6723
        } else {
6724
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
6725
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
6726
6727
            foreach ($sessionsByCategory as $sessionsInCategory) {
6728
                $sessions = array_merge($sessions, $sessionsInCategory);
6729
            }
6730
        }
6731
6732
        $sessions = array_map(
6733
            function ($sessionInfo) {
6734
                if (!isset($sessionInfo['session_id'])) {
6735
                    $sessionInfo['session_id'] = $sessionInfo['id'];
6736
                }
6737
                if (!isset($sessionInfo['session_name'])) {
6738
                    $sessionInfo['session_name'] = $sessionInfo['name'];
6739
                }
6740
6741
                return $sessionInfo;
6742
            },
6743
            $sessions
6744
        );
6745
6746
        $calendarSessions = [];
6747
6748
        foreach ($sessions as $sessionInfo) {
6749
            if (!empty($sessionInfo['duration'])) {
6750
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
6751
                    $sessionInfo['session_id'],
6752
                    $userInfo['id']
6753
                );
6754
6755
                if (empty($courseAccess)) {
6756
                    continue;
6757
                }
6758
6759
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
6760
                $lastAccessDate = clone $firstAcessDate;
6761
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
6762
6763
                $firstAccessYear = (int) $firstAcessDate->format('Y');
6764
                $lastAccessYear = (int) $lastAccessDate->format('Y');
6765
6766
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
6767
                    $calendarSessions[$sessionInfo['session_id']] = [
6768
                        'name' => $sessionInfo['session_name'],
6769
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
6770
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
6771
                    ];
6772
                }
6773
6774
                continue;
6775
            }
6776
6777
            $accessStartDate = !empty($sessionInfo['access_start_date'])
6778
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6779
                : null;
6780
            $accessEndDate = !empty($sessionInfo['access_end_date'])
6781
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6782
                : null;
6783
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
6784
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
6785
6786
            $isValid = false;
6787
6788
            if ($accessStartYear && $accessEndYear) {
6789
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
6790
                    $isValid = true;
6791
                }
6792
            }
6793
6794
            if ($accessStartYear && !$accessEndYear) {
6795
                if ($accessStartYear == $searchYear) {
6796
                    $isValid = true;
6797
                }
6798
            }
6799
6800
            if (!$accessStartYear && $accessEndYear) {
6801
                if ($accessEndYear == $searchYear) {
6802
                    $isValid = true;
6803
                }
6804
            }
6805
6806
            if ($isValid) {
6807
                $calendarSessions[$sessionInfo['session_id']] = [
6808
                    'name' => $sessionInfo['session_name'],
6809
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
6810
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
6811
                ];
6812
            }
6813
        }
6814
6815
        return $calendarSessions;
6816
    }
6817
6818
    /**
6819
     * Get sessions info for planification calendar.
6820
     *
6821
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
6822
     * @param int   $searchYear
6823
     *
6824
     * @throws Exception
6825
     *
6826
     * @return array
6827
     */
6828
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
6829
    {
6830
        $timezone = new DateTimeZone(api_get_timezone());
6831
        $calendar = [];
6832
6833
        foreach ($sessionsList as $sessionId => $sessionInfo) {
6834
            $startDate = $sessionInfo['access_start_date']
6835
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6836
                : null;
6837
            $endDate = $sessionInfo['access_end_date']
6838
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6839
                : null;
6840
6841
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
6842
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
6843
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
6844
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
6845
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
6846
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
6847
6848
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
6849
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
6850
6851
            $calendar[] = [
6852
                'id' => $sessionId,
6853
                'name' => $sessionInfo['name'],
6854
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
0 ignored issues
show
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

6854
                'human_date' => SessionManager::convertSessionDateToString($startDate, /** @scrutinizer ignore-type */ $endDate, false, true),
Loading history...
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

6854
                'human_date' => SessionManager::convertSessionDateToString(/** @scrutinizer ignore-type */ $startDate, $endDate, false, true),
Loading history...
6855
                'start_in_last_year' => $startYear < $searchYear,
6856
                'end_in_next_year' => $endYear > $searchYear,
6857
                'no_start' => !$startWeek,
6858
                'no_end' => !$endWeek,
6859
                'start' => $start,
6860
                'duration' => $duration > 0 ? $duration : 1,
6861
            ];
6862
        }
6863
6864
        usort(
6865
            $calendar,
6866
            function ($sA, $sB) {
6867
                if ($sA['start'] == $sB['start']) {
6868
                    return 0;
6869
                }
6870
6871
                if ($sA['start'] < $sB['start']) {
6872
                    return -1;
6873
                }
6874
6875
                return 1;
6876
            }
6877
        );
6878
6879
        return $calendar;
6880
    }
6881
6882
    /**
6883
     * Return the user's full name. Optionally with the username.
6884
     *
6885
     * @param bool $includeUsername Optional. By default username is not included.
6886
     *
6887
     * @return string
6888
     */
6889
    public static function formatUserFullName(User $user, $includeUsername = false)
6890
    {
6891
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
6892
6893
        if ($includeUsername && api_get_configuration_value('hide_username_with_complete_name') !== true) {
6894
            $username = $user->getUsername();
6895
6896
            return "$fullName ($username)";
6897
        }
6898
6899
        return $fullName;
6900
    }
6901
6902
    /**
6903
     * @param int $userId
6904
     *
6905
     * @return array
6906
     */
6907
    public static function getUserCareers($userId)
6908
    {
6909
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6910
        $tableCareer = Database::get_main_table(TABLE_CAREER);
6911
        $userId = (int) $userId;
6912
6913
        $sql = "SELECT c.id, c.name
6914
                FROM $table uc
6915
                INNER JOIN $tableCareer c
6916
                ON uc.career_id = c.id
6917
                WHERE user_id = $userId
6918
                ORDER BY uc.created_at
6919
                ";
6920
        $result = Database::query($sql);
6921
6922
        return Database::store_result($result, 'ASSOC');
6923
    }
6924
6925
    /**
6926
     * @param int $userId
6927
     * @param int $careerId
6928
     */
6929
    public static function addUserCareer($userId, $careerId)
6930
    {
6931
        if (!api_get_configuration_value('allow_career_users')) {
6932
            return false;
6933
        }
6934
6935
        if (self::userHasCareer($userId, $careerId) === false) {
6936
            $params = ['user_id' => $userId, 'career_id' => $careerId, 'created_at' => api_get_utc_datetime(), 'updated_at' => api_get_utc_datetime()];
6937
            $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6938
            Database::insert($table, $params);
6939
        }
6940
6941
        return true;
6942
    }
6943
6944
    /**
6945
     * @param int   $userCareerId
6946
     * @param array $data
6947
     *
6948
     * @return bool
6949
     */
6950
    public static function updateUserCareer($userCareerId, $data)
6951
    {
6952
        if (!api_get_configuration_value('allow_career_users')) {
6953
            return false;
6954
        }
6955
6956
        $params = ['extra_data' => $data, 'updated_at' => api_get_utc_datetime()];
6957
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6958
        Database::update(
6959
            $table,
6960
            $params,
6961
            ['id = ?' => (int) $userCareerId]
6962
        );
6963
6964
        return true;
6965
    }
6966
6967
    /**
6968
     * @param int $userId
6969
     * @param int $careerId
6970
     *
6971
     * @return array
6972
     */
6973
    public static function getUserCareer($userId, $careerId)
6974
    {
6975
        $userId = (int) $userId;
6976
        $careerId = (int) $careerId;
6977
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6978
6979
        $sql = "SELECT * FROM $table WHERE user_id = $userId AND career_id = $careerId";
6980
        $result = Database::query($sql);
6981
6982
        return Database::fetch_array($result, 'ASSOC');
6983
    }
6984
6985
    /**
6986
     * @param int $userId
6987
     * @param int $careerId
6988
     *
6989
     * @return bool
6990
     */
6991
    public static function userHasCareer($userId, $careerId)
6992
    {
6993
        $userId = (int) $userId;
6994
        $careerId = (int) $careerId;
6995
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6996
6997
        $sql = "SELECT id FROM $table WHERE user_id = $userId AND career_id = $careerId";
6998
        $result = Database::query($sql);
6999
7000
        return Database::num_rows($result) > 0;
7001
    }
7002
7003
    /**
7004
     * @return EncoderFactory
7005
     */
7006
    private static function getEncoderFactory()
7007
    {
7008
        $encryption = self::getPasswordEncryption();
7009
        $encoders = [
7010
            'Chamilo\\UserBundle\\Entity\\User' => new \Chamilo\UserBundle\Security\Encoder($encryption),
7011
        ];
7012
7013
        $encoderFactory = new EncoderFactory($encoders);
7014
7015
        return $encoderFactory;
7016
    }
7017
7018
    /**
7019
     * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface
7020
     */
7021
    private static function getEncoder(User $user)
7022
    {
7023
        $encoderFactory = self::getEncoderFactory();
7024
7025
        return $encoderFactory->getEncoder($user);
7026
    }
7027
7028
    /**
7029
     * Disables or enables a user.
7030
     *
7031
     * @param int $user_id
7032
     * @param int $active  Enable or disable
7033
     *
7034
     * @return bool True on success, false on failure
7035
     * @assert (-1,0) === false
7036
     * @assert (1,1) === true
7037
     */
7038
    private static function change_active_state($user_id, $active)
7039
    {
7040
        $user_id = (int) $user_id;
7041
        $active = (int) $active;
7042
7043
        if (empty($user_id)) {
7044
            return false;
7045
        }
7046
7047
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
7048
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
7049
        $r = Database::query($sql);
7050
        $ev = LOG_USER_DISABLE;
7051
        if ($active == 1) {
7052
            $ev = LOG_USER_ENABLE;
7053
        }
7054
        if ($r !== false) {
7055
            Event::addEvent($ev, LOG_USER_ID, $user_id);
7056
        }
7057
7058
        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...
7059
    }
7060
7061
    /**
7062
     * Get either a Gravatar URL or complete image tag for a specified email address.
7063
     *
7064
     * @param string $email The email address
7065
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
7066
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
7067
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
7068
     * @param bool   $img   True to return a complete IMG tag False for just the URL
7069
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
7070
     *
7071
     * @return string containing either just a URL or a complete image tag
7072
     * @source http://gravatar.com/site/implement/images/php/
7073
     */
7074
    private static function getGravatar(
7075
        $email,
7076
        $s = 80,
7077
        $d = 'mm',
7078
        $r = 'g',
7079
        $img = false,
7080
        $atts = []
7081
    ) {
7082
        $url = 'http://www.gravatar.com/avatar/';
7083
        if (!empty($_SERVER['HTTPS'])) {
7084
            $url = 'https://secure.gravatar.com/avatar/';
7085
        }
7086
        $url .= md5(strtolower(trim($email)));
7087
        $url .= "?s=$s&d=$d&r=$r";
7088
        if ($img) {
7089
            $url = '<img src="'.$url.'"';
7090
            foreach ($atts as $key => $val) {
7091
                $url .= ' '.$key.'="'.$val.'"';
7092
            }
7093
            $url .= ' />';
7094
        }
7095
7096
        return $url;
7097
    }
7098
}
7099