Completed
Push — master ( 8b2aa0...562606 )
by Julito
09:06
created

UserManager::getExtraFilterableFields()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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

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

6430
                '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

6430
                'human_date' => SessionManager::convertSessionDateToString($startDate, /** @scrutinizer ignore-type */ $endDate, false, true),
Loading history...
6431
                'start_in_last_year' => $startYear < $searchYear,
6432
                'end_in_next_year' => $endYear > $searchYear,
6433
                'no_start' => !$startWeek,
6434
                'no_end' => !$endWeek,
6435
                'start' => $start,
6436
                'duration' => $duration > 0 ? $duration : 1,
6437
            ];
6438
        }
6439
6440
        usort(
6441
            $calendar,
6442
            function ($sA, $sB) {
6443
                if ($sA['start'] == $sB['start']) {
6444
                    return 0;
6445
                }
6446
6447
                if ($sA['start'] < $sB['start']) {
6448
                    return -1;
6449
                }
6450
6451
                return 1;
6452
            }
6453
        );
6454
6455
        return $calendar;
6456
    }
6457
6458
    /**
6459
     * Return the user's full name. Optionally with the username.
6460
     *
6461
     * @param User $user
6462
     * @param bool $includeUsername Optional. By default username is not included.
6463
     *
6464
     * @return string
6465
     */
6466
    public static function formatUserFullName(User $user, $includeUsername = false): string
6467
    {
6468
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
6469
6470
        if ($includeUsername && api_get_setting('profile.hide_username_with_complete_name') === 'false') {
6471
            $username = $user->getUsername();
6472
6473
            return "$fullName ($username)";
6474
        }
6475
6476
        return $fullName;
6477
    }
6478
6479
    /**
6480
     * Disables or enables a user.
6481
     *
6482
     * @param int $user_id
6483
     * @param int $active  Enable or disable
6484
     *
6485
     * @return bool True on success, false on failure
6486
     * @assert (-1,0) === false
6487
     * @assert (1,1) === true
6488
     */
6489
    private static function change_active_state($user_id, $active)
6490
    {
6491
        $user_id = (int) $user_id;
6492
        $active = (int) $active;
6493
6494
        if (empty($user_id)) {
6495
            return false;
6496
        }
6497
6498
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6499
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
6500
        $r = Database::query($sql);
6501
        $ev = LOG_USER_DISABLE;
6502
        if ($active == 1) {
6503
            $ev = LOG_USER_ENABLE;
6504
        }
6505
        if ($r !== false) {
6506
            Event::addEvent($ev, LOG_USER_ID, $user_id);
6507
        }
6508
6509
        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...
6510
    }
6511
6512
    /**
6513
     * Get either a Gravatar URL or complete image tag for a specified email address.
6514
     *
6515
     * @param string $email The email address
6516
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
6517
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
6518
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
6519
     * @param bool   $img   True to return a complete IMG tag False for just the URL
6520
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
6521
     *
6522
     * @return string containing either just a URL or a complete image tag
6523
     * @source http://gravatar.com/site/implement/images/php/
6524
     */
6525
    private static function getGravatar(
6526
        $email,
6527
        $s = 80,
6528
        $d = 'mm',
6529
        $r = 'g',
6530
        $img = false,
6531
        $atts = []
6532
    ) {
6533
        $url = 'http://www.gravatar.com/avatar/';
6534
        if (!empty($_SERVER['HTTPS'])) {
6535
            $url = 'https://secure.gravatar.com/avatar/';
6536
        }
6537
        $url .= md5(strtolower(trim($email)));
6538
        $url .= "?s=$s&d=$d&r=$r";
6539
        if ($img) {
6540
            $url = '<img src="'.$url.'"';
6541
            foreach ($atts as $key => $val) {
6542
                $url .= ' '.$key.'="'.$val.'"';
6543
            }
6544
            $url .= ' />';
6545
        }
6546
6547
        return $url;
6548
    }
6549
}
6550