Passed
Push — master ( 6f8cb0...cccadf )
by Julito
09:47
created

UserManager::addUserCareer()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 2
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
5
use Chamilo\CoreBundle\Entity\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...
6
use Chamilo\CoreBundle\Entity\SkillRelUserComment;
7
use Chamilo\CoreBundle\Framework\Container;
8
use Chamilo\CoreBundle\Repository\AccessUrlRepository;
9
use Chamilo\UserBundle\Entity\User;
10
use Chamilo\UserBundle\Repository\UserRepository;
11
use ChamiloSession as Session;
12
13
/**
14
 * Class UserManager.
15
 *
16
 * This library provides functions for user management.
17
 * Include/require it in your code to use its functionality.
18
 *
19
 * @package chamilo.library
20
 *
21
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
22
 */
23
class UserManager
24
{
25
    // This constants are deprecated use the constants located in ExtraField
26
    public const USER_FIELD_TYPE_TEXT = 1;
27
    public const USER_FIELD_TYPE_TEXTAREA = 2;
28
    public const USER_FIELD_TYPE_RADIO = 3;
29
    public const USER_FIELD_TYPE_SELECT = 4;
30
    public const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
31
    public const USER_FIELD_TYPE_DATE = 6;
32
    public const USER_FIELD_TYPE_DATETIME = 7;
33
    public const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
34
    public const USER_FIELD_TYPE_DIVIDER = 9;
35
    public const USER_FIELD_TYPE_TAG = 10;
36
    public const USER_FIELD_TYPE_TIMEZONE = 11;
37
    public const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
38
    public const USER_FIELD_TYPE_FILE = 13;
39
    public 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
        return Database::getManager()->getRepository('ChamiloUserBundle:User');
60
    }
61
62
    /**
63
     * Create/update/delete methods are available in the UserManager
64
     * (based in the Sonata\UserBundle\Entity\UserManager).
65
     *
66
     * @return \Sonata\UserBundle\Entity\UserManager
67
     */
68
    public static function getManager()
69
    {
70
        return Container::getUserManager();
71
    }
72
73
    /**
74
     * @param string $encryptionMethod
75
     */
76
    public static function setPasswordEncryption($encryptionMethod)
77
    {
78
        self::$encryptionMethod = $encryptionMethod;
79
    }
80
81
    /**
82
     * @return bool|mixed
83
     */
84
    public static function getPasswordEncryption()
85
    {
86
        $encryptionMethod = self::$encryptionMethod;
87
        if (empty($encryptionMethod)) {
88
            $encryptionMethod = api_get_configuration_value('password_encryption');
89
        }
90
91
        return $encryptionMethod;
92
    }
93
94
    /**
95
     * Validates the password.
96
     *
97
     * @param $encoded
98
     * @param $raw
99
     * @param $salt
100
     *
101
     * @return bool
102
     */
103
    public static function isPasswordValid($encoded, $raw, $salt)
104
    {
105
        $encoder = new \Chamilo\UserBundle\Security\Encoder(self::getPasswordEncryption());
0 ignored issues
show
Bug introduced by
It seems like self::getPasswordEncryption() can also be of type boolean; however, parameter $passwordEncrypt of Chamilo\UserBundle\Security\Encoder::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

105
        $encoder = new \Chamilo\UserBundle\Security\Encoder(/** @scrutinizer ignore-type */ self::getPasswordEncryption());
Loading history...
106
        $validPassword = $encoder->isPasswordValid($encoded, $raw, $salt);
107
108
        return $validPassword;
109
    }
110
111
    /**
112
     * @param int    $userId
113
     * @param string $password
114
     */
115
    public static function updatePassword($userId, $password)
116
    {
117
        $repository = self::getRepository();
118
        /** @var User $user */
119
        $user = $repository->find($userId);
120
        $userManager = self::getManager();
121
        $user->setPlainPassword($password);
122
        $userManager->updateUser($user, true);
123
    }
124
125
    /**
126
     * Creates a new user for the platform.
127
     *
128
     * @author Hugues Peeters <[email protected]>,
129
     * @author Roan Embrechts <[email protected]>
130
     *
131
     * @param string        $firstName
132
     * @param string        $lastName
133
     * @param int           $status                  (1 for course tutor, 5 for student, 6 for anonymous)
134
     * @param string        $email
135
     * @param string        $loginName
136
     * @param string        $password
137
     * @param string        $official_code           Any official code (optional)
138
     * @param string        $language                User language    (optional)
139
     * @param string        $phone                   Phone number    (optional)
140
     * @param string        $picture_uri             Picture URI        (optional)
141
     * @param string        $authSource              Authentication source (defaults to 'platform', dependind on constant)
142
     * @param string        $expirationDate          Account expiration date (optional, defaults to null)
143
     * @param int           $active                  Whether the account is enabled or disabled by default
144
     * @param int           $hr_dept_id              The department of HR in which the user is registered (defaults to 0)
145
     * @param array         $extra                   Extra fields
146
     * @param string        $encrypt_method          Used if password is given encrypted. Set to an empty string by default
147
     * @param bool          $send_mail
148
     * @param bool          $isAdmin
149
     * @param string        $address
150
     * @param bool          $sendEmailToAllAdmins
151
     * @param FormValidator $form
152
     * @param int           $creatorId
153
     * @param array         $emailTemplate
154
     * @param string        $redirectToURLAfterLogin
155
     *
156
     * @return mixed new user id - if the new user creation succeeds, false otherwise
157
     * @desc The function tries to retrieve user id from the session.
158
     * If it exists, the current user id is the creator id. If a problem arises,
159
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
160
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
161
     */
162
    public static function create_user(
163
        $firstName,
164
        $lastName,
165
        $status,
166
        $email,
167
        $loginName,
168
        $password,
169
        $official_code = '',
170
        $language = '',
171
        $phone = '',
172
        $picture_uri = '',
173
        $authSource = PLATFORM_AUTH_SOURCE,
174
        $expirationDate = null,
175
        $active = 1,
176
        $hr_dept_id = 0,
177
        $extra = [],
178
        $encrypt_method = '',
0 ignored issues
show
Unused Code introduced by
The parameter $encrypt_method is not used and could be removed. ( Ignorable by Annotation )

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

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

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

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

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

341
            $user->setExpirationDate(/** @scrutinizer ignore-type */ $expirationDate);
Loading history...
342
        }
343
        try {
344
            $userManager->updateUser($user);
345
            $userId = $user->getId();
346
347
            // Add user to a group
348
            $statusToGroup = [
349
                COURSEMANAGER => 'TEACHER',
350
                STUDENT => 'STUDENT',
351
                DRH => 'RRHH',
352
                SESSIONADMIN => 'SESSION_ADMIN',
353
                STUDENT_BOSS => 'STUDENT_BOSS',
354
                INVITEE => 'INVITEE',
355
            ];
356
357
            $group = Container::$container->get('fos_user.group_manager')->findGroupBy(['code' => $statusToGroup[$status]]);
358
            if ($group) {
359
                $user->addGroup($group);
360
                $userManager->updateUser($user);
361
            }
362
        } catch (Exception $e) {
363
            error_log($e->getMessage());
364
        }
365
366
        if (!empty($userId)) {
367
            $return = $userId;
368
            $sql = "UPDATE $table_user SET user_id = $return WHERE id = $return";
369
            Database::query($sql);
370
371
            if ($isAdmin) {
372
                self::add_user_as_admin($user);
373
            }
374
375
            if (api_get_multiple_access_url()) {
376
                UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
377
            } else {
378
                //we are adding by default the access_url_user table with access_url_id = 1
379
                UrlManager::add_user_to_url($userId, 1);
380
            }
381
382
            if (is_array($extra) && count($extra) > 0) {
383
                $extra['item_id'] = $userId;
384
                $courseFieldValue = new ExtraFieldValue('user');
385
                $courseFieldValue->saveFieldValues($extra);
386
            } else {
387
                // Create notify settings by default
388
                self::update_extra_field_value(
389
                    $userId,
390
                    'mail_notify_invitation',
391
                    '1'
392
                );
393
                self::update_extra_field_value(
394
                    $userId,
395
                    'mail_notify_message',
396
                    '1'
397
                );
398
                self::update_extra_field_value(
399
                    $userId,
400
                    'mail_notify_group_message',
401
                    '1'
402
                );
403
            }
404
405
            self::update_extra_field_value(
406
                $userId,
407
                'already_logged_in',
408
                'false'
409
            );
410
411
            if (!empty($redirectToURLAfterLogin) && api_get_configuration_value('plugin_redirection_enabled')) {
412
                RedirectionPlugin::insert($userId, $redirectToURLAfterLogin);
413
            }
414
415
            if (!empty($email) && $send_mail) {
416
                $recipient_name = api_get_person_name(
417
                    $firstName,
418
                    $lastName,
419
                    null,
420
                    PERSON_NAME_EMAIL_ADDRESS
421
                );
422
                $tplSubject = new Template(
423
                    null,
424
                    false,
425
                    false,
426
                    false,
427
                    false,
428
                    false
429
                );
430
                $layoutSubject = $tplSubject->get_template('mail/subject_registration_platform.tpl');
431
                $emailSubject = $tplSubject->fetch($layoutSubject);
432
                $sender_name = api_get_person_name(
433
                    api_get_setting('administratorName'),
434
                    api_get_setting('administratorSurname'),
435
                    null,
436
                    PERSON_NAME_EMAIL_ADDRESS
437
                );
438
                $email_admin = api_get_setting('emailAdministrator');
439
440
                $url = api_get_path(WEB_PATH);
441
                if (api_is_multiple_url_enabled()) {
442
                    $access_url_id = api_get_current_access_url_id();
443
                    if ($access_url_id != -1) {
444
                        $urlInfo = api_get_access_url($access_url_id);
445
                        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...
446
                            $url = $urlInfo['url'];
447
                        }
448
                    }
449
                }
450
451
                $tplContent = new Template(
452
                    null,
453
                    false,
454
                    false,
455
                    false,
456
                    false,
457
                    false
458
                );
459
                // variables for the default template
460
                $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
461
                $tplContent->assign('login_name', $loginName);
462
                $tplContent->assign('original_password', stripslashes($original_password));
463
                $tplContent->assign('mailWebPath', $url);
464
                $tplContent->assign('new_user', $user);
465
466
                $layoutContent = $tplContent->get_template('mail/content_registration_platform.tpl');
467
                $emailBody = $tplContent->fetch($layoutContent);
468
469
                $userInfo = api_get_user_info($userId);
470
                $mailTemplateManager = new MailTemplateManager();
471
472
                $phoneNumber = isset($extra['mobile_phone_number']) ? $extra['mobile_phone_number'] : null;
473
474
                $additionalParameters = [
475
                    'smsType' => SmsPlugin::WELCOME_LOGIN_PASSWORD,
476
                    'userId' => $return,
477
                    'mobilePhoneNumber' => $phoneNumber,
478
                    'password' => $original_password,
479
                ];
480
481
                $emailBodyTemplate = '';
482
                if (!empty($emailTemplate)) {
483
                    if (isset($emailTemplate['content_registration_platform.tpl']) &&
484
                        !empty($emailTemplate['content_registration_platform.tpl'])
485
                    ) {
486
                        $emailBodyTemplate = $mailTemplateManager->parseTemplate(
487
                            $emailTemplate['content_registration_platform.tpl'],
488
                            $userInfo
0 ignored issues
show
Bug introduced by
It seems like $userInfo can also be of type false; however, parameter $userInfo of MailTemplateManager::parseTemplate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

488
                            /** @scrutinizer ignore-type */ $userInfo
Loading history...
489
                        );
490
                    }
491
                }
492
493
                $twoEmail = api_get_configuration_value('send_two_inscription_confirmation_mail');
494
                if ($twoEmail === true) {
495
                    $layoutContent = $tplContent->get_template('mail/new_user_first_email_confirmation.tpl');
496
                    $emailBody = $tplContent->fetch($layoutContent);
497
498
                    if (!empty($emailBodyTemplate) &&
499
                        isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
500
                        !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
501
                    ) {
502
                        $emailBody = $mailTemplateManager->parseTemplate(
503
                            $emailTemplate['new_user_first_email_confirmation.tpl'],
504
                            $userInfo
505
                        );
506
                    }
507
508
                    api_mail_html(
509
                        $recipient_name,
510
                        $email,
511
                        $emailSubject,
512
                        $emailBody,
513
                        $sender_name,
514
                        $email_admin,
515
                        null,
516
                        null,
517
                        null,
518
                        $additionalParameters
519
                    );
520
521
                    $layoutContent = $tplContent->get_template('mail/new_user_second_email_confirmation.tpl');
522
                    $emailBody = $tplContent->fetch($layoutContent);
523
524
                    if (!empty($emailBodyTemplate) &&
525
                        isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
526
                        !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
527
                    ) {
528
                        $emailBody = $mailTemplateManager->parseTemplate(
529
                            $emailTemplate['new_user_second_email_confirmation.tpl'],
530
                            $userInfo
531
                        );
532
                    }
533
534
                    api_mail_html(
535
                        $recipient_name,
536
                        $email,
537
                        $emailSubject,
538
                        $emailBody,
539
                        $sender_name,
540
                        $email_admin,
541
                        null,
542
                        null,
543
                        null,
544
                        $additionalParameters
545
                    );
546
                } else {
547
                    if (!empty($emailBodyTemplate)) {
548
                        $emailBody = $emailBodyTemplate;
549
                    }
550
                    $sendToInbox = api_get_configuration_value('send_inscription_msg_to_inbox');
551
                    if ($sendToInbox) {
552
                        $adminList = self::get_all_administrators();
553
                        $senderId = 1;
554
                        if (!empty($adminList)) {
555
                            $adminInfo = current($adminList);
556
                            $senderId = $adminInfo['user_id'];
557
                        }
558
559
                        MessageManager::send_message_simple(
560
                            $userId,
561
                            $emailSubject,
562
                            $emailBody,
563
                            $senderId
564
                        );
565
                    } else {
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
                        );
578
                    }
579
                }
580
581
                $notification = api_get_configuration_value('send_notification_when_user_added');
582
                if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
583
                    foreach ($notification['admins'] as $adminId) {
584
                        $emailSubjectToAdmin = get_lang('UserAdded').': '.api_get_person_name($firstName, $lastName);
585
                        MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
0 ignored issues
show
Bug introduced by
It seems like $emailBody can also be of type false; however, parameter $message of MessageManager::send_message_simple() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

585
                        MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, /** @scrutinizer ignore-type */ $emailBody, $userId);
Loading history...
586
                    }
587
                }
588
589
                if ($sendEmailToAllAdmins) {
590
                    $adminList = self::get_all_administrators();
591
592
                    $tplContent = new Template(
593
                        null,
594
                        false,
595
                        false,
596
                        false,
597
                        false,
598
                        false
599
                    );
600
                    // variables for the default template
601
                    $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
602
                    $tplContent->assign('user_added', $user);
603
                    $renderer = FormValidator::getDefaultRenderer();
604
                    // Form template
605
                    $elementTemplate = ' {label}: {element} <br />';
606
                    $renderer->setElementTemplate($elementTemplate);
607
                    /** @var FormValidator $form */
608
                    $form->freeze(null, $elementTemplate);
609
                    $form->removeElement('submit');
610
                    $formData = $form->returnForm();
611
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
612
                    $tplContent->assign('link', Display::url($url, $url));
613
                    $tplContent->assign('form', $formData);
614
615
                    $layoutContent = $tplContent->get_template('mail/content_registration_platform_to_admin.tpl');
616
                    $emailBody = $tplContent->fetch($layoutContent);
617
618
                    if (!empty($emailBodyTemplate) &&
619
                        isset($emailTemplate['content_registration_platform_to_admin.tpl']) &&
620
                        !empty($emailTemplate['content_registration_platform_to_admin.tpl'])
621
                    ) {
622
                        $emailBody = $mailTemplateManager->parseTemplate(
623
                            $emailTemplate['content_registration_platform_to_admin.tpl'],
624
                            $userInfo
625
                        );
626
                    }
627
628
                    $subject = get_lang('UserAdded');
629
630
                    foreach ($adminList as $adminId => $data) {
631
                        MessageManager::send_message_simple(
632
                            $adminId,
633
                            $subject,
634
                            $emailBody,
635
                            $userId
636
                        );
637
                    }
638
                }
639
                /* ENDS MANAGE EVENT WITH MAIL */
640
            }
641
642
            if (!empty($hook)) {
643
                $hook->setEventData([
644
                    'return' => $userId,
645
                    'originalPassword' => $original_password,
646
                ]);
647
                $hook->notifyCreateUser(HOOK_EVENT_TYPE_POST);
648
            }
649
            Event::addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId, null, $creatorId);
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

649
            Event::/** @scrutinizer ignore-call */ 
650
                   addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId, null, $creatorId);

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

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

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

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

Loading history...
1102
        $send_email = false,
1103
        $reset_password = 0,
1104
        $address = null,
1105
        $emailTemplate = []
1106
    ) {
1107
        $hook = HookUpdateUser::create();
1108
        if (!empty($hook)) {
1109
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_PRE);
1110
        }
1111
        $original_password = $password;
1112
        $user_id = (int) $user_id;
1113
        $creator_id = (int) $creator_id;
1114
1115
        if (empty($user_id)) {
1116
            return false;
1117
        }
1118
1119
        $userManager = self::getManager();
1120
        /** @var User $user */
1121
        $user = self::getRepository()->find($user_id);
1122
1123
        if (empty($user)) {
1124
            return false;
1125
        }
1126
1127
        if ($reset_password == 0) {
1128
            $password = null;
1129
            $auth_source = $user->getAuthSource();
1130
        } elseif ($reset_password == 1) {
1131
            $original_password = $password = api_generate_password();
1132
            $auth_source = PLATFORM_AUTH_SOURCE;
1133
        } elseif ($reset_password == 2) {
1134
            $password = $password;
1135
            $auth_source = PLATFORM_AUTH_SOURCE;
1136
        } elseif ($reset_password == 3) {
1137
            $password = $password;
1138
            $auth_source = $auth_source;
1139
        }
1140
1141
        // Checking the user language
1142
        $languages = array_keys(api_get_languages());
1143
        if (!in_array($language, $languages)) {
1144
            $language = api_get_setting('platformLanguage');
1145
        }
1146
1147
        $change_active = 0;
1148
        $isUserActive = $user->getActive();
1149
        if ($isUserActive != $active) {
1150
            $change_active = 1;
1151
        }
1152
1153
        $originalUsername = $user->getUsername();
1154
1155
        // If username is different from original then check if it exists.
1156
        if ($originalUsername !== $username) {
1157
            $available = self::is_username_available($username);
1158
            if ($available === false) {
1159
                return false;
1160
            }
1161
        }
1162
1163
        if (!empty($expiration_date)) {
1164
            $expiration_date = api_get_utc_datetime($expiration_date);
1165
            $expiration_date = new \DateTime(
1166
                $expiration_date,
1167
                new DateTimeZone('UTC')
1168
            );
1169
        }
1170
1171
        $user
1172
            ->setLastname($lastname)
1173
            ->setFirstname($firstname)
1174
            ->setUsername($username)
1175
            ->setStatus($status)
1176
            ->setAuthSource($auth_source)
1177
            ->setLanguage($language)
1178
            ->setLocale($language)
1179
            ->setEmail($email)
1180
            ->setOfficialCode($official_code)
1181
            ->setPhone($phone)
1182
            ->setAddress($address)
1183
            ->setPictureUri($picture_uri)
1184
            ->setExpirationDate($expiration_date)
1185
            ->setActive($active)
1186
            ->setEnabled($active)
1187
            ->setHrDeptId($hr_dept_id)
1188
        ;
1189
1190
        if (!is_null($password)) {
1191
            $user->setPlainPassword($password);
1192
        }
1193
1194
        $userManager->updateUser($user, true);
1195
1196
        if ($change_active == 1) {
1197
            if ($active == 1) {
1198
                $event_title = LOG_USER_ENABLE;
1199
            } else {
1200
                $event_title = LOG_USER_DISABLE;
1201
            }
1202
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
1203
        }
1204
1205
        if (is_array($extra) && count($extra) > 0) {
1206
            $res = true;
1207
            foreach ($extra as $fname => $fvalue) {
1208
                $res = $res && self::update_extra_field_value(
1209
                    $user_id,
1210
                    $fname,
1211
                    $fvalue
1212
                );
1213
            }
1214
        }
1215
1216
        if (!empty($email) && $send_email) {
1217
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
1218
            $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('YourReg').' '.api_get_setting('siteName');
1219
            $sender_name = api_get_person_name(
1220
                api_get_setting('administratorName'),
1221
                api_get_setting('administratorSurname'),
1222
                null,
1223
                PERSON_NAME_EMAIL_ADDRESS
1224
            );
1225
            $email_admin = api_get_setting('emailAdministrator');
1226
            $url = api_get_path(WEB_PATH);
1227
            if (api_is_multiple_url_enabled()) {
1228
                $access_url_id = api_get_current_access_url_id();
1229
                if ($access_url_id != -1) {
1230
                    $url = api_get_access_url($access_url_id);
1231
                    $url = $url['url'];
1232
                }
1233
            }
1234
1235
            $tplContent = new Template(
1236
                null,
1237
                false,
1238
                false,
1239
                false,
1240
                false,
1241
                false
1242
            );
1243
            // variables for the default template
1244
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
1245
            $tplContent->assign('login_name', $username);
1246
1247
            $originalPassword = '';
1248
            if ($reset_password > 0) {
1249
                $originalPassword = stripslashes($original_password);
1250
            }
1251
            $tplContent->assign('original_password', $originalPassword);
1252
            $tplContent->assign('portal_url', $url);
1253
1254
            $layoutContent = $tplContent->get_template('mail/user_edit_content.tpl');
1255
            $emailBody = $tplContent->fetch($layoutContent);
1256
1257
            $mailTemplateManager = new MailTemplateManager();
1258
1259
            if (!empty($emailTemplate) &&
1260
                isset($emailTemplate['user_edit_content.tpl']) &&
1261
                !empty($emailTemplate['user_edit_content.tpl'])
1262
            ) {
1263
                $userInfo = api_get_user_info($user_id);
1264
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], $userInfo);
0 ignored issues
show
Bug introduced by
It seems like $userInfo can also be of type false and mixed; however, parameter $userInfo of MailTemplateManager::parseTemplate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

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

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

1978
            /** @scrutinizer ignore-type */ $userInfo
Loading history...
1979
        );
1980
        $pictureWebFile = $imageWebPath['file'];
1981
        $pictureWebDir = $imageWebPath['dir'];
1982
1983
        $pictureAnonymousSize = '128';
1984
        $gravatarSize = 22;
1985
        $realSizeName = 'small_';
1986
1987
        switch ($size) {
1988
            case USER_IMAGE_SIZE_SMALL:
1989
                $pictureAnonymousSize = '32';
1990
                $realSizeName = 'small_';
1991
                $gravatarSize = 32;
1992
                break;
1993
            case USER_IMAGE_SIZE_MEDIUM:
1994
                $pictureAnonymousSize = '64';
1995
                $realSizeName = 'medium_';
1996
                $gravatarSize = 64;
1997
                break;
1998
            case USER_IMAGE_SIZE_ORIGINAL:
1999
                $pictureAnonymousSize = '128';
2000
                $realSizeName = '';
2001
                $gravatarSize = 128;
2002
                break;
2003
            case USER_IMAGE_SIZE_BIG:
2004
                $pictureAnonymousSize = '128';
2005
                $realSizeName = 'big_';
2006
                $gravatarSize = 128;
2007
                break;
2008
        }
2009
2010
        $gravatarEnabled = api_get_setting('gravatar_enabled');
2011
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
2012
        if ($pictureWebFile == 'unknown.jpg' || empty($pictureWebFile)) {
2013
            if ($gravatarEnabled === 'true') {
2014
                $file = self::getGravatar(
2015
                    $imageWebPath['email'],
2016
                    $gravatarSize,
2017
                    api_get_setting('gravatar_type')
2018
                );
2019
2020
                if ($addRandomId) {
2021
                    $file .= '&rand='.uniqid();
2022
                }
2023
2024
                return $file;
2025
            }
2026
2027
            return $anonymousPath;
2028
        }
2029
2030
        $pictureSysPath = self::get_user_picture_path_by_id($user_id, 'system');
2031
        $file = $pictureSysPath['dir'].$realSizeName.$pictureWebFile;
2032
        $picture = '';
2033
        if (file_exists($file)) {
2034
            $picture = $pictureWebDir.$realSizeName.$pictureWebFile;
2035
        } else {
2036
            $file = $pictureSysPath['dir'].$pictureWebFile;
2037
            if (file_exists($file) && !is_dir($file)) {
2038
                $picture = $pictureWebFile['dir'].$pictureWebFile;
2039
            }
2040
        }
2041
2042
        if (empty($picture)) {
2043
            return $anonymousPath;
2044
        }
2045
2046
        if ($addRandomId) {
2047
            $picture .= '?rand='.uniqid();
2048
        }
2049
2050
        return $picture;
2051
    }
2052
2053
    /**
2054
     * Creates new user photos in various sizes of a user, or deletes user photos.
2055
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2056
     *
2057
     * @param int    $user_id the user internal identification number
2058
     * @param string $file    The common file name for the newly created photos.
2059
     *                        It will be checked and modified for compatibility with the file system.
2060
     *                        If full name is provided, path component is ignored.
2061
     *                        If an empty name is provided, then old user photos are deleted only,
2062
     *
2063
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
2064
     *
2065
     * @param string $source_file    the full system name of the image from which user photos will be created
2066
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
2067
     *
2068
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
2069
     *              When deletion is requested returns empty string.
2070
     *              In case of internal error or negative validation returns FALSE.
2071
     */
2072
    public static function update_user_picture(
2073
        $user_id,
2074
        $file = null,
2075
        $source_file = null,
2076
        $cropParameters = ''
2077
    ) {
2078
        if (empty($user_id)) {
2079
            return false;
2080
        }
2081
        $delete = empty($file);
2082
        if (empty($source_file)) {
2083
            $source_file = $file;
2084
        }
2085
2086
        // User-reserved directory where photos have to be placed.
2087
        $path_info = self::getUserPicturePathById($user_id, 'system');
2088
        $path = $path_info['dir'];
2089
2090
        // If this directory does not exist - we create it.
2091
        if (!file_exists($path)) {
2092
            mkdir($path, api_get_permissions_for_new_directories(), true);
2093
        }
2094
2095
        // The old photos (if any).
2096
        $old_file = $path_info['file'];
2097
2098
        // Let us delete them.
2099
        if ($old_file != 'unknown.jpg') {
2100
            if (api_get_setting('platform.keep_old_images_after_delete') == 'true') {
2101
                $prefix = 'saved_'.date('Y_m_d_H_i_s').'_'.uniqid('').'_';
2102
                @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

2102
                /** @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...
2103
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
2104
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
2105
                @rename($path.$old_file, $path.$prefix.$old_file);
2106
            } else {
2107
                @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

2107
                /** @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...
2108
                @unlink($path.'medium_'.$old_file);
2109
                @unlink($path.'big_'.$old_file);
2110
                @unlink($path.$old_file);
2111
            }
2112
        }
2113
2114
        // Exit if only deletion has been requested. Return an empty picture name.
2115
        if ($delete) {
2116
            return '';
0 ignored issues
show
Bug Best Practice introduced by
The expression return '' returns the type string which is incompatible with the documented return type boolean.
Loading history...
2117
        }
2118
2119
        // Validation 2.
2120
        $allowed_types = api_get_supported_image_extensions();
2121
        $file = str_replace('\\', '/', $file);
2122
        $filename = (($pos = strrpos($file, '/')) !== false) ? substr($file, $pos + 1) : $file;
2123
        $extension = strtolower(substr(strrchr($filename, '.'), 1));
2124
        if (!in_array($extension, $allowed_types)) {
2125
            return false;
2126
        }
2127
2128
        // This is the common name for the new photos.
2129
        if ($old_file != 'unknown.jpg') {
2130
            $old_extension = strtolower(substr(strrchr($old_file, '.'), 1));
2131
            $filename = in_array($old_extension, $allowed_types) ? substr($old_file, 0, -strlen($old_extension)) : $old_file;
2132
            $filename = (substr($filename, -1) == '.') ? $filename.$extension : $filename.'.'.$extension;
2133
        } else {
2134
            $filename = api_replace_dangerous_char($filename);
2135
            $filename = uniqid('').'_'.$filename;
2136
            // We always prefix user photos with user ids, so on setting
2137
            // api_get_setting('split_users_upload_directory') === 'true'
2138
            // the correspondent directories to be found successfully.
2139
            $filename = $user_id.'_'.$filename;
2140
        }
2141
2142
        //Crop the image to adjust 1:1 ratio
2143
        $image = new Image($source_file);
2144
        $image->crop($cropParameters);
2145
2146
        // Storing the new photos in 4 versions with various sizes.
2147
        $userPath = self::getUserPathById($user_id, 'system');
2148
2149
        // If this path does not exist - we create it.
2150
        if (!file_exists($userPath)) {
2151
            mkdir($userPath, api_get_permissions_for_new_directories(), true);
2152
        }
2153
        $small = new Image($source_file);
2154
        $small->resize(32);
2155
        $small->send_image($userPath.'small_'.$filename);
2156
        $medium = new Image($source_file);
2157
        $medium->resize(85);
2158
        $medium->send_image($userPath.'medium_'.$filename);
2159
        $normal = new Image($source_file);
2160
        $normal->resize(200);
2161
        $normal->send_image($userPath.$filename);
2162
2163
        $big = new Image($source_file); // This is the original picture.
2164
        $big->send_image($userPath.'big_'.$filename);
2165
2166
        $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
$big 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...
2167
2168
        return $result ? $filename : false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result ? $filename : false returns the type string which is incompatible with the documented return type boolean.
Loading history...
2169
    }
2170
2171
    /**
2172
     * Update User extra field file type into {user_folder}/{$extra_field}.
2173
     *
2174
     * @param int    $user_id     The user internal identification number
2175
     * @param string $extra_field The $extra_field The extra field name
2176
     * @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...
2177
     * @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...
2178
     *
2179
     * @return bool|null return filename if success, but false
2180
     */
2181
    public static function update_user_extra_file(
2182
        $user_id,
2183
        $extra_field = '',
2184
        $file = null,
2185
        $source_file = null
2186
    ) {
2187
        // Add Filter
2188
        $source_file = Security::filter_filename($source_file);
2189
        $file = Security::filter_filename($file);
2190
2191
        if (empty($user_id)) {
2192
            return false;
2193
        }
2194
2195
        if (empty($source_file)) {
2196
            $source_file = $file;
2197
        }
2198
2199
        // User-reserved directory where extra file have to be placed.
2200
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2201
        $path = $path_info['dir'];
2202
        if (!empty($extra_field)) {
2203
            $path .= $extra_field.'/';
2204
        }
2205
        // If this directory does not exist - we create it.
2206
        if (!file_exists($path)) {
2207
            @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

2207
            /** @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...
2208
        }
2209
2210
        if (filter_extension($file)) {
2211
            if (@move_uploaded_file($source_file, $path.$file)) {
2212
                if ($extra_field) {
2213
                    return $extra_field.'/'.$file;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extra_field . '/' . $file returns the type string which is incompatible with the documented return type boolean|null.
Loading history...
2214
                } else {
2215
                    return $file;
2216
                }
2217
            }
2218
        }
2219
2220
        return false; // this should be returned if anything went wrong with the upload
2221
    }
2222
2223
    /**
2224
     * Deletes user photos.
2225
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2226
     *
2227
     * @param int $userId the user internal identification number
2228
     *
2229
     * @return mixed returns empty string on success, FALSE on error
2230
     */
2231
    public static function deleteUserPicture($userId)
2232
    {
2233
        return self::update_user_picture($userId);
2234
    }
2235
2236
    /**
2237
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
2238
     * doesn't have any.
2239
     *
2240
     * If there has been a request to remove a production, the function will return
2241
     * without building the list unless forced to do so by the optional second
2242
     * parameter. This increases performance by avoiding to read through the
2243
     * productions on the filesystem before the removal request has been carried
2244
     * out because they'll have to be re-read afterwards anyway.
2245
     *
2246
     * @param int  $user_id    User id
2247
     * @param bool $force      Optional parameter to force building after a removal request
2248
     * @param bool $showDelete
2249
     *
2250
     * @return string A string containing the XHTML code to display the production list, or FALSE
2251
     */
2252
    public static function build_production_list($user_id, $force = false, $showDelete = false)
2253
    {
2254
        if (!$force && !empty($_POST['remove_production'])) {
2255
            return true; // postpone reading from the filesystem
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type string.
Loading history...
2256
        }
2257
2258
        $productions = self::get_user_productions($user_id);
2259
2260
        if (empty($productions)) {
2261
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
2262
        }
2263
2264
        $production_dir = self::getUserPathById($user_id, 'web');
2265
        $del_image = Display::returnIconPath('delete.png');
2266
        $add_image = Display::returnIconPath('archive.png');
2267
        $del_text = get_lang('Delete');
2268
        $production_list = '';
2269
        if (count($productions) > 0) {
2270
            $production_list = '<div class="files-production"><ul id="productions">';
2271
            foreach ($productions as $file) {
2272
                $production_list .= '<li>
2273
                    <img src="'.$add_image.'" />
2274
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2275
                        '.htmlentities($file).'
2276
                    </a>';
2277
                if ($showDelete) {
2278
                    $production_list .= '&nbsp;&nbsp;
2279
                        <input 
2280
                            style="width:16px;" 
2281
                            type="image" 
2282
                            name="remove_production['.urlencode($file).']" 
2283
                            src="'.$del_image.'" 
2284
                            alt="'.$del_text.'" 
2285
                            title="'.$del_text.' '.htmlentities($file).'" 
2286
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2287
                }
2288
            }
2289
            $production_list .= '</ul></div>';
2290
        }
2291
2292
        return $production_list;
2293
    }
2294
2295
    /**
2296
     * Returns an array with the user's productions.
2297
     *
2298
     * @param int $user_id User id
2299
     *
2300
     * @return array An array containing the user's productions
2301
     */
2302
    public static function get_user_productions($user_id)
2303
    {
2304
        $production_repository = self::getUserPathById($user_id, 'system');
2305
        $productions = [];
2306
2307
        if (is_dir($production_repository)) {
2308
            $handle = opendir($production_repository);
2309
            while ($file = readdir($handle)) {
0 ignored issues
show
Bug introduced by
It seems like $handle can also be of type false; however, parameter $dir_handle of readdir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

2309
            while ($file = readdir(/** @scrutinizer ignore-type */ $handle)) {
Loading history...
2310
                if ($file == '.' ||
2311
                    $file == '..' ||
2312
                    $file == '.htaccess' ||
2313
                    is_dir($production_repository.$file)
2314
                ) {
2315
                    // skip current/parent directory and .htaccess
2316
                    continue;
2317
                }
2318
2319
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2320
                    // User's photos should not be listed as productions.
2321
                    continue;
2322
                }
2323
                $productions[] = $file;
2324
            }
2325
        }
2326
2327
        return $productions;
2328
    }
2329
2330
    /**
2331
     * Remove a user production.
2332
     *
2333
     * @param int    $user_id    User id
2334
     * @param string $production The production to remove
2335
     *
2336
     * @return bool
2337
     */
2338
    public static function remove_user_production($user_id, $production)
2339
    {
2340
        $production_path = self::get_user_picture_path_by_id($user_id, 'system');
2341
        $production_file = $production_path['dir'].$production;
2342
        if (is_file($production_file)) {
2343
            unlink($production_file);
2344
2345
            return true;
2346
        }
2347
2348
        return false;
2349
    }
2350
2351
    /**
2352
     * Update an extra field value for a given user.
2353
     *
2354
     * @param int    $userId   User ID
2355
     * @param string $variable Field variable name
2356
     * @param string $value    Field value
2357
     *
2358
     * @return bool true if field updated, false otherwise
2359
     */
2360
    public static function update_extra_field_value($userId, $variable, $value = '')
2361
    {
2362
        $extraFieldValue = new ExtraFieldValue('user');
2363
        $params = [
2364
            'item_id' => $userId,
2365
            'variable' => $variable,
2366
            'value' => $value,
2367
        ];
2368
2369
        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...
2370
    }
2371
2372
    /**
2373
     * Get an array of extra fields with field details (type, default value and options).
2374
     *
2375
     * @param    int    Offset (from which row)
2376
     * @param    int    Number of items
2377
     * @param    int    Column on which sorting is made
2378
     * @param    string    Sorting direction
2379
     * @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...
2380
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2381
     *
2382
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2383
     */
2384
    public static function get_extra_fields(
2385
        $from = 0,
2386
        $number_of_items = 0,
2387
        $column = 5,
2388
        $direction = 'ASC',
2389
        $all_visibility = true,
2390
        $field_filter = null
2391
    ) {
2392
        $fields = [];
2393
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2394
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2395
        $columns = [
2396
            'id',
2397
            'variable',
2398
            'field_type',
2399
            'display_text',
2400
            'default_value',
2401
            'field_order',
2402
            'filter',
2403
        ];
2404
        $column = (int) $column;
2405
        $sort_direction = '';
2406
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2407
            $sort_direction = strtoupper($direction);
2408
        }
2409
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2410
        $sqlf = "SELECT * FROM $t_uf WHERE extra_field_type = $extraFieldType ";
2411
        if (!$all_visibility) {
2412
            $sqlf .= " AND visible_to_self = 1 ";
2413
        }
2414
        if (!is_null($field_filter)) {
2415
            $field_filter = (int) $field_filter;
2416
            $sqlf .= " AND filter = $field_filter ";
2417
        }
2418
        $sqlf .= " ORDER BY ".$columns[$column]." $sort_direction ";
2419
        if ($number_of_items != 0) {
2420
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2421
        }
2422
        $resf = Database::query($sqlf);
2423
        if (Database::num_rows($resf) > 0) {
2424
            while ($rowf = Database::fetch_array($resf)) {
2425
                $fields[$rowf['id']] = [
2426
                    0 => $rowf['id'],
2427
                    1 => $rowf['variable'],
2428
                    2 => $rowf['field_type'],
2429
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2430
                    4 => $rowf['default_value'],
2431
                    5 => $rowf['field_order'],
2432
                    6 => $rowf['visible_to_self'],
2433
                    7 => $rowf['changeable'],
2434
                    8 => $rowf['filter'],
2435
                    9 => [],
2436
                    10 => '<a name="'.$rowf['id'].'"></a>',
2437
                ];
2438
2439
                $sqlo = "SELECT * FROM $t_ufo
2440
                         WHERE field_id = ".$rowf['id']."
2441
                         ORDER BY option_order ASC";
2442
                $reso = Database::query($sqlo);
2443
                if (Database::num_rows($reso) > 0) {
2444
                    while ($rowo = Database::fetch_array($reso)) {
2445
                        $fields[$rowf['id']][9][$rowo['id']] = [
2446
                            0 => $rowo['id'],
2447
                            1 => $rowo['option_value'],
2448
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2449
                            3 => $rowo['option_order'],
2450
                        ];
2451
                    }
2452
                }
2453
            }
2454
        }
2455
2456
        return $fields;
2457
    }
2458
2459
    /**
2460
     * Get valid filenames in $user_folder/{$extra_field}/.
2461
     *
2462
     * @param $user_id
2463
     * @param $extra_field
2464
     * @param bool $full_path
2465
     *
2466
     * @return array
2467
     */
2468
    public static function get_user_extra_files($user_id, $extra_field, $full_path = false)
2469
    {
2470
        if (!$full_path) {
2471
            // Nothing to do
2472
        } else {
2473
            $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2474
            $path = $path_info['dir'];
2475
        }
2476
        $extra_data = self::get_extra_user_data_by_field($user_id, $extra_field);
2477
        $extra_files = $extra_data[$extra_field];
2478
2479
        $files = [];
2480
        if (is_array($extra_files)) {
2481
            foreach ($extra_files as $key => $value) {
2482
                if (!$full_path) {
2483
                    // Relative path from user folder
2484
                    $files[] = $value;
2485
                } else {
2486
                    $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...
2487
                }
2488
            }
2489
        } elseif (!empty($extra_files)) {
2490
            if (!$full_path) {
2491
                // Relative path from user folder
2492
                $files[] = $extra_files;
2493
            } else {
2494
                $files[] = $path.$extra_files;
2495
            }
2496
        }
2497
2498
        return $files; // can be an empty array
2499
    }
2500
2501
    /**
2502
     * Creates a new extra field.
2503
     *
2504
     * @param string $variable    Field's internal variable name
2505
     * @param int    $fieldType   Field's type
2506
     * @param string $displayText Field's language var name
2507
     * @param string $default     Field's default value
2508
     *
2509
     * @return int
2510
     */
2511
    public static function create_extra_field(
2512
        $variable,
2513
        $fieldType,
2514
        $displayText,
2515
        $default
2516
    ) {
2517
        $extraField = new ExtraField('user');
2518
        $params = [
2519
            'variable' => $variable,
2520
            'field_type' => $fieldType,
2521
            'display_text' => $displayText,
2522
            'default_value' => $default,
2523
        ];
2524
2525
        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...
2526
    }
2527
2528
    /**
2529
     * Check if a field is available.
2530
     *
2531
     * @param string $variable
2532
     *
2533
     * @return bool
2534
     */
2535
    public static function is_extra_field_available($variable)
2536
    {
2537
        $extraField = new ExtraField('user');
2538
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2539
2540
        return !empty($data) ? true : false;
2541
    }
2542
2543
    /**
2544
     * Gets user extra fields data.
2545
     *
2546
     * @param    int    User ID
2547
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2548
     * @param    bool    Whether to return invisible fields as well
2549
     * @param    bool    Whether to split multiple-selection fields or not
2550
     *
2551
     * @return array Array of fields => value for the given user
2552
     */
2553
    public static function get_extra_user_data(
2554
        $user_id,
2555
        $prefix = false,
2556
        $allVisibility = true,
2557
        $splitMultiple = false,
0 ignored issues
show
Unused Code introduced by
The parameter $splitMultiple is not used and could be removed. ( Ignorable by Annotation )

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

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

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

Loading history...
2558
        $fieldFilter = null
2559
    ) {
2560
        $user_id = (int) $user_id;
2561
2562
        if (empty($user_id)) {
2563
            return [];
2564
        }
2565
2566
        $extra_data = [];
2567
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2568
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2569
        $user_id = (int) $user_id;
2570
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2571
                FROM $t_uf f
2572
                WHERE
2573
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
2574
                ";
2575
        $filter_cond = '';
2576
2577
        if (!$allVisibility) {
2578
            if (isset($fieldFilter)) {
2579
                $fieldFilter = (int) $fieldFilter;
2580
                $filter_cond .= " AND filter = $fieldFilter ";
2581
            }
2582
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2583
        } else {
2584
            if (isset($fieldFilter)) {
2585
                $fieldFilter = (int) $fieldFilter;
2586
                $sql .= " AND filter = $fieldFilter ";
2587
            }
2588
        }
2589
2590
        $sql .= ' ORDER BY f.field_order';
2591
2592
        $res = Database::query($sql);
2593
        if (Database::num_rows($res) > 0) {
2594
            while ($row = Database::fetch_array($res)) {
2595
                if ($row['type'] == self::USER_FIELD_TYPE_TAG) {
2596
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2597
                    $extra_data['extra_'.$row['fvar']] = $tags;
2598
                } else {
2599
                    $sqlu = "SELECT value as fval
2600
                            FROM $t_ufv
2601
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2602
                    $resu = Database::query($sqlu);
2603
                    // get default value
2604
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2605
                               WHERE id=".$row['id'];
2606
                    $res_df = Database::query($sql_df);
2607
2608
                    if (Database::num_rows($resu) > 0) {
2609
                        $rowu = Database::fetch_array($resu);
2610
                        $fval = $rowu['fval'];
2611
                        if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
2612
                            $fval = explode(';', $rowu['fval']);
2613
                        }
2614
                    } else {
2615
                        $row_df = Database::fetch_array($res_df);
2616
                        $fval = $row_df['fval_df'];
2617
                    }
2618
                    // We get here (and fill the $extra_data array) even if there
2619
                    // is no user with data (we fill it with default values)
2620
                    if ($prefix) {
2621
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
2622
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2623
                        } else {
2624
                            $extra_data['extra_'.$row['fvar']] = $fval;
2625
                        }
2626
                    } else {
2627
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
2628
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2629
                        } else {
2630
                            $extra_data[$row['fvar']] = $fval;
2631
                        }
2632
                    }
2633
                }
2634
            }
2635
        }
2636
2637
        return $extra_data;
2638
    }
2639
2640
    /** Get extra user data by field
2641
     * @param int    user ID
2642
     * @param string the internal variable name of the field
2643
     *
2644
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2645
     */
2646
    public static function get_extra_user_data_by_field(
2647
        $user_id,
2648
        $field_variable,
2649
        $prefix = false,
2650
        $all_visibility = true,
2651
        $splitmultiple = false
0 ignored issues
show
Unused Code introduced by
The parameter $splitmultiple is not used and could be removed. ( Ignorable by Annotation )

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

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

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

Loading history...
2652
    ) {
2653
        $user_id = (int) $user_id;
2654
2655
        if (empty($user_id)) {
2656
            return [];
2657
        }
2658
2659
        $extra_data = [];
2660
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2661
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2662
2663
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2664
                FROM $t_uf f
2665
                WHERE f.variable = '$field_variable' ";
2666
2667
        if (!$all_visibility) {
2668
            $sql .= " AND f.visible_to_self = 1 ";
2669
        }
2670
2671
        $sql .= " AND extra_field_type = ".EntityExtraField::USER_FIELD_TYPE;
2672
        $sql .= " ORDER BY f.field_order ";
2673
2674
        $res = Database::query($sql);
2675
        if (Database::num_rows($res) > 0) {
2676
            while ($row = Database::fetch_array($res)) {
2677
                $sqlu = "SELECT value as fval FROM $t_ufv v 
2678
                         INNER JOIN $t_uf f
2679
                         ON (v.field_id = f.id)
2680
                         WHERE
2681
                            extra_field_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2682
                            field_id = ".$row['id']." AND
2683
                            item_id = ".$user_id;
2684
                $resu = Database::query($sqlu);
2685
                $fval = '';
2686
                if (Database::num_rows($resu) > 0) {
2687
                    $rowu = Database::fetch_array($resu);
2688
                    $fval = $rowu['fval'];
2689
                    if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
2690
                        $fval = explode(';', $rowu['fval']);
2691
                    }
2692
                }
2693
                if ($prefix) {
2694
                    $extra_data['extra_'.$row['fvar']] = $fval;
2695
                } else {
2696
                    $extra_data[$row['fvar']] = $fval;
2697
                }
2698
            }
2699
        }
2700
2701
        return $extra_data;
2702
    }
2703
2704
    /**
2705
     * Get the extra field information for a certain field (the options as well).
2706
     *
2707
     * @param int $variable The name of the field we want to know everything about
2708
     *
2709
     * @return array Array containing all the information about the extra profile field
2710
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2711
     *               as returned by the database)
2712
     *
2713
     * @author Julio Montoya
2714
     *
2715
     * @since v1.8.6
2716
     */
2717
    public static function get_extra_field_information_by_name($variable)
2718
    {
2719
        $extraField = new ExtraField('user');
2720
2721
        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...
2722
    }
2723
2724
    /**
2725
     * Get the extra field information for user tag (the options as well).
2726
     *
2727
     * @param int $variable The name of the field we want to know everything about
2728
     *
2729
     * @return array Array containing all the information about the extra profile field
2730
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2731
     *               as returned by the database)
2732
     *
2733
     * @author José Loguercio
2734
     *
2735
     * @since v1.11.0
2736
     */
2737
    public static function get_extra_field_tags_information_by_name($variable)
2738
    {
2739
        $extraField = new ExtraField('user');
2740
2741
        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...
2742
    }
2743
2744
    /**
2745
     * Get all the extra field information of a certain field (also the options).
2746
     *
2747
     * @param int $fieldId the ID of the field we want to know everything of
2748
     *
2749
     * @return array $return containing all th information about the extra profile field
2750
     *
2751
     * @author Julio Montoya
2752
     *
2753
     * @deprecated
2754
     * @since v1.8.6
2755
     */
2756
    public static function get_extra_field_information($fieldId)
2757
    {
2758
        $extraField = new ExtraField('user');
2759
2760
        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...
2761
    }
2762
2763
    /**
2764
     * Get extra user data by value.
2765
     *
2766
     * @param string $variable       the internal variable name of the field
2767
     * @param string $value          the internal value of the field
2768
     * @param bool   $all_visibility
2769
     *
2770
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2771
     */
2772
    public static function get_extra_user_data_by_value($variable, $value, $all_visibility = true)
0 ignored issues
show
Unused Code introduced by
The parameter $all_visibility is not used and could be removed. ( Ignorable by Annotation )

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

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

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

Loading history...
2773
    {
2774
        $extraFieldValue = new ExtraFieldValue('user');
2775
        $extraField = new ExtraField('user');
2776
2777
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2778
2779
        if (false === $info) {
2780
            return [];
2781
        }
2782
2783
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2784
            $variable,
2785
            $value,
2786
            false,
2787
            false,
2788
            true
2789
        );
2790
2791
        $result = [];
2792
        if (!empty($data)) {
2793
            foreach ($data as $item) {
2794
                $result[] = $item['item_id'];
2795
            }
2796
        }
2797
2798
        return $result;
2799
    }
2800
2801
    /**
2802
     * Get extra user data by tags value.
2803
     *
2804
     * @param int    $fieldId the ID of the field we want to know everything of
2805
     * @param string $tag     the tag name for search
2806
     *
2807
     * @return array with extra data info of a user
2808
     *
2809
     * @author José Loguercio
2810
     *
2811
     * @since v1.11.0
2812
     */
2813
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2814
    {
2815
        $extraField = new ExtraField('user');
2816
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2817
        $array = [];
2818
        foreach ($result as $index => $user) {
2819
            $array[] = $user['user_id'];
2820
        }
2821
2822
        return $array;
2823
    }
2824
2825
    /**
2826
     * Get extra user data by field variable.
2827
     *
2828
     * @param string $variable field variable
2829
     *
2830
     * @return array data
2831
     */
2832
    public static function get_extra_user_data_by_field_variable($variable)
2833
    {
2834
        $extraInfo = self::get_extra_field_information_by_name($variable);
2835
        $field_id = (int) $extraInfo['id'];
2836
2837
        $extraField = new ExtraFieldValue('user');
2838
        $data = $extraField->getValuesByFieldId($field_id);
2839
2840
        if (!empty($data)) {
2841
            foreach ($data as $row) {
2842
                $user_id = $row['item_id'];
2843
                $data[$user_id] = $row;
2844
            }
2845
        }
2846
2847
        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...
2848
    }
2849
2850
    /**
2851
     * Get extra user data tags by field variable.
2852
     *
2853
     * @param string $variable field variable
2854
     *
2855
     * @return array
2856
     */
2857
    public static function get_extra_user_data_for_tags($variable)
2858
    {
2859
        $data = self::get_extra_field_tags_information_by_name($variable);
2860
2861
        return $data;
2862
    }
2863
2864
    /**
2865
     * Gives a list of [session_category][session_id] for the current user.
2866
     *
2867
     * @param int  $user_id
2868
     * @param bool $is_time_over                 whether to fill the first element or not
2869
     *                                           (to give space for courses out of categories)
2870
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2871
     * @param bool $ignoreTimeLimit              ignore time start/end
2872
     * @param bool $getCount
2873
     *
2874
     * @return array list of statuses [session_category][session_id]
2875
     *
2876
     * @todo ensure multiple access urls are managed correctly
2877
     */
2878
    public static function get_sessions_by_category(
2879
        $user_id,
2880
        $is_time_over = true,
2881
        $ignore_visibility_for_admins = false,
2882
        $ignoreTimeLimit = false,
2883
        $getCount = false
2884
    ) {
2885
        $user_id = (int) $user_id;
2886
2887
        if (empty($user_id)) {
2888
            return [];
2889
        }
2890
2891
        $allowOrder = api_get_configuration_value('session_list_order');
2892
        $position = '';
2893
        if ($allowOrder) {
2894
            $position = ', s.position AS position ';
2895
        }
2896
2897
        // Get the list of sessions per user
2898
        $now = new DateTime('now', new DateTimeZone('UTC'));
2899
2900
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2901
        // join would not catch session-courses where the user is general
2902
        // session coach but which do not have students nor coaches registered
2903
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
2904
2905
        if (!$getCount) {
2906
            $dqlSelect = " DISTINCT
2907
                s.id,
2908
                s.name,
2909
                s.accessStartDate AS access_start_date,
2910
                s.accessEndDate AS access_end_date,
2911
                s.duration,
2912
                sc.id AS session_category_id,
2913
                sc.name AS session_category_name,
2914
                sc.dateStart AS session_category_date_start,
2915
                sc.dateEnd AS session_category_date_end,
2916
                s.coachAccessStartDate AS coach_access_start_date,
2917
                s.coachAccessEndDate AS coach_access_end_date,
2918
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
2919
                $position
2920
            ";
2921
        }
2922
2923
        $dql = "SELECT $dqlSelect
2924
                FROM ChamiloCoreBundle:Session AS s
2925
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2926
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2927
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc ";
2928
2929
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
2930
        // is awfully inefficient for large sets of data (1m25s for 58K
2931
        // sessions, BT#14115) but executing a similar query twice and grouping
2932
        // the results afterwards in PHP takes about 1/1000th of the time
2933
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
2934
        $dqlStudent = $dql." WHERE scu.user = :user AND url.url = :url ";
2935
        $dqlCoach = $dql." WHERE s.generalCoach = :user AND url.url = :url ";
2936
2937
        // Default order
2938
        $order = 'ORDER BY sc.name, s.name';
2939
2940
        // Order by date if showing all sessions
2941
        $showAllSessions = api_get_configuration_value('show_all_sessions_on_my_course_page') === true;
2942
        if ($showAllSessions) {
2943
            $order = 'ORDER BY s.accessStartDate';
2944
        }
2945
2946
        // Order by position
2947
        if ($allowOrder) {
2948
            $order = 'ORDER BY s.position';
2949
        }
2950
2951
        // Order by dates according to settings
2952
        $orderBySettings = api_get_configuration_value('my_courses_session_order');
2953
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
2954
            $field = $orderBySettings['field'];
2955
            $orderSetting = $orderBySettings['order'];
2956
            switch ($field) {
2957
                case 'start_date':
2958
                    $order = " ORDER BY s.accessStartDate $orderSetting";
2959
                    break;
2960
                case 'end_date':
2961
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
2962
                    if ($orderSetting == 'asc') {
2963
                        // Put null values at the end
2964
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
2965
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
2966
                    }
2967
                    break;
2968
                case 'name':
2969
                    $order = " ORDER BY s.name $orderSetting ";
2970
                    break;
2971
            }
2972
        }
2973
2974
        $dqlStudent .= $order;
2975
        $dqlCoach .= $order;
2976
2977
        $accessUrlId = api_get_current_access_url_id();
2978
        $dqlStudent = Database::getManager()
2979
            ->createQuery($dqlStudent)
2980
            ->setParameters(
2981
                ['user' => $user_id, 'url' => $accessUrlId]
2982
            )
2983
        ;
2984
        $dqlCoach = Database::getManager()
2985
            ->createQuery($dqlCoach)
2986
            ->setParameters(
2987
                ['user' => $user_id, 'url' => $accessUrlId]
2988
            )
2989
        ;
2990
2991
        if ($getCount) {
2992
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
2993
        }
2994
2995
        $sessionDataStudent = $dqlStudent->getResult();
2996
        $sessionDataCoach = $dqlCoach->getResult();
2997
2998
        $sessionData = [];
2999
        // First fill $sessionData with student sessions
3000
        foreach ($sessionDataStudent as $row) {
3001
            $sessionData[$row['id']] = $row;
3002
        }
3003
        // Overwrite session data of the user as a student with session data
3004
        // of the user as a coach.
3005
        // There shouldn't be such duplicate rows, but just in case...
3006
        foreach ($sessionDataCoach as $row) {
3007
            $sessionData[$row['id']] = $row;
3008
        }
3009
3010
        $collapsable = api_get_configuration_value('allow_user_session_collapsable');
3011
        $extraField = new ExtraFieldValue('session');
3012
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
3013
3014
        $categories = [];
3015
        foreach ($sessionData as $row) {
3016
            $session_id = $row['id'];
3017
            $coachList = SessionManager::getCoachesBySession($session_id);
3018
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
3019
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
3020
            $courseList = self::get_courses_list_by_session(
3021
                $user_id,
3022
                $session_id
3023
            );
3024
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
3025
3026
            // User portal filters:
3027
            if ($ignoreTimeLimit === false) {
3028
                if ($is_time_over) {
3029
                    // History
3030
                    if ($row['duration']) {
3031
                        if ($daysLeft >= 0) {
3032
                            continue;
3033
                        }
3034
                    } else {
3035
                        if (empty($row['access_end_date'])) {
3036
                            continue;
3037
                        } else {
3038
                            if ($row['access_end_date'] > $now) {
3039
                                continue;
3040
                            }
3041
                        }
3042
                    }
3043
                } else {
3044
                    // Current user portal
3045
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
3046
                    $isCoachOfCourse = in_array($user_id, $coachList);
3047
3048
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
3049
                        // Teachers can access the session depending in the access_coach date
3050
                    } else {
3051
                        if ($row['duration']) {
3052
                            if ($daysLeft <= 0) {
3053
                                continue;
3054
                            }
3055
                        } else {
3056
                            if (isset($row['access_end_date']) &&
3057
                                !empty($row['access_end_date'])
3058
                            ) {
3059
                                if ($row['access_end_date'] <= $now) {
3060
                                    continue;
3061
                                }
3062
                            }
3063
                        }
3064
                    }
3065
                }
3066
            }
3067
3068
            $categories[$row['session_category_id']]['session_category'] = [
3069
                'id' => $row['session_category_id'],
3070
                'name' => $row['session_category_name'],
3071
                'date_start' => $categoryStart,
3072
                'date_end' => $categoryEnd,
3073
            ];
3074
3075
            $visibility = api_get_session_visibility(
3076
                $session_id,
3077
                null,
3078
                $ignore_visibility_for_admins
3079
            );
3080
3081
            if ($visibility != SESSION_VISIBLE) {
3082
                // Course Coach session visibility.
3083
                $blockedCourseCount = 0;
3084
                $closedVisibilityList = [
3085
                    COURSE_VISIBILITY_CLOSED,
3086
                    COURSE_VISIBILITY_HIDDEN,
3087
                ];
3088
3089
                foreach ($courseList as $course) {
3090
                    // Checking session visibility
3091
                    $sessionCourseVisibility = api_get_session_visibility(
3092
                        $session_id,
3093
                        $course['real_id'],
3094
                        $ignore_visibility_for_admins
3095
                    );
3096
3097
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
3098
                    if ($courseIsVisible === false || $sessionCourseVisibility == SESSION_INVISIBLE) {
3099
                        $blockedCourseCount++;
3100
                    }
3101
                }
3102
3103
                // If all courses are blocked then no show in the list.
3104
                if ($blockedCourseCount === count($courseList)) {
3105
                    $visibility = SESSION_INVISIBLE;
3106
                } else {
3107
                    $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...
3108
                }
3109
            }
3110
3111
            switch ($visibility) {
3112
                case SESSION_VISIBLE_READ_ONLY:
3113
                case SESSION_VISIBLE:
3114
                case SESSION_AVAILABLE:
3115
                    break;
3116
                case SESSION_INVISIBLE:
3117
                    if ($ignore_visibility_for_admins === false) {
3118
                        continue 2;
3119
                    }
3120
            }
3121
3122
            $collapsed = '';
3123
            $collapsedAction = '';
3124
            if ($collapsable) {
3125
                $collapsableData = SessionManager::getCollapsableData(
3126
                    $user_id,
3127
                    $session_id,
3128
                    $extraField,
3129
                    $collapsableLink
3130
                );
3131
                $collapsed = $collapsableData['collapsed'];
3132
                $collapsedAction = $collapsableData['collapsable_link'];
3133
            }
3134
3135
            $categories[$row['session_category_id']]['sessions'][] = [
3136
                'session_name' => $row['name'],
3137
                'session_id' => $row['id'],
3138
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
3139
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
3140
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
3141
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
3142
                'courses' => $courseList,
3143
                'collapsed' => $collapsed,
3144
                'collapsable_link' => $collapsedAction,
3145
                'duration' => $row['duration'],
3146
            ];
3147
        }
3148
3149
        return $categories;
3150
    }
3151
3152
    /**
3153
     * Gives a list of [session_id-course_code] => [status] for the current user.
3154
     *
3155
     * @param int $user_id
3156
     * @param int $sessionLimit
3157
     *
3158
     * @return array list of statuses (session_id-course_code => status)
3159
     */
3160
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
3161
    {
3162
        // Database Table Definitions
3163
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3164
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
3165
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3166
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3167
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3168
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3169
3170
        $user_id = (int) $user_id;
3171
3172
        if (empty($user_id)) {
3173
            return [];
3174
        }
3175
3176
        // We filter the courses from the URL
3177
        $join_access_url = $where_access_url = '';
3178
        if (api_get_multiple_access_url()) {
3179
            $access_url_id = api_get_current_access_url_id();
3180
            if ($access_url_id != -1) {
3181
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3182
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
3183
                $where_access_url = " AND access_url_id = $access_url_id ";
3184
            }
3185
        }
3186
3187
        // Courses in which we subscribed out of any session
3188
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3189
3190
        $sql = "SELECT
3191
                    course.code,
3192
                    course_rel_user.status course_rel_status,
3193
                    course_rel_user.sort sort,
3194
                    course_rel_user.user_course_cat user_course_cat
3195
                 FROM $tbl_course_user course_rel_user
3196
                 LEFT JOIN $tbl_course course
3197
                 ON course.id = course_rel_user.c_id
3198
                 LEFT JOIN $tbl_user_course_category user_course_category
3199
                 ON course_rel_user.user_course_cat = user_course_category.id
3200
                 $join_access_url
3201
                 WHERE
3202
                    course_rel_user.user_id = '".$user_id."' AND
3203
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
3204
                    $where_access_url
3205
                 ORDER BY user_course_category.sort, course_rel_user.sort, course.title ASC";
3206
3207
        $course_list_sql_result = Database::query($sql);
3208
3209
        $personal_course_list = [];
3210
        if (Database::num_rows($course_list_sql_result) > 0) {
3211
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3212
                $course_info = api_get_course_info($result_row['code']);
3213
                $result_row['course_info'] = $course_info;
3214
                $personal_course_list[] = $result_row;
3215
            }
3216
        }
3217
3218
        $coachCourseConditions = '';
3219
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
3220
        if (api_is_allowed_to_create_course()) {
3221
            $sessionListFromCourseCoach = [];
3222
            $sql = " SELECT DISTINCT session_id
3223
                    FROM $tbl_session_course_user
3224
                    WHERE user_id = $user_id AND status = 2 ";
3225
3226
            $result = Database::query($sql);
3227
            if (Database::num_rows($result)) {
3228
                $result = Database::store_result($result);
3229
                foreach ($result as $session) {
3230
                    $sessionListFromCourseCoach[] = $session['session_id'];
3231
                }
3232
            }
3233
            if (!empty($sessionListFromCourseCoach)) {
3234
                $condition = implode("','", $sessionListFromCourseCoach);
3235
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
3236
            }
3237
        }
3238
3239
        // Get the list of sessions where the user is subscribed
3240
        // This is divided into two different queries
3241
        $sessions = [];
3242
        $sessionLimitRestriction = '';
3243
        if (!empty($sessionLimit)) {
3244
            $sessionLimit = (int) $sessionLimit;
3245
            $sessionLimitRestriction = "LIMIT $sessionLimit";
3246
        }
3247
3248
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
3249
                FROM $tbl_session_user su INNER JOIN $tbl_session s
3250
                ON (s.id = su.session_id)
3251
                WHERE (
3252
                    su.user_id = $user_id AND
3253
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
3254
                )
3255
                $coachCourseConditions
3256
                ORDER BY access_start_date, access_end_date, name
3257
                $sessionLimitRestriction
3258
        ";
3259
3260
        $result = Database::query($sql);
3261
        if (Database::num_rows($result) > 0) {
3262
            while ($row = Database::fetch_assoc($result)) {
3263
                $sessions[$row['id']] = $row;
3264
            }
3265
        }
3266
3267
        $sql = "SELECT DISTINCT
3268
                id, name, access_start_date, access_end_date
3269
                FROM $tbl_session s
3270
                WHERE (
3271
                    id_coach = $user_id
3272
                )
3273
                $coachCourseConditions
3274
                ORDER BY access_start_date, access_end_date, name";
3275
3276
        $result = Database::query($sql);
3277
        if (Database::num_rows($result) > 0) {
3278
            while ($row = Database::fetch_assoc($result)) {
3279
                if (empty($sessions[$row['id']])) {
3280
                    $sessions[$row['id']] = $row;
3281
                }
3282
            }
3283
        }
3284
3285
        if (api_is_allowed_to_create_course()) {
3286
            foreach ($sessions as $enreg) {
3287
                $session_id = $enreg['id'];
3288
                $session_visibility = api_get_session_visibility($session_id);
3289
3290
                if ($session_visibility == SESSION_INVISIBLE) {
3291
                    continue;
3292
                }
3293
3294
                // This query is horribly slow when more than a few thousand
3295
                // users and just a few sessions to which they are subscribed
3296
                $sql = "SELECT DISTINCT
3297
                        course.code code,
3298
                        course.title i,
3299
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
3300
                        email, course.course_language l,
3301
                        1 sort,
3302
                        category_code user_course_cat,
3303
                        access_start_date,
3304
                        access_end_date,
3305
                        session.id as session_id,
3306
                        session.name as session_name
3307
                    FROM $tbl_session_course_user as session_course_user
3308
                    INNER JOIN $tbl_course AS course
3309
                        ON course.id = session_course_user.c_id
3310
                    INNER JOIN $tbl_session as session
3311
                        ON session.id = session_course_user.session_id
3312
                    LEFT JOIN $tbl_user as user
3313
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
3314
                    WHERE
3315
                        session_course_user.session_id = $session_id AND (
3316
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
3317
                            OR session.id_coach = $user_id
3318
                        )
3319
                    ORDER BY i";
3320
                $course_list_sql_result = Database::query($sql);
3321
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3322
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3323
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3324
                    $personal_course_list[$key] = $result_row;
3325
                }
3326
            }
3327
        }
3328
3329
        foreach ($sessions as $enreg) {
3330
            $session_id = $enreg['id'];
3331
            $session_visibility = api_get_session_visibility($session_id);
3332
            if ($session_visibility == SESSION_INVISIBLE) {
3333
                continue;
3334
            }
3335
3336
            /* This query is very similar to the above query,
3337
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3338
            $sql = "SELECT DISTINCT
3339
                course.code code,
3340
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
3341
                email,
3342
                course.course_language l,
3343
                1 sort,
3344
                category_code user_course_cat,
3345
                access_start_date,
3346
                access_end_date,
3347
                session.id as session_id,
3348
                session.name as session_name,
3349
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
3350
            FROM $tbl_session_course_user as session_course_user
3351
            INNER JOIN $tbl_course AS course
3352
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
3353
            INNER JOIN $tbl_session as session 
3354
            ON session_course_user.session_id = session.id
3355
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
3356
            WHERE session_course_user.user_id = $user_id
3357
            ORDER BY i";
3358
3359
            $course_list_sql_result = Database::query($sql);
3360
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3361
                $result_row['course_info'] = api_get_course_info($result_row['code']);
3362
                $key = $result_row['session_id'].' - '.$result_row['code'];
3363
                if (!isset($personal_course_list[$key])) {
3364
                    $personal_course_list[$key] = $result_row;
3365
                }
3366
            }
3367
        }
3368
3369
        return $personal_course_list;
3370
    }
3371
3372
    /**
3373
     * Gives a list of courses for the given user in the given session.
3374
     *
3375
     * @param int $user_id
3376
     * @param int $session_id
3377
     *
3378
     * @return array list of statuses (session_id-course_code => status)
3379
     */
3380
    public static function get_courses_list_by_session($user_id, $session_id)
3381
    {
3382
        // Database Table Definitions
3383
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3384
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
3385
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3386
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3387
3388
        $user_id = (int) $user_id;
3389
        $session_id = (int) $session_id;
3390
        // We filter the courses from the URL
3391
        $join_access_url = $where_access_url = '';
3392
        if (api_get_multiple_access_url()) {
3393
            $urlId = api_get_current_access_url_id();
3394
            if ($urlId != -1) {
3395
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3396
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
3397
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
3398
            }
3399
        }
3400
3401
        /* This query is very similar to the query below, but it will check the
3402
        session_rel_course_user table if there are courses registered
3403
        to our user or not */
3404
        $sql = "SELECT DISTINCT
3405
                    c.visibility,
3406
                    c.id as real_id,
3407
                    c.code as course_code,
3408
                    sc.position
3409
                FROM $tbl_session_course_user as scu
3410
                INNER JOIN $tbl_session_course sc
3411
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3412
                INNER JOIN $tableCourse as c
3413
                ON (scu.c_id = c.id)
3414
                $join_access_url
3415
                WHERE
3416
                    scu.user_id = $user_id AND
3417
                    scu.session_id = $session_id
3418
                    $where_access_url
3419
                ORDER BY sc.position ASC";
3420
3421
        $myCourseList = [];
3422
        $courses = [];
3423
        $result = Database::query($sql);
3424
        if (Database::num_rows($result) > 0) {
3425
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3426
                $result_row['status'] = 5;
3427
                if (!in_array($result_row['real_id'], $courses)) {
3428
                    $position = $result_row['position'];
3429
                    if (!isset($myCourseList[$position])) {
3430
                        $myCourseList[$position] = $result_row;
3431
                    } else {
3432
                        $myCourseList[] = $result_row;
3433
                    }
3434
                    $courses[] = $result_row['real_id'];
3435
                }
3436
            }
3437
        }
3438
3439
        if (api_is_allowed_to_create_course()) {
3440
            $sql = "SELECT DISTINCT
3441
                        c.visibility, 
3442
                        c.id as real_id,
3443
                        c.code as course_code,
3444
                        sc.position
3445
                    FROM $tbl_session_course_user as scu
3446
                    INNER JOIN $tbl_session as s
3447
                    ON (scu.session_id = s.id)
3448
                    INNER JOIN $tbl_session_course sc
3449
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3450
                    INNER JOIN $tableCourse as c
3451
                    ON (scu.c_id = c.id)
3452
                    $join_access_url
3453
                    WHERE
3454
                      s.id = $session_id AND
3455
                      (
3456
                        (scu.user_id = $user_id AND scu.status = 2) OR
3457
                        s.id_coach = $user_id
3458
                      )
3459
                    $where_access_url
3460
                    ORDER BY sc.position ASC";
3461
            $result = Database::query($sql);
3462
3463
            if (Database::num_rows($result) > 0) {
3464
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3465
                    $result_row['status'] = 2;
3466
                    if (!in_array($result_row['real_id'], $courses)) {
3467
                        $position = $result_row['position'];
3468
                        if (!isset($myCourseList[$position])) {
3469
                            $myCourseList[$position] = $result_row;
3470
                        } else {
3471
                            $myCourseList[] = $result_row;
3472
                        }
3473
                        $courses[] = $result_row['real_id'];
3474
                    }
3475
                }
3476
            }
3477
        }
3478
3479
        if (api_is_drh()) {
3480
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3481
            $sessionList = array_keys($sessionList);
3482
            if (in_array($session_id, $sessionList)) {
3483
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3484
                if (!empty($courseList)) {
3485
                    foreach ($courseList as $course) {
3486
                        if (!in_array($course['id'], $courses)) {
3487
                            $position = $course['position'];
3488
                            if (!isset($myCourseList[$position])) {
3489
                                $myCourseList[$position] = $course;
3490
                            } else {
3491
                                $myCourseList[] = $course;
3492
                            }
3493
                        }
3494
                    }
3495
                }
3496
            }
3497
        } else {
3498
            //check if user is general coach for this session
3499
            $sessionInfo = api_get_session_info($session_id);
3500
            if ($sessionInfo['id_coach'] == $user_id) {
3501
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3502
                if (!empty($courseList)) {
3503
                    foreach ($courseList as $course) {
3504
                        if (!in_array($course['id'], $courses)) {
3505
                            $position = $course['position'];
3506
                            if (!isset($myCourseList[$position])) {
3507
                                $myCourseList[$position] = $course;
3508
                            } else {
3509
                                $myCourseList[] = $course;
3510
                            }
3511
                        }
3512
                    }
3513
                }
3514
            }
3515
        }
3516
3517
        if (!empty($myCourseList)) {
3518
            ksort($myCourseList);
3519
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
3520
            if (empty($checkPosition)) {
3521
                // The session course list doesn't have any position,
3522
                // then order the course list by course code
3523
                $list = array_column($myCourseList, 'course_code');
3524
                array_multisort($myCourseList, SORT_ASC, $list);
3525
            }
3526
        }
3527
3528
        return $myCourseList;
3529
    }
3530
3531
    /**
3532
     * Get user id from a username.
3533
     *
3534
     * @param string $username
3535
     *
3536
     * @return int User ID (or false if not found)
3537
     */
3538
    public static function get_user_id_from_username($username)
3539
    {
3540
        if (empty($username)) {
3541
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3542
        }
3543
        $username = trim($username);
3544
        $username = Database::escape_string($username);
3545
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3546
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3547
        $res = Database::query($sql);
3548
3549
        if ($res === false) {
3550
            return false;
3551
        }
3552
        if (Database::num_rows($res) !== 1) {
3553
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3554
        }
3555
        $row = Database::fetch_array($res);
3556
3557
        return $row['id'];
3558
    }
3559
3560
    /**
3561
     * Get the users files upload from his share_folder.
3562
     *
3563
     * @param string $user_id      User ID
3564
     * @param string $course       course directory
3565
     * @param string $resourceType resource type: images, all
3566
     *
3567
     * @return string
3568
     */
3569
    public static function get_user_upload_files_by_course(
3570
        $user_id,
3571
        $course,
3572
        $resourceType = 'all'
3573
    ) {
3574
        $return = '';
3575
        $user_id = (int) $user_id;
3576
3577
        if (!empty($user_id) && !empty($course)) {
3578
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3579
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3580
            $file_list = [];
3581
3582
            if (is_dir($path)) {
3583
                $handle = opendir($path);
3584
                while ($file = readdir($handle)) {
0 ignored issues
show
Bug introduced by
It seems like $handle can also be of type false; however, parameter $dir_handle of readdir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

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

5848
            Event::/** @scrutinizer ignore-call */ 
5849
                   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...
5849
5850
            // Logout the current user
5851
            self::loginDelete(api_get_user_id());
5852
5853
            Session::erase('_user');
5854
            Session::erase('is_platformAdmin');
5855
            Session::erase('is_allowedCreateCourse');
5856
            Session::erase('_uid');
5857
5858
            // Cleaning session variables
5859
            $_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...
5860
            $_user['lastName'] = $userInfo['lastname'];
5861
            $_user['mail'] = $userInfo['email'];
5862
            $_user['official_code'] = $userInfo['official_code'];
5863
            $_user['picture_uri'] = $userInfo['picture_uri'];
5864
            $_user['user_id'] = $userId;
5865
            $_user['id'] = $userId;
5866
            $_user['status'] = $userInfo['status'];
5867
5868
            // Filling session variables with new data
5869
            Session::write('_uid', $userId);
5870
            Session::write('_user', $userInfo);
5871
            Session::write('is_platformAdmin', (bool) self::is_admin($userId));
5872
            Session::write('is_allowedCreateCourse', $userInfo['status'] == 1);
5873
            // will be useful later to know if the user is actually an admin or not (example reporting)
5874
            Session::write('login_as', true);
5875
            $logInfo = [
5876
                'tool' => 'login',
5877
                'tool_id' => 0,
5878
                'tool_id_detail' => 0,
5879
                'info' => $userId,
5880
            ];
5881
            Event::registerLog($logInfo);
5882
5883
            return true;
5884
        }
5885
5886
        return false;
5887
    }
5888
5889
    /**
5890
     * Remove all login records from the track_e_online stats table,
5891
     * for the given user ID.
5892
     *
5893
     * @param int $userId User ID
5894
     */
5895
    public static function loginDelete($userId)
5896
    {
5897
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5898
        $userId = (int) $userId;
5899
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
5900
        Database::query($query);
5901
    }
5902
5903
    /**
5904
     * Login as first admin user registered in the platform.
5905
     *
5906
     * @return array
5907
     */
5908
    public static function logInAsFirstAdmin()
5909
    {
5910
        $adminList = self::get_all_administrators();
5911
5912
        if (!empty($adminList)) {
5913
            $userInfo = current($adminList);
5914
            if (!empty($userInfo)) {
5915
                $result = self::loginAsUser($userInfo['user_id'], false);
5916
                if ($result && api_is_platform_admin()) {
5917
                    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...
5918
                }
5919
            }
5920
        }
5921
5922
        return [];
5923
    }
5924
5925
    /**
5926
     * Check if user is teacher of a student based in their courses.
5927
     *
5928
     * @param $teacherId
5929
     * @param $studentId
5930
     *
5931
     * @return array
5932
     */
5933
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
5934
    {
5935
        $courses = CourseManager::getCoursesFollowedByUser(
5936
            $teacherId,
5937
            COURSEMANAGER
5938
        );
5939
        if (empty($courses)) {
5940
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
5941
        }
5942
5943
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
5944
        if (empty($coursesFromUser)) {
5945
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
5946
        }
5947
5948
        $coursesCodeList = array_column($courses, 'code');
5949
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
5950
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
5951
        $commonCourses = array_filter($commonCourses);
5952
5953
        if (!empty($commonCourses)) {
5954
            return $commonCourses;
5955
        }
5956
5957
        return [];
5958
    }
5959
5960
    /**
5961
     * @param int $teacherId
5962
     * @param int $studentId
5963
     *
5964
     * @return bool
5965
     */
5966
    public static function isTeacherOfStudent($teacherId, $studentId)
5967
    {
5968
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
5969
            $teacherId,
5970
            $studentId
5971
        );
5972
5973
        if (!empty($courses)) {
5974
            return true;
5975
        }
5976
5977
        return false;
5978
    }
5979
5980
    /**
5981
     * Send user confirmation mail.
5982
     *
5983
     * @param User $user
5984
     *
5985
     * @throws Exception
5986
     */
5987
    public static function sendUserConfirmationMail(User $user)
5988
    {
5989
        $uniqueId = api_get_unique_id();
5990
        $user->setConfirmationToken($uniqueId);
5991
5992
        Database::getManager()->persist($user);
5993
        Database::getManager()->flush();
5994
5995
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
5996
5997
        // Check if the user was originally set for an automated subscription to a course or session
5998
        $courseCodeToRedirect = Session::read('course_redirect');
5999
        $sessionToRedirect = Session::read('session_redirect');
6000
        if (!empty($courseCodeToRedirect)) {
6001
            $url .= '&c='.$courseCodeToRedirect;
6002
        }
6003
        if (!empty($sessionToRedirect)) {
6004
            $url .= '&s='.$sessionToRedirect;
6005
        }
6006
        $mailSubject = get_lang('RegistrationConfirmation');
6007
        $mailBody = get_lang('RegistrationConfirmationEmailMessage')
6008
            .PHP_EOL
6009
            .Display::url($url, $url);
6010
6011
        api_mail_html(
6012
            self::formatUserFullName($user),
6013
            $user->getEmail(),
6014
            $mailSubject,
6015
            $mailBody
6016
        );
6017
        Display::addFlash(Display::return_message(get_lang('CheckYourEmailAndFollowInstructions')));
6018
    }
6019
6020
    /**
6021
     * Anonymize a user. Replace personal info by anonymous info.
6022
     *
6023
     * @param int  $userId   User id
6024
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
6025
     *
6026
     * @throws \Exception
6027
     *
6028
     * @return bool
6029
     * @assert (0) === false
6030
     */
6031
    public static function anonymize($userId, $deleteIP = true)
6032
    {
6033
        global $debug;
6034
6035
        $userId = (int) $userId;
6036
6037
        if (empty($userId)) {
6038
            return false;
6039
        }
6040
6041
        $em = Database::getManager();
6042
        $user = api_get_user_entity($userId);
6043
        $uniqueId = uniqid('anon', true);
6044
        $user
6045
            ->setFirstname($uniqueId)
6046
            ->setLastname($uniqueId)
6047
            ->setBiography('')
6048
            ->setAddress('')
6049
            ->setCurriculumItems(null)
6050
            ->setDateOfBirth(null)
6051
            ->setCompetences('')
6052
            ->setDiplomas('')
6053
            ->setOpenarea('')
6054
            ->setTeach('')
6055
            ->setProductions(null)
6056
            ->setOpenid('')
6057
            ->setEmailCanonical($uniqueId.'@example.com')
6058
            ->setEmail($uniqueId.'@example.com')
6059
            ->setUsername($uniqueId)
6060
            ->setUsernameCanonical($uniqueId)
6061
            ->setPhone('')
6062
            ->setOfficialCode('')
6063
        ;
6064
6065
        self::deleteUserPicture($userId);
6066
        self::cleanUserRequestsOfRemoval($userId);
6067
6068
        // The IP address is a border-case personal data, as it does
6069
        // not directly allow for personal identification (it is not
6070
        // a completely safe value in most countries - the IP could
6071
        // be used by neighbours and crackers)
6072
        if ($deleteIP) {
6073
            $substitute = '127.0.0.1';
6074
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
6075
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
6076
            $res = Database::query($sql);
6077
            if ($res === false && $debug > 0) {
6078
                error_log("Could not anonymize IP address for user $userId ($sql)");
6079
            }
6080
6081
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6082
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6083
            $res = Database::query($sql);
6084
            if ($res === false && $debug > 0) {
6085
                error_log("Could not anonymize IP address for user $userId ($sql)");
6086
            }
6087
6088
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6089
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE exe_user_id = $userId";
6090
            $res = Database::query($sql);
6091
            if ($res === false && $debug > 0) {
6092
                error_log("Could not anonymize IP address for user $userId ($sql)");
6093
            }
6094
6095
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6096
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6097
            $res = Database::query($sql);
6098
            if ($res === false && $debug > 0) {
6099
                error_log("Could not anonymize IP address for user $userId ($sql)");
6100
            }
6101
6102
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6103
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6104
            $res = Database::query($sql);
6105
            if ($res === false && $debug > 0) {
6106
                error_log("Could not anonymize IP address for user $userId ($sql)");
6107
            }
6108
6109
            $table = Database::get_course_table(TABLE_WIKI);
6110
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6111
            $res = Database::query($sql);
6112
            if ($res === false && $debug > 0) {
6113
                error_log("Could not anonymize IP address for user $userId ($sql)");
6114
            }
6115
6116
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
6117
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
6118
            $res = Database::query($sql);
6119
            if ($res === false && $debug > 0) {
6120
                error_log("Could not anonymize IP address for user $userId ($sql)");
6121
            }
6122
6123
            $table = Database::get_course_table(TABLE_WIKI);
6124
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6125
            $res = Database::query($sql);
6126
            if ($res === false && $debug > 0) {
6127
                error_log("Could not anonymize IP address for user $userId ($sql)");
6128
            }
6129
        }
6130
        $em->persist($user);
6131
        $em->flush($user);
6132
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
6133
6134
        return true;
6135
    }
6136
6137
    /**
6138
     * @param int $userId
6139
     *
6140
     * @throws Exception
6141
     *
6142
     * @return string
6143
     */
6144
    public static function anonymizeUserWithVerification($userId)
6145
    {
6146
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
6147
6148
        $message = '';
6149
        if (api_is_platform_admin() ||
6150
            ($allowDelete && api_is_session_admin())
6151
        ) {
6152
            $userToUpdateInfo = api_get_user_info($userId);
6153
            $currentUserId = api_get_user_id();
6154
6155
            if ($userToUpdateInfo &&
6156
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
6157
            ) {
6158
                if ($userId != $currentUserId &&
6159
                    self::anonymize($userId)
6160
                ) {
6161
                    $message = Display::return_message(
6162
                        sprintf(get_lang('UserXAnonymized'), $userToUpdateInfo['complete_name_with_username']),
6163
                        'confirmation'
6164
                    );
6165
                } else {
6166
                    $message = Display::return_message(
6167
                        sprintf(get_lang('CannotAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
6168
                        'error'
6169
                    );
6170
                }
6171
            } else {
6172
                $message = Display::return_message(
6173
                    sprintf(get_lang('NoPermissionToAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
6174
                    'error'
6175
                );
6176
            }
6177
        }
6178
6179
        return $message;
6180
    }
6181
6182
    /**
6183
     * @param int $userId
6184
     *
6185
     * @throws Exception
6186
     *
6187
     * @return string
6188
     */
6189
    public static function deleteUserWithVerification($userId)
6190
    {
6191
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
6192
        $message = Display::return_message(get_lang('CannotDeleteUser'), 'error');
6193
        $userToUpdateInfo = api_get_user_info($userId);
6194
6195
        // User must exist.
6196
        if (empty($userToUpdateInfo)) {
6197
            return $message;
6198
        }
6199
6200
        $currentUserId = api_get_user_id();
6201
6202
        // Cannot delete myself.
6203
        if ($userId == $currentUserId) {
6204
            return $message;
6205
        }
6206
6207
        if (api_is_platform_admin() ||
6208
            ($allowDelete && api_is_session_admin())
6209
        ) {
6210
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
6211
                if (self::delete_user($userId)) {
6212
                    $message = Display::return_message(
6213
                        get_lang('UserDeleted').': '.$userToUpdateInfo['complete_name_with_username'],
6214
                        'confirmation'
6215
                    );
6216
                } else {
6217
                    $message = Display::return_message(get_lang('CannotDeleteUserBecauseOwnsCourse'), 'error');
6218
                }
6219
            }
6220
        }
6221
6222
        return $message;
6223
    }
6224
6225
    /**
6226
     * @return array
6227
     */
6228
    public static function createDataPrivacyExtraFields()
6229
    {
6230
        self::create_extra_field(
6231
            'request_for_legal_agreement_consent_removal_justification',
6232
            1, //text
6233
            'Request for legal agreement consent removal justification	',
6234
            ''
6235
        );
6236
6237
        self::create_extra_field(
6238
            'request_for_delete_account_justification',
6239
            1, //text
6240
            'Request for delete account justification',
6241
            ''
6242
        );
6243
6244
        $extraFieldId = self::create_extra_field(
6245
            'request_for_legal_agreement_consent_removal',
6246
            1, //text
6247
            'Request for legal agreement consent removal',
6248
            ''
6249
        );
6250
6251
        $extraFieldIdDeleteAccount = self::create_extra_field(
6252
            'request_for_delete_account',
6253
            1, //text
6254
            'Request for delete user account',
6255
            ''
6256
        );
6257
6258
        return [
6259
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
6260
            'delete_legal' => $extraFieldId,
6261
        ];
6262
    }
6263
6264
    /**
6265
     * @param int $userId
6266
     */
6267
    public static function cleanUserRequestsOfRemoval($userId)
6268
    {
6269
        $userId = (int) $userId;
6270
6271
        $extraFieldValue = new ExtraFieldValue('user');
6272
        $extraFieldsToDelete = [
6273
            'legal_accept',
6274
            'request_for_legal_agreement_consent_removal',
6275
            'request_for_legal_agreement_consent_removal_justification',
6276
            'request_for_delete_account_justification', // just in case delete also this
6277
            'request_for_delete_account',
6278
        ];
6279
6280
        foreach ($extraFieldsToDelete as $variable) {
6281
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
6282
                $userId,
6283
                $variable
6284
            );
6285
            if ($value && isset($value['id'])) {
6286
                $extraFieldValue->delete($value['id']);
6287
            }
6288
        }
6289
    }
6290
6291
    /**
6292
     * @param array $userInfo
6293
     * @param int   $searchYear
6294
     *
6295
     * @throws Exception
6296
     *
6297
     * @return array
6298
     */
6299
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
6300
    {
6301
        $timezone = new DateTimeZone(api_get_timezone());
6302
6303
        $sessions = [];
6304
        if (DRH == $userInfo['status']) {
6305
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
6306
        } elseif (api_is_platform_admin(true)) {
6307
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
6308
        } else {
6309
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
6310
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
6311
6312
            foreach ($sessionsByCategory as $sessionsInCategory) {
6313
                $sessions = array_merge($sessions, $sessionsInCategory);
6314
            }
6315
        }
6316
6317
        $sessions = array_map(
6318
            function ($sessionInfo) {
6319
                if (!isset($sessionInfo['session_id'])) {
6320
                    $sessionInfo['session_id'] = $sessionInfo['id'];
6321
                }
6322
                if (!isset($sessionInfo['session_name'])) {
6323
                    $sessionInfo['session_name'] = $sessionInfo['name'];
6324
                }
6325
6326
                return $sessionInfo;
6327
            },
6328
            $sessions
6329
        );
6330
6331
        $calendarSessions = [];
6332
6333
        foreach ($sessions as $sessionInfo) {
6334
            if (!empty($sessionInfo['duration'])) {
6335
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
6336
                    $sessionInfo['session_id'],
6337
                    $userInfo['id']
6338
                );
6339
6340
                if (empty($courseAccess)) {
6341
                    continue;
6342
                }
6343
6344
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
6345
                $lastAccessDate = clone $firstAcessDate;
6346
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
6347
6348
                $firstAccessYear = (int) $firstAcessDate->format('Y');
6349
                $lastAccessYear = (int) $lastAccessDate->format('Y');
6350
6351
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
6352
                    $calendarSessions[$sessionInfo['session_id']] = [
6353
                        'name' => $sessionInfo['session_name'],
6354
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
6355
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
6356
                    ];
6357
                }
6358
6359
                continue;
6360
            }
6361
6362
            $accessStartDate = !empty($sessionInfo['access_start_date'])
6363
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6364
                : null;
6365
            $accessEndDate = !empty($sessionInfo['access_end_date'])
6366
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6367
                : null;
6368
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
6369
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
6370
6371
            $isValid = false;
6372
6373
            if ($accessStartYear && $accessEndYear) {
6374
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
6375
                    $isValid = true;
6376
                }
6377
            }
6378
6379
            if ($accessStartYear && !$accessEndYear) {
6380
                if ($accessStartYear == $searchYear) {
6381
                    $isValid = true;
6382
                }
6383
            }
6384
6385
            if (!$accessStartYear && $accessEndYear) {
6386
                if ($accessEndYear == $searchYear) {
6387
                    $isValid = true;
6388
                }
6389
            }
6390
6391
            if ($isValid) {
6392
                $calendarSessions[$sessionInfo['session_id']] = [
6393
                    'name' => $sessionInfo['session_name'],
6394
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
6395
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
6396
                ];
6397
            }
6398
        }
6399
6400
        return $calendarSessions;
6401
    }
6402
6403
    /**
6404
     * Get sessions info for planification calendar.
6405
     *
6406
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
6407
     * @param int   $searchYear
6408
     *
6409
     * @throws Exception
6410
     *
6411
     * @return array
6412
     */
6413
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
6414
    {
6415
        $timezone = new DateTimeZone(api_get_timezone());
6416
        $calendar = [];
6417
6418
        foreach ($sessionsList as $sessionId => $sessionInfo) {
6419
            $startDate = $sessionInfo['access_start_date']
6420
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6421
                : null;
6422
            $endDate = $sessionInfo['access_end_date']
6423
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6424
                : null;
6425
6426
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
6427
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
6428
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
6429
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
6430
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
6431
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
6432
6433
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
6434
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
6435
6436
            $calendar[] = [
6437
                'id' => $sessionId,
6438
                'name' => $sessionInfo['name'],
6439
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
0 ignored issues
show
Bug introduced by
It seems like $startDate can also be of type DateTime; however, parameter $startDate of SessionManager::convertSessionDateToString() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

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

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

6439
                'human_date' => SessionManager::convertSessionDateToString($startDate, /** @scrutinizer ignore-type */ $endDate, false, true),
Loading history...
6440
                'start_in_last_year' => $startYear < $searchYear,
6441
                'end_in_next_year' => $endYear > $searchYear,
6442
                'no_start' => !$startWeek,
6443
                'no_end' => !$endWeek,
6444
                'start' => $start,
6445
                'duration' => $duration > 0 ? $duration : 1,
6446
            ];
6447
        }
6448
6449
        usort(
6450
            $calendar,
6451
            function ($sA, $sB) {
6452
                if ($sA['start'] == $sB['start']) {
6453
                    return 0;
6454
                }
6455
6456
                if ($sA['start'] < $sB['start']) {
6457
                    return -1;
6458
                }
6459
6460
                return 1;
6461
            }
6462
        );
6463
6464
        return $calendar;
6465
    }
6466
6467
    /**
6468
     * Return the user's full name. Optionally with the username.
6469
     *
6470
     * @param User $user
6471
     * @param bool $includeUsername Optional. By default username is not included.
6472
     *
6473
     * @return string
6474
     */
6475
    public static function formatUserFullName(User $user, $includeUsername = false): string
6476
    {
6477
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
6478
6479
        if ($includeUsername && api_get_setting('profile.hide_username_with_complete_name') === 'false') {
6480
            $username = $user->getUsername();
6481
6482
            return "$fullName ($username)";
6483
        }
6484
6485
        return $fullName;
6486
    }
6487
6488
    /**
6489
     * @param int $userId
6490
     *
6491
     * @return array
6492
     */
6493
    public static function getUserCareers($userId)
6494
    {
6495
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6496
        $tableCareer = Database::get_main_table(TABLE_CAREER);
6497
        $userId = (int) $userId;
6498
6499
        $sql = "SELECT c.id, c.name 
6500
                FROM $table uc 
6501
                INNER JOIN $tableCareer c 
6502
                ON uc.career_id = c.id 
6503
                WHERE user_id = $userId 
6504
                ORDER BY uc.created_at
6505
                ";
6506
        $result = Database::query($sql);
6507
6508
        return Database::store_result($result, 'ASSOC');
6509
    }
6510
6511
    /**
6512
     * @param int $userId
6513
     * @param int $careerId
6514
     */
6515
    public static function addUserCareer($userId, $careerId)
6516
    {
6517
        if (!api_get_configuration_value('allow_career_users')) {
6518
            return false;
6519
        }
6520
6521
        if (self::userHasCareer($userId, $careerId) === false) {
6522
            $params = ['user_id' => $userId, 'career_id' => $careerId, 'created_at' => api_get_utc_datetime(), 'updated_at' => api_get_utc_datetime()];
6523
            $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6524
            Database::insert($table, $params);
6525
        }
6526
6527
        return true;
6528
    }
6529
6530
    /**
6531
     * @param int   $userCareerId
6532
     * @param array $data
6533
     *
6534
     * @return bool
6535
     */
6536
    public static function updateUserCareer($userCareerId, $data)
6537
    {
6538
        if (!api_get_configuration_value('allow_career_users')) {
6539
            return false;
6540
        }
6541
6542
        $params = ['extra_data' => $data, 'updated_at' => api_get_utc_datetime()];
6543
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6544
        Database::update(
6545
            $table,
6546
            $params,
6547
            ['id = ?' => (int) $userCareerId]
6548
        );
6549
6550
        return true;
6551
    }
6552
6553
    /**
6554
     * @param int $userId
6555
     * @param int $careerId
6556
     *
6557
     * @return array
6558
     */
6559
    public static function getUserCareer($userId, $careerId)
6560
    {
6561
        $userId = (int) $userId;
6562
        $careerId = (int) $careerId;
6563
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6564
6565
        $sql = "SELECT * FROM $table WHERE user_id = $userId AND career_id = $careerId";
6566
        $result = Database::query($sql);
6567
6568
        return Database::fetch_array($result, 'ASSOC');
6569
    }
6570
6571
    /**
6572
     * @param int $userId
6573
     * @param int $careerId
6574
     *
6575
     * @return bool
6576
     */
6577
    public static function userHasCareer($userId, $careerId)
6578
    {
6579
        $userId = (int) $userId;
6580
        $careerId = (int) $careerId;
6581
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6582
6583
        $sql = "SELECT id FROM $table WHERE user_id = $userId AND career_id = $careerId";
6584
        $result = Database::query($sql);
6585
6586
        return Database::num_rows($result) > 0;
6587
    }
6588
6589
    /**
6590
     * @return EncoderFactory
6591
     */
6592
    private static function getEncoderFactory()
6593
    {
6594
        $encryption = self::getPasswordEncryption();
6595
        $encoders = [
6596
            'Chamilo\\UserBundle\\Entity\\User' => new \Chamilo\UserBundle\Security\Encoder($encryption),
0 ignored issues
show
Bug introduced by
It seems like $encryption can also be of type boolean; however, parameter $passwordEncrypt of Chamilo\UserBundle\Security\Encoder::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

6596
            'Chamilo\\UserBundle\\Entity\\User' => new \Chamilo\UserBundle\Security\Encoder(/** @scrutinizer ignore-type */ $encryption),
Loading history...
6597
        ];
6598
6599
        $encoderFactory = new EncoderFactory($encoders);
6600
6601
        return $encoderFactory;
6602
    }
6603
6604
    /**
6605
     * @param User $user
6606
     *
6607
     * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface
6608
     */
6609
    private static function getEncoder(User $user)
6610
    {
6611
        $encoderFactory = self::getEncoderFactory();
6612
6613
        return $encoderFactory->getEncoder($user);
6614
    }
6615
6616
    /**
6617
     * Disables or enables a user.
6618
     *
6619
     * @param int $user_id
6620
     * @param int $active  Enable or disable
6621
     *
6622
     * @return bool True on success, false on failure
6623
     * @assert (-1,0) === false
6624
     * @assert (1,1) === true
6625
     */
6626
    private static function change_active_state($user_id, $active)
6627
    {
6628
        $user_id = (int) $user_id;
6629
        $active = (int) $active;
6630
6631
        if (empty($user_id)) {
6632
            return false;
6633
        }
6634
6635
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6636
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
6637
        $r = Database::query($sql);
6638
        $ev = LOG_USER_DISABLE;
6639
        if ($active == 1) {
6640
            $ev = LOG_USER_ENABLE;
6641
        }
6642
        if ($r !== false) {
6643
            Event::addEvent($ev, LOG_USER_ID, $user_id);
6644
        }
6645
6646
        return $r;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $r returns the type Doctrine\DBAL\Driver\Statement which is incompatible with the documented return type boolean.
Loading history...
6647
    }
6648
6649
    /**
6650
     * Get either a Gravatar URL or complete image tag for a specified email address.
6651
     *
6652
     * @param string $email The email address
6653
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
6654
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
6655
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
6656
     * @param bool   $img   True to return a complete IMG tag False for just the URL
6657
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
6658
     *
6659
     * @return string containing either just a URL or a complete image tag
6660
     * @source http://gravatar.com/site/implement/images/php/
6661
     */
6662
    private static function getGravatar(
6663
        $email,
6664
        $s = 80,
6665
        $d = 'mm',
6666
        $r = 'g',
6667
        $img = false,
6668
        $atts = []
6669
    ) {
6670
        $url = 'http://www.gravatar.com/avatar/';
6671
        if (!empty($_SERVER['HTTPS'])) {
6672
            $url = 'https://secure.gravatar.com/avatar/';
6673
        }
6674
        $url .= md5(strtolower(trim($email)));
6675
        $url .= "?s=$s&d=$d&r=$r";
6676
        if ($img) {
6677
            $url = '<img src="'.$url.'"';
6678
            foreach ($atts as $key => $val) {
6679
                $url .= ' '.$key.'="'.$val.'"';
6680
            }
6681
            $url .= ' />';
6682
        }
6683
6684
        return $url;
6685
    }
6686
}
6687