Passed
Push — master ( 06a804...32f36d )
by Julito
13:31
created

UserManager::get_user_picture_path_by_id()   B

Complexity

Conditions 8
Paths 15

Size

Total Lines 51
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 33
nc 15
nop 3
dl 0
loc 51
rs 8.1475
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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());
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
     *
172
     * @return mixed new user id - if the new user creation succeeds, false otherwise
173
     * @desc The function tries to retrieve user id from the session.
174
     * If it exists, the current user id is the creator id. If a problem arises,
175
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
176
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
177
     */
178
    public static function create_user(
179
        $firstName,
180
        $lastName,
181
        $status,
182
        $email,
183
        $loginName,
184
        $password,
185
        $official_code = '',
186
        $language = '',
187
        $phone = '',
188
        $picture_uri = '',
189
        $authSource = PLATFORM_AUTH_SOURCE,
190
        $expirationDate = null,
191
        $active = 1,
192
        $hr_dept_id = 0,
193
        $extra = [],
194
        $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

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

352
            $user->setExpirationDate(/** @scrutinizer ignore-type */ $expirationDate);
Loading history...
353
        }
354
        try {
355
            $userManager->updateUser($user);
356
            $userId = $user->getId();
357
358
            // Add user to a group
359
            $statusToGroup = [
360
                COURSEMANAGER => 'TEACHER',
361
                STUDENT => 'STUDENT',
362
                DRH => 'RRHH',
363
                SESSIONADMIN => 'SESSION_ADMIN',
364
                STUDENT_BOSS => 'STUDENT_BOSS',
365
                INVITEE => 'INVITEE',
366
            ];
367
368
            $group = Container::$container->get('fos_user.group_manager')->findGroupBy(['code' => $statusToGroup[$status]]);
369
            if ($group) {
370
                $user->addGroup($group);
371
                $userManager->updateUser($user);
372
            }
373
        } catch (Exception $e) {
374
            error_log($e->getMessage());
375
        }
376
377
        if (!empty($userId)) {
378
            $return = $userId;
379
            $sql = "UPDATE $table_user SET user_id = $return WHERE id = $return";
380
            Database::query($sql);
381
382
            if ($isAdmin) {
383
                self::add_user_as_admin($user);
384
            }
385
386
            if (api_get_multiple_access_url()) {
387
                UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
388
            } else {
389
                //we are adding by default the access_url_user table with access_url_id = 1
390
                UrlManager::add_user_to_url($userId, 1);
391
            }
392
393
            $extra['item_id'] = $userId;
394
395
            if (is_array($extra) && count($extra) > 0) {
396
                $courseFieldValue = new ExtraFieldValue('user');
397
                $courseFieldValue->saveFieldValues($extra);
398
            } else {
399
                // Create notify settings by default
400
                self::update_extra_field_value(
401
                    $userId,
402
                    'mail_notify_invitation',
403
                    '1'
404
                );
405
                self::update_extra_field_value(
406
                    $userId,
407
                    'mail_notify_message',
408
                    '1'
409
                );
410
                self::update_extra_field_value(
411
                    $userId,
412
                    'mail_notify_group_message',
413
                    '1'
414
                );
415
            }
416
417
            self::update_extra_field_value(
418
                $userId,
419
                'already_logged_in',
420
                'false'
421
            );
422
423
            if (!empty($email) && $send_mail) {
424
                $recipient_name = api_get_person_name(
425
                    $firstName,
426
                    $lastName,
427
                    null,
428
                    PERSON_NAME_EMAIL_ADDRESS
429
                );
430
                $tplSubject = new Template(
431
                    null,
432
                    false,
433
                    false,
434
                    false,
435
                    false,
436
                    false
437
                );
438
                $layoutSubject = $tplSubject->get_template(
439
                    'mail/subject_registration_platform.tpl'
440
                );
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
                $phoneNumber = isset($extra['mobile_phone_number']) ? $extra['mobile_phone_number'] : null;
480
481
                $additionalParameters = [
482
                    'smsType' => SmsPlugin::WELCOME_LOGIN_PASSWORD,
483
                    'userId' => $return,
484
                    'mobilePhoneNumber' => $phoneNumber,
485
                    'password' => $original_password,
486
                ];
487
488
                $twoEmail = api_get_configuration_value('send_two_inscription_confirmation_mail');
489
                if ($twoEmail === true) {
490
                    $layoutContent = $tplContent->get_template('mail/new_user_first_email_confirmation.tpl');
491
                    $emailBody = $tplContent->fetch($layoutContent);
492
493
                    api_mail_html(
494
                        $recipient_name,
495
                        $email,
496
                        $emailSubject,
497
                        $emailBody,
498
                        $sender_name,
499
                        $email_admin,
500
                        null,
501
                        null,
502
                        null,
503
                        $additionalParameters
504
                    );
505
506
                    $layoutContent = $tplContent->get_template('mail/new_user_second_email_confirmation.tpl');
507
                    $emailBody = $tplContent->fetch($layoutContent);
508
509
                    api_mail_html(
510
                        $recipient_name,
511
                        $email,
512
                        $emailSubject,
513
                        $emailBody,
514
                        $sender_name,
515
                        $email_admin,
516
                        null,
517
                        null,
518
                        null,
519
                        $additionalParameters
520
                    );
521
                } else {
522
                    $sendToInbox = api_get_configuration_value('send_inscription_msg_to_inbox');
523
                    if ($sendToInbox) {
524
                        $adminList = self::get_all_administrators();
525
                        $senderId = 1;
526
                        if (!empty($adminList)) {
527
                            $adminInfo = current($adminList);
528
                            $senderId = $adminInfo['user_id'];
529
                        }
530
531
                        MessageManager::send_message_simple(
532
                            $userId,
533
                            $emailSubject,
534
                            $emailBody,
535
                            $senderId
536
                        );
537
                    } else {
538
                        api_mail_html(
539
                            $recipient_name,
540
                            $email,
541
                            $emailSubject,
542
                            $emailBody,
543
                            $sender_name,
544
                            $email_admin,
545
                            null,
546
                            null,
547
                            null,
548
                            $additionalParameters
549
                        );
550
                    }
551
                }
552
553
                $notification = api_get_configuration_value('send_notification_when_user_added');
554
                if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
555
                    foreach ($notification['admins'] as $adminId) {
556
                        $emailSubjectToAdmin = get_lang('UserAdded').': '.api_get_person_name($firstName, $lastName);
557
                        MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
558
                    }
559
                }
560
561
                if ($sendEmailToAllAdmins) {
562
                    $adminList = self::get_all_administrators();
563
564
                    $tplContent = new Template(
565
                        null,
566
                        false,
567
                        false,
568
                        false,
569
                        false,
570
                        false
571
                    );
572
                    // variables for the default template
573
                    $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
574
                    $tplContent->assign('user_added', $user);
575
                    $renderer = FormValidator::getDefaultRenderer();
576
                    // Form template
577
                    $elementTemplate = ' {label}: {element} <br />';
578
                    $renderer->setElementTemplate($elementTemplate);
579
                    /** @var FormValidator $form */
580
                    $form->freeze(null, $elementTemplate);
581
                    $form->removeElement('submit');
582
                    $formData = $form->returnForm();
583
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
584
                    $tplContent->assign('link', Display::url($url, $url));
585
                    $tplContent->assign('form', $formData);
586
587
                    $layoutContent = $tplContent->get_template('mail/content_registration_platform_to_admin.tpl');
588
                    $emailBody = $tplContent->fetch($layoutContent);
589
                    $subject = get_lang('UserAdded');
590
591
                    foreach ($adminList as $adminId => $data) {
592
                        MessageManager::send_message_simple(
593
                            $adminId,
594
                            $subject,
595
                            $emailBody,
596
                            $userId
597
                        );
598
                    }
599
                }
600
                /* ENDS MANAGE EVENT WITH MAIL */
601
            }
602
603
            if (!empty($hook)) {
604
                $hook->setEventData([
605
                    'return' => $userId,
606
                    'originalPassword' => $original_password,
607
                ]);
608
                $hook->notifyCreateUser(HOOK_EVENT_TYPE_POST);
609
            }
610
            Event::addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId, null, $creatorId);
611
        } else {
612
            Display::addFlash(
613
                Display::return_message(get_lang('ErrorContactPlatformAdmin'))
614
            );
615
616
            return false;
617
        }
618
619
        return $return;
620
    }
621
622
    /**
623
     * Can user be deleted? This function checks whether there's a course
624
     * in which the given user is the
625
     * only course administrator. If that is the case, the user can't be
626
     * deleted because the course would remain without a course admin.
627
     *
628
     * @param int $user_id The user id
629
     *
630
     * @return bool true if user can be deleted
631
     * @assert (null) === false
632
     * @assert (-1) === false
633
     * @assert ('abc') === false
634
     */
635
    public static function canDeleteUser($user_id)
636
    {
637
        $deny = api_get_configuration_value('deny_delete_users');
638
639
        if ($deny) {
640
            return false;
641
        }
642
643
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
644
        if ($user_id != strval(intval($user_id))) {
645
            return false;
646
        }
647
        if ($user_id === false) {
648
            return false;
649
        }
650
        $sql = "SELECT * FROM $table_course_user
651
                WHERE status = 1 AND user_id = ".$user_id;
652
        $res = Database::query($sql);
653
        while ($course = Database::fetch_object($res)) {
654
            $sql = "SELECT id FROM $table_course_user
655
                    WHERE status=1 AND c_id = ".intval($course->c_id);
656
            $res2 = Database::query($sql);
657
            if (Database::num_rows($res2) == 1) {
658
                return false;
659
            }
660
        }
661
662
        return true;
663
    }
664
665
    /**
666
     * Delete a user from the platform, and all its belongings. This is a
667
     * very dangerous function that should only be accessible by
668
     * super-admins. Other roles should only be able to disable a user,
669
     * which removes access to the platform but doesn't delete anything.
670
     *
671
     * @param int The ID of th user to be deleted
672
     *
673
     * @throws Exception
674
     *
675
     * @return bool true if user is successfully deleted, false otherwise
676
     * @assert (null) === false
677
     * @assert ('abc') === false
678
     */
679
    public static function delete_user($user_id)
680
    {
681
        $user_id = (int) $user_id;
682
683
        if (empty($user_id)) {
684
            return false;
685
        }
686
687
        if (!self::canDeleteUser($user_id)) {
688
            return false;
689
        }
690
691
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
692
        $usergroup_rel_user = Database::get_main_table(TABLE_USERGROUP_REL_USER);
693
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
694
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
695
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
696
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
697
        $table_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
698
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
699
        $table_group = Database::get_course_table(TABLE_GROUP_USER);
700
        $table_work = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
701
702
        // Unsubscribe the user from all groups in all his courses
703
        $sql = "SELECT c.id 
704
                FROM $table_course c 
705
                INNER JOIN $table_course_user cu
706
                ON (c.id = cu.c_id)
707
                WHERE
708
                    cu.user_id = '".$user_id."' AND
709
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
710
                ";
711
712
        $res = Database::query($sql);
713
        while ($course = Database::fetch_object($res)) {
714
            $sql = "DELETE FROM $table_group
715
                    WHERE c_id = {$course->id} AND user_id = $user_id";
716
            Database::query($sql);
717
        }
718
719
        // Unsubscribe user from usergroup_rel_user
720
        $sql = "DELETE FROM $usergroup_rel_user WHERE user_id = '".$user_id."'";
721
        Database::query($sql);
722
723
        // Unsubscribe user from all courses
724
        $sql = "DELETE FROM $table_course_user WHERE user_id = '".$user_id."'";
725
        Database::query($sql);
726
727
        // Unsubscribe user from all courses in sessions
728
        $sql = "DELETE FROM $table_session_course_user WHERE user_id = '".$user_id."'";
729
        Database::query($sql);
730
731
        // If the user was added as a id_coach then set the current admin as coach see BT#
732
        $currentUserId = api_get_user_id();
733
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
734
                WHERE id_coach = '".$user_id."'";
735
        Database::query($sql);
736
737
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
738
                WHERE session_admin_id = '".$user_id."'";
739
        Database::query($sql);
740
741
        // Unsubscribe user from all sessions
742
        $sql = "DELETE FROM $table_session_user
743
                WHERE user_id = '".$user_id."'";
744
        Database::query($sql);
745
746
        // Delete user picture
747
        /* TODO: Logic about api_get_setting('split_users_upload_directory') == 'true'
748
        a user has 4 different sized photos to be deleted. */
749
        $user_info = api_get_user_info($user_id);
750
751
        if (strlen($user_info['picture_uri']) > 0) {
752
            $path = self::getUserPathById($user_id, 'system');
753
            $img_path = $path.$user_info['picture_uri'];
754
            if (file_exists($img_path)) {
755
                unlink($img_path);
756
            }
757
        }
758
759
        // Delete the personal course categories
760
        $course_cat_table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
761
        $sql = "DELETE FROM $course_cat_table WHERE user_id = '".$user_id."'";
762
        Database::query($sql);
763
764
        // Delete user from the admin table
765
        $sql = "DELETE FROM $table_admin WHERE user_id = '".$user_id."'";
766
        Database::query($sql);
767
768
        // Delete the personal agenda-items from this user
769
        $agenda_table = Database::get_main_table(TABLE_PERSONAL_AGENDA);
770
        $sql = "DELETE FROM $agenda_table WHERE user = '".$user_id."'";
771
        Database::query($sql);
772
773
        $gradebook_results_table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_RESULT);
774
        $sql = 'DELETE FROM '.$gradebook_results_table.' WHERE user_id = '.$user_id;
775
        Database::query($sql);
776
777
        $extraFieldValue = new ExtraFieldValue('user');
778
        $extraFieldValue->deleteValuesByItem($user_id);
779
780
        UrlManager::deleteUserFromAllUrls($user_id);
781
782
        if (api_get_setting('allow_social_tool') === 'true') {
783
            $userGroup = new UserGroup();
784
            //Delete user from portal groups
785
            $group_list = $userGroup->get_groups_by_user($user_id);
786
            if (!empty($group_list)) {
787
                foreach ($group_list as $group_id => $data) {
788
                    $userGroup->delete_user_rel_group($user_id, $group_id);
789
                }
790
            }
791
792
            // Delete user from friend lists
793
            SocialManager::remove_user_rel_user($user_id, true);
794
        }
795
796
        // Removing survey invitation
797
        SurveyManager::delete_all_survey_invitations_by_user($user_id);
798
799
        // Delete students works
800
        $sql = "DELETE FROM $table_work WHERE user_id = $user_id AND c_id <> 0";
801
        Database::query($sql);
802
803
        $sql = "UPDATE c_item_property SET to_user_id = NULL
804
                WHERE to_user_id = '".$user_id."'";
805
        Database::query($sql);
806
807
        $sql = "UPDATE c_item_property SET insert_user_id = NULL
808
                WHERE insert_user_id = '".$user_id."'";
809
        Database::query($sql);
810
811
        $sql = "UPDATE c_item_property SET lastedit_user_id = NULL
812
                WHERE lastedit_user_id = '".$user_id."'";
813
        Database::query($sql);
814
815
        // Skills
816
        $em = Database::getManager();
817
818
        $criteria = ['user' => $user_id];
819
        $skills = $em->getRepository('ChamiloCoreBundle:SkillRelUser')->findBy($criteria);
820
        if ($skills) {
821
            /** @var SkillRelUser $skill */
822
            foreach ($skills as $skill) {
823
                $comments = $skill->getComments();
824
                if ($comments) {
825
                    /** @var SkillRelUserComment $comment */
826
                    foreach ($comments as $comment) {
827
                        $em->remove($comment);
828
                    }
829
                }
830
                $em->remove($skill);
831
            }
832
            $em->flush();
833
        }
834
835
        // ExtraFieldSavedSearch
836
        $criteria = ['user' => $user_id];
837
        $searchList = $em->getRepository('ChamiloCoreBundle:ExtraFieldSavedSearch')->findBy($criteria);
838
        if ($searchList) {
839
            foreach ($searchList as $search) {
840
                $em->remove($search);
841
            }
842
            $em->flush();
843
        }
844
845
        $connection = Database::getManager()->getConnection();
846
        $tableExists = $connection->getSchemaManager()->tablesExist(['plugin_bbb_room']);
847
        if ($tableExists) {
848
            // Delete user from database
849
            $sql = "DELETE FROM plugin_bbb_room WHERE participant_id = $user_id";
850
            Database::query($sql);
851
        }
852
853
        // Delete user/ticket relationships :(
854
        $tableExists = $connection->getSchemaManager()->tablesExist(['ticket_ticket']);
855
        if ($tableExists) {
856
            TicketManager::deleteUserFromTicketSystem($user_id);
857
        }
858
859
        $tableExists = $connection->getSchemaManager()->tablesExist(['c_lp_category_user']);
860
        if ($tableExists) {
861
            $sql = "DELETE FROM c_lp_category_user WHERE user_id = $user_id";
862
            Database::query($sql);
863
        }
864
865
        // Delete user from database
866
        $sql = "DELETE FROM $table_user WHERE id = '".$user_id."'";
867
        Database::query($sql);
868
869
        // Add event to system log
870
        $user_id_manager = api_get_user_id();
871
872
        Event::addEvent(
873
            LOG_USER_DELETE,
874
            LOG_USER_ID,
875
            $user_id,
876
            api_get_utc_datetime(),
877
            $user_id_manager
878
        );
879
880
        Event::addEvent(
881
            LOG_USER_DELETE,
882
            LOG_USER_OBJECT,
883
            $user_info,
884
            api_get_utc_datetime(),
885
            $user_id_manager
886
        );
887
        $cacheAvailable = api_get_configuration_value('apc');
888
        if ($cacheAvailable === true) {
889
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
890
            if (apcu_exists($apcVar)) {
891
                apcu_delete($apcVar);
892
            }
893
        }
894
895
        return true;
896
    }
897
898
    /**
899
     * Deletes users completely. Can be called either as:
900
     * - UserManager::delete_users(1, 2, 3); or
901
     * - UserManager::delete_users(array(1, 2, 3));.
902
     *
903
     * @param array|int $ids
904
     *
905
     * @return bool True if at least one user was successfuly deleted. False otherwise.
906
     *
907
     * @author Laurent Opprecht
908
     *
909
     * @uses \UserManager::delete_user() to actually delete each user
910
     * @assert (null) === false
911
     * @assert (-1) === false
912
     * @assert (array(-1)) === false
913
     */
914
    public static function delete_users($ids = [])
915
    {
916
        $result = false;
917
        $ids = is_array($ids) ? $ids : func_get_args();
918
        if (!is_array($ids) || count($ids) == 0) {
919
            return false;
920
        }
921
        $ids = array_map('intval', $ids);
922
        foreach ($ids as $id) {
923
            if (empty($id) || $id < 1) {
924
                continue;
925
            }
926
            $deleted = self::delete_user($id);
927
            $result = $deleted || $result;
928
        }
929
930
        return $result;
931
    }
932
933
    /**
934
     * Disable users. Can be called either as:
935
     * - UserManager::deactivate_users(1, 2, 3);
936
     * - UserManager::deactivate_users(array(1, 2, 3));.
937
     *
938
     * @param array|int $ids
939
     *
940
     * @return bool
941
     *
942
     * @author Laurent Opprecht
943
     * @assert (null) === false
944
     * @assert (array(-1)) === false
945
     */
946
    public static function deactivate_users($ids = [])
947
    {
948
        if (empty($ids)) {
949
            return false;
950
        }
951
952
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
953
954
        $ids = is_array($ids) ? $ids : func_get_args();
955
        $ids = array_map('intval', $ids);
956
        $ids = implode(',', $ids);
957
958
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
959
        $r = Database::query($sql);
960
        if ($r !== false) {
961
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
962
963
            return true;
964
        }
965
966
        return false;
967
    }
968
969
    /**
970
     * Enable users. Can be called either as:
971
     * - UserManager::activate_users(1, 2, 3);
972
     * - UserManager::activate_users(array(1, 2, 3));.
973
     *
974
     * @param array|int IDs of the users to enable
975
     *
976
     * @return bool
977
     *
978
     * @author Laurent Opprecht
979
     * @assert (null) === false
980
     * @assert (array(-1)) === false
981
     */
982
    public static function activate_users($ids = [])
983
    {
984
        if (empty($ids)) {
985
            return false;
986
        }
987
988
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
989
990
        $ids = is_array($ids) ? $ids : func_get_args();
991
        $ids = array_map('intval', $ids);
992
        $ids = implode(',', $ids);
993
994
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
995
        $r = Database::query($sql);
996
        if ($r !== false) {
997
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
998
999
            return true;
1000
        }
1001
1002
        return false;
1003
    }
1004
1005
    /**
1006
     * Update user information with all the parameters passed to this function.
1007
     *
1008
     * @param int    $user_id         The ID of the user to be updated
1009
     * @param string $firstname       The user's firstname
1010
     * @param string $lastname        The user's lastname
1011
     * @param string $username        The user's username (login)
1012
     * @param string $password        The user's password
1013
     * @param string $auth_source     The authentication source (default: "platform")
1014
     * @param string $email           The user's e-mail address
1015
     * @param int    $status          The user's status
1016
     * @param string $official_code   The user's official code (usually just an internal institutional code)
1017
     * @param string $phone           The user's phone number
1018
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
1019
     * @param string $expiration_date The date at which this user will be automatically disabled
1020
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
1021
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
1022
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
1023
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
1024
     * @param string $language        The language to which the user account will be set
1025
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
1026
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
1027
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
1028
     * @param string $address
1029
     *
1030
     * @return bool|int False on error, or the user ID if the user information was updated
1031
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
1032
     */
1033
    public static function update_user(
1034
        $user_id,
1035
        $firstname,
1036
        $lastname,
1037
        $username,
1038
        $password = null,
1039
        $auth_source = null,
1040
        $email,
1041
        $status,
1042
        $official_code,
1043
        $phone,
1044
        $picture_uri,
1045
        $expiration_date,
1046
        $active,
1047
        $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

1047
        /** @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...
1048
        $hr_dept_id = 0,
1049
        $extra = null,
1050
        $language = 'english',
1051
        $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

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

2031
                /** @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...
2032
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
2033
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
2034
                @rename($path.$old_file, $path.$prefix.$old_file);
2035
            } else {
2036
                @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

2036
                /** @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...
2037
                @unlink($path.'medium_'.$old_file);
2038
                @unlink($path.'big_'.$old_file);
2039
                @unlink($path.$old_file);
2040
            }
2041
        }
2042
2043
        // Exit if only deletion has been requested. Return an empty picture name.
2044
        if ($delete) {
2045
            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...
2046
        }
2047
2048
        // Validation 2.
2049
        $allowed_types = api_get_supported_image_extensions();
2050
        $file = str_replace('\\', '/', $file);
2051
        $filename = (($pos = strrpos($file, '/')) !== false) ? substr($file, $pos + 1) : $file;
2052
        $extension = strtolower(substr(strrchr($filename, '.'), 1));
2053
        if (!in_array($extension, $allowed_types)) {
2054
            return false;
2055
        }
2056
2057
        // This is the common name for the new photos.
2058
        if (KEEP_THE_NAME_WHEN_CHANGE_IMAGE && $old_file != 'unknown.jpg') {
0 ignored issues
show
Bug introduced by
The constant KEEP_THE_NAME_WHEN_CHANGE_IMAGE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2059
            $old_extension = strtolower(substr(strrchr($old_file, '.'), 1));
2060
            $filename = in_array($old_extension, $allowed_types) ? substr($old_file, 0, -strlen($old_extension)) : $old_file;
2061
            $filename = (substr($filename, -1) == '.') ? $filename.$extension : $filename.'.'.$extension;
2062
        } else {
2063
            $filename = api_replace_dangerous_char($filename);
2064
            if (PREFIX_IMAGE_FILENAME_WITH_UID) {
0 ignored issues
show
Bug introduced by
The constant PREFIX_IMAGE_FILENAME_WITH_UID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2065
                $filename = uniqid('').'_'.$filename;
2066
            }
2067
            // We always prefix user photos with user ids, so on setting
2068
            // api_get_setting('split_users_upload_directory') === 'true'
2069
            // the correspondent directories to be found successfully.
2070
            $filename = $user_id.'_'.$filename;
2071
        }
2072
2073
        //Crop the image to adjust 1:1 ratio
2074
        $image = new Image($source_file);
2075
        $image->crop($cropParameters);
2076
2077
        // Storing the new photos in 4 versions with various sizes.
2078
        $userPath = self::getUserPathById($user_id, 'system');
2079
2080
        // If this path does not exist - we create it.
2081
        if (!file_exists($userPath)) {
2082
            mkdir($userPath, api_get_permissions_for_new_directories(), true);
2083
        }
2084
        $small = new Image($source_file);
2085
        $small->resize(32);
2086
        $small->send_image($userPath.'small_'.$filename);
2087
        $medium = new Image($source_file);
2088
        $medium->resize(85);
2089
        $medium->send_image($userPath.'medium_'.$filename);
2090
        $normal = new Image($source_file);
2091
        $normal->resize(200);
2092
        $normal->send_image($userPath.$filename);
2093
2094
        $big = new Image($source_file); // This is the original picture.
2095
        $big->send_image($userPath.'big_'.$filename);
2096
2097
        $result = $small && $medium && $normal && $big;
0 ignored issues
show
introduced by
$normal is of type Image, thus it always evaluated to true.
Loading history...
introduced by
$medium is of type Image, thus it always evaluated to true.
Loading history...
introduced by
$big is of type Image, thus it always evaluated to true.
Loading history...
2098
2099
        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...
2100
    }
2101
2102
    /**
2103
     * Update User extra field file type into {user_folder}/{$extra_field}.
2104
     *
2105
     * @param int    $user_id     The user internal identification number
2106
     * @param string $extra_field The $extra_field The extra field name
2107
     * @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...
2108
     * @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...
2109
     *
2110
     * @return bool|null return filename if success, but false
2111
     */
2112
    public static function update_user_extra_file(
2113
        $user_id,
2114
        $extra_field = '',
2115
        $file = null,
2116
        $source_file = null
2117
    ) {
2118
        // Add Filter
2119
        $source_file = Security::filter_filename($source_file);
2120
        $file = Security::filter_filename($file);
2121
2122
        if (empty($user_id)) {
2123
            return false;
2124
        }
2125
2126
        if (empty($source_file)) {
2127
            $source_file = $file;
2128
        }
2129
2130
        // User-reserved directory where extra file have to be placed.
2131
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2132
        $path = $path_info['dir'];
2133
        if (!empty($extra_field)) {
2134
            $path .= $extra_field.'/';
2135
        }
2136
        // If this directory does not exist - we create it.
2137
        if (!file_exists($path)) {
2138
            @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

2138
            /** @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...
2139
        }
2140
2141
        if (filter_extension($file)) {
2142
            if (@move_uploaded_file($source_file, $path.$file)) {
2143
                if ($extra_field) {
2144
                    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 null|boolean.
Loading history...
2145
                } else {
2146
                    return $file;
2147
                }
2148
            }
2149
        }
2150
2151
        return false; // this should be returned if anything went wrong with the upload
2152
    }
2153
2154
    /**
2155
     * Deletes user photos.
2156
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2157
     *
2158
     * @param int $userId the user internal identification number
2159
     *
2160
     * @return mixed returns empty string on success, FALSE on error
2161
     */
2162
    public static function deleteUserPicture($userId)
2163
    {
2164
        return self::update_user_picture($userId);
2165
    }
2166
2167
    /**
2168
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
2169
     * doesn't have any.
2170
     *
2171
     * If there has been a request to remove a production, the function will return
2172
     * without building the list unless forced to do so by the optional second
2173
     * parameter. This increases performance by avoiding to read through the
2174
     * productions on the filesystem before the removal request has been carried
2175
     * out because they'll have to be re-read afterwards anyway.
2176
     *
2177
     * @param int  $user_id    User id
2178
     * @param bool $force      Optional parameter to force building after a removal request
2179
     * @param bool $showDelete
2180
     *
2181
     * @return string A string containing the XHTML code to display the production list, or FALSE
2182
     */
2183
    public static function build_production_list($user_id, $force = false, $showDelete = false)
2184
    {
2185
        if (!$force && !empty($_POST['remove_production'])) {
2186
            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...
2187
        }
2188
2189
        $productions = self::get_user_productions($user_id);
2190
2191
        if (empty($productions)) {
2192
            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...
2193
        }
2194
2195
        $production_dir = self::getUserPathById($user_id, 'web');
2196
        $del_image = Display::returnIconPath('delete.png');
2197
        $add_image = Display::returnIconPath('archive.png');
2198
        $del_text = get_lang('Delete');
2199
        $production_list = '';
2200
        if (count($productions) > 0) {
2201
            $production_list = '<div class="files-production"><ul id="productions">';
2202
            foreach ($productions as $file) {
2203
                $production_list .= '<li>
2204
                    <img src="'.$add_image.'" />
2205
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2206
                        '.htmlentities($file).'
2207
                    </a>';
2208
                if ($showDelete) {
2209
                    $production_list .= '&nbsp;&nbsp;
2210
                        <input 
2211
                            style="width:16px;" 
2212
                            type="image" 
2213
                            name="remove_production['.urlencode($file).']" 
2214
                            src="'.$del_image.'" 
2215
                            alt="'.$del_text.'" 
2216
                            title="'.$del_text.' '.htmlentities($file).'" 
2217
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2218
                }
2219
            }
2220
            $production_list .= '</ul></div>';
2221
        }
2222
2223
        return $production_list;
2224
    }
2225
2226
    /**
2227
     * Returns an array with the user's productions.
2228
     *
2229
     * @param int $user_id User id
2230
     *
2231
     * @return array An array containing the user's productions
2232
     */
2233
    public static function get_user_productions($user_id)
2234
    {
2235
        $production_repository = self::getUserPathById($user_id, 'system');
2236
        $productions = [];
2237
2238
        if (is_dir($production_repository)) {
2239
            $handle = opendir($production_repository);
2240
            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

2240
            while ($file = readdir(/** @scrutinizer ignore-type */ $handle)) {
Loading history...
2241
                if ($file == '.' ||
2242
                    $file == '..' ||
2243
                    $file == '.htaccess' ||
2244
                    is_dir($production_repository.$file)
2245
                ) {
2246
                    // skip current/parent directory and .htaccess
2247
                    continue;
2248
                }
2249
2250
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2251
                    // User's photos should not be listed as productions.
2252
                    continue;
2253
                }
2254
                $productions[] = $file;
2255
            }
2256
        }
2257
2258
        return $productions;
2259
    }
2260
2261
    /**
2262
     * Remove a user production.
2263
     *
2264
     * @param int    $user_id    User id
2265
     * @param string $production The production to remove
2266
     *
2267
     * @return bool
2268
     */
2269
    public static function remove_user_production($user_id, $production)
2270
    {
2271
        $production_path = self::get_user_picture_path_by_id($user_id, 'system');
2272
        $production_file = $production_path['dir'].$production;
2273
        if (is_file($production_file)) {
2274
            unlink($production_file);
2275
2276
            return true;
2277
        }
2278
2279
        return false;
2280
    }
2281
2282
    /**
2283
     * Update an extra field value for a given user.
2284
     *
2285
     * @param int    $userId   User ID
2286
     * @param string $variable Field variable name
2287
     * @param string $value    Field value
2288
     *
2289
     * @return bool true if field updated, false otherwise
2290
     */
2291
    public static function update_extra_field_value($userId, $variable, $value = '')
2292
    {
2293
        $extraFieldValue = new ExtraFieldValue('user');
2294
        $params = [
2295
            'item_id' => $userId,
2296
            'variable' => $variable,
2297
            'value' => $value,
2298
        ];
2299
2300
        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...
2301
    }
2302
2303
    /**
2304
     * Get an array of extra fields with field details (type, default value and options).
2305
     *
2306
     * @param    int    Offset (from which row)
2307
     * @param    int    Number of items
2308
     * @param    int    Column on which sorting is made
2309
     * @param    string    Sorting direction
2310
     * @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...
2311
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2312
     *
2313
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2314
     */
2315
    public static function get_extra_fields(
2316
        $from = 0,
2317
        $number_of_items = 0,
2318
        $column = 5,
2319
        $direction = 'ASC',
2320
        $all_visibility = true,
2321
        $field_filter = null
2322
    ) {
2323
        $fields = [];
2324
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2325
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2326
        $columns = [
2327
            'id',
2328
            'variable',
2329
            'field_type',
2330
            'display_text',
2331
            'default_value',
2332
            'field_order',
2333
            'filter',
2334
        ];
2335
        $column = (int) $column;
2336
        $sort_direction = '';
2337
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2338
            $sort_direction = strtoupper($direction);
2339
        }
2340
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2341
        $sqlf = "SELECT * FROM $t_uf WHERE extra_field_type = $extraFieldType ";
2342
        if (!$all_visibility) {
2343
            $sqlf .= " AND visible_to_self = 1 ";
2344
        }
2345
        if (!is_null($field_filter)) {
2346
            $field_filter = (int) $field_filter;
2347
            $sqlf .= " AND filter = $field_filter ";
2348
        }
2349
        $sqlf .= " ORDER BY ".$columns[$column]." $sort_direction ";
2350
        if ($number_of_items != 0) {
2351
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2352
        }
2353
        $resf = Database::query($sqlf);
2354
        if (Database::num_rows($resf) > 0) {
2355
            while ($rowf = Database::fetch_array($resf)) {
2356
                $fields[$rowf['id']] = [
2357
                    0 => $rowf['id'],
2358
                    1 => $rowf['variable'],
2359
                    2 => $rowf['field_type'],
2360
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2361
                    4 => $rowf['default_value'],
2362
                    5 => $rowf['field_order'],
2363
                    6 => $rowf['visible_to_self'],
2364
                    7 => $rowf['changeable'],
2365
                    8 => $rowf['filter'],
2366
                    9 => [],
2367
                    10 => '<a name="'.$rowf['id'].'"></a>',
2368
                ];
2369
2370
                $sqlo = "SELECT * FROM $t_ufo
2371
                         WHERE field_id = ".$rowf['id']."
2372
                         ORDER BY option_order ASC";
2373
                $reso = Database::query($sqlo);
2374
                if (Database::num_rows($reso) > 0) {
2375
                    while ($rowo = Database::fetch_array($reso)) {
2376
                        $fields[$rowf['id']][9][$rowo['id']] = [
2377
                            0 => $rowo['id'],
2378
                            1 => $rowo['option_value'],
2379
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2380
                            3 => $rowo['option_order'],
2381
                        ];
2382
                    }
2383
                }
2384
            }
2385
        }
2386
2387
        return $fields;
2388
    }
2389
2390
    /**
2391
     * Build a list of extra file already uploaded in $user_folder/{$extra_field}/.
2392
     *
2393
     * @param $user_id
2394
     * @param $extra_field
2395
     * @param bool $force
2396
     * @param bool $showDelete
2397
     *
2398
     * @return bool|string
2399
     */
2400
    public static function build_user_extra_file_list(
2401
        $user_id,
2402
        $extra_field,
2403
        $force = false,
2404
        $showDelete = false
2405
    ) {
2406
        if (!$force && !empty($_POST['remove_'.$extra_field])) {
2407
            return true; // postpone reading from the filesystem
2408
        }
2409
2410
        $extra_files = self::get_user_extra_files($user_id, $extra_field);
2411
        if (empty($extra_files)) {
2412
            return false;
2413
        }
2414
2415
        $path_info = self::get_user_picture_path_by_id($user_id, 'web');
2416
        $path = $path_info['dir'];
2417
        $del_image = Display::returnIconPath('delete.png');
2418
2419
        $del_text = get_lang('Delete');
2420
        $extra_file_list = '';
2421
        if (count($extra_files) > 0) {
2422
            $extra_file_list = '<div class="files-production"><ul id="productions">';
2423
            foreach ($extra_files as $file) {
2424
                $filename = substr($file, strlen($extra_field) + 1);
2425
                $extra_file_list .= '<li>'.Display::return_icon('archive.png').
2426
                    '<a href="'.$path.$extra_field.'/'.urlencode($filename).'" target="_blank">
2427
                        '.htmlentities($filename).
2428
                    '</a> ';
2429
                if ($showDelete) {
2430
                    $extra_file_list .= '<input 
2431
                        style="width:16px;" 
2432
                        type="image" 
2433
                        name="remove_extra_'.$extra_field.'['.urlencode($file).']" 
2434
                        src="'.$del_image.'" 
2435
                        alt="'.$del_text.'"
2436
                        title="'.$del_text.' '.htmlentities($filename).'" 
2437
                        onclick="javascript: return confirmation(\''.htmlentities($filename).'\');" /></li>';
2438
                }
2439
            }
2440
            $extra_file_list .= '</ul></div>';
2441
        }
2442
2443
        return $extra_file_list;
2444
    }
2445
2446
    /**
2447
     * Get valid filenames in $user_folder/{$extra_field}/.
2448
     *
2449
     * @param $user_id
2450
     * @param $extra_field
2451
     * @param bool $full_path
2452
     *
2453
     * @return array
2454
     */
2455
    public static function get_user_extra_files($user_id, $extra_field, $full_path = false)
2456
    {
2457
        if (!$full_path) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
2458
            // Nothing to do
2459
        } else {
2460
            $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2461
            $path = $path_info['dir'];
2462
        }
2463
        $extra_data = self::get_extra_user_data_by_field($user_id, $extra_field);
2464
        $extra_files = $extra_data[$extra_field];
2465
        if (is_array($extra_files)) {
2466
            foreach ($extra_files as $key => $value) {
2467
                if (!$full_path) {
2468
                    // Relative path from user folder
2469
                    $files[] = $value;
2470
                } else {
2471
                    $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...
2472
                }
2473
            }
2474
        } elseif (!empty($extra_files)) {
2475
            if (!$full_path) {
2476
                // Relative path from user folder
2477
                $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...
2478
            } else {
2479
                $files[] = $path.$extra_files;
2480
            }
2481
        }
2482
2483
        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...
2484
    }
2485
2486
    /**
2487
     * Remove an {$extra_file} from the user folder $user_folder/{$extra_field}/.
2488
     *
2489
     * @param int    $user_id
2490
     * @param string $extra_field
2491
     * @param string $extra_file
2492
     *
2493
     * @return bool
2494
     */
2495
    public static function remove_user_extra_file($user_id, $extra_field, $extra_file)
2496
    {
2497
        $extra_file = Security::filter_filename($extra_file);
2498
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2499
        if (strpos($extra_file, $extra_field) !== false) {
2500
            $path_extra_file = $path_info['dir'].$extra_file;
2501
        } else {
2502
            $path_extra_file = $path_info['dir'].$extra_field.'/'.$extra_file;
2503
        }
2504
        if (is_file($path_extra_file)) {
2505
            unlink($path_extra_file);
2506
2507
            return true;
2508
        }
2509
2510
        return false;
2511
    }
2512
2513
    /**
2514
     * Creates a new extra field.
2515
     *
2516
     * @param string $variable    Field's internal variable name
2517
     * @param int    $fieldType   Field's type
2518
     * @param string $displayText Field's language var name
2519
     * @param string $default     Field's default value
2520
     *
2521
     * @return int
2522
     */
2523
    public static function create_extra_field(
2524
        $variable,
2525
        $fieldType,
2526
        $displayText,
2527
        $default
2528
    ) {
2529
        $extraField = new ExtraField('user');
2530
        $params = [
2531
            'variable' => $variable,
2532
            'field_type' => $fieldType,
2533
            'display_text' => $displayText,
2534
            'default_value' => $default,
2535
        ];
2536
2537
        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...
2538
    }
2539
2540
    /**
2541
     * Check if a field is available.
2542
     *
2543
     * @param string $variable
2544
     *
2545
     * @return bool
2546
     */
2547
    public static function is_extra_field_available($variable)
2548
    {
2549
        $extraField = new ExtraField('user');
2550
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2551
2552
        return !empty($data) ? true : false;
2553
    }
2554
2555
    /**
2556
     * Gets user extra fields data.
2557
     *
2558
     * @param    int    User ID
2559
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2560
     * @param    bool    Whether to return invisible fields as well
2561
     * @param    bool    Whether to split multiple-selection fields or not
2562
     *
2563
     * @return array Array of fields => value for the given user
2564
     */
2565
    public static function get_extra_user_data(
2566
        $user_id,
2567
        $prefix = false,
2568
        $allVisibility = true,
2569
        $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

2569
        /** @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...
2570
        $fieldFilter = null
2571
    ) {
2572
        // A sanity check.
2573
        if (empty($user_id)) {
2574
            $user_id = 0;
2575
        } else {
2576
            if ($user_id != strval(intval($user_id))) {
2577
                return [];
2578
            }
2579
        }
2580
        $extra_data = [];
2581
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2582
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2583
        $user_id = (int) $user_id;
2584
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2585
                FROM $t_uf f
2586
                WHERE
2587
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
2588
                ";
2589
        $filter_cond = '';
2590
2591
        if (!$allVisibility) {
2592
            if (isset($fieldFilter)) {
2593
                $fieldFilter = (int) $fieldFilter;
2594
                $filter_cond .= " AND filter = $fieldFilter ";
2595
            }
2596
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2597
        } else {
2598
            if (isset($fieldFilter)) {
2599
                $fieldFilter = (int) $fieldFilter;
2600
                $sql .= " AND filter = $fieldFilter ";
2601
            }
2602
        }
2603
2604
        $sql .= " ORDER BY f.field_order";
2605
2606
        $res = Database::query($sql);
2607
        if (Database::num_rows($res) > 0) {
2608
            while ($row = Database::fetch_array($res)) {
2609
                if ($row['type'] == self::USER_FIELD_TYPE_TAG) {
2610
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2611
                    $extra_data['extra_'.$row['fvar']] = $tags;
2612
                } else {
2613
                    $sqlu = "SELECT value as fval
2614
                            FROM $t_ufv
2615
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2616
                    $resu = Database::query($sqlu);
2617
                    // get default value
2618
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2619
                               WHERE id=".$row['id'];
2620
                    $res_df = Database::query($sql_df);
2621
2622
                    if (Database::num_rows($resu) > 0) {
2623
                        $rowu = Database::fetch_array($resu);
2624
                        $fval = $rowu['fval'];
2625
                        if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
2626
                            $fval = explode(';', $rowu['fval']);
2627
                        }
2628
                    } else {
2629
                        $row_df = Database::fetch_array($res_df);
2630
                        $fval = $row_df['fval_df'];
2631
                    }
2632
                    // We get here (and fill the $extra_data array) even if there
2633
                    // is no user with data (we fill it with default values)
2634
                    if ($prefix) {
2635
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
2636
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2637
                        } else {
2638
                            $extra_data['extra_'.$row['fvar']] = $fval;
2639
                        }
2640
                    } else {
2641
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
2642
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2643
                        } else {
2644
                            $extra_data[$row['fvar']] = $fval;
2645
                        }
2646
                    }
2647
                }
2648
            }
2649
        }
2650
2651
        return $extra_data;
2652
    }
2653
2654
    /** Get extra user data by field
2655
     * @param int    user ID
2656
     * @param string the internal variable name of the field
2657
     *
2658
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2659
     */
2660
    public static function get_extra_user_data_by_field(
2661
        $user_id,
2662
        $field_variable,
2663
        $prefix = false,
2664
        $all_visibility = true,
2665
        $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

2665
        /** @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...
2666
    ) {
2667
        // A sanity check.
2668
        if (empty($user_id)) {
2669
            $user_id = 0;
2670
        } else {
2671
            if ($user_id != strval(intval($user_id))) {
2672
                return [];
2673
            }
2674
        }
2675
        $extra_data = [];
2676
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2677
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2678
        $user_id = (int) $user_id;
2679
2680
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2681
                FROM $t_uf f
2682
                WHERE f.variable = '$field_variable' ";
2683
2684
        if (!$all_visibility) {
2685
            $sql .= " AND f.visible_to_self = 1 ";
2686
        }
2687
2688
        $sql .= " AND extra_field_type = ".EntityExtraField::USER_FIELD_TYPE;
2689
        $sql .= " ORDER BY f.field_order";
2690
2691
        $res = Database::query($sql);
2692
        if (Database::num_rows($res) > 0) {
2693
            while ($row = Database::fetch_array($res)) {
2694
                $sqlu = "SELECT value as fval FROM $t_ufv v 
2695
                         INNER JOIN $t_uf f
2696
                         ON (v.field_id = f.id)
2697
                         WHERE
2698
                            extra_field_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2699
                            field_id = ".$row['id']." AND
2700
                            item_id = ".$user_id;
2701
                $resu = Database::query($sqlu);
2702
                $fval = '';
2703
                if (Database::num_rows($resu) > 0) {
2704
                    $rowu = Database::fetch_array($resu);
2705
                    $fval = $rowu['fval'];
2706
                    if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
2707
                        $fval = explode(';', $rowu['fval']);
2708
                    }
2709
                }
2710
                if ($prefix) {
2711
                    $extra_data['extra_'.$row['fvar']] = $fval;
2712
                } else {
2713
                    $extra_data[$row['fvar']] = $fval;
2714
                }
2715
            }
2716
        }
2717
2718
        return $extra_data;
2719
    }
2720
2721
    /**
2722
     * Get the extra field information for a certain field (the options as well).
2723
     *
2724
     * @param int $variable The name of the field we want to know everything about
2725
     *
2726
     * @return array Array containing all the information about the extra profile field
2727
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2728
     *               as returned by the database)
2729
     *
2730
     * @author Julio Montoya
2731
     *
2732
     * @since v1.8.6
2733
     */
2734
    public static function get_extra_field_information_by_name($variable)
2735
    {
2736
        $extraField = new ExtraField('user');
2737
2738
        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...
2739
    }
2740
2741
    /**
2742
     * Get the extra field information for user tag (the options as well).
2743
     *
2744
     * @param int $variable The name of the field we want to know everything about
2745
     *
2746
     * @return array Array containing all the information about the extra profile field
2747
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2748
     *               as returned by the database)
2749
     *
2750
     * @author José Loguercio
2751
     *
2752
     * @since v1.11.0
2753
     */
2754
    public static function get_extra_field_tags_information_by_name($variable)
2755
    {
2756
        $extraField = new ExtraField('user');
2757
2758
        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...
2759
    }
2760
2761
    /**
2762
     * @param string $type
2763
     *
2764
     * @return array
2765
     */
2766
    public static function get_all_extra_field_by_type($type)
2767
    {
2768
        $extraField = new ExtraField('user');
2769
2770
        return $extraField->get_all_extra_field_by_type($type);
2771
    }
2772
2773
    /**
2774
     * Get all the extra field information of a certain field (also the options).
2775
     *
2776
     * @param int $fieldId the ID of the field we want to know everything of
2777
     *
2778
     * @return array $return containing all th information about the extra profile field
2779
     *
2780
     * @author Julio Montoya
2781
     *
2782
     * @deprecated
2783
     * @since v1.8.6
2784
     */
2785
    public static function get_extra_field_information($fieldId)
2786
    {
2787
        $extraField = new ExtraField('user');
2788
2789
        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...
2790
    }
2791
2792
    /**
2793
     * Get extra user data by value.
2794
     *
2795
     * @param string $variable       the internal variable name of the field
2796
     * @param string $value          the internal value of the field
2797
     * @param bool   $all_visibility
2798
     *
2799
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2800
     */
2801
    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

2801
    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...
2802
    {
2803
        $extraFieldValue = new ExtraFieldValue('user');
2804
        $extraField = new ExtraField('user');
2805
2806
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2807
2808
        if (false === $info) {
2809
            return [];
2810
        }
2811
2812
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2813
            $variable,
2814
            $value,
2815
            false,
2816
            false,
2817
            true
2818
        );
2819
2820
        $result = [];
2821
        if (!empty($data)) {
2822
            foreach ($data as $item) {
2823
                $result[] = $item['item_id'];
2824
            }
2825
        }
2826
2827
        return $result;
2828
    }
2829
2830
    /**
2831
     * Get extra user data by tags value.
2832
     *
2833
     * @param int    $fieldId the ID of the field we want to know everything of
2834
     * @param string $tag     the tag name for search
2835
     *
2836
     * @return array with extra data info of a user
2837
     *
2838
     * @author José Loguercio
2839
     *
2840
     * @since v1.11.0
2841
     */
2842
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2843
    {
2844
        $extraField = new ExtraField('user');
2845
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2846
        $array = [];
2847
        foreach ($result as $index => $user) {
2848
            $array[] = $user['user_id'];
2849
        }
2850
2851
        return $array;
2852
    }
2853
2854
    /**
2855
     * Get extra user data by field variable.
2856
     *
2857
     * @param string $variable field variable
2858
     *
2859
     * @return array data
2860
     */
2861
    public static function get_extra_user_data_by_field_variable($variable)
2862
    {
2863
        $extraInfo = self::get_extra_field_information_by_name($variable);
2864
        $field_id = (int) $extraInfo['id'];
2865
2866
        $extraField = new ExtraFieldValue('user');
2867
        $data = $extraField->getValuesByFieldId($field_id);
2868
2869
        if (!empty($data)) {
2870
            foreach ($data as $row) {
2871
                $user_id = $row['item_id'];
2872
                $data[$user_id] = $row;
2873
            }
2874
        }
2875
2876
        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...
2877
    }
2878
2879
    /**
2880
     * Get extra user data tags by field variable.
2881
     *
2882
     * @param string $variable field variable
2883
     *
2884
     * @return array
2885
     */
2886
    public static function get_extra_user_data_for_tags($variable)
2887
    {
2888
        $data = self::get_extra_field_tags_information_by_name($variable);
2889
2890
        return $data;
2891
    }
2892
2893
    /**
2894
     * Gives a list of [session_category][session_id] for the current user.
2895
     *
2896
     * @param int  $user_id
2897
     * @param bool $is_time_over                 whether to fill the first element or not
2898
     *                                           (to give space for courses out of categories)
2899
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2900
     * @param bool $ignoreTimeLimit              ignore time start/end
2901
     *
2902
     * @return array list of statuses [session_category][session_id]
2903
     *
2904
     * @todo ensure multiple access urls are managed correctly
2905
     */
2906
    public static function get_sessions_by_category(
2907
        $user_id,
2908
        $is_time_over = true,
2909
        $ignore_visibility_for_admins = false,
2910
        $ignoreTimeLimit = false
2911
    ) {
2912
        if ($user_id != strval(intval($user_id))) {
2913
            return [];
2914
        }
2915
2916
        $allowOrder = api_get_configuration_value('session_list_order');
2917
        $position = '';
2918
        if ($allowOrder) {
2919
            $position = ', s.position AS position ';
2920
        }
2921
2922
        // Get the list of sessions per user
2923
        $now = new DateTime('now', new DateTimeZone('UTC'));
2924
2925
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2926
        // join would not catch session-courses where the user is general
2927
        // session coach but which do not have students nor coaches registered
2928
2929
        // master changes
2930
        $dql = "SELECT DISTINCT
2931
                    s.id,
2932
                    s.name,
2933
                    s.accessStartDate AS access_start_date,
2934
                    s.accessEndDate AS access_end_date,
2935
                    s.duration,
2936
                    sc.id AS session_category_id,
2937
                    sc.name AS session_category_name,
2938
                    sc.dateStart AS session_category_date_start,
2939
                    sc.dateEnd AS session_category_date_end,
2940
                    s.coachAccessStartDate AS coach_access_start_date,
2941
                    s.coachAccessEndDate AS coach_access_end_date,
2942
                    CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
2943
                    $position
2944
                FROM ChamiloCoreBundle:Session AS s
2945
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2946
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2947
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc ";
2948
2949
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
2950
        // is awfully inefficient for large sets of data (1m25s for 58K
2951
        // sessions, BT#14115) but executing a similar query twice and grouping
2952
        // the results afterwards in PHP takes about 1/1000th of the time
2953
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
2954
        $dqlStudent = $dql." WHERE scu.user = :user AND url.url = :url ";
2955
        $dqlCoach = $dql." WHERE s.generalCoach = :user AND url.url = :url ";
2956
2957
        // Default order
2958
        $order = 'ORDER BY sc.name, s.name';
2959
2960
        // Order by date if showing all sessions
2961
        $showAllSessions = api_get_configuration_value('show_all_sessions_on_my_course_page') === true;
2962
        if ($showAllSessions) {
2963
            $order = 'ORDER BY s.accessStartDate';
2964
        }
2965
2966
        // Order by position
2967
        if ($allowOrder) {
2968
            $order = 'ORDER BY s.position';
2969
        }
2970
2971
        // Order by dates according to settings
2972
        $orderBySettings = api_get_configuration_value('my_courses_session_order');
2973
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
2974
            $field = $orderBySettings['field'];
2975
            $orderSetting = $orderBySettings['order'];
2976
            switch ($field) {
2977
                case 'start_date':
2978
                    $order = " ORDER BY s.accessStartDate $orderSetting";
2979
                    break;
2980
                case 'end_date':
2981
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
2982
                    if ($orderSetting == 'asc') {
2983
                        // Put null values at the end
2984
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
2985
                        $order = " ORDER BY _isFieldNull asc, s.accessEndDate asc";
2986
                    }
2987
                    break;
2988
            }
2989
        }
2990
2991
        $dqlStudent .= $order;
2992
        $dqlCoach .= $order;
2993
2994
        $accessUrlId = api_get_current_access_url_id();
2995
        $dqlStudent = Database::getManager()
2996
            ->createQuery($dqlStudent)
2997
            ->setParameters(
2998
                ['user' => $user_id, 'url' => $accessUrlId]
2999
            )
3000
        ;
3001
        $dqlCoach = Database::getManager()
3002
            ->createQuery($dqlCoach)
3003
            ->setParameters(
3004
                ['user' => $user_id, 'url' => $accessUrlId]
3005
            )
3006
        ;
3007
3008
        $sessionDataStudent = $dqlStudent->getResult();
3009
        $sessionDataCoach = $dqlCoach->getResult();
3010
3011
        $sessionData = [];
3012
        // First fill $sessionData with student sessions
3013
        foreach ($sessionDataStudent as $row) {
3014
            $sessionData[$row['id']] = $row;
3015
        }
3016
        // Overwrite session data of the user as a student with session data
3017
        // of the user as a coach.
3018
        // There shouldn't be such duplicate rows, but just in case...
3019
        foreach ($sessionDataCoach as $row) {
3020
            $sessionData[$row['id']] = $row;
3021
        }
3022
3023
        $categories = [];
3024
        foreach ($sessionData as $row) {
3025
            $session_id = $row['id'];
3026
            $coachList = SessionManager::getCoachesBySession($session_id);
3027
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
3028
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
3029
            $courseList = self::get_courses_list_by_session(
3030
                $user_id,
3031
                $session_id
3032
            );
3033
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
3034
3035
            // User portal filters:
3036
            if ($ignoreTimeLimit === false) {
3037
                if ($is_time_over) {
3038
                    // History
3039
                    if ($row['duration']) {
3040
                        if ($daysLeft >= 0) {
3041
                            continue;
3042
                        }
3043
                    } else {
3044
                        if (empty($row['access_end_date'])) {
3045
                            continue;
3046
                        } else {
3047
                            if ($row['access_end_date'] > $now) {
3048
                                continue;
3049
                            }
3050
                        }
3051
                    }
3052
                } else {
3053
                    // Current user portal
3054
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
3055
                    $isCoachOfCourse = in_array($user_id, $coachList);
3056
3057
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
3058
                        // Teachers can access the session depending in the access_coach date
3059
                    } else {
3060
                        if ($row['duration']) {
3061
                            if ($daysLeft <= 0) {
3062
                                continue;
3063
                            }
3064
                        } else {
3065
                            if (isset($row['access_end_date']) &&
3066
                                !empty($row['access_end_date'])
3067
                            ) {
3068
                                if ($row['access_end_date'] <= $now) {
3069
                                    continue;
3070
                                }
3071
                            }
3072
                        }
3073
                    }
3074
                }
3075
            }
3076
3077
            $categories[$row['session_category_id']]['session_category'] = [
3078
                'id' => $row['session_category_id'],
3079
                'name' => $row['session_category_name'],
3080
                'date_start' => $categoryStart,
3081
                'date_end' => $categoryEnd,
3082
            ];
3083
3084
            $visibility = api_get_session_visibility(
3085
                $session_id,
3086
                null,
3087
                $ignore_visibility_for_admins
3088
            );
3089
3090
            if ($visibility != SESSION_VISIBLE) {
3091
                // Course Coach session visibility.
3092
                $blockedCourseCount = 0;
3093
                $closedVisibilityList = [
3094
                    COURSE_VISIBILITY_CLOSED,
3095
                    COURSE_VISIBILITY_HIDDEN,
3096
                ];
3097
3098
                foreach ($courseList as $course) {
3099
                    // Checking session visibility
3100
                    $sessionCourseVisibility = api_get_session_visibility(
3101
                        $session_id,
3102
                        $course['real_id'],
3103
                        $ignore_visibility_for_admins
3104
                    );
3105
3106
                    $courseIsVisible = !in_array(
3107
                        $course['visibility'],
3108
                        $closedVisibilityList
3109
                    );
3110
                    if ($courseIsVisible === false || $sessionCourseVisibility == SESSION_INVISIBLE) {
3111
                        $blockedCourseCount++;
3112
                    }
3113
                }
3114
3115
                // If all courses are blocked then no show in the list.
3116
                if ($blockedCourseCount === count($courseList)) {
3117
                    $visibility = SESSION_INVISIBLE;
3118
                } else {
3119
                    $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...
3120
                }
3121
            }
3122
3123
            switch ($visibility) {
3124
                case SESSION_VISIBLE_READ_ONLY:
3125
                case SESSION_VISIBLE:
3126
                case SESSION_AVAILABLE:
3127
                    break;
3128
                case SESSION_INVISIBLE:
3129
                    if ($ignore_visibility_for_admins === false) {
3130
                        continue 2;
3131
                    }
3132
            }
3133
3134
            $categories[$row['session_category_id']]['sessions'][] = [
3135
                'session_name' => $row['name'],
3136
                'session_id' => $row['id'],
3137
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
3138
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
3139
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
3140
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
3141
                'courses' => $courseList,
3142
            ];
3143
        }
3144
3145
        return $categories;
3146
    }
3147
3148
    /**
3149
     * Gives a list of [session_id-course_code] => [status] for the current user.
3150
     *
3151
     * @param int $user_id
3152
     * @param int $sessionLimit
3153
     *
3154
     * @return array list of statuses (session_id-course_code => status)
3155
     */
3156
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
3157
    {
3158
        // Database Table Definitions
3159
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3160
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
3161
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3162
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3163
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3164
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3165
3166
        if ($user_id != strval(intval($user_id))) {
3167
            return [];
3168
        }
3169
3170
        // We filter the courses from the URL
3171
        $join_access_url = $where_access_url = '';
3172
3173
        if (api_get_multiple_access_url()) {
3174
            $access_url_id = api_get_current_access_url_id();
3175
            if ($access_url_id != -1) {
3176
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3177
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
3178
                $where_access_url = " AND access_url_id = $access_url_id ";
3179
            }
3180
        }
3181
3182
        // Courses in which we subscribed out of any session
3183
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3184
3185
        $sql = "SELECT
3186
                    course.code,
3187
                    course_rel_user.status course_rel_status,
3188
                    course_rel_user.sort sort,
3189
                    course_rel_user.user_course_cat user_course_cat
3190
                 FROM $tbl_course_user course_rel_user
3191
                 LEFT JOIN $tbl_course course
3192
                 ON course.id = course_rel_user.c_id
3193
                 LEFT JOIN $tbl_user_course_category user_course_category
3194
                 ON course_rel_user.user_course_cat = user_course_category.id
3195
                 $join_access_url
3196
                 WHERE
3197
                    course_rel_user.user_id = '".$user_id."' AND
3198
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
3199
                    $where_access_url
3200
                 ORDER BY user_course_category.sort, course_rel_user.sort, course.title ASC";
3201
3202
        $course_list_sql_result = Database::query($sql);
3203
3204
        $personal_course_list = [];
3205
        if (Database::num_rows($course_list_sql_result) > 0) {
3206
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3207
                $course_info = api_get_course_info($result_row['code']);
3208
                $result_row['course_info'] = $course_info;
3209
                $personal_course_list[] = $result_row;
3210
            }
3211
        }
3212
3213
        $coachCourseConditions = '';
3214
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
3215
        if (api_is_allowed_to_create_course()) {
3216
            $sessionListFromCourseCoach = [];
3217
            $sql = " SELECT DISTINCT session_id
3218
                    FROM $tbl_session_course_user
3219
                    WHERE user_id = $user_id AND status = 2 ";
3220
3221
            $result = Database::query($sql);
3222
            if (Database::num_rows($result)) {
3223
                $result = Database::store_result($result);
3224
                foreach ($result as $session) {
3225
                    $sessionListFromCourseCoach[] = $session['session_id'];
3226
                }
3227
            }
3228
            if (!empty($sessionListFromCourseCoach)) {
3229
                $condition = implode("','", $sessionListFromCourseCoach);
3230
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
3231
            }
3232
        }
3233
3234
        // Get the list of sessions where the user is subscribed
3235
        // This is divided into two different queries
3236
        $sessions = [];
3237
        $sessionLimitRestriction = '';
3238
        if (!empty($sessionLimit)) {
3239
            $sessionLimit = (int) $sessionLimit;
3240
            $sessionLimitRestriction = "LIMIT $sessionLimit";
3241
        }
3242
3243
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
3244
                FROM $tbl_session_user su INNER JOIN $tbl_session s
3245
                ON (s.id = su.session_id)
3246
                WHERE (
3247
                    su.user_id = $user_id AND
3248
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
3249
                )
3250
                $coachCourseConditions
3251
                ORDER BY access_start_date, access_end_date, name
3252
                $sessionLimitRestriction
3253
        ";
3254
3255
        $result = Database::query($sql);
3256
        if (Database::num_rows($result) > 0) {
3257
            while ($row = Database::fetch_assoc($result)) {
3258
                $sessions[$row['id']] = $row;
3259
            }
3260
        }
3261
3262
        $sql = "SELECT DISTINCT
3263
                id, name, access_start_date, access_end_date
3264
                FROM $tbl_session s
3265
                WHERE (
3266
                    id_coach = $user_id
3267
                )
3268
                $coachCourseConditions
3269
                ORDER BY access_start_date, access_end_date, name";
3270
3271
        $result = Database::query($sql);
3272
        if (Database::num_rows($result) > 0) {
3273
            while ($row = Database::fetch_assoc($result)) {
3274
                if (empty($sessions[$row['id']])) {
3275
                    $sessions[$row['id']] = $row;
3276
                }
3277
            }
3278
        }
3279
3280
        if (api_is_allowed_to_create_course()) {
3281
            foreach ($sessions as $enreg) {
3282
                $session_id = $enreg['id'];
3283
                $session_visibility = api_get_session_visibility($session_id);
3284
3285
                if ($session_visibility == SESSION_INVISIBLE) {
3286
                    continue;
3287
                }
3288
3289
                // This query is horribly slow when more than a few thousand
3290
                // users and just a few sessions to which they are subscribed
3291
                $sql = "SELECT DISTINCT
3292
                        course.code code,
3293
                        course.title i,
3294
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
3295
                        email, course.course_language l,
3296
                        1 sort,
3297
                        category_code user_course_cat,
3298
                        access_start_date,
3299
                        access_end_date,
3300
                        session.id as session_id,
3301
                        session.name as session_name
3302
                    FROM $tbl_session_course_user as session_course_user
3303
                    INNER JOIN $tbl_course AS course
3304
                        ON course.id = session_course_user.c_id
3305
                    INNER JOIN $tbl_session as session
3306
                        ON session.id = session_course_user.session_id
3307
                    LEFT JOIN $tbl_user as user
3308
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
3309
                    WHERE
3310
                        session_course_user.session_id = $session_id AND (
3311
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
3312
                            OR session.id_coach = $user_id
3313
                        )
3314
                    ORDER BY i";
3315
                $course_list_sql_result = Database::query($sql);
3316
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3317
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3318
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3319
                    $personal_course_list[$key] = $result_row;
3320
                }
3321
            }
3322
        }
3323
3324
        foreach ($sessions as $enreg) {
3325
            $session_id = $enreg['id'];
3326
            $session_visibility = api_get_session_visibility($session_id);
3327
            if ($session_visibility == SESSION_INVISIBLE) {
3328
                continue;
3329
            }
3330
3331
            /* This query is very similar to the above query,
3332
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3333
            $sql = "SELECT DISTINCT
3334
                course.code code,
3335
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
3336
                email,
3337
                course.course_language l,
3338
                1 sort,
3339
                category_code user_course_cat,
3340
                access_start_date,
3341
                access_end_date,
3342
                session.id as session_id,
3343
                session.name as session_name,
3344
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
3345
            FROM $tbl_session_course_user as session_course_user
3346
            INNER JOIN $tbl_course AS course
3347
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
3348
            INNER JOIN $tbl_session as session 
3349
            ON session_course_user.session_id = session.id
3350
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
3351
            WHERE session_course_user.user_id = $user_id
3352
            ORDER BY i";
3353
3354
            $course_list_sql_result = Database::query($sql);
3355
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3356
                $result_row['course_info'] = api_get_course_info($result_row['code']);
3357
                $key = $result_row['session_id'].' - '.$result_row['code'];
3358
                if (!isset($personal_course_list[$key])) {
3359
                    $personal_course_list[$key] = $result_row;
3360
                }
3361
            }
3362
        }
3363
3364
        return $personal_course_list;
3365
    }
3366
3367
    /**
3368
     * Gives a list of courses for the given user in the given session.
3369
     *
3370
     * @param int $user_id
3371
     * @param int $session_id
3372
     *
3373
     * @return array list of statuses (session_id-course_code => status)
3374
     */
3375
    public static function get_courses_list_by_session($user_id, $session_id)
3376
    {
3377
        // Database Table Definitions
3378
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3379
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
3380
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3381
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3382
3383
        $user_id = (int) $user_id;
3384
        $session_id = (int) $session_id;
3385
        //we filter the courses from the URL
3386
        $join_access_url = $where_access_url = '';
3387
3388
        if (api_get_multiple_access_url()) {
3389
            $urlId = api_get_current_access_url_id();
3390
            if ($urlId != -1) {
3391
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3392
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
3393
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
3394
            }
3395
        }
3396
3397
        /* This query is very similar to the query below, but it will check the
3398
        session_rel_course_user table if there are courses registered
3399
        to our user or not */
3400
        $sql = "SELECT DISTINCT
3401
                    c.visibility,
3402
                    c.id as real_id,
3403
                    c.code as course_code,
3404
                    sc.position
3405
                FROM $tbl_session_course_user as scu
3406
                INNER JOIN $tbl_session_course sc
3407
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3408
                INNER JOIN $tableCourse as c
3409
                ON (scu.c_id = c.id)
3410
                $join_access_url
3411
                WHERE
3412
                    scu.user_id = $user_id AND
3413
                    scu.session_id = $session_id
3414
                    $where_access_url
3415
                ORDER BY sc.position ASC";
3416
3417
        $myCourseList = [];
3418
        $courses = [];
3419
        $result = Database::query($sql);
3420
        if (Database::num_rows($result) > 0) {
3421
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3422
                $result_row['status'] = 5;
3423
                if (!in_array($result_row['real_id'], $courses)) {
3424
                    $position = $result_row['position'];
3425
                    if (!isset($myCourseList[$position])) {
3426
                        $myCourseList[$position] = $result_row;
3427
                    } else {
3428
                        $myCourseList[] = $result_row;
3429
                    }
3430
                    $courses[] = $result_row['real_id'];
3431
                }
3432
            }
3433
        }
3434
3435
        if (api_is_allowed_to_create_course()) {
3436
            $sql = "SELECT DISTINCT
3437
                        c.visibility, 
3438
                        c.id as real_id,
3439
                        c.code as course_code,
3440
                        sc.position
3441
                    FROM $tbl_session_course_user as scu
3442
                    INNER JOIN $tbl_session as s
3443
                    ON (scu.session_id = s.id)
3444
                    INNER JOIN $tbl_session_course sc
3445
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3446
                    INNER JOIN $tableCourse as c
3447
                    ON (scu.c_id = c.id)
3448
                    $join_access_url
3449
                    WHERE
3450
                      s.id = $session_id AND
3451
                      (
3452
                        (scu.user_id = $user_id AND scu.status = 2) OR
3453
                        s.id_coach = $user_id
3454
                      )
3455
                    $where_access_url
3456
                    ORDER BY sc.position ASC";
3457
            $result = Database::query($sql);
3458
3459
            if (Database::num_rows($result) > 0) {
3460
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3461
                    $result_row['status'] = 2;
3462
                    if (!in_array($result_row['real_id'], $courses)) {
3463
                        $position = $result_row['position'];
3464
                        if (!isset($myCourseList[$position])) {
3465
                            $myCourseList[$position] = $result_row;
3466
                        } else {
3467
                            $myCourseList[] = $result_row;
3468
                        }
3469
                        $courses[] = $result_row['real_id'];
3470
                    }
3471
                }
3472
            }
3473
        }
3474
3475
        if (api_is_drh()) {
3476
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3477
            $sessionList = array_keys($sessionList);
3478
            if (in_array($session_id, $sessionList)) {
3479
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3480
                if (!empty($courseList)) {
3481
                    foreach ($courseList as $course) {
3482
                        if (!in_array($course['id'], $courses)) {
3483
                            $position = $course['position'];
3484
                            if (!isset($myCourseList[$position])) {
3485
                                $myCourseList[$position] = $course;
3486
                            } else {
3487
                                $myCourseList[] = $course;
3488
                            }
3489
                        }
3490
                    }
3491
                }
3492
            }
3493
        } else {
3494
            //check if user is general coach for this session
3495
            $sessionInfo = api_get_session_info($session_id);
3496
            if ($sessionInfo['id_coach'] == $user_id) {
3497
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3498
                if (!empty($courseList)) {
3499
                    foreach ($courseList as $course) {
3500
                        if (!in_array($course['id'], $courses)) {
3501
                            $position = $course['position'];
3502
                            if (!isset($myCourseList[$position])) {
3503
                                $myCourseList[$position] = $course;
3504
                            } else {
3505
                                $myCourseList[] = $course;
3506
                            }
3507
                        }
3508
                    }
3509
                }
3510
            }
3511
        }
3512
3513
        if (!empty($myCourseList)) {
3514
            ksort($myCourseList);
3515
        }
3516
3517
        return $myCourseList;
3518
    }
3519
3520
    /**
3521
     * Get user id from a username.
3522
     *
3523
     * @param string $username
3524
     *
3525
     * @return int User ID (or false if not found)
3526
     */
3527
    public static function get_user_id_from_username($username)
3528
    {
3529
        if (empty($username)) {
3530
            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...
3531
        }
3532
        $username = trim($username);
3533
        $username = Database::escape_string($username);
3534
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3535
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3536
        $res = Database::query($sql);
3537
3538
        if ($res === false) {
3539
            return false;
3540
        }
3541
        if (Database::num_rows($res) !== 1) {
3542
            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...
3543
        }
3544
        $row = Database::fetch_array($res);
3545
3546
        return $row['id'];
3547
    }
3548
3549
    /**
3550
     * Get the users files upload from his share_folder.
3551
     *
3552
     * @param string $user_id      User ID
3553
     * @param string $course       course directory
3554
     * @param string $resourceType resource type: images, all
3555
     *
3556
     * @return string
3557
     */
3558
    public static function get_user_upload_files_by_course(
3559
        $user_id,
3560
        $course,
3561
        $resourceType = 'all'
3562
    ) {
3563
        $return = '';
3564
        if (!empty($user_id) && !empty($course)) {
3565
            $user_id = (int) $user_id;
3566
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3567
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3568
            $file_list = [];
3569
3570
            if (is_dir($path)) {
3571
                $handle = opendir($path);
3572
                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

3572
                while ($file = readdir(/** @scrutinizer ignore-type */ $handle)) {
Loading history...
3573
                    if ($file == '.' || $file == '..' || $file == '.htaccess' || is_dir($path.$file)) {
3574
                        continue; // skip current/parent directory and .htaccess
3575
                    }
3576
                    $file_list[] = $file;
3577
                }
3578
                if (count($file_list) > 0) {
3579
                    $return = "<h4>$course</h4>";
3580
                    $return .= '<ul class="thumbnails">';
3581
                }
3582
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
3583
                foreach ($file_list as $file) {
3584
                    if ($resourceType == 'all') {
3585
                        $return .= '<li>
3586
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
3587
                    } elseif ($resourceType == 'images') {
3588
                        //get extension
3589
                        $ext = explode('.', $file);
3590
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
3591
                            $return .= '<li class="span2">
3592
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
3593
                                                <img src="'.$web_path.urlencode($file).'" >
3594
                                            </a>
3595
                                        </li>';
3596
                        }
3597
                    }
3598
                }
3599
                if (count($file_list) > 0) {
3600
                    $return .= '</ul>';
3601
                }
3602
            }
3603
        }
3604
3605
        return $return;
3606
    }
3607
3608
    /**
3609
     * Gets the API key (or keys) and return them into an array.
3610
     *
3611
     * @param int     Optional user id (defaults to the result of api_get_user_id())
3612
     * @param string $api_service
3613
     *
3614
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
3615
     */
3616
    public static function get_api_keys($user_id = null, $api_service = 'dokeos')
3617
    {
3618
        if ($user_id != strval(intval($user_id))) {
3619
            return false;
3620
        }
3621
        if (empty($user_id)) {
3622
            $user_id = api_get_user_id();
3623
        }
3624
        if ($user_id === false) {
3625
            return false;
3626
        }
3627
        $service_name = Database::escape_string($api_service);
3628
        if (is_string($service_name) === false) {
3629
            return false;
3630
        }
3631
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3632
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
3633
        $res = Database::query($sql);
3634
        if ($res === false) {
3635
            return false;
3636
        } //error during query
3637
        $num = Database::num_rows($res);
3638
        if ($num == 0) {
3639
            return false;
3640
        }
3641
        $list = [];
3642
        while ($row = Database::fetch_array($res)) {
3643
            $list[$row['id']] = $row['api_key'];
3644
        }
3645
3646
        return $list;
3647
    }
3648
3649
    /**
3650
     * Adds a new API key to the users' account.
3651
     *
3652
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
3653
     * @param string $api_service
3654
     *
3655
     * @return bool True on success, false on failure
3656
     */
3657
    public static function add_api_key($user_id = null, $api_service = 'dokeos')
3658
    {
3659
        if ($user_id != strval(intval($user_id))) {
3660
            return false;
3661
        }
3662
        if (empty($user_id)) {
3663
            $user_id = api_get_user_id();
3664
        }
3665
        if ($user_id === false) {
3666
            return false;
3667
        }
3668
        $service_name = Database::escape_string($api_service);
3669
        if (is_string($service_name) === false) {
3670
            return false;
3671
        }
3672
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3673
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
3674
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
3675
        $res = Database::query($sql);
3676
        if ($res === false) {
3677
            return false;
3678
        } //error during query
3679
        $num = Database::insert_id();
3680
3681
        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...
3682
    }
3683
3684
    /**
3685
     * Deletes an API key from the user's account.
3686
     *
3687
     * @param   int     API key's internal ID
3688
     *
3689
     * @return bool True on success, false on failure
3690
     */
3691
    public static function delete_api_key($key_id)
3692
    {
3693
        if ($key_id != strval(intval($key_id))) {
3694
            return false;
3695
        }
3696
        if ($key_id === false) {
3697
            return false;
3698
        }
3699
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3700
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
3701
        $res = Database::query($sql);
3702
        if ($res === false) {
3703
            return false;
3704
        } //error during query
3705
        $num = Database::num_rows($res);
3706
        if ($num !== 1) {
3707
            return false;
3708
        }
3709
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
3710
        $res = Database::query($sql);
3711
        if ($res === false) {
3712
            return false;
3713
        } //error during query
3714
3715
        return true;
3716
    }
3717
3718
    /**
3719
     * Regenerate an API key from the user's account.
3720
     *
3721
     * @param   int     user ID (defaults to the results of api_get_user_id())
3722
     * @param   string  API key's internal ID
3723
     *
3724
     * @return int num
3725
     */
3726
    public static function update_api_key($user_id, $api_service)
3727
    {
3728
        if ($user_id != strval(intval($user_id))) {
3729
            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...
3730
        }
3731
        if ($user_id === false) {
3732
            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...
3733
        }
3734
        $service_name = Database::escape_string($api_service);
3735
        if (is_string($service_name) === false) {
3736
            return false;
3737
        }
3738
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3739
        $sql = "SELECT id FROM $t_api 
3740
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3741
        $res = Database::query($sql);
3742
        $num = Database::num_rows($res);
3743
        if ($num == 1) {
3744
            $id_key = Database::fetch_array($res, 'ASSOC');
3745
            self::delete_api_key($id_key['id']);
3746
            $num = self::add_api_key($user_id, $api_service);
3747
        } elseif ($num == 0) {
3748
            $num = self::add_api_key($user_id, $api_service);
3749
        }
3750
3751
        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...
3752
    }
3753
3754
    /**
3755
     * @param   int     user ID (defaults to the results of api_get_user_id())
3756
     * @param   string    API key's internal ID
3757
     *
3758
     * @return int row ID, or return false if not found
3759
     */
3760
    public static function get_api_key_id($user_id, $api_service)
3761
    {
3762
        if ($user_id != strval(intval($user_id))) {
3763
            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...
3764
        }
3765
        if ($user_id === false) {
3766
            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...
3767
        }
3768
        if (empty($api_service)) {
3769
            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...
3770
        }
3771
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3772
        $api_service = Database::escape_string($api_service);
3773
        $sql = "SELECT id FROM $t_api 
3774
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3775
        $res = Database::query($sql);
3776
        if (Database::num_rows($res) < 1) {
3777
            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...
3778
        }
3779
        $row = Database::fetch_array($res, 'ASSOC');
3780
3781
        return $row['id'];
3782
    }
3783
3784
    /**
3785
     * Checks if a user_id is platform admin.
3786
     *
3787
     * @param   int user ID
3788
     *
3789
     * @return bool True if is admin, false otherwise
3790
     *
3791
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
3792
     */
3793
    public static function is_admin($user_id)
3794
    {
3795
        if (empty($user_id) || $user_id != strval(intval($user_id))) {
3796
            return false;
3797
        }
3798
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3799
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3800
        $res = Database::query($sql);
3801
3802
        return Database::num_rows($res) === 1;
3803
    }
3804
3805
    /**
3806
     * Get the total count of users.
3807
     *
3808
     * @param   int     Status of users to be counted
3809
     * @param   int     Access URL ID (optional)
3810
     *
3811
     * @return mixed Number of users or false on error
3812
     */
3813
    public static function get_number_of_users($status = 0, $access_url_id = 1)
3814
    {
3815
        $t_u = Database::get_main_table(TABLE_MAIN_USER);
3816
        $t_a = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3817
3818
        if (api_is_multiple_url_enabled()) {
3819
            $sql = "SELECT count(u.id) 
3820
                    FROM $t_u u 
3821
                    INNER JOIN $t_a url_user
3822
                    ON (u.id = url_user.user_id)
3823
                    WHERE url_user.access_url_id = $access_url_id                
3824
            ";
3825
        } else {
3826
            $sql = "SELECT count(u.id) 
3827
                    FROM $t_u u
3828
                    WHERE 1 = 1 ";
3829
        }
3830
        if (is_int($status) && $status > 0) {
3831
            $sql .= " AND u.status = $status ";
3832
        }
3833
        $res = Database::query($sql);
3834
        if (Database::num_rows($res) === 1) {
3835
            return (int) Database::result($res, 0, 0);
3836
        }
3837
3838
        return false;
3839
    }
3840
3841
    /**
3842
     * Gets the tags of a specific field_id
3843
     * USER TAGS.
3844
     *
3845
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
3846
     *
3847
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
3848
     *    Called it "books" for example.
3849
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
3850
     * 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
3851
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
3852
     * 5. Test and enjoy.
3853
     *
3854
     * @param string $tag
3855
     * @param int    $field_id      field_id
3856
     * @param string $return_format how we are going to result value in array or in a string (json)
3857
     * @param $limit
3858
     *
3859
     * @return mixed
3860
     */
3861
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
3862
    {
3863
        // database table definition
3864
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3865
        $field_id = (int) $field_id;
3866
        $limit = (int) $limit;
3867
        $tag = trim(Database::escape_string($tag));
3868
3869
        // all the information of the field
3870
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
3871
                WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag LIMIT $limit";
3872
        $result = Database::query($sql);
3873
        $return = [];
3874
        if (Database::num_rows($result) > 0) {
3875
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3876
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
3877
            }
3878
        }
3879
        if ($return_format === 'json') {
3880
            $return = json_encode($return);
3881
        }
3882
3883
        return $return;
3884
    }
3885
3886
    /**
3887
     * @param int $field_id
3888
     * @param int $limit
3889
     *
3890
     * @return array
3891
     */
3892
    public static function get_top_tags($field_id, $limit = 100)
3893
    {
3894
        // database table definition
3895
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3896
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3897
        $field_id = (int) $field_id;
3898
        $limit = (int) $limit;
3899
        // all the information of the field
3900
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
3901
                INNER JOIN $table_user_tag ut
3902
                ON (ut.id = uv.tag_id)
3903
                WHERE field_id = $field_id
3904
                GROUP BY tag_id
3905
                ORDER BY count DESC
3906
                LIMIT $limit";
3907
        $result = Database::query($sql);
3908
        $return = [];
3909
        if (Database::num_rows($result) > 0) {
3910
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3911
                $return[] = $row;
3912
            }
3913
        }
3914
3915
        return $return;
3916
    }
3917
3918
    /**
3919
     * Get user's tags.
3920
     *
3921
     * @param int $user_id
3922
     * @param int $field_id
3923
     *
3924
     * @return array
3925
     */
3926
    public static function get_user_tags($user_id, $field_id)
3927
    {
3928
        // database table definition
3929
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3930
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3931
        $field_id = (int) $field_id;
3932
        $user_id = (int) $user_id;
3933
3934
        // all the information of the field
3935
        $sql = "SELECT ut.id, tag, count
3936
                FROM $table_user_tag ut
3937
                INNER JOIN $table_user_tag_values uv
3938
                ON (uv.tag_id=ut.ID)
3939
                WHERE field_id = $field_id AND user_id = $user_id
3940
                ORDER BY tag";
3941
        $result = Database::query($sql);
3942
        $return = [];
3943
        if (Database::num_rows($result) > 0) {
3944
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3945
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3946
            }
3947
        }
3948
3949
        return $return;
3950
    }
3951
3952
    /**
3953
     * Get user's tags.
3954
     *
3955
     * @param int  $user_id
3956
     * @param int  $field_id
3957
     * @param bool $show_links show links or not
3958
     *
3959
     * @return string
3960
     */
3961
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
3962
    {
3963
        // database table definition
3964
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3965
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3966
        $field_id = (int) $field_id;
3967
        $user_id = (int) $user_id;
3968
3969
        // all the information of the field
3970
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
3971
                INNER JOIN $table_user_tag_values uv
3972
                ON (uv.tag_id = ut.id)
3973
                WHERE field_id = $field_id AND user_id = $user_id
3974
                ORDER BY tag";
3975
3976
        $result = Database::query($sql);
3977
        $return = [];
3978
        if (Database::num_rows($result) > 0) {
3979
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3980
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3981
            }
3982
        }
3983
        $user_tags = $return;
3984
        $tag_tmp = [];
3985
        foreach ($user_tags as $tag) {
3986
            if ($show_links) {
3987
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
3988
                    $tag['tag'].
3989
                '</a>';
3990
            } else {
3991
                $tag_tmp[] = $tag['tag'];
3992
            }
3993
        }
3994
3995
        if (is_array($user_tags) && count($user_tags) > 0) {
3996
            return implode(', ', $tag_tmp);
3997
        } else {
3998
            return '';
3999
        }
4000
    }
4001
4002
    /**
4003
     * Get the tag id.
4004
     *
4005
     * @param int $tag
4006
     * @param int $field_id
4007
     *
4008
     * @return int returns 0 if fails otherwise the tag id
4009
     */
4010
    public static function get_tag_id($tag, $field_id)
4011
    {
4012
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4013
        $tag = Database::escape_string($tag);
4014
        $field_id = (int) $field_id;
4015
        //with COLLATE latin1_bin to select query in a case sensitive mode
4016
        $sql = "SELECT id FROM $table_user_tag
4017
                WHERE tag LIKE '$tag' AND field_id = $field_id";
4018
        $result = Database::query($sql);
4019
        if (Database::num_rows($result) > 0) {
4020
            $row = Database::fetch_array($result, 'ASSOC');
4021
4022
            return $row['id'];
4023
        } else {
4024
            return 0;
4025
        }
4026
    }
4027
4028
    /**
4029
     * Get the tag id.
4030
     *
4031
     * @param int $tag_id
4032
     * @param int $field_id
4033
     *
4034
     * @return int 0 if fails otherwise the tag id
4035
     */
4036
    public static function get_tag_id_from_id($tag_id, $field_id)
4037
    {
4038
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4039
        $tag_id = (int) $tag_id;
4040
        $field_id = (int) $field_id;
4041
        $sql = "SELECT id FROM $table_user_tag
4042
                WHERE id = '$tag_id' AND field_id = $field_id";
4043
        $result = Database::query($sql);
4044
        if (Database::num_rows($result) > 0) {
4045
            $row = Database::fetch_array($result, 'ASSOC');
4046
4047
            return $row['id'];
4048
        } else {
4049
            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...
4050
        }
4051
    }
4052
4053
    /**
4054
     * Adds a user-tag value.
4055
     *
4056
     * @param mixed $tag
4057
     * @param int   $user_id
4058
     * @param int   $field_id field id of the tag
4059
     *
4060
     * @return bool True if the tag was inserted or updated. False otherwise.
4061
     *              The return value doesn't take into account *values* added to the tag.
4062
     *              Only the creation/update of the tag field itself.
4063
     */
4064
    public static function add_tag($tag, $user_id, $field_id)
4065
    {
4066
        // database table definition
4067
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4068
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4069
        $tag = trim(Database::escape_string($tag));
4070
        $user_id = (int) $user_id;
4071
        $field_id = (int) $field_id;
4072
4073
        $tag_id = self::get_tag_id($tag, $field_id);
4074
4075
        /* IMPORTANT
4076
         *  @todo we don't create tags with numbers
4077
         *
4078
         */
4079
        if (is_numeric($tag)) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

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