Passed
Push — 1.11.x ( e079d3...5e804f )
by Julito
22:30 queued 13:02
created

UserManager::logInAsFirstAdmin()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 4
nop 0
dl 0
loc 15
rs 9.6111
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\Filesystem\Filesystem;
13
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
14
15
/**
16
 * Class UserManager.
17
 *
18
 * This library provides functions for user management.
19
 * Include/require it in your code to use its functionality.
20
 *
21
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
22
 */
23
class UserManager
24
{
25
    // This constants are deprecated use the constants located in ExtraField
26
    const USER_FIELD_TYPE_TEXT = 1;
27
    const USER_FIELD_TYPE_TEXTAREA = 2;
28
    const USER_FIELD_TYPE_RADIO = 3;
29
    const USER_FIELD_TYPE_SELECT = 4;
30
    const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
31
    const USER_FIELD_TYPE_DATE = 6;
32
    const USER_FIELD_TYPE_DATETIME = 7;
33
    const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
34
    const USER_FIELD_TYPE_DIVIDER = 9;
35
    const USER_FIELD_TYPE_TAG = 10;
36
    const USER_FIELD_TYPE_TIMEZONE = 11;
37
    const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
38
    const USER_FIELD_TYPE_FILE = 13;
39
    const USER_FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
40
41
    private static $encryptionMethod;
42
43
    /**
44
     * Constructor.
45
     *
46
     * @assert () === null
47
     */
48
    public function __construct()
49
    {
50
    }
51
52
    /**
53
     * Repository is use to query the DB, selects, etc.
54
     *
55
     * @return UserRepository
56
     */
57
    public static function getRepository()
58
    {
59
        /** @var UserRepository $userRepository */
60
61
        return Database::getManager()->getRepository('ChamiloUserBundle:User');
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
122
        return $encoder->isPasswordValid($encoded, $raw, $salt);
123
    }
124
125
    /**
126
     * @param string $raw
127
     *
128
     * @return string
129
     */
130
    public static function encryptPassword($raw, User $user)
131
    {
132
        $encoder = self::getEncoder($user);
133
134
        return $encoder->encodePassword(
135
            $raw,
136
            $user->getSalt()
137
        );
138
    }
139
140
    /**
141
     * @param int    $userId
142
     * @param string $password
143
     */
144
    public static function updatePassword($userId, $password)
145
    {
146
        $repository = self::getRepository();
147
        /** @var User $user */
148
        $user = $repository->find($userId);
149
        $userManager = self::getManager();
150
        $user->setPlainPassword($password);
151
        $userManager->updateUser($user, true);
152
        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

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

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

2546
                /** @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...
2547
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
2548
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
2549
                @rename($path.$old_file, $path.$prefix.$old_file);
2550
            } else {
2551
                @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

2551
                /** @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...
2552
                @unlink($path.'medium_'.$old_file);
2553
                @unlink($path.'big_'.$old_file);
2554
                @unlink($path.$old_file);
2555
            }
2556
        }
2557
2558
        // Exit if only deletion has been requested. Return an empty picture name.
2559
        if ($delete) {
2560
            return '';
2561
        }
2562
2563
        // Validation 2.
2564
        $allowed_types = api_get_supported_image_extensions();
2565
        $file = str_replace('\\', '/', $file);
2566
        $filename = (($pos = strrpos($file, '/')) !== false) ? substr($file, $pos + 1) : $file;
2567
        $extension = strtolower(substr(strrchr($filename, '.'), 1));
2568
        if (!in_array($extension, $allowed_types)) {
2569
            return false;
2570
        }
2571
2572
        // This is the common name for the new photos.
2573
        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...
2574
            $old_extension = strtolower(substr(strrchr($old_file, '.'), 1));
2575
            $filename = in_array($old_extension, $allowed_types) ? substr($old_file, 0, -strlen($old_extension)) : $old_file;
2576
            $filename = (substr($filename, -1) == '.') ? $filename.$extension : $filename.'.'.$extension;
2577
        } else {
2578
            $filename = api_replace_dangerous_char($filename);
2579
            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...
2580
                $filename = uniqid('').'_'.$filename;
2581
            }
2582
            // We always prefix user photos with user ids, so on setting
2583
            // api_get_setting('split_users_upload_directory') === 'true'
2584
            // the correspondent directories to be found successfully.
2585
            $filename = $user_id.'_'.$filename;
2586
        }
2587
2588
        //Crop the image to adjust 1:1 ratio
2589
        $image = new Image($source_file);
2590
        $image->crop($cropParameters);
2591
2592
        // Storing the new photos in 4 versions with various sizes.
2593
        $userPath = self::getUserPathById($user_id, 'system');
2594
2595
        // If this path does not exist - we create it.
2596
        if (!file_exists($userPath)) {
2597
            mkdir($userPath, api_get_permissions_for_new_directories(), true);
2598
        }
2599
        $small = new Image($source_file);
2600
        $small->resize(32);
2601
        $small->send_image($userPath.'small_'.$filename);
2602
        $medium = new Image($source_file);
2603
        $medium->resize(85);
2604
        $medium->send_image($userPath.'medium_'.$filename);
2605
        $normal = new Image($source_file);
2606
        $normal->resize(200);
2607
        $normal->send_image($userPath.$filename);
2608
2609
        $big = new Image($source_file); // This is the original picture.
2610
        $big->send_image($userPath.'big_'.$filename);
2611
2612
        $result = $small && $medium && $normal && $big;
0 ignored issues
show
introduced by
$normal is of type Image, thus it always evaluated to true.
Loading history...
introduced by
$medium is of type Image, thus it always evaluated to true.
Loading history...
introduced by
$big is of type Image, thus it always evaluated to true.
Loading history...
2613
2614
        return $result ? $filename : false;
2615
    }
2616
2617
    /**
2618
     * Update User extra field file type into {user_folder}/{$extra_field}.
2619
     *
2620
     * @param int    $user_id     The user internal identification number
2621
     * @param string $extra_field The $extra_field The extra field name
2622
     * @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...
2623
     * @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...
2624
     *
2625
     * @return bool|null return filename if success, but false
2626
     */
2627
    public static function update_user_extra_file(
2628
        $user_id,
2629
        $extra_field = '',
2630
        $file = null,
2631
        $source_file = null
2632
    ) {
2633
        // Add Filter
2634
        $source_file = Security::filter_filename($source_file);
2635
        $file = Security::filter_filename($file);
2636
2637
        if (empty($user_id)) {
2638
            return false;
2639
        }
2640
2641
        if (empty($source_file)) {
2642
            $source_file = $file;
2643
        }
2644
2645
        // User-reserved directory where extra file have to be placed.
2646
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2647
        $path = $path_info['dir'];
2648
        if (!empty($extra_field)) {
2649
            $path .= $extra_field.'/';
2650
        }
2651
        // If this directory does not exist - we create it.
2652
        if (!file_exists($path)) {
2653
            @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

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

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