Passed
Push — master ( d0b226...f5358a )
by Julito
10:41
created

UserManager::get_all_administrators()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 29
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 20
nc 4
nop 0
dl 0
loc 29
rs 9.6
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
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
13
14
/**
15
 * Class UserManager.
16
 *
17
 * This library provides functions for user management.
18
 * Include/require it in your code to use its functionality.
19
 *
20
 * @package chamilo.library
21
 *
22
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
23
 */
24
class UserManager
25
{
26
    // This constants are deprecated use the constants located in ExtraField
27
    public const USER_FIELD_TYPE_TEXT = 1;
28
    public const USER_FIELD_TYPE_TEXTAREA = 2;
29
    public const USER_FIELD_TYPE_RADIO = 3;
30
    public const USER_FIELD_TYPE_SELECT = 4;
31
    public const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
32
    public const USER_FIELD_TYPE_DATE = 6;
33
    public const USER_FIELD_TYPE_DATETIME = 7;
34
    public const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
35
    public const USER_FIELD_TYPE_DIVIDER = 9;
36
    public const USER_FIELD_TYPE_TAG = 10;
37
    public const USER_FIELD_TYPE_TIMEZONE = 11;
38
    public const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
39
    public const USER_FIELD_TYPE_FILE = 13;
40
    public const USER_FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
41
42
    private static $encryptionMethod;
43
44
    /**
45
     * Constructor.
46
     *
47
     * @assert () === null
48
     */
49
    public function __construct()
50
    {
51
    }
52
53
    /**
54
     * Repository is use to query the DB, selects, etc.
55
     *
56
     * @return UserRepository
57
     */
58
    public static function getRepository()
59
    {
60
        return Database::getManager()->getRepository('ChamiloUserBundle:User');
61
    }
62
63
    /**
64
     * Create/update/delete methods are available in the UserManager
65
     * (based in the Sonata\UserBundle\Entity\UserManager).
66
     *
67
     * @return \Sonata\UserBundle\Entity\UserManager
68
     */
69
    public static function getManager()
70
    {
71
        return Container::getUserManager();
72
    }
73
74
    /**
75
     * @param string $encryptionMethod
76
     */
77
    public static function setPasswordEncryption($encryptionMethod)
78
    {
79
        self::$encryptionMethod = $encryptionMethod;
80
    }
81
82
    /**
83
     * @return bool|mixed
84
     */
85
    public static function getPasswordEncryption()
86
    {
87
        $encryptionMethod = self::$encryptionMethod;
88
        if (empty($encryptionMethod)) {
89
            $encryptionMethod = api_get_configuration_value('password_encryption');
90
        }
91
92
        return $encryptionMethod;
93
    }
94
95
    /**
96
     * Validates the password.
97
     *
98
     * @param $encoded
99
     * @param $raw
100
     * @param $salt
101
     *
102
     * @return bool
103
     */
104
    public static function isPasswordValid($encoded, $raw, $salt)
105
    {
106
        $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

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

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

354
            $user->setExpirationDate(/** @scrutinizer ignore-type */ $expirationDate);
Loading history...
355
        }
356
        try {
357
            $userManager->updateUser($user);
358
            $userId = $user->getId();
359
360
            // Add user to a group
361
            $statusToGroup = [
362
                COURSEMANAGER => 'TEACHER',
363
                STUDENT => 'STUDENT',
364
                DRH => 'RRHH',
365
                SESSIONADMIN => 'SESSION_ADMIN',
366
                STUDENT_BOSS => 'STUDENT_BOSS',
367
                INVITEE => 'INVITEE',
368
            ];
369
370
            $group = Container::$container->get('fos_user.group_manager')->findGroupBy(['code' => $statusToGroup[$status]]);
371
            if ($group) {
372
                $user->addGroup($group);
373
                $userManager->updateUser($user);
374
            }
375
        } catch (Exception $e) {
376
            error_log($e->getMessage());
377
        }
378
379
        if (!empty($userId)) {
380
            $return = $userId;
381
            $sql = "UPDATE $table_user SET user_id = $return WHERE id = $return";
382
            Database::query($sql);
383
384
            if ($isAdmin) {
385
                self::add_user_as_admin($user);
386
            }
387
388
            if (api_get_multiple_access_url()) {
389
                UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
390
            } else {
391
                //we are adding by default the access_url_user table with access_url_id = 1
392
                UrlManager::add_user_to_url($userId, 1);
393
            }
394
395
            $extra['item_id'] = $userId;
396
397
            if (is_array($extra) && count($extra) > 0) {
398
                $courseFieldValue = new ExtraFieldValue('user');
399
                $courseFieldValue->saveFieldValues($extra);
400
            } else {
401
                // Create notify settings by default
402
                self::update_extra_field_value(
403
                    $userId,
404
                    'mail_notify_invitation',
405
                    '1'
406
                );
407
                self::update_extra_field_value(
408
                    $userId,
409
                    'mail_notify_message',
410
                    '1'
411
                );
412
                self::update_extra_field_value(
413
                    $userId,
414
                    'mail_notify_group_message',
415
                    '1'
416
                );
417
            }
418
419
            self::update_extra_field_value(
420
                $userId,
421
                'already_logged_in',
422
                'false'
423
            );
424
425
            if (!empty($email) && $send_mail) {
426
                $recipient_name = api_get_person_name(
427
                    $firstName,
428
                    $lastName,
429
                    null,
430
                    PERSON_NAME_EMAIL_ADDRESS
431
                );
432
                $tplSubject = new Template(
433
                    null,
434
                    false,
435
                    false,
436
                    false,
437
                    false,
438
                    false
439
                );
440
                $layoutSubject = $tplSubject->get_template('mail/subject_registration_platform.tpl');
441
                $emailSubject = $tplSubject->fetch($layoutSubject);
442
                $sender_name = api_get_person_name(
443
                    api_get_setting('administratorName'),
444
                    api_get_setting('administratorSurname'),
445
                    null,
446
                    PERSON_NAME_EMAIL_ADDRESS
447
                );
448
                $email_admin = api_get_setting('emailAdministrator');
449
450
                $url = api_get_path(WEB_PATH);
451
                if (api_is_multiple_url_enabled()) {
452
                    $access_url_id = api_get_current_access_url_id();
453
                    if ($access_url_id != -1) {
454
                        $urlInfo = api_get_access_url($access_url_id);
455
                        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...
456
                            $url = $urlInfo['url'];
457
                        }
458
                    }
459
                }
460
461
                $tplContent = new Template(
462
                    null,
463
                    false,
464
                    false,
465
                    false,
466
                    false,
467
                    false
468
                );
469
                // variables for the default template
470
                $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
471
                $tplContent->assign('login_name', $loginName);
472
                $tplContent->assign('original_password', stripslashes($original_password));
473
                $tplContent->assign('mailWebPath', $url);
474
                $tplContent->assign('new_user', $user);
475
476
                $layoutContent = $tplContent->get_template('mail/content_registration_platform.tpl');
477
                $emailBody = $tplContent->fetch($layoutContent);
478
479
                $userInfo = api_get_user_info($userId);
480
                $mailTemplateManager = new MailTemplateManager();
481
482
                $phoneNumber = isset($extra['mobile_phone_number']) ? $extra['mobile_phone_number'] : null;
483
484
                $additionalParameters = [
485
                    'smsType' => SmsPlugin::WELCOME_LOGIN_PASSWORD,
486
                    'userId' => $return,
487
                    'mobilePhoneNumber' => $phoneNumber,
488
                    'password' => $original_password,
489
                ];
490
491
                $emailBodyTemplate = '';
492
                if (!empty($emailTemplate)) {
493
                    if (isset($emailTemplate['content_registration_platform.tpl']) &&
494
                        !empty($emailTemplate['content_registration_platform.tpl'])
495
                    ) {
496
                        $emailBodyTemplate = $mailTemplateManager->parseTemplate(
497
                            $emailTemplate['content_registration_platform.tpl'],
498
                            $userInfo
499
                        );
500
                    }
501
                }
502
503
                $twoEmail = api_get_configuration_value('send_two_inscription_confirmation_mail');
504
                if ($twoEmail === true) {
505
                    $layoutContent = $tplContent->get_template('mail/new_user_first_email_confirmation.tpl');
506
                    $emailBody = $tplContent->fetch($layoutContent);
507
508
                    if (!empty($emailBodyTemplate) &&
509
                        isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
510
                        !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
511
                    ) {
512
                        $emailBody = $mailTemplateManager->parseTemplate(
513
                            $emailTemplate['new_user_first_email_confirmation.tpl'],
514
                            $userInfo
515
                        );
516
                    }
517
518
                    api_mail_html(
519
                        $recipient_name,
520
                        $email,
521
                        $emailSubject,
522
                        $emailBody,
523
                        $sender_name,
524
                        $email_admin,
525
                        null,
526
                        null,
527
                        null,
528
                        $additionalParameters
529
                    );
530
531
                    $layoutContent = $tplContent->get_template('mail/new_user_second_email_confirmation.tpl');
532
                    $emailBody = $tplContent->fetch($layoutContent);
533
534
                    if (!empty($emailBodyTemplate) &&
535
                        isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
536
                        !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
537
                    ) {
538
                        $emailBody = $mailTemplateManager->parseTemplate(
539
                            $emailTemplate['new_user_second_email_confirmation.tpl'],
540
                            $userInfo
541
                        );
542
                    }
543
544
                    api_mail_html(
545
                        $recipient_name,
546
                        $email,
547
                        $emailSubject,
548
                        $emailBody,
549
                        $sender_name,
550
                        $email_admin,
551
                        null,
552
                        null,
553
                        null,
554
                        $additionalParameters
555
                    );
556
                } else {
557
                    if (!empty($emailBodyTemplate)) {
558
                        $emailBody = $emailBodyTemplate;
559
                    }
560
                    $sendToInbox = api_get_configuration_value('send_inscription_msg_to_inbox');
561
                    if ($sendToInbox) {
562
                        $adminList = self::get_all_administrators();
563
                        $senderId = 1;
564
                        if (!empty($adminList)) {
565
                            $adminInfo = current($adminList);
566
                            $senderId = $adminInfo['user_id'];
567
                        }
568
569
                        MessageManager::send_message_simple(
570
                            $userId,
571
                            $emailSubject,
572
                            $emailBody,
573
                            $senderId
574
                        );
575
                    } else {
576
                        api_mail_html(
577
                            $recipient_name,
578
                            $email,
579
                            $emailSubject,
580
                            $emailBody,
581
                            $sender_name,
582
                            $email_admin,
583
                            null,
584
                            null,
585
                            null,
586
                            $additionalParameters
587
                        );
588
                    }
589
                }
590
591
                $notification = api_get_configuration_value('send_notification_when_user_added');
592
                if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
593
                    foreach ($notification['admins'] as $adminId) {
594
                        $emailSubjectToAdmin = get_lang('UserAdded').': '.api_get_person_name($firstName, $lastName);
595
                        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

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

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

1104
        /** @scrutinizer ignore-unused */ $creator_id = null,

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...
1105
        $hr_dept_id = 0,
1106
        $extra = null,
1107
        $language = 'english',
1108
        $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

1108
        /** @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...
1109
        $send_email = false,
1110
        $reset_password = 0,
1111
        $address = null,
1112
        $emailTemplate = []
1113
    ) {
1114
        $hook = HookUpdateUser::create();
1115
        if (!empty($hook)) {
1116
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_PRE);
1117
        }
1118
        $original_password = $password;
1119
1120
        if ($user_id != strval(intval($user_id))) {
1121
            return false;
1122
        }
1123
1124
        if (empty($user_id)) {
1125
            return false;
1126
        }
1127
1128
        $userManager = self::getManager();
1129
        /** @var User $user */
1130
        $user = self::getRepository()->find($user_id);
1131
1132
        if (empty($user)) {
1133
            return false;
1134
        }
1135
1136
        if ($reset_password == 0) {
1137
            $password = null;
1138
            $auth_source = $user->getAuthSource();
1139
        } elseif ($reset_password == 1) {
1140
            $original_password = $password = api_generate_password();
1141
            $auth_source = PLATFORM_AUTH_SOURCE;
1142
        } elseif ($reset_password == 2) {
1143
            $password = $password;
1144
            $auth_source = PLATFORM_AUTH_SOURCE;
1145
        } elseif ($reset_password == 3) {
1146
            $password = $password;
1147
            $auth_source = $auth_source;
1148
        }
1149
1150
        // Checking the user language
1151
        $languages = array_keys(api_get_languages());
1152
        if (!in_array($language, $languages)) {
1153
            $language = api_get_setting('platformLanguage');
1154
        }
1155
1156
        $change_active = 0;
1157
        $isUserActive = $user->getActive();
1158
        if ($isUserActive != $active) {
1159
            $change_active = 1;
1160
        }
1161
1162
        $originalUsername = $user->getUsername();
1163
1164
        // If username is different from original then check if it exists.
1165
        if ($originalUsername !== $username) {
1166
            $available = self::is_username_available($username);
1167
            if ($available === false) {
1168
                return false;
1169
            }
1170
        }
1171
1172
        if (!empty($expiration_date)) {
1173
            $expiration_date = api_get_utc_datetime($expiration_date);
1174
            $expiration_date = new \DateTime(
1175
                $expiration_date,
1176
                new DateTimeZone('UTC')
1177
            );
1178
        }
1179
1180
        $user
1181
            ->setLastname($lastname)
1182
            ->setFirstname($firstname)
1183
            ->setUsername($username)
1184
            ->setStatus($status)
1185
            ->setAuthSource($auth_source)
1186
            ->setLanguage($language)
1187
            ->setLocale($language)
1188
            ->setEmail($email)
1189
            ->setOfficialCode($official_code)
1190
            ->setPhone($phone)
1191
            ->setAddress($address)
1192
            ->setPictureUri($picture_uri)
1193
            ->setExpirationDate($expiration_date)
1194
            ->setActive($active)
1195
            ->setEnabled($active)
1196
            ->setHrDeptId($hr_dept_id)
1197
        ;
1198
1199
        if (!is_null($password)) {
1200
            $user->setPlainPassword($password);
1201
        }
1202
1203
        $userManager->updateUser($user, true);
1204
1205
        if ($change_active == 1) {
1206
            if ($active == 1) {
1207
                $event_title = LOG_USER_ENABLE;
1208
            } else {
1209
                $event_title = LOG_USER_DISABLE;
1210
            }
1211
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
1212
        }
1213
1214
        if (is_array($extra) && count($extra) > 0) {
1215
            $res = true;
1216
            foreach ($extra as $fname => $fvalue) {
1217
                $res = $res && self::update_extra_field_value(
1218
                    $user_id,
1219
                    $fname,
1220
                    $fvalue
1221
                );
1222
            }
1223
        }
1224
1225
        if (!empty($email) && $send_email) {
1226
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
1227
            $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('YourReg').' '.api_get_setting('siteName');
1228
            $sender_name = api_get_person_name(
1229
                api_get_setting('administratorName'),
1230
                api_get_setting('administratorSurname'),
1231
                null,
1232
                PERSON_NAME_EMAIL_ADDRESS
1233
            );
1234
            $email_admin = api_get_setting('emailAdministrator');
1235
            $url = api_get_path(WEB_PATH);
1236
            if (api_is_multiple_url_enabled()) {
1237
                $access_url_id = api_get_current_access_url_id();
1238
                if ($access_url_id != -1) {
1239
                    $url = api_get_access_url($access_url_id);
1240
                    $url = $url['url'];
1241
                }
1242
            }
1243
1244
            $tplContent = new Template(
1245
                null,
1246
                false,
1247
                false,
1248
                false,
1249
                false,
1250
                false
1251
            );
1252
            // variables for the default template
1253
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
1254
            $tplContent->assign('login_name', $username);
1255
1256
            $originalPassword = '';
1257
            if ($reset_password > 0) {
1258
                $originalPassword = stripslashes($original_password);
1259
            }
1260
            $tplContent->assign('original_password', $originalPassword);
1261
            $tplContent->assign('portal_url', $url);
1262
1263
            $layoutContent = $tplContent->get_template('mail/user_edit_content.tpl');
1264
            $emailBody = $tplContent->fetch($layoutContent);
1265
1266
            $mailTemplateManager = new MailTemplateManager();
1267
1268
            if (!empty($emailTemplate) &&
1269
                isset($emailTemplate['user_edit_content.tpl']) &&
1270
                !empty($emailTemplate['user_edit_content.tpl'])
1271
            ) {
1272
                $userInfo = api_get_user_info($user_id);
1273
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], $userInfo);
1274
            }
1275
            api_mail_html(
1276
                $recipient_name,
1277
                $email,
1278
                $emailsubject,
1279
                $emailBody,
1280
                $sender_name,
1281
                $email_admin
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
        $sql = "SELECT item_id as user_id
1360
                FROM $t_uf uf
1361
                INNER JOIN $t_ufv ufv
1362
                ON ufv.field_id=uf.id
1363
                WHERE
1364
                    variable='$original_user_id_name' AND
1365
                    value='$original_user_id_value' AND
1366
                    extra_field_type = $extraFieldType
1367
                ";
1368
        $res = Database::query($sql);
1369
        $row = Database::fetch_object($res);
1370
        if ($row) {
1371
            return $row->user_id;
1372
        } else {
1373
            return 0;
1374
        }
1375
    }
1376
1377
    /**
1378
     * Check if a username is available.
1379
     *
1380
     * @param string $username the wanted username
1381
     *
1382
     * @return bool true if the wanted username is available
1383
     * @assert ('') === false
1384
     * @assert ('xyzxyzxyz') === true
1385
     */
1386
    public static function is_username_available($username)
1387
    {
1388
        if (empty($username)) {
1389
            return false;
1390
        }
1391
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1392
        $sql = "SELECT username FROM $table_user
1393
                WHERE username = '".Database::escape_string($username)."'";
1394
        $res = Database::query($sql);
1395
1396
        return Database::num_rows($res) == 0;
1397
    }
1398
1399
    /**
1400
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1401
     *
1402
     * @param string $firstname the first name of the user
1403
     * @param string $lastname  the last name of the user
1404
     *
1405
     * @return string suggests a username that contains only ASCII-letters and digits,
1406
     *                without check for uniqueness within the system
1407
     *
1408
     * @author Julio Montoya Armas
1409
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1410
     * @assert ('','') === false
1411
     * @assert ('a','b') === 'ab'
1412
     */
1413
    public static function create_username($firstname, $lastname)
1414
    {
1415
        if (empty($firstname) && empty($lastname)) {
1416
            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...
1417
        }
1418
1419
        // The first letter only.
1420
        $firstname = api_substr(
1421
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1422
            0,
1423
            1
1424
        );
1425
        //Looking for a space in the lastname
1426
        $pos = api_strpos($lastname, ' ');
1427
        if ($pos !== false) {
1428
            $lastname = api_substr($lastname, 0, $pos);
1429
        }
1430
1431
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1432
        $username = $firstname.$lastname;
1433
        if (empty($username)) {
1434
            $username = 'user';
1435
        }
1436
1437
        $username = URLify::transliterate($username);
1438
1439
        return strtolower(substr($username, 0, USERNAME_MAX_LENGTH - 3));
1440
    }
1441
1442
    /**
1443
     * Creates a unique username, using:
1444
     * 1. the first name and the last name of a user;
1445
     * 2. an already created username but not checked for uniqueness yet.
1446
     *
1447
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1448
     *                          parameter is treated as username which is to be checked f
1449
     *                          or uniqueness and to be modified when it is necessary.
1450
     * @param string $lastname  the last name of the user
1451
     *
1452
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1453
     *                Note: When the method is called several times with same parameters,
1454
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1455
     *
1456
     * @author Ivan Tcholakov, 2009
1457
     */
1458
    public static function create_unique_username($firstname, $lastname = null)
1459
    {
1460
        if (is_null($lastname)) {
1461
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1462
            // For making this method tolerant of mistakes,
1463
            // let us transliterate and purify the suggested input username anyway.
1464
            // So, instead of the sentence $username = $firstname; we place the following:
1465
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1466
        } else {
1467
            $username = self::create_username($firstname, $lastname);
1468
        }
1469
        if (!self::is_username_available($username)) {
1470
            $i = 2;
1471
            $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1472
            while (!self::is_username_available($temp_username)) {
1473
                $i++;
1474
                $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1475
            }
1476
            $username = $temp_username;
1477
        }
1478
1479
        $username = URLify::transliterate($username);
1480
1481
        return $username;
1482
    }
1483
1484
    /**
1485
     * Modifies a given username accordingly to the specification for valid characters and length.
1486
     *
1487
     * @param $username string          The input username
1488
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1489
     *                     otherwise compliance may be partial. The default value is FALSE.
1490
     *
1491
     * @return string the resulting purified username
1492
     */
1493
    public static function purify_username($username, $strict = false)
1494
    {
1495
        if ($strict) {
1496
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1497
            // into ASCII letters in order they not to be totally removed.
1498
            // 2. Applying the strict purifier.
1499
            // 3. Length limitation.
1500
            $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);
1501
            $return = URLify::transliterate($return);
1502
1503
            return $return;
1504
        }
1505
1506
        // 1. Applying the shallow purifier.
1507
        // 2. Length limitation.
1508
        return substr(
1509
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1510
            0,
1511
            USERNAME_MAX_LENGTH
1512
        );
1513
    }
1514
1515
    /**
1516
     * Checks whether the user id exists in the database.
1517
     *
1518
     * @param int $userId User id
1519
     *
1520
     * @return bool True if user id was found, false otherwise
1521
     */
1522
    public static function is_user_id_valid($userId)
1523
    {
1524
        $resultData = Database::select(
1525
            'COUNT(1) AS count',
1526
            Database::get_main_table(TABLE_MAIN_USER),
1527
            [
1528
                'where' => ['id = ?' => (int) $userId],
1529
            ],
1530
            'first'
1531
        );
1532
1533
        if ($resultData === false) {
1534
            return false;
1535
        }
1536
1537
        return $resultData['count'] > 0;
1538
    }
1539
1540
    /**
1541
     * Checks whether a given username matches to the specification strictly.
1542
     * The empty username is assumed here as invalid.
1543
     * Mostly this function is to be used in the user interface built-in validation routines
1544
     * for providing feedback while usernames are enterd manually.
1545
     *
1546
     * @param string $username the input username
1547
     *
1548
     * @return bool returns TRUE if the username is valid, FALSE otherwise
1549
     */
1550
    public static function is_username_valid($username)
1551
    {
1552
        return !empty($username) && $username == self::purify_username($username, true);
1553
    }
1554
1555
    /**
1556
     * Checks whether a username is empty. If the username contains whitespace characters,
1557
     * such as spaces, tabulators, newlines, etc.,
1558
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
1559
     *
1560
     * @param string $username the given username
1561
     *
1562
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1563
     */
1564
    public static function is_username_empty($username)
1565
    {
1566
        return strlen(self::purify_username($username, false)) == 0;
1567
    }
1568
1569
    /**
1570
     * Checks whether a username is too long or not.
1571
     *
1572
     * @param string $username the given username, it should contain only ASCII-letters and digits
1573
     *
1574
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1575
     */
1576
    public static function is_username_too_long($username)
1577
    {
1578
        return strlen($username) > USERNAME_MAX_LENGTH;
1579
    }
1580
1581
    /**
1582
     * Get the users by ID.
1583
     *
1584
     * @param array  $ids    student ids
1585
     * @param string $active
1586
     * @param string $order
1587
     * @param string $limit
1588
     *
1589
     * @return array $result student information
1590
     */
1591
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
1592
    {
1593
        if (empty($ids)) {
1594
            return [];
1595
        }
1596
1597
        $ids = is_array($ids) ? $ids : [$ids];
1598
        $ids = array_map('intval', $ids);
1599
        $ids = implode(',', $ids);
1600
1601
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
1602
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
1603
        if (!is_null($active)) {
1604
            $sql .= ' AND active='.($active ? '1' : '0');
1605
        }
1606
1607
        if (!is_null($order)) {
1608
            $order = Database::escape_string($order);
1609
            $sql .= ' ORDER BY '.$order;
1610
        }
1611
1612
        if (!is_null($limit)) {
1613
            $limit = Database::escape_string($limit);
1614
            $sql .= ' LIMIT '.$limit;
1615
        }
1616
1617
        $rs = Database::query($sql);
1618
        $result = [];
1619
        while ($row = Database::fetch_array($rs)) {
1620
            $result[] = $row;
1621
        }
1622
1623
        return $result;
1624
    }
1625
1626
    /**
1627
     * Get a list of users of which the given conditions match with an = 'cond'.
1628
     *
1629
     * @param array $conditions a list of condition (example : status=>STUDENT)
1630
     * @param array $order_by   a list of fields on which sort
1631
     *
1632
     * @return array an array with all users of the platform
1633
     *
1634
     * @todo security filter order by
1635
     */
1636
    public static function get_user_list(
1637
        $conditions = [],
1638
        $order_by = [],
1639
        $limit_from = false,
1640
        $limit_to = false
1641
    ) {
1642
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1643
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1644
        $return_array = [];
1645
        $sql = "SELECT user.* FROM $user_table user ";
1646
1647
        if (api_is_multiple_url_enabled()) {
1648
            $urlId = api_get_current_access_url_id();
1649
            $sql .= " INNER JOIN $userUrlTable url_user
1650
                      ON (user.user_id = url_user.user_id)
1651
                      WHERE url_user.access_url_id = $urlId";
1652
        } else {
1653
            $sql .= " WHERE 1=1 ";
1654
        }
1655
1656
        if (count($conditions) > 0) {
1657
            foreach ($conditions as $field => $value) {
1658
                $field = Database::escape_string($field);
1659
                $value = Database::escape_string($value);
1660
                $sql .= " AND $field = '$value'";
1661
            }
1662
        }
1663
1664
        if (count($order_by) > 0) {
1665
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1666
        }
1667
1668
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1669
            $limit_from = (int) $limit_from;
1670
            $limit_to = (int) $limit_to;
1671
            $sql .= " LIMIT $limit_from, $limit_to";
1672
        }
1673
        $sql_result = Database::query($sql);
1674
        while ($result = Database::fetch_array($sql_result)) {
1675
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1676
            $return_array[] = $result;
1677
        }
1678
1679
        return $return_array;
1680
    }
1681
1682
    /**
1683
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
1684
     *
1685
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
1686
     * @param array  $order_by         a list of fields on which sort
1687
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
1688
     * @param string $condition        Whether we want the filters to be combined by AND or OR
1689
     * @param array  $onlyThisUserList
1690
     *
1691
     * @return array an array with all users of the platform
1692
     *
1693
     * @todo optional course code parameter, optional sorting parameters...
1694
     * @todo security filter order_by
1695
     */
1696
    public static function getUserListLike(
1697
        $conditions = [],
1698
        $order_by = [],
1699
        $simple_like = false,
1700
        $condition = 'AND',
1701
        $onlyThisUserList = []
1702
    ) {
1703
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1704
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1705
        $return_array = [];
1706
        $sql_query = "SELECT user.id FROM $user_table user ";
1707
1708
        if (api_is_multiple_url_enabled()) {
1709
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
1710
        }
1711
1712
        $sql_query .= ' WHERE 1 = 1 ';
1713
        if (count($conditions) > 0) {
1714
            $temp_conditions = [];
1715
            foreach ($conditions as $field => $value) {
1716
                $field = Database::escape_string($field);
1717
                $value = Database::escape_string($value);
1718
                if ($simple_like) {
1719
                    $temp_conditions[] = $field." LIKE '$value%'";
1720
                } else {
1721
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
1722
                }
1723
            }
1724
            if (!empty($temp_conditions)) {
1725
                $sql_query .= ' AND '.implode(' '.$condition.' ', $temp_conditions);
1726
            }
1727
1728
            if (api_is_multiple_url_enabled()) {
1729
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1730
            }
1731
        } else {
1732
            if (api_is_multiple_url_enabled()) {
1733
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1734
            }
1735
        }
1736
1737
        if (!empty($onlyThisUserList)) {
1738
            $onlyThisUserListToString = implode("','", $onlyThisUserList);
1739
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
1740
        }
1741
1742
        if (count($order_by) > 0) {
1743
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1744
        }
1745
1746
        $sql_result = Database::query($sql_query);
1747
        while ($result = Database::fetch_array($sql_result)) {
1748
            $userInfo = api_get_user_info($result['id']);
1749
            $return_array[] = $userInfo;
1750
        }
1751
1752
        return $return_array;
1753
    }
1754
1755
    /**
1756
     * Get user picture URL or path from user ID (returns an array).
1757
     * The return format is a complete path, enabling recovery of the directory
1758
     * with dirname() or the file with basename(). This also works for the
1759
     * functions dealing with the user's productions, as they are located in
1760
     * the same directory.
1761
     *
1762
     * @param int    $id       User ID
1763
     * @param string $type     Type of path to return (can be 'system', 'web')
1764
     * @param array  $userInfo user information to avoid query the DB
1765
     *                         returns the /main/img/unknown.jpg image set it at true
1766
     *
1767
     * @return array Array of 2 elements: 'dir' and 'file' which contain
1768
     *               the dir and file as the name implies if image does not exist it will
1769
     *               return the unknow image if anonymous parameter is true if not it returns an empty array
1770
     */
1771
    public static function get_user_picture_path_by_id(
1772
        $id,
1773
        $type = 'web',
1774
        $userInfo = []
1775
    ) {
1776
        switch ($type) {
1777
            case 'system': // Base: absolute system path.
1778
                $base = api_get_path(SYS_CODE_PATH);
1779
                break;
1780
            case 'web': // Base: absolute web path.
1781
            default:
1782
                $base = api_get_path(WEB_CODE_PATH);
1783
                break;
1784
        }
1785
1786
        $anonymousPath = [
1787
            'dir' => $base.'img/',
1788
            'file' => 'unknown.jpg',
1789
            'email' => '',
1790
        ];
1791
1792
        if (empty($id) || empty($type)) {
1793
            return $anonymousPath;
1794
        }
1795
1796
        $id = (int) $id;
1797
        if (empty($userInfo)) {
1798
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
1799
            $sql = "SELECT email, picture_uri FROM $user_table
1800
                    WHERE id=".$id;
1801
            $res = Database::query($sql);
1802
1803
            if (!Database::num_rows($res)) {
1804
                return $anonymousPath;
1805
            }
1806
            $user = Database::fetch_array($res);
1807
            if (empty($user['picture_uri'])) {
1808
                return $anonymousPath;
1809
            }
1810
        } else {
1811
            $user = $userInfo;
1812
        }
1813
1814
        $pictureFilename = trim($user['picture_uri']);
1815
1816
        $dir = self::getUserPathById($id, $type);
1817
1818
        return [
1819
            'dir' => $dir,
1820
            'file' => $pictureFilename,
1821
            'email' => $user['email'],
1822
        ];
1823
    }
1824
1825
    /**
1826
     * *** READ BEFORE REVIEW THIS FUNCTION ***
1827
     * This function is a exact copy from get_user_picture_path_by_id() and it was create it to avoid
1828
     * a recursive calls for get_user_picture_path_by_id() in another functions when you update a user picture
1829
     * in same script, so you can find this function usage in update_user_picture() function.
1830
     *
1831
     * @param int    $id       User ID
1832
     * @param string $type     Type of path to return (can be 'system', 'web')
1833
     * @param array  $userInfo user information to avoid query the DB
1834
     *                         returns the /main/img/unknown.jpg image set it at true
1835
     *
1836
     * @return array Array of 2 elements: 'dir' and 'file' which contain
1837
     *               the dir and file as the name implies if image does not exist it will
1838
     *               return the unknown image if anonymous parameter is true if not it returns an empty array
1839
     */
1840
    public static function getUserPicturePathById($id, $type = 'web', $userInfo = [])
1841
    {
1842
        switch ($type) {
1843
            case 'system': // Base: absolute system path.
1844
                $base = api_get_path(SYS_CODE_PATH);
1845
                break;
1846
            case 'web': // Base: absolute web path.
1847
            default:
1848
                $base = api_get_path(WEB_CODE_PATH);
1849
                break;
1850
        }
1851
1852
        $anonymousPath = [
1853
            'dir' => $base.'img/',
1854
            'file' => 'unknown.jpg',
1855
            'email' => '',
1856
        ];
1857
1858
        if (empty($id) || empty($type)) {
1859
            return $anonymousPath;
1860
        }
1861
1862
        $id = (int) $id;
1863
1864
        if (empty($userInfo)) {
1865
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
1866
            $sql = "SELECT email, picture_uri FROM $user_table WHERE id = $id";
1867
            $res = Database::query($sql);
1868
1869
            if (!Database::num_rows($res)) {
1870
                return $anonymousPath;
1871
            }
1872
            $user = Database::fetch_array($res);
1873
1874
            if (empty($user['picture_uri'])) {
1875
                return $anonymousPath;
1876
            }
1877
        } else {
1878
            $user = $userInfo;
1879
        }
1880
1881
        $pictureFilename = trim($user['picture_uri']);
1882
        $dir = self::getUserPathById($id, $type);
1883
1884
        return [
1885
            'dir' => $dir,
1886
            'file' => $pictureFilename,
1887
            'email' => $user['email'],
1888
        ];
1889
    }
1890
1891
    /**
1892
     * Get user path from user ID (returns an array).
1893
     * The return format is a complete path to a folder ending with "/"
1894
     * In case the first level of subdirectory of users/ does not exist, the
1895
     * function will attempt to create it. Probably not the right place to do it
1896
     * but at least it avoids headaches in many other places.
1897
     *
1898
     * @param int    $id   User ID
1899
     * @param string $type Type of path to return (can be 'system', 'web', 'last')
1900
     *
1901
     * @return string User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
1902
     */
1903
    public static function getUserPathById($id, $type)
1904
    {
1905
        $id = (int) $id;
1906
        if (!$id) {
1907
            return null;
1908
        }
1909
1910
        $userPath = "users/$id/";
1911
        if (api_get_setting('split_users_upload_directory') === 'true') {
1912
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
1913
            // In exceptional cases, on some portals, the intermediate base user
1914
            // directory might not have been created. Make sure it is before
1915
            // going further.
1916
1917
            $rootPath = api_get_path(SYS_UPLOAD_PATH).'users/'.substr((string) $id, 0, 1);
1918
            if (!is_dir($rootPath)) {
1919
                $perm = api_get_permissions_for_new_directories();
1920
                try {
1921
                    mkdir($rootPath, $perm);
1922
                } catch (Exception $e) {
1923
                    error_log($e->getMessage());
1924
                }
1925
            }
1926
        }
1927
        switch ($type) {
1928
            case 'system': // Base: absolute system path.
1929
                $userPath = api_get_path(SYS_UPLOAD_PATH).$userPath;
1930
                break;
1931
            case 'web': // Base: absolute web path.
1932
                $userPath = api_get_path(WEB_UPLOAD_PATH).$userPath;
1933
                break;
1934
            case 'last': // Only the last part starting with users/
1935
                break;
1936
        }
1937
1938
        return $userPath;
1939
    }
1940
1941
    /**
1942
     * Gets the current user image.
1943
     *
1944
     * @param string $user_id
1945
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
1946
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
1947
     * @param bool   $addRandomId
1948
     * @param array  $userInfo    to avoid query the DB
1949
     *
1950
     * @return string
1951
     */
1952
    public static function getUserPicture(
1953
        $user_id,
1954
        $size = USER_IMAGE_SIZE_MEDIUM,
1955
        $addRandomId = true,
1956
        $userInfo = []
1957
    ) {
1958
        // Make sure userInfo is defined. Otherwise, define it!
1959
        if (empty($userInfo) || !is_array($userInfo) || count($userInfo) == 0) {
1960
            if (empty($user_id)) {
1961
                return '';
1962
            } else {
1963
                $userInfo = api_get_user_info($user_id);
1964
            }
1965
        }
1966
1967
        $imageWebPath = self::get_user_picture_path_by_id(
1968
            $user_id,
1969
            'web',
1970
            $userInfo
1971
        );
1972
        $pictureWebFile = $imageWebPath['file'];
1973
        $pictureWebDir = $imageWebPath['dir'];
1974
1975
        $pictureAnonymousSize = '128';
1976
        $gravatarSize = 22;
1977
        $realSizeName = 'small_';
1978
1979
        switch ($size) {
1980
            case USER_IMAGE_SIZE_SMALL:
1981
                $pictureAnonymousSize = '32';
1982
                $realSizeName = 'small_';
1983
                $gravatarSize = 32;
1984
                break;
1985
            case USER_IMAGE_SIZE_MEDIUM:
1986
                $pictureAnonymousSize = '64';
1987
                $realSizeName = 'medium_';
1988
                $gravatarSize = 64;
1989
                break;
1990
            case USER_IMAGE_SIZE_ORIGINAL:
1991
                $pictureAnonymousSize = '128';
1992
                $realSizeName = '';
1993
                $gravatarSize = 128;
1994
                break;
1995
            case USER_IMAGE_SIZE_BIG:
1996
                $pictureAnonymousSize = '128';
1997
                $realSizeName = 'big_';
1998
                $gravatarSize = 128;
1999
                break;
2000
        }
2001
2002
        $gravatarEnabled = api_get_setting('gravatar_enabled');
2003
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
2004
        if ($pictureWebFile == 'unknown.jpg' || empty($pictureWebFile)) {
2005
            if ($gravatarEnabled === 'true') {
2006
                $file = self::getGravatar(
2007
                    $imageWebPath['email'],
2008
                    $gravatarSize,
2009
                    api_get_setting('gravatar_type')
2010
                );
2011
2012
                if ($addRandomId) {
2013
                    $file .= '&rand='.uniqid();
2014
                }
2015
2016
                return $file;
2017
            }
2018
2019
            return $anonymousPath;
2020
        }
2021
2022
        $pictureSysPath = self::get_user_picture_path_by_id($user_id, 'system');
2023
        $file = $pictureSysPath['dir'].$realSizeName.$pictureWebFile;
2024
        $picture = '';
2025
        if (file_exists($file)) {
2026
            $picture = $pictureWebDir.$realSizeName.$pictureWebFile;
2027
        } else {
2028
            $file = $pictureSysPath['dir'].$pictureWebFile;
2029
            if (file_exists($file) && !is_dir($file)) {
2030
                $picture = $pictureWebFile['dir'].$pictureWebFile;
2031
            }
2032
        }
2033
2034
        if (empty($picture)) {
2035
            return $anonymousPath;
2036
        }
2037
2038
        if ($addRandomId) {
2039
            $picture .= '?rand='.uniqid();
2040
        }
2041
2042
        return $picture;
2043
    }
2044
2045
    /**
2046
     * Creates new user photos in various sizes of a user, or deletes user photos.
2047
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2048
     *
2049
     * @param int    $user_id the user internal identification number
2050
     * @param string $file    The common file name for the newly created photos.
2051
     *                        It will be checked and modified for compatibility with the file system.
2052
     *                        If full name is provided, path component is ignored.
2053
     *                        If an empty name is provided, then old user photos are deleted only,
2054
     *
2055
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
2056
     *
2057
     * @param string $source_file    the full system name of the image from which user photos will be created
2058
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
2059
     *
2060
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
2061
     *              When deletion is requested returns empty string.
2062
     *              In case of internal error or negative validation returns FALSE.
2063
     */
2064
    public static function update_user_picture(
2065
        $user_id,
2066
        $file = null,
2067
        $source_file = null,
2068
        $cropParameters = ''
2069
    ) {
2070
        if (empty($user_id)) {
2071
            return false;
2072
        }
2073
        $delete = empty($file);
2074
        if (empty($source_file)) {
2075
            $source_file = $file;
2076
        }
2077
2078
        // User-reserved directory where photos have to be placed.
2079
        $path_info = self::getUserPicturePathById($user_id, 'system');
2080
        $path = $path_info['dir'];
2081
2082
        // If this directory does not exist - we create it.
2083
        if (!file_exists($path)) {
2084
            mkdir($path, api_get_permissions_for_new_directories(), true);
2085
        }
2086
2087
        // The old photos (if any).
2088
        $old_file = $path_info['file'];
2089
2090
        // Let us delete them.
2091
        if ($old_file != 'unknown.jpg') {
2092
            if (api_get_setting('platform.keep_old_images_after_delete') == 'true') {
2093
                $prefix = 'saved_'.date('Y_m_d_H_i_s').'_'.uniqid('').'_';
2094
                @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

2094
                /** @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...
2095
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
2096
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
2097
                @rename($path.$old_file, $path.$prefix.$old_file);
2098
            } else {
2099
                @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

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

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

2301
            while ($file = readdir(/** @scrutinizer ignore-type */ $handle)) {
Loading history...
2302
                if ($file == '.' ||
2303
                    $file == '..' ||
2304
                    $file == '.htaccess' ||
2305
                    is_dir($production_repository.$file)
2306
                ) {
2307
                    // skip current/parent directory and .htaccess
2308
                    continue;
2309
                }
2310
2311
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2312
                    // User's photos should not be listed as productions.
2313
                    continue;
2314
                }
2315
                $productions[] = $file;
2316
            }
2317
        }
2318
2319
        return $productions;
2320
    }
2321
2322
    /**
2323
     * Remove a user production.
2324
     *
2325
     * @param int    $user_id    User id
2326
     * @param string $production The production to remove
2327
     *
2328
     * @return bool
2329
     */
2330
    public static function remove_user_production($user_id, $production)
2331
    {
2332
        $production_path = self::get_user_picture_path_by_id($user_id, 'system');
2333
        $production_file = $production_path['dir'].$production;
2334
        if (is_file($production_file)) {
2335
            unlink($production_file);
2336
2337
            return true;
2338
        }
2339
2340
        return false;
2341
    }
2342
2343
    /**
2344
     * Update an extra field value for a given user.
2345
     *
2346
     * @param int    $userId   User ID
2347
     * @param string $variable Field variable name
2348
     * @param string $value    Field value
2349
     *
2350
     * @return bool true if field updated, false otherwise
2351
     */
2352
    public static function update_extra_field_value($userId, $variable, $value = '')
2353
    {
2354
        $extraFieldValue = new ExtraFieldValue('user');
2355
        $params = [
2356
            'item_id' => $userId,
2357
            'variable' => $variable,
2358
            'value' => $value,
2359
        ];
2360
2361
        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...
2362
    }
2363
2364
    /**
2365
     * Get an array of extra fields with field details (type, default value and options).
2366
     *
2367
     * @param    int    Offset (from which row)
2368
     * @param    int    Number of items
2369
     * @param    int    Column on which sorting is made
2370
     * @param    string    Sorting direction
2371
     * @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...
2372
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2373
     *
2374
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2375
     */
2376
    public static function get_extra_fields(
2377
        $from = 0,
2378
        $number_of_items = 0,
2379
        $column = 5,
2380
        $direction = 'ASC',
2381
        $all_visibility = true,
2382
        $field_filter = null
2383
    ) {
2384
        $fields = [];
2385
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2386
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2387
        $columns = [
2388
            'id',
2389
            'variable',
2390
            'field_type',
2391
            'display_text',
2392
            'default_value',
2393
            'field_order',
2394
            'filter',
2395
        ];
2396
        $column = (int) $column;
2397
        $sort_direction = '';
2398
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2399
            $sort_direction = strtoupper($direction);
2400
        }
2401
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2402
        $sqlf = "SELECT * FROM $t_uf WHERE extra_field_type = $extraFieldType ";
2403
        if (!$all_visibility) {
2404
            $sqlf .= " AND visible_to_self = 1 ";
2405
        }
2406
        if (!is_null($field_filter)) {
2407
            $field_filter = (int) $field_filter;
2408
            $sqlf .= " AND filter = $field_filter ";
2409
        }
2410
        $sqlf .= " ORDER BY ".$columns[$column]." $sort_direction ";
2411
        if ($number_of_items != 0) {
2412
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2413
        }
2414
        $resf = Database::query($sqlf);
2415
        if (Database::num_rows($resf) > 0) {
2416
            while ($rowf = Database::fetch_array($resf)) {
2417
                $fields[$rowf['id']] = [
2418
                    0 => $rowf['id'],
2419
                    1 => $rowf['variable'],
2420
                    2 => $rowf['field_type'],
2421
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2422
                    4 => $rowf['default_value'],
2423
                    5 => $rowf['field_order'],
2424
                    6 => $rowf['visible_to_self'],
2425
                    7 => $rowf['changeable'],
2426
                    8 => $rowf['filter'],
2427
                    9 => [],
2428
                    10 => '<a name="'.$rowf['id'].'"></a>',
2429
                ];
2430
2431
                $sqlo = "SELECT * FROM $t_ufo
2432
                         WHERE field_id = ".$rowf['id']."
2433
                         ORDER BY option_order ASC";
2434
                $reso = Database::query($sqlo);
2435
                if (Database::num_rows($reso) > 0) {
2436
                    while ($rowo = Database::fetch_array($reso)) {
2437
                        $fields[$rowf['id']][9][$rowo['id']] = [
2438
                            0 => $rowo['id'],
2439
                            1 => $rowo['option_value'],
2440
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2441
                            3 => $rowo['option_order'],
2442
                        ];
2443
                    }
2444
                }
2445
            }
2446
        }
2447
2448
        return $fields;
2449
    }
2450
2451
    /**
2452
     * Build a list of extra file already uploaded in $user_folder/{$extra_field}/.
2453
     *
2454
     * @param $user_id
2455
     * @param $extra_field
2456
     * @param bool $force
2457
     * @param bool $showDelete
2458
     *
2459
     * @return bool|string
2460
     */
2461
    public static function build_user_extra_file_list(
2462
        $user_id,
2463
        $extra_field,
2464
        $force = false,
2465
        $showDelete = false
2466
    ) {
2467
        if (!$force && !empty($_POST['remove_'.$extra_field])) {
2468
            return true; // postpone reading from the filesystem
2469
        }
2470
2471
        $extra_files = self::get_user_extra_files($user_id, $extra_field);
2472
        if (empty($extra_files)) {
2473
            return false;
2474
        }
2475
2476
        $path_info = self::get_user_picture_path_by_id($user_id, 'web');
2477
        $path = $path_info['dir'];
2478
        $del_image = Display::returnIconPath('delete.png');
2479
2480
        $del_text = get_lang('Delete');
2481
        $extra_file_list = '';
2482
        if (count($extra_files) > 0) {
2483
            $extra_file_list = '<div class="files-production"><ul id="productions">';
2484
            foreach ($extra_files as $file) {
2485
                $filename = substr($file, strlen($extra_field) + 1);
2486
                $extra_file_list .= '<li>'.Display::return_icon('archive.png').
2487
                    '<a href="'.$path.$extra_field.'/'.urlencode($filename).'" target="_blank">
2488
                        '.htmlentities($filename).
2489
                    '</a> ';
2490
                if ($showDelete) {
2491
                    $extra_file_list .= '<input 
2492
                        style="width:16px;" 
2493
                        type="image" 
2494
                        name="remove_extra_'.$extra_field.'['.urlencode($file).']" 
2495
                        src="'.$del_image.'" 
2496
                        alt="'.$del_text.'"
2497
                        title="'.$del_text.' '.htmlentities($filename).'" 
2498
                        onclick="javascript: return confirmation(\''.htmlentities($filename).'\');" /></li>';
2499
                }
2500
            }
2501
            $extra_file_list .= '</ul></div>';
2502
        }
2503
2504
        return $extra_file_list;
2505
    }
2506
2507
    /**
2508
     * Get valid filenames in $user_folder/{$extra_field}/.
2509
     *
2510
     * @param $user_id
2511
     * @param $extra_field
2512
     * @param bool $full_path
2513
     *
2514
     * @return array
2515
     */
2516
    public static function get_user_extra_files($user_id, $extra_field, $full_path = false)
2517
    {
2518
        if (!$full_path) {
2519
            // Nothing to do
2520
        } else {
2521
            $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2522
            $path = $path_info['dir'];
2523
        }
2524
        $extra_data = self::get_extra_user_data_by_field($user_id, $extra_field);
2525
        $extra_files = $extra_data[$extra_field];
2526
        if (is_array($extra_files)) {
2527
            foreach ($extra_files as $key => $value) {
2528
                if (!$full_path) {
2529
                    // Relative path from user folder
2530
                    $files[] = $value;
2531
                } else {
2532
                    $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...
2533
                }
2534
            }
2535
        } elseif (!empty($extra_files)) {
2536
            if (!$full_path) {
2537
                // Relative path from user folder
2538
                $files[] = $extra_files;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$files was never initialized. Although not strictly required by PHP, it is generally a good practice to add $files = array(); before regardless.
Loading history...
2539
            } else {
2540
                $files[] = $path.$extra_files;
2541
            }
2542
        }
2543
2544
        return $files; // can be an empty array
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $files does not seem to be defined for all execution paths leading up to this point.
Loading history...
2545
    }
2546
2547
    /**
2548
     * Remove an {$extra_file} from the user folder $user_folder/{$extra_field}/.
2549
     *
2550
     * @param int    $user_id
2551
     * @param string $extra_field
2552
     * @param string $extra_file
2553
     *
2554
     * @return bool
2555
     */
2556
    public static function remove_user_extra_file($user_id, $extra_field, $extra_file)
2557
    {
2558
        $extra_file = Security::filter_filename($extra_file);
2559
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2560
        if (strpos($extra_file, $extra_field) !== false) {
2561
            $path_extra_file = $path_info['dir'].$extra_file;
2562
        } else {
2563
            $path_extra_file = $path_info['dir'].$extra_field.'/'.$extra_file;
2564
        }
2565
        if (is_file($path_extra_file)) {
2566
            unlink($path_extra_file);
2567
2568
            return true;
2569
        }
2570
2571
        return false;
2572
    }
2573
2574
    /**
2575
     * Creates a new extra field.
2576
     *
2577
     * @param string $variable    Field's internal variable name
2578
     * @param int    $fieldType   Field's type
2579
     * @param string $displayText Field's language var name
2580
     * @param string $default     Field's default value
2581
     *
2582
     * @return int
2583
     */
2584
    public static function create_extra_field(
2585
        $variable,
2586
        $fieldType,
2587
        $displayText,
2588
        $default
2589
    ) {
2590
        $extraField = new ExtraField('user');
2591
        $params = [
2592
            'variable' => $variable,
2593
            'field_type' => $fieldType,
2594
            'display_text' => $displayText,
2595
            'default_value' => $default,
2596
        ];
2597
2598
        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...
2599
    }
2600
2601
    /**
2602
     * Check if a field is available.
2603
     *
2604
     * @param string $variable
2605
     *
2606
     * @return bool
2607
     */
2608
    public static function is_extra_field_available($variable)
2609
    {
2610
        $extraField = new ExtraField('user');
2611
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2612
2613
        return !empty($data) ? true : false;
2614
    }
2615
2616
    /**
2617
     * Gets user extra fields data.
2618
     *
2619
     * @param    int    User ID
2620
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2621
     * @param    bool    Whether to return invisible fields as well
2622
     * @param    bool    Whether to split multiple-selection fields or not
2623
     *
2624
     * @return array Array of fields => value for the given user
2625
     */
2626
    public static function get_extra_user_data(
2627
        $user_id,
2628
        $prefix = false,
2629
        $allVisibility = true,
2630
        $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

2630
        /** @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...
2631
        $fieldFilter = null
2632
    ) {
2633
        $user_id = (int) $user_id;
2634
2635
        if (empty($user_id)) {
2636
            return [];
2637
        }
2638
2639
        $extra_data = [];
2640
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2641
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2642
        $user_id = (int) $user_id;
2643
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2644
                FROM $t_uf f
2645
                WHERE
2646
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
2647
                ";
2648
        $filter_cond = '';
2649
2650
        if (!$allVisibility) {
2651
            if (isset($fieldFilter)) {
2652
                $fieldFilter = (int) $fieldFilter;
2653
                $filter_cond .= " AND filter = $fieldFilter ";
2654
            }
2655
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2656
        } else {
2657
            if (isset($fieldFilter)) {
2658
                $fieldFilter = (int) $fieldFilter;
2659
                $sql .= " AND filter = $fieldFilter ";
2660
            }
2661
        }
2662
2663
        $sql .= ' ORDER BY f.field_order';
2664
2665
        $res = Database::query($sql);
2666
        if (Database::num_rows($res) > 0) {
2667
            while ($row = Database::fetch_array($res)) {
2668
                if ($row['type'] == self::USER_FIELD_TYPE_TAG) {
2669
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2670
                    $extra_data['extra_'.$row['fvar']] = $tags;
2671
                } else {
2672
                    $sqlu = "SELECT value as fval
2673
                            FROM $t_ufv
2674
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2675
                    $resu = Database::query($sqlu);
2676
                    // get default value
2677
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2678
                               WHERE id=".$row['id'];
2679
                    $res_df = Database::query($sql_df);
2680
2681
                    if (Database::num_rows($resu) > 0) {
2682
                        $rowu = Database::fetch_array($resu);
2683
                        $fval = $rowu['fval'];
2684
                        if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
2685
                            $fval = explode(';', $rowu['fval']);
2686
                        }
2687
                    } else {
2688
                        $row_df = Database::fetch_array($res_df);
2689
                        $fval = $row_df['fval_df'];
2690
                    }
2691
                    // We get here (and fill the $extra_data array) even if there
2692
                    // is no user with data (we fill it with default values)
2693
                    if ($prefix) {
2694
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
2695
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2696
                        } else {
2697
                            $extra_data['extra_'.$row['fvar']] = $fval;
2698
                        }
2699
                    } else {
2700
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
2701
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2702
                        } else {
2703
                            $extra_data[$row['fvar']] = $fval;
2704
                        }
2705
                    }
2706
                }
2707
            }
2708
        }
2709
2710
        return $extra_data;
2711
    }
2712
2713
    /** Get extra user data by field
2714
     * @param int    user ID
2715
     * @param string the internal variable name of the field
2716
     *
2717
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2718
     */
2719
    public static function get_extra_user_data_by_field(
2720
        $user_id,
2721
        $field_variable,
2722
        $prefix = false,
2723
        $all_visibility = true,
2724
        $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

2724
        /** @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...
2725
    ) {
2726
        // A sanity check.
2727
        if (empty($user_id)) {
2728
            $user_id = 0;
2729
        } else {
2730
            if ($user_id != strval(intval($user_id))) {
2731
                return [];
2732
            }
2733
        }
2734
        $extra_data = [];
2735
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2736
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2737
        $user_id = (int) $user_id;
2738
2739
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2740
                FROM $t_uf f
2741
                WHERE f.variable = '$field_variable' ";
2742
2743
        if (!$all_visibility) {
2744
            $sql .= " AND f.visible_to_self = 1 ";
2745
        }
2746
2747
        $sql .= " AND extra_field_type = ".EntityExtraField::USER_FIELD_TYPE;
2748
        $sql .= " ORDER BY f.field_order";
2749
2750
        $res = Database::query($sql);
2751
        if (Database::num_rows($res) > 0) {
2752
            while ($row = Database::fetch_array($res)) {
2753
                $sqlu = "SELECT value as fval FROM $t_ufv v 
2754
                         INNER JOIN $t_uf f
2755
                         ON (v.field_id = f.id)
2756
                         WHERE
2757
                            extra_field_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2758
                            field_id = ".$row['id']." AND
2759
                            item_id = ".$user_id;
2760
                $resu = Database::query($sqlu);
2761
                $fval = '';
2762
                if (Database::num_rows($resu) > 0) {
2763
                    $rowu = Database::fetch_array($resu);
2764
                    $fval = $rowu['fval'];
2765
                    if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
2766
                        $fval = explode(';', $rowu['fval']);
2767
                    }
2768
                }
2769
                if ($prefix) {
2770
                    $extra_data['extra_'.$row['fvar']] = $fval;
2771
                } else {
2772
                    $extra_data[$row['fvar']] = $fval;
2773
                }
2774
            }
2775
        }
2776
2777
        return $extra_data;
2778
    }
2779
2780
    /**
2781
     * Get the extra field information for a certain field (the options as well).
2782
     *
2783
     * @param int $variable The name of the field we want to know everything about
2784
     *
2785
     * @return array Array containing all the information about the extra profile field
2786
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2787
     *               as returned by the database)
2788
     *
2789
     * @author Julio Montoya
2790
     *
2791
     * @since v1.8.6
2792
     */
2793
    public static function get_extra_field_information_by_name($variable)
2794
    {
2795
        $extraField = new ExtraField('user');
2796
2797
        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...
2798
    }
2799
2800
    /**
2801
     * Get the extra field information for user tag (the options as well).
2802
     *
2803
     * @param int $variable The name of the field we want to know everything about
2804
     *
2805
     * @return array Array containing all the information about the extra profile field
2806
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2807
     *               as returned by the database)
2808
     *
2809
     * @author José Loguercio
2810
     *
2811
     * @since v1.11.0
2812
     */
2813
    public static function get_extra_field_tags_information_by_name($variable)
2814
    {
2815
        $extraField = new ExtraField('user');
2816
2817
        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...
2818
    }
2819
2820
    /**
2821
     * @param string $type
2822
     *
2823
     * @return array
2824
     */
2825
    public static function get_all_extra_field_by_type($type)
2826
    {
2827
        $extraField = new ExtraField('user');
2828
2829
        return $extraField->get_all_extra_field_by_type($type);
2830
    }
2831
2832
    /**
2833
     * Get all the extra field information of a certain field (also the options).
2834
     *
2835
     * @param int $fieldId the ID of the field we want to know everything of
2836
     *
2837
     * @return array $return containing all th information about the extra profile field
2838
     *
2839
     * @author Julio Montoya
2840
     *
2841
     * @deprecated
2842
     * @since v1.8.6
2843
     */
2844
    public static function get_extra_field_information($fieldId)
2845
    {
2846
        $extraField = new ExtraField('user');
2847
2848
        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...
2849
    }
2850
2851
    /**
2852
     * Get extra user data by value.
2853
     *
2854
     * @param string $variable       the internal variable name of the field
2855
     * @param string $value          the internal value of the field
2856
     * @param bool   $all_visibility
2857
     *
2858
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2859
     */
2860
    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

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

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

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

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

6606
                'human_date' => SessionManager::convertSessionDateToString($startDate, /** @scrutinizer ignore-type */ $endDate, false, true),
Loading history...
6607
                'start_in_last_year' => $startYear < $searchYear,
6608
                'end_in_next_year' => $endYear > $searchYear,
6609
                'no_start' => !$startWeek,
6610
                'no_end' => !$endWeek,
6611
                'start' => $start,
6612
                'duration' => $duration > 0 ? $duration : 1,
6613
            ];
6614
        }
6615
6616
        usort(
6617
            $calendar,
6618
            function ($sA, $sB) {
6619
                if ($sA['start'] == $sB['start']) {
6620
                    return 0;
6621
                }
6622
6623
                if ($sA['start'] < $sB['start']) {
6624
                    return -1;
6625
                }
6626
6627
                return 1;
6628
            }
6629
        );
6630
6631
        return $calendar;
6632
    }
6633
6634
6635
    /**
6636
     * Return the user's full name. Optionally with the username.
6637
     *
6638
     * @param User $user
6639
     * @param bool $includeUsername Optional. By default username is not included.
6640
     *
6641
     * @return string
6642
     */
6643
    public static function formatUserFullName(User $user, $includeUsername = false): string
6644
    {
6645
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
6646
6647
        if ($includeUsername && api_get_setting('profile.hide_username_with_complete_name') === 'false') {
6648
            $username = $user->getUsername();
6649
6650
            return "$fullName ($username)";
6651
        }
6652
6653
        return $fullName;
6654
    }
6655
6656
    /**
6657
     * @return EncoderFactory
6658
     */
6659
    private static function getEncoderFactory()
6660
    {
6661
        $encryption = self::getPasswordEncryption();
6662
        $encoders = [
6663
            'Chamilo\\UserBundle\\Entity\\User' => new \Chamilo\UserBundle\Security\Encoder($encryption),
0 ignored issues
show
Bug introduced by
It seems like $encryption can also be of type boolean; however, parameter $passwordEncrypt of Chamilo\UserBundle\Security\Encoder::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

6663
            'Chamilo\\UserBundle\\Entity\\User' => new \Chamilo\UserBundle\Security\Encoder(/** @scrutinizer ignore-type */ $encryption),
Loading history...
6664
        ];
6665
6666
        $encoderFactory = new EncoderFactory($encoders);
6667
6668
        return $encoderFactory;
6669
    }
6670
6671
    /**
6672
     * @param User $user
6673
     *
6674
     * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface
6675
     */
6676
    private static function getEncoder(User $user)
6677
    {
6678
        $encoderFactory = self::getEncoderFactory();
6679
6680
        return $encoderFactory->getEncoder($user);
6681
    }
6682
6683
    /**
6684
     * Disables or enables a user.
6685
     *
6686
     * @param int $user_id
6687
     * @param int $active  Enable or disable
6688
     *
6689
     * @return bool True on success, false on failure
6690
     * @assert (-1,0) === false
6691
     * @assert (1,1) === true
6692
     */
6693
    private static function change_active_state($user_id, $active)
6694
    {
6695
        if (strval(intval($user_id)) != $user_id) {
6696
            return false;
6697
        }
6698
        if ($user_id < 1) {
6699
            return false;
6700
        }
6701
        $user_id = intval($user_id);
6702
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6703
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
6704
        $r = Database::query($sql);
6705
        $ev = LOG_USER_DISABLE;
6706
        if ($active == 1) {
6707
            $ev = LOG_USER_ENABLE;
6708
        }
6709
        if ($r !== false) {
6710
            Event::addEvent($ev, LOG_USER_ID, $user_id);
6711
        }
6712
6713
        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...
6714
    }
6715
6716
    /**
6717
     * Get either a Gravatar URL or complete image tag for a specified email address.
6718
     *
6719
     * @param string $email The email address
6720
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
6721
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
6722
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
6723
     * @param bool   $img   True to return a complete IMG tag False for just the URL
6724
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
6725
     *
6726
     * @return string containing either just a URL or a complete image tag
6727
     * @source http://gravatar.com/site/implement/images/php/
6728
     */
6729
    private static function getGravatar(
6730
        $email,
6731
        $s = 80,
6732
        $d = 'mm',
6733
        $r = 'g',
6734
        $img = false,
6735
        $atts = []
6736
    ) {
6737
        $url = 'http://www.gravatar.com/avatar/';
6738
        if (!empty($_SERVER['HTTPS'])) {
6739
            $url = 'https://secure.gravatar.com/avatar/';
6740
        }
6741
        $url .= md5(strtolower(trim($email)));
6742
        $url .= "?s=$s&d=$d&r=$r";
6743
        if ($img) {
6744
            $url = '<img src="'.$url.'"';
6745
            foreach ($atts as $key => $val) {
6746
                $url .= ' '.$key.'="'.$val.'"';
6747
            }
6748
            $url .= ' />';
6749
        }
6750
6751
        return $url;
6752
    }
6753
}
6754