Completed
Push — master ( 88715c...d7c14e )
by Julito
09:34
created

UserManager::removeAllBossFromStudent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 1
dl 0
loc 14
rs 10
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
5
use Chamilo\CoreBundle\Entity\SkillRelUser;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, SkillRelUser. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
6
use Chamilo\CoreBundle\Entity\SkillRelUserComment;
7
use Chamilo\CoreBundle\Framework\Container;
8
use Chamilo\CoreBundle\Repository\AccessUrlRepository;
9
use Chamilo\UserBundle\Entity\User;
10
use Chamilo\UserBundle\Repository\UserRepository;
11
use ChamiloSession as Session;
12
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);
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
        // Unsubscribe the user from all groups in all his courses
753
        $sql = "SELECT c.id 
754
                FROM $table_course c 
755
                INNER JOIN $table_course_user cu
756
                ON (c.id = cu.c_id)
757
                WHERE
758
                    cu.user_id = '".$user_id."' AND
759
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
760
                ";
761
762
        $res = Database::query($sql);
763
        while ($course = Database::fetch_object($res)) {
764
            $sql = "DELETE FROM $table_group
765
                    WHERE c_id = {$course->id} AND user_id = $user_id";
766
            Database::query($sql);
767
        }
768
769
        // Unsubscribe user from usergroup_rel_user
770
        $sql = "DELETE FROM $usergroup_rel_user WHERE user_id = '".$user_id."'";
771
        Database::query($sql);
772
773
        // Unsubscribe user from all courses
774
        $sql = "DELETE FROM $table_course_user WHERE user_id = '".$user_id."'";
775
        Database::query($sql);
776
777
        // Unsubscribe user from all courses in sessions
778
        $sql = "DELETE FROM $table_session_course_user WHERE user_id = '".$user_id."'";
779
        Database::query($sql);
780
781
        // If the user was added as a id_coach then set the current admin as coach see BT#
782
        $currentUserId = api_get_user_id();
783
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
784
                WHERE id_coach = '".$user_id."'";
785
        Database::query($sql);
786
787
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
788
                WHERE session_admin_id = '".$user_id."'";
789
        Database::query($sql);
790
791
        // Unsubscribe user from all sessions
792
        $sql = "DELETE FROM $table_session_user
793
                WHERE user_id = '".$user_id."'";
794
        Database::query($sql);
795
796
        // Delete user picture
797
        /* TODO: Logic about api_get_setting('split_users_upload_directory') == 'true'
798
        a user has 4 different sized photos to be deleted. */
799
        $user_info = api_get_user_info($user_id);
800
801
        if (strlen($user_info['picture_uri']) > 0) {
802
            $path = self::getUserPathById($user_id, 'system');
803
            $img_path = $path.$user_info['picture_uri'];
804
            if (file_exists($img_path)) {
805
                unlink($img_path);
806
            }
807
        }
808
809
        // Delete the personal course categories
810
        $course_cat_table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
811
        $sql = "DELETE FROM $course_cat_table WHERE user_id = '".$user_id."'";
812
        Database::query($sql);
813
814
        // Delete user from the admin table
815
        $sql = "DELETE FROM $table_admin WHERE user_id = '".$user_id."'";
816
        Database::query($sql);
817
818
        // Delete the personal agenda-items from this user
819
        $agenda_table = Database::get_main_table(TABLE_PERSONAL_AGENDA);
820
        $sql = "DELETE FROM $agenda_table WHERE user = '".$user_id."'";
821
        Database::query($sql);
822
823
        $gradebook_results_table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_RESULT);
824
        $sql = 'DELETE FROM '.$gradebook_results_table.' WHERE user_id = '.$user_id;
825
        Database::query($sql);
826
827
        $extraFieldValue = new ExtraFieldValue('user');
828
        $extraFieldValue->deleteValuesByItem($user_id);
829
830
        UrlManager::deleteUserFromAllUrls($user_id);
831
832
        if (api_get_setting('allow_social_tool') === 'true') {
833
            $userGroup = new UserGroup();
834
            //Delete user from portal groups
835
            $group_list = $userGroup->get_groups_by_user($user_id);
836
            if (!empty($group_list)) {
837
                foreach ($group_list as $group_id => $data) {
838
                    $userGroup->delete_user_rel_group($user_id, $group_id);
839
                }
840
            }
841
842
            // Delete user from friend lists
843
            SocialManager::remove_user_rel_user($user_id, true);
844
        }
845
846
        // Removing survey invitation
847
        SurveyManager::delete_all_survey_invitations_by_user($user_id);
848
849
        // Delete students works
850
        $sql = "DELETE FROM $table_work WHERE user_id = $user_id AND c_id <> 0";
851
        Database::query($sql);
852
853
        $sql = "UPDATE c_item_property SET to_user_id = NULL
854
                WHERE to_user_id = '".$user_id."'";
855
        Database::query($sql);
856
857
        $sql = "UPDATE c_item_property SET insert_user_id = NULL
858
                WHERE insert_user_id = '".$user_id."'";
859
        Database::query($sql);
860
861
        $sql = "UPDATE c_item_property SET lastedit_user_id = NULL
862
                WHERE lastedit_user_id = '".$user_id."'";
863
        Database::query($sql);
864
865
        // Skills
866
        $em = Database::getManager();
867
868
        $criteria = ['user' => $user_id];
869
        $skills = $em->getRepository('ChamiloCoreBundle:SkillRelUser')->findBy($criteria);
870
        if ($skills) {
871
            /** @var SkillRelUser $skill */
872
            foreach ($skills as $skill) {
873
                $comments = $skill->getComments();
874
                if ($comments) {
875
                    /** @var SkillRelUserComment $comment */
876
                    foreach ($comments as $comment) {
877
                        $em->remove($comment);
878
                    }
879
                }
880
                $em->remove($skill);
881
            }
882
            $em->flush();
883
        }
884
885
        // ExtraFieldSavedSearch
886
        $criteria = ['user' => $user_id];
887
        $searchList = $em->getRepository('ChamiloCoreBundle:ExtraFieldSavedSearch')->findBy($criteria);
888
        if ($searchList) {
889
            foreach ($searchList as $search) {
890
                $em->remove($search);
891
            }
892
            $em->flush();
893
        }
894
895
        $connection = Database::getManager()->getConnection();
896
        $tableExists = $connection->getSchemaManager()->tablesExist(['plugin_bbb_room']);
897
        if ($tableExists) {
898
            // Delete user from database
899
            $sql = "DELETE FROM plugin_bbb_room WHERE participant_id = $user_id";
900
            Database::query($sql);
901
        }
902
903
        // Delete user/ticket relationships :(
904
        $tableExists = $connection->getSchemaManager()->tablesExist(['ticket_ticket']);
905
        if ($tableExists) {
906
            TicketManager::deleteUserFromTicketSystem($user_id);
907
        }
908
909
        $tableExists = $connection->getSchemaManager()->tablesExist(['c_lp_category_user']);
910
        if ($tableExists) {
911
            $sql = "DELETE FROM c_lp_category_user WHERE user_id = $user_id";
912
            Database::query($sql);
913
        }
914
915
        $app_plugin = new AppPlugin();
916
        $app_plugin->performActionsWhenDeletingItem('user', $user_id);
917
918
        // Delete user from database
919
        $sql = "DELETE FROM $table_user WHERE id = '".$user_id."'";
920
        Database::query($sql);
921
922
        // Add event to system log
923
        $user_id_manager = api_get_user_id();
924
925
        Event::addEvent(
926
            LOG_USER_DELETE,
927
            LOG_USER_ID,
928
            $user_id,
929
            api_get_utc_datetime(),
930
            $user_id_manager
931
        );
932
933
        Event::addEvent(
934
            LOG_USER_DELETE,
935
            LOG_USER_OBJECT,
936
            $user_info,
937
            api_get_utc_datetime(),
938
            $user_id_manager
939
        );
940
        $cacheAvailable = api_get_configuration_value('apc');
941
        if ($cacheAvailable === true) {
942
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
943
            if (apcu_exists($apcVar)) {
944
                apcu_delete($apcVar);
945
            }
946
        }
947
948
        return true;
949
    }
950
951
    /**
952
     * Deletes users completely. Can be called either as:
953
     * - UserManager::delete_users(1, 2, 3); or
954
     * - UserManager::delete_users(array(1, 2, 3));.
955
     *
956
     * @param array|int $ids
957
     *
958
     * @return bool True if at least one user was successfuly deleted. False otherwise.
959
     *
960
     * @author Laurent Opprecht
961
     *
962
     * @uses \UserManager::delete_user() to actually delete each user
963
     * @assert (null) === false
964
     * @assert (-1) === false
965
     * @assert (array(-1)) === false
966
     */
967
    public static function delete_users($ids = [])
968
    {
969
        $result = false;
970
        $ids = is_array($ids) ? $ids : func_get_args();
971
        if (!is_array($ids) || count($ids) == 0) {
972
            return false;
973
        }
974
        $ids = array_map('intval', $ids);
975
        foreach ($ids as $id) {
976
            if (empty($id) || $id < 1) {
977
                continue;
978
            }
979
            $deleted = self::delete_user($id);
980
            $result = $deleted || $result;
981
        }
982
983
        return $result;
984
    }
985
986
    /**
987
     * Disable users. Can be called either as:
988
     * - UserManager::deactivate_users(1, 2, 3);
989
     * - UserManager::deactivate_users(array(1, 2, 3));.
990
     *
991
     * @param array|int $ids
992
     *
993
     * @return bool
994
     *
995
     * @author Laurent Opprecht
996
     * @assert (null) === false
997
     * @assert (array(-1)) === false
998
     */
999
    public static function deactivate_users($ids = [])
1000
    {
1001
        if (empty($ids)) {
1002
            return false;
1003
        }
1004
1005
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1006
1007
        $ids = is_array($ids) ? $ids : func_get_args();
1008
        $ids = array_map('intval', $ids);
1009
        $ids = implode(',', $ids);
1010
1011
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
1012
        $r = Database::query($sql);
1013
        if ($r !== false) {
1014
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
1015
1016
            return true;
1017
        }
1018
1019
        return false;
1020
    }
1021
1022
    /**
1023
     * Enable users. Can be called either as:
1024
     * - UserManager::activate_users(1, 2, 3);
1025
     * - UserManager::activate_users(array(1, 2, 3));.
1026
     *
1027
     * @param array|int IDs of the users to enable
1028
     *
1029
     * @return bool
1030
     *
1031
     * @author Laurent Opprecht
1032
     * @assert (null) === false
1033
     * @assert (array(-1)) === false
1034
     */
1035
    public static function activate_users($ids = [])
1036
    {
1037
        if (empty($ids)) {
1038
            return false;
1039
        }
1040
1041
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1042
1043
        $ids = is_array($ids) ? $ids : func_get_args();
1044
        $ids = array_map('intval', $ids);
1045
        $ids = implode(',', $ids);
1046
1047
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
1048
        $r = Database::query($sql);
1049
        if ($r !== false) {
1050
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
1051
1052
            return true;
1053
        }
1054
1055
        return false;
1056
    }
1057
1058
    /**
1059
     * Update user information with all the parameters passed to this function.
1060
     *
1061
     * @param int    $user_id         The ID of the user to be updated
1062
     * @param string $firstname       The user's firstname
1063
     * @param string $lastname        The user's lastname
1064
     * @param string $username        The user's username (login)
1065
     * @param string $password        The user's password
1066
     * @param string $auth_source     The authentication source (default: "platform")
1067
     * @param string $email           The user's e-mail address
1068
     * @param int    $status          The user's status
1069
     * @param string $official_code   The user's official code (usually just an internal institutional code)
1070
     * @param string $phone           The user's phone number
1071
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
1072
     * @param string $expiration_date The date at which this user will be automatically disabled
1073
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
1074
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
1075
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
1076
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
1077
     * @param string $language        The language to which the user account will be set
1078
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
1079
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
1080
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
1081
     * @param string $address
1082
     * @param array  $emailTemplate
1083
     *
1084
     * @return bool|int False on error, or the user ID if the user information was updated
1085
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
1086
     */
1087
    public static function update_user(
1088
        $user_id,
1089
        $firstname,
1090
        $lastname,
1091
        $username,
1092
        $password = null,
1093
        $auth_source = null,
1094
        $email,
1095
        $status,
1096
        $official_code,
1097
        $phone,
1098
        $picture_uri,
1099
        $expiration_date,
1100
        $active,
1101
        $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

1101
        /** @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...
1102
        $hr_dept_id = 0,
1103
        $extra = null,
1104
        $language = 'english',
1105
        $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

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

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

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

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

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

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

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

2859
    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...
2860
    {
2861
        $extraFieldValue = new ExtraFieldValue('user');
2862
        $extraField = new ExtraField('user');
2863
2864
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2865
2866
        if (false === $info) {
2867
            return [];
2868
        }
2869
2870
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2871
            $variable,
2872
            $value,
2873
            false,
2874
            false,
2875
            true
2876
        );
2877
2878
        $result = [];
2879
        if (!empty($data)) {
2880
            foreach ($data as $item) {
2881
                $result[] = $item['item_id'];
2882
            }
2883
        }
2884
2885
        return $result;
2886
    }
2887
2888
    /**
2889
     * Get extra user data by tags value.
2890
     *
2891
     * @param int    $fieldId the ID of the field we want to know everything of
2892
     * @param string $tag     the tag name for search
2893
     *
2894
     * @return array with extra data info of a user
2895
     *
2896
     * @author José Loguercio
2897
     *
2898
     * @since v1.11.0
2899
     */
2900
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2901
    {
2902
        $extraField = new ExtraField('user');
2903
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2904
        $array = [];
2905
        foreach ($result as $index => $user) {
2906
            $array[] = $user['user_id'];
2907
        }
2908
2909
        return $array;
2910
    }
2911
2912
    /**
2913
     * Get extra user data by field variable.
2914
     *
2915
     * @param string $variable field variable
2916
     *
2917
     * @return array data
2918
     */
2919
    public static function get_extra_user_data_by_field_variable($variable)
2920
    {
2921
        $extraInfo = self::get_extra_field_information_by_name($variable);
2922
        $field_id = (int) $extraInfo['id'];
2923
2924
        $extraField = new ExtraFieldValue('user');
2925
        $data = $extraField->getValuesByFieldId($field_id);
2926
2927
        if (!empty($data)) {
2928
            foreach ($data as $row) {
2929
                $user_id = $row['item_id'];
2930
                $data[$user_id] = $row;
2931
            }
2932
        }
2933
2934
        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...
2935
    }
2936
2937
    /**
2938
     * Get extra user data tags by field variable.
2939
     *
2940
     * @param string $variable field variable
2941
     *
2942
     * @return array
2943
     */
2944
    public static function get_extra_user_data_for_tags($variable)
2945
    {
2946
        $data = self::get_extra_field_tags_information_by_name($variable);
2947
2948
        return $data;
2949
    }
2950
2951
    /**
2952
     * Gives a list of [session_category][session_id] for the current user.
2953
     *
2954
     * @param int  $user_id
2955
     * @param bool $is_time_over                 whether to fill the first element or not
2956
     *                                           (to give space for courses out of categories)
2957
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2958
     * @param bool $ignoreTimeLimit              ignore time start/end
2959
     *
2960
     * @return array list of statuses [session_category][session_id]
2961
     *
2962
     * @todo ensure multiple access urls are managed correctly
2963
     */
2964
    public static function get_sessions_by_category(
2965
        $user_id,
2966
        $is_time_over = true,
2967
        $ignore_visibility_for_admins = false,
2968
        $ignoreTimeLimit = false
2969
    ) {
2970
        if ($user_id != strval(intval($user_id))) {
2971
            return [];
2972
        }
2973
2974
        $allowOrder = api_get_configuration_value('session_list_order');
2975
        $position = '';
2976
        if ($allowOrder) {
2977
            $position = ', s.position AS position ';
2978
        }
2979
2980
        // Get the list of sessions per user
2981
        $now = new DateTime('now', new DateTimeZone('UTC'));
2982
2983
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2984
        // join would not catch session-courses where the user is general
2985
        // session coach but which do not have students nor coaches registered
2986
2987
        // master changes
2988
        $dql = "SELECT DISTINCT
2989
                    s.id,
2990
                    s.name,
2991
                    s.accessStartDate AS access_start_date,
2992
                    s.accessEndDate AS access_end_date,
2993
                    s.duration,
2994
                    sc.id AS session_category_id,
2995
                    sc.name AS session_category_name,
2996
                    sc.dateStart AS session_category_date_start,
2997
                    sc.dateEnd AS session_category_date_end,
2998
                    s.coachAccessStartDate AS coach_access_start_date,
2999
                    s.coachAccessEndDate AS coach_access_end_date,
3000
                    CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
3001
                    $position
3002
                FROM ChamiloCoreBundle:Session AS s
3003
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
3004
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
3005
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc ";
3006
3007
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
3008
        // is awfully inefficient for large sets of data (1m25s for 58K
3009
        // sessions, BT#14115) but executing a similar query twice and grouping
3010
        // the results afterwards in PHP takes about 1/1000th of the time
3011
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
3012
        $dqlStudent = $dql." WHERE scu.user = :user AND url.url = :url ";
3013
        $dqlCoach = $dql." WHERE s.generalCoach = :user AND url.url = :url ";
3014
3015
        // Default order
3016
        $order = 'ORDER BY sc.name, s.name';
3017
3018
        // Order by date if showing all sessions
3019
        $showAllSessions = api_get_configuration_value('show_all_sessions_on_my_course_page') === true;
3020
        if ($showAllSessions) {
3021
            $order = 'ORDER BY s.accessStartDate';
3022
        }
3023
3024
        // Order by position
3025
        if ($allowOrder) {
3026
            $order = 'ORDER BY s.position';
3027
        }
3028
3029
        // Order by dates according to settings
3030
        $orderBySettings = api_get_configuration_value('my_courses_session_order');
3031
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
3032
            $field = $orderBySettings['field'];
3033
            $orderSetting = $orderBySettings['order'];
3034
            switch ($field) {
3035
                case 'start_date':
3036
                    $order = " ORDER BY s.accessStartDate $orderSetting";
3037
                    break;
3038
                case 'end_date':
3039
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
3040
                    if ($orderSetting == 'asc') {
3041
                        // Put null values at the end
3042
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
3043
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
3044
                    }
3045
                    break;
3046
            }
3047
        }
3048
3049
        $dqlStudent .= $order;
3050
        $dqlCoach .= $order;
3051
3052
        $accessUrlId = api_get_current_access_url_id();
3053
        $dqlStudent = Database::getManager()
3054
            ->createQuery($dqlStudent)
3055
            ->setParameters(
3056
                ['user' => $user_id, 'url' => $accessUrlId]
3057
            )
3058
        ;
3059
        $dqlCoach = Database::getManager()
3060
            ->createQuery($dqlCoach)
3061
            ->setParameters(
3062
                ['user' => $user_id, 'url' => $accessUrlId]
3063
            )
3064
        ;
3065
3066
        $sessionDataStudent = $dqlStudent->getResult();
3067
        $sessionDataCoach = $dqlCoach->getResult();
3068
3069
        $sessionData = [];
3070
        // First fill $sessionData with student sessions
3071
        foreach ($sessionDataStudent as $row) {
3072
            $sessionData[$row['id']] = $row;
3073
        }
3074
        // Overwrite session data of the user as a student with session data
3075
        // of the user as a coach.
3076
        // There shouldn't be such duplicate rows, but just in case...
3077
        foreach ($sessionDataCoach as $row) {
3078
            $sessionData[$row['id']] = $row;
3079
        }
3080
3081
        $collapsable = api_get_configuration_value('allow_user_session_collapsable');
3082
        $extraField = new ExtraFieldValue('session');
3083
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
3084
3085
        $categories = [];
3086
        foreach ($sessionData as $row) {
3087
            $session_id = $row['id'];
3088
            $coachList = SessionManager::getCoachesBySession($session_id);
3089
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
3090
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
3091
            $courseList = self::get_courses_list_by_session(
3092
                $user_id,
3093
                $session_id
3094
            );
3095
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
3096
3097
            // User portal filters:
3098
            if ($ignoreTimeLimit === false) {
3099
                if ($is_time_over) {
3100
                    // History
3101
                    if ($row['duration']) {
3102
                        if ($daysLeft >= 0) {
3103
                            continue;
3104
                        }
3105
                    } else {
3106
                        if (empty($row['access_end_date'])) {
3107
                            continue;
3108
                        } else {
3109
                            if ($row['access_end_date'] > $now) {
3110
                                continue;
3111
                            }
3112
                        }
3113
                    }
3114
                } else {
3115
                    // Current user portal
3116
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
3117
                    $isCoachOfCourse = in_array($user_id, $coachList);
3118
3119
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
3120
                        // Teachers can access the session depending in the access_coach date
3121
                    } else {
3122
                        if ($row['duration']) {
3123
                            if ($daysLeft <= 0) {
3124
                                continue;
3125
                            }
3126
                        } else {
3127
                            if (isset($row['access_end_date']) &&
3128
                                !empty($row['access_end_date'])
3129
                            ) {
3130
                                if ($row['access_end_date'] <= $now) {
3131
                                    continue;
3132
                                }
3133
                            }
3134
                        }
3135
                    }
3136
                }
3137
            }
3138
3139
            $categories[$row['session_category_id']]['session_category'] = [
3140
                'id' => $row['session_category_id'],
3141
                'name' => $row['session_category_name'],
3142
                'date_start' => $categoryStart,
3143
                'date_end' => $categoryEnd,
3144
            ];
3145
3146
            $visibility = api_get_session_visibility(
3147
                $session_id,
3148
                null,
3149
                $ignore_visibility_for_admins
3150
            );
3151
3152
            if ($visibility != SESSION_VISIBLE) {
3153
                // Course Coach session visibility.
3154
                $blockedCourseCount = 0;
3155
                $closedVisibilityList = [
3156
                    COURSE_VISIBILITY_CLOSED,
3157
                    COURSE_VISIBILITY_HIDDEN,
3158
                ];
3159
3160
                foreach ($courseList as $course) {
3161
                    // Checking session visibility
3162
                    $sessionCourseVisibility = api_get_session_visibility(
3163
                        $session_id,
3164
                        $course['real_id'],
3165
                        $ignore_visibility_for_admins
3166
                    );
3167
3168
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
3169
                    if ($courseIsVisible === false || $sessionCourseVisibility == SESSION_INVISIBLE) {
3170
                        $blockedCourseCount++;
3171
                    }
3172
                }
3173
3174
                // If all courses are blocked then no show in the list.
3175
                if ($blockedCourseCount === count($courseList)) {
3176
                    $visibility = SESSION_INVISIBLE;
3177
                } else {
3178
                    $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...
3179
                }
3180
            }
3181
3182
            switch ($visibility) {
3183
                case SESSION_VISIBLE_READ_ONLY:
3184
                case SESSION_VISIBLE:
3185
                case SESSION_AVAILABLE:
3186
                    break;
3187
                case SESSION_INVISIBLE:
3188
                    if ($ignore_visibility_for_admins === false) {
3189
                        continue 2;
3190
                    }
3191
            }
3192
3193
            $collapsed = '';
3194
            $collapsedAction = '';
3195
            if ($collapsable) {
3196
                $collapsableData = Sessionmanager::getCollapsableData(
0 ignored issues
show
Bug introduced by
The method getCollapsableData() does not exist on SessionManager. ( Ignorable by Annotation )

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

3196
                /** @scrutinizer ignore-call */ 
3197
                $collapsableData = Sessionmanager::getCollapsableData(

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...
3197
                    $user_id,
3198
                    $session_id,
3199
                    $extraField,
3200
                    $collapsableLink
3201
                );
3202
                $collapsed = $collapsableData['collapsed'];
3203
                $collapsedAction = $collapsableData['collapsable_link'];
3204
            }
3205
3206
            $categories[$row['session_category_id']]['sessions'][] = [
3207
                'session_name' => $row['name'],
3208
                'session_id' => $row['id'],
3209
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
3210
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
3211
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
3212
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
3213
                'courses' => $courseList,
3214
                'collapsed' => $collapsed,
3215
                'collapsable_link' => $collapsedAction,
3216
            ];
3217
        }
3218
3219
        return $categories;
3220
    }
3221
3222
    /**
3223
     * Gives a list of [session_id-course_code] => [status] for the current user.
3224
     *
3225
     * @param int $user_id
3226
     * @param int $sessionLimit
3227
     *
3228
     * @return array list of statuses (session_id-course_code => status)
3229
     */
3230
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
3231
    {
3232
        // Database Table Definitions
3233
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3234
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
3235
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3236
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3237
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3238
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3239
3240
        if ($user_id != strval(intval($user_id))) {
3241
            return [];
3242
        }
3243
3244
        // We filter the courses from the URL
3245
        $join_access_url = $where_access_url = '';
3246
3247
        if (api_get_multiple_access_url()) {
3248
            $access_url_id = api_get_current_access_url_id();
3249
            if ($access_url_id != -1) {
3250
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3251
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
3252
                $where_access_url = " AND access_url_id = $access_url_id ";
3253
            }
3254
        }
3255
3256
        // Courses in which we subscribed out of any session
3257
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3258
3259
        $sql = "SELECT
3260
                    course.code,
3261
                    course_rel_user.status course_rel_status,
3262
                    course_rel_user.sort sort,
3263
                    course_rel_user.user_course_cat user_course_cat
3264
                 FROM $tbl_course_user course_rel_user
3265
                 LEFT JOIN $tbl_course course
3266
                 ON course.id = course_rel_user.c_id
3267
                 LEFT JOIN $tbl_user_course_category user_course_category
3268
                 ON course_rel_user.user_course_cat = user_course_category.id
3269
                 $join_access_url
3270
                 WHERE
3271
                    course_rel_user.user_id = '".$user_id."' AND
3272
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
3273
                    $where_access_url
3274
                 ORDER BY user_course_category.sort, course_rel_user.sort, course.title ASC";
3275
3276
        $course_list_sql_result = Database::query($sql);
3277
3278
        $personal_course_list = [];
3279
        if (Database::num_rows($course_list_sql_result) > 0) {
3280
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3281
                $course_info = api_get_course_info($result_row['code']);
3282
                $result_row['course_info'] = $course_info;
3283
                $personal_course_list[] = $result_row;
3284
            }
3285
        }
3286
3287
        $coachCourseConditions = '';
3288
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
3289
        if (api_is_allowed_to_create_course()) {
3290
            $sessionListFromCourseCoach = [];
3291
            $sql = " SELECT DISTINCT session_id
3292
                    FROM $tbl_session_course_user
3293
                    WHERE user_id = $user_id AND status = 2 ";
3294
3295
            $result = Database::query($sql);
3296
            if (Database::num_rows($result)) {
3297
                $result = Database::store_result($result);
3298
                foreach ($result as $session) {
3299
                    $sessionListFromCourseCoach[] = $session['session_id'];
3300
                }
3301
            }
3302
            if (!empty($sessionListFromCourseCoach)) {
3303
                $condition = implode("','", $sessionListFromCourseCoach);
3304
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
3305
            }
3306
        }
3307
3308
        // Get the list of sessions where the user is subscribed
3309
        // This is divided into two different queries
3310
        $sessions = [];
3311
        $sessionLimitRestriction = '';
3312
        if (!empty($sessionLimit)) {
3313
            $sessionLimit = (int) $sessionLimit;
3314
            $sessionLimitRestriction = "LIMIT $sessionLimit";
3315
        }
3316
3317
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
3318
                FROM $tbl_session_user su INNER JOIN $tbl_session s
3319
                ON (s.id = su.session_id)
3320
                WHERE (
3321
                    su.user_id = $user_id AND
3322
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
3323
                )
3324
                $coachCourseConditions
3325
                ORDER BY access_start_date, access_end_date, name
3326
                $sessionLimitRestriction
3327
        ";
3328
3329
        $result = Database::query($sql);
3330
        if (Database::num_rows($result) > 0) {
3331
            while ($row = Database::fetch_assoc($result)) {
3332
                $sessions[$row['id']] = $row;
3333
            }
3334
        }
3335
3336
        $sql = "SELECT DISTINCT
3337
                id, name, access_start_date, access_end_date
3338
                FROM $tbl_session s
3339
                WHERE (
3340
                    id_coach = $user_id
3341
                )
3342
                $coachCourseConditions
3343
                ORDER BY access_start_date, access_end_date, name";
3344
3345
        $result = Database::query($sql);
3346
        if (Database::num_rows($result) > 0) {
3347
            while ($row = Database::fetch_assoc($result)) {
3348
                if (empty($sessions[$row['id']])) {
3349
                    $sessions[$row['id']] = $row;
3350
                }
3351
            }
3352
        }
3353
3354
        if (api_is_allowed_to_create_course()) {
3355
            foreach ($sessions as $enreg) {
3356
                $session_id = $enreg['id'];
3357
                $session_visibility = api_get_session_visibility($session_id);
3358
3359
                if ($session_visibility == SESSION_INVISIBLE) {
3360
                    continue;
3361
                }
3362
3363
                // This query is horribly slow when more than a few thousand
3364
                // users and just a few sessions to which they are subscribed
3365
                $sql = "SELECT DISTINCT
3366
                        course.code code,
3367
                        course.title i,
3368
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
3369
                        email, course.course_language l,
3370
                        1 sort,
3371
                        category_code user_course_cat,
3372
                        access_start_date,
3373
                        access_end_date,
3374
                        session.id as session_id,
3375
                        session.name as session_name
3376
                    FROM $tbl_session_course_user as session_course_user
3377
                    INNER JOIN $tbl_course AS course
3378
                        ON course.id = session_course_user.c_id
3379
                    INNER JOIN $tbl_session as session
3380
                        ON session.id = session_course_user.session_id
3381
                    LEFT JOIN $tbl_user as user
3382
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
3383
                    WHERE
3384
                        session_course_user.session_id = $session_id AND (
3385
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
3386
                            OR session.id_coach = $user_id
3387
                        )
3388
                    ORDER BY i";
3389
                $course_list_sql_result = Database::query($sql);
3390
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3391
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3392
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3393
                    $personal_course_list[$key] = $result_row;
3394
                }
3395
            }
3396
        }
3397
3398
        foreach ($sessions as $enreg) {
3399
            $session_id = $enreg['id'];
3400
            $session_visibility = api_get_session_visibility($session_id);
3401
            if ($session_visibility == SESSION_INVISIBLE) {
3402
                continue;
3403
            }
3404
3405
            /* This query is very similar to the above query,
3406
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3407
            $sql = "SELECT DISTINCT
3408
                course.code code,
3409
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
3410
                email,
3411
                course.course_language l,
3412
                1 sort,
3413
                category_code user_course_cat,
3414
                access_start_date,
3415
                access_end_date,
3416
                session.id as session_id,
3417
                session.name as session_name,
3418
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
3419
            FROM $tbl_session_course_user as session_course_user
3420
            INNER JOIN $tbl_course AS course
3421
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
3422
            INNER JOIN $tbl_session as session 
3423
            ON session_course_user.session_id = session.id
3424
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
3425
            WHERE session_course_user.user_id = $user_id
3426
            ORDER BY i";
3427
3428
            $course_list_sql_result = Database::query($sql);
3429
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3430
                $result_row['course_info'] = api_get_course_info($result_row['code']);
3431
                $key = $result_row['session_id'].' - '.$result_row['code'];
3432
                if (!isset($personal_course_list[$key])) {
3433
                    $personal_course_list[$key] = $result_row;
3434
                }
3435
            }
3436
        }
3437
3438
        return $personal_course_list;
3439
    }
3440
3441
    /**
3442
     * Gives a list of courses for the given user in the given session.
3443
     *
3444
     * @param int $user_id
3445
     * @param int $session_id
3446
     *
3447
     * @return array list of statuses (session_id-course_code => status)
3448
     */
3449
    public static function get_courses_list_by_session($user_id, $session_id)
3450
    {
3451
        // Database Table Definitions
3452
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3453
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
3454
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3455
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3456
3457
        $user_id = (int) $user_id;
3458
        $session_id = (int) $session_id;
3459
        //we filter the courses from the URL
3460
        $join_access_url = $where_access_url = '';
3461
3462
        if (api_get_multiple_access_url()) {
3463
            $urlId = api_get_current_access_url_id();
3464
            if ($urlId != -1) {
3465
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3466
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
3467
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
3468
            }
3469
        }
3470
3471
        /* This query is very similar to the query below, but it will check the
3472
        session_rel_course_user table if there are courses registered
3473
        to our user or not */
3474
        $sql = "SELECT DISTINCT
3475
                    c.visibility,
3476
                    c.id as real_id,
3477
                    c.code as course_code,
3478
                    sc.position
3479
                FROM $tbl_session_course_user as scu
3480
                INNER JOIN $tbl_session_course sc
3481
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3482
                INNER JOIN $tableCourse as c
3483
                ON (scu.c_id = c.id)
3484
                $join_access_url
3485
                WHERE
3486
                    scu.user_id = $user_id AND
3487
                    scu.session_id = $session_id
3488
                    $where_access_url
3489
                ORDER BY sc.position ASC";
3490
3491
        $myCourseList = [];
3492
        $courses = [];
3493
        $result = Database::query($sql);
3494
        if (Database::num_rows($result) > 0) {
3495
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3496
                $result_row['status'] = 5;
3497
                if (!in_array($result_row['real_id'], $courses)) {
3498
                    $position = $result_row['position'];
3499
                    if (!isset($myCourseList[$position])) {
3500
                        $myCourseList[$position] = $result_row;
3501
                    } else {
3502
                        $myCourseList[] = $result_row;
3503
                    }
3504
                    $courses[] = $result_row['real_id'];
3505
                }
3506
            }
3507
        }
3508
3509
        if (api_is_allowed_to_create_course()) {
3510
            $sql = "SELECT DISTINCT
3511
                        c.visibility, 
3512
                        c.id as real_id,
3513
                        c.code as course_code,
3514
                        sc.position
3515
                    FROM $tbl_session_course_user as scu
3516
                    INNER JOIN $tbl_session as s
3517
                    ON (scu.session_id = s.id)
3518
                    INNER JOIN $tbl_session_course sc
3519
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3520
                    INNER JOIN $tableCourse as c
3521
                    ON (scu.c_id = c.id)
3522
                    $join_access_url
3523
                    WHERE
3524
                      s.id = $session_id AND
3525
                      (
3526
                        (scu.user_id = $user_id AND scu.status = 2) OR
3527
                        s.id_coach = $user_id
3528
                      )
3529
                    $where_access_url
3530
                    ORDER BY sc.position ASC";
3531
            $result = Database::query($sql);
3532
3533
            if (Database::num_rows($result) > 0) {
3534
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3535
                    $result_row['status'] = 2;
3536
                    if (!in_array($result_row['real_id'], $courses)) {
3537
                        $position = $result_row['position'];
3538
                        if (!isset($myCourseList[$position])) {
3539
                            $myCourseList[$position] = $result_row;
3540
                        } else {
3541
                            $myCourseList[] = $result_row;
3542
                        }
3543
                        $courses[] = $result_row['real_id'];
3544
                    }
3545
                }
3546
            }
3547
        }
3548
3549
        if (api_is_drh()) {
3550
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3551
            $sessionList = array_keys($sessionList);
3552
            if (in_array($session_id, $sessionList)) {
3553
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3554
                if (!empty($courseList)) {
3555
                    foreach ($courseList as $course) {
3556
                        if (!in_array($course['id'], $courses)) {
3557
                            $position = $course['position'];
3558
                            if (!isset($myCourseList[$position])) {
3559
                                $myCourseList[$position] = $course;
3560
                            } else {
3561
                                $myCourseList[] = $course;
3562
                            }
3563
                        }
3564
                    }
3565
                }
3566
            }
3567
        } else {
3568
            //check if user is general coach for this session
3569
            $sessionInfo = api_get_session_info($session_id);
3570
            if ($sessionInfo['id_coach'] == $user_id) {
3571
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3572
                if (!empty($courseList)) {
3573
                    foreach ($courseList as $course) {
3574
                        if (!in_array($course['id'], $courses)) {
3575
                            $position = $course['position'];
3576
                            if (!isset($myCourseList[$position])) {
3577
                                $myCourseList[$position] = $course;
3578
                            } else {
3579
                                $myCourseList[] = $course;
3580
                            }
3581
                        }
3582
                    }
3583
                }
3584
            }
3585
        }
3586
3587
        if (!empty($myCourseList)) {
3588
            ksort($myCourseList);
3589
        }
3590
3591
        return $myCourseList;
3592
    }
3593
3594
    /**
3595
     * Get user id from a username.
3596
     *
3597
     * @param string $username
3598
     *
3599
     * @return int User ID (or false if not found)
3600
     */
3601
    public static function get_user_id_from_username($username)
3602
    {
3603
        if (empty($username)) {
3604
            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...
3605
        }
3606
        $username = trim($username);
3607
        $username = Database::escape_string($username);
3608
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3609
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3610
        $res = Database::query($sql);
3611
3612
        if ($res === false) {
3613
            return false;
3614
        }
3615
        if (Database::num_rows($res) !== 1) {
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
        $row = Database::fetch_array($res);
3619
3620
        return $row['id'];
3621
    }
3622
3623
    /**
3624
     * Get the users files upload from his share_folder.
3625
     *
3626
     * @param string $user_id      User ID
3627
     * @param string $course       course directory
3628
     * @param string $resourceType resource type: images, all
3629
     *
3630
     * @return string
3631
     */
3632
    public static function get_user_upload_files_by_course(
3633
        $user_id,
3634
        $course,
3635
        $resourceType = 'all'
3636
    ) {
3637
        $return = '';
3638
        if (!empty($user_id) && !empty($course)) {
3639
            $user_id = (int) $user_id;
3640
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3641
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3642
            $file_list = [];
3643
3644
            if (is_dir($path)) {
3645
                $handle = opendir($path);
3646
                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

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

6462
            'Chamilo\\UserBundle\\Entity\\User' => new \Chamilo\UserBundle\Security\Encoder(/** @scrutinizer ignore-type */ $encryption),
Loading history...
6463
        ];
6464
6465
        $encoderFactory = new EncoderFactory($encoders);
6466
6467
        return $encoderFactory;
6468
    }
6469
6470
    /**
6471
     * @param User $user
6472
     *
6473
     * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface
6474
     */
6475
    private static function getEncoder(User $user)
6476
    {
6477
        $encoderFactory = self::getEncoderFactory();
6478
6479
        return $encoderFactory->getEncoder($user);
6480
    }
6481
6482
    /**
6483
     * Disables or enables a user.
6484
     *
6485
     * @param int $user_id
6486
     * @param int $active  Enable or disable
6487
     *
6488
     * @return bool True on success, false on failure
6489
     * @assert (-1,0) === false
6490
     * @assert (1,1) === true
6491
     */
6492
    private static function change_active_state($user_id, $active)
6493
    {
6494
        if (strval(intval($user_id)) != $user_id) {
6495
            return false;
6496
        }
6497
        if ($user_id < 1) {
6498
            return false;
6499
        }
6500
        $user_id = intval($user_id);
6501
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6502
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
6503
        $r = Database::query($sql);
6504
        $ev = LOG_USER_DISABLE;
6505
        if ($active == 1) {
6506
            $ev = LOG_USER_ENABLE;
6507
        }
6508
        if ($r !== false) {
6509
            Event::addEvent($ev, LOG_USER_ID, $user_id);
6510
        }
6511
6512
        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...
6513
    }
6514
6515
    /**
6516
     * Get either a Gravatar URL or complete image tag for a specified email address.
6517
     *
6518
     * @param string $email The email address
6519
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
6520
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
6521
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
6522
     * @param bool   $img   True to return a complete IMG tag False for just the URL
6523
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
6524
     *
6525
     * @return string containing either just a URL or a complete image tag
6526
     * @source http://gravatar.com/site/implement/images/php/
6527
     */
6528
    private static function getGravatar(
6529
        $email,
6530
        $s = 80,
6531
        $d = 'mm',
6532
        $r = 'g',
6533
        $img = false,
6534
        $atts = []
6535
    ) {
6536
        $url = 'http://www.gravatar.com/avatar/';
6537
        if (!empty($_SERVER['HTTPS'])) {
6538
            $url = 'https://secure.gravatar.com/avatar/';
6539
        }
6540
        $url .= md5(strtolower(trim($email)));
6541
        $url .= "?s=$s&d=$d&r=$r";
6542
        if ($img) {
6543
            $url = '<img src="'.$url.'"';
6544
            foreach ($atts as $key => $val) {
6545
                $url .= ' '.$key.'="'.$val.'"';
6546
            }
6547
            $url .= ' />';
6548
        }
6549
6550
        return $url;
6551
    }
6552
}
6553