Completed
Push — master ( af42cb...3888f0 )
by Julito
13:17
created

UserManager::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

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

1070
        /** @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...
1071
        $hr_dept_id = 0,
1072
        $extra = null,
1073
        $language = 'english',
1074
        $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

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

2054
                /** @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...
2055
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
2056
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
2057
                @rename($path.$old_file, $path.$prefix.$old_file);
2058
            } else {
2059
                @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

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

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

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

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

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

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

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