Passed
Push — master ( 0085e5...a02707 )
by Julito
10:26
created

UserManager::removeUserAdmin()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 9
rs 10
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
5
use Chamilo\CoreBundle\Entity\ExtraFieldSavedSearch;
6
use Chamilo\CoreBundle\Entity\ResourceNode;
7
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...
8
use Chamilo\CoreBundle\Entity\SkillRelUserComment;
9
use Chamilo\CoreBundle\Entity\User;
10
use Chamilo\CoreBundle\Framework\Container;
11
use Chamilo\CoreBundle\Repository\UserRepository;
12
use ChamiloSession as Session;
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
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
21
 */
22
class UserManager
23
{
24
    // This constants are deprecated use the constants located in ExtraField
25
    public const USER_FIELD_TYPE_TEXT = 1;
26
    public const USER_FIELD_TYPE_TEXTAREA = 2;
27
    public const USER_FIELD_TYPE_RADIO = 3;
28
    public const USER_FIELD_TYPE_SELECT = 4;
29
    public const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
30
    public const USER_FIELD_TYPE_DATE = 6;
31
    public const USER_FIELD_TYPE_DATETIME = 7;
32
    public const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
33
    public const USER_FIELD_TYPE_DIVIDER = 9;
34
    public const USER_FIELD_TYPE_TAG = 10;
35
    public const USER_FIELD_TYPE_TIMEZONE = 11;
36
    public const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
37
    public const USER_FIELD_TYPE_FILE = 13;
38
    public const USER_FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
39
40
    private static $encryptionMethod;
41
42
    /**
43
     * Constructor.
44
     *
45
     * @assert () === null
46
     */
47
    public function __construct()
48
    {
49
    }
50
51
    /**
52
     * Repository is use to query the DB, selects, etc.
53
     *
54
     * @return UserRepository
55
     */
56
    public static function getRepository()
57
    {
58
        return Container::$container->get('Chamilo\CoreBundle\Repository\UserRepository');
59
    }
60
61
    /**
62
     * Create/update/delete methods are available in the UserManager
63
     * (based in the Sonata\UserBundle\Entity\UserManager).
64
     */
65
    public static function getManager()
66
    {
67
        return Container::getUserManager();
68
    }
69
70
    /**
71
     * @param string $encryptionMethod
72
     */
73
    public static function setPasswordEncryption($encryptionMethod)
74
    {
75
        self::$encryptionMethod = $encryptionMethod;
76
    }
77
78
    /**
79
     * @return bool|mixed
80
     */
81
    public static function getPasswordEncryption()
82
    {
83
        $encryptionMethod = self::$encryptionMethod;
84
        if (empty($encryptionMethod)) {
85
            $encryptionMethod = api_get_configuration_value('password_encryption');
86
        }
87
88
        return $encryptionMethod;
89
    }
90
91
    /**
92
     * Validates the password.
93
     *
94
     * @param $encoded
95
     * @param $raw
96
     * @param $salt
97
     *
98
     * @return bool
99
     */
100
    public static function isPasswordValid($encoded, $raw, $salt)
101
    {
102
        $encoder = new \Chamilo\CoreBundle\Security\Encoder(self::getPasswordEncryption());
103
        $validPassword = $encoder->isPasswordValid($encoded, $raw, $salt);
104
105
        return $validPassword;
106
    }
107
108
    /**
109
     * @param int    $userId
110
     * @param string $password
111
     */
112
    public static function updatePassword($userId, $password)
113
    {
114
        $user = api_get_user_entity($userId);
115
        $userManager = self::getManager();
116
        $user->setPlainPassword($password);
117
        $userManager->updateUser($user, true);
118
    }
119
120
    /**
121
     * Creates a new user for the platform.
122
     *
123
     * @author Hugues Peeters <[email protected]>,
124
     * @author Roan Embrechts <[email protected]>
125
     *
126
     * @param string        $firstName
127
     * @param string        $lastName
128
     * @param int           $status                  (1 for course tutor, 5 for student, 6 for anonymous)
129
     * @param string        $email
130
     * @param string        $loginName
131
     * @param string        $password
132
     * @param string        $official_code           Any official code (optional)
133
     * @param string        $language                User language    (optional)
134
     * @param string        $phone                   Phone number    (optional)
135
     * @param string        $picture_uri             Picture URI        (optional)
136
     * @param string        $authSource              Authentication source (defaults to 'platform', dependind on constant)
137
     * @param string        $expirationDate          Account expiration date (optional, defaults to null)
138
     * @param int           $active                  Whether the account is enabled or disabled by default
139
     * @param int           $hr_dept_id              The department of HR in which the user is registered (defaults to 0)
140
     * @param array         $extra                   Extra fields
141
     * @param string        $encrypt_method          Used if password is given encrypted. Set to an empty string by default
142
     * @param bool          $send_mail
143
     * @param bool          $isAdmin
144
     * @param string        $address
145
     * @param bool          $sendEmailToAllAdmins
146
     * @param FormValidator $form
147
     * @param int           $creatorId
148
     * @param array         $emailTemplate
149
     * @param string        $redirectToURLAfterLogin
150
     * @param bool          $addUserToNode
151
     *
152
     * @return mixed new user id - if the new user creation succeeds, false otherwise
153
     * @desc The function tries to retrieve user id from the session.
154
     * If it exists, the current user id is the creator id. If a problem arises,
155
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
156
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
157
     */
158
    public static function create_user(
159
        $firstName,
160
        $lastName,
161
        $status,
162
        $email,
163
        $loginName,
164
        $password,
165
        $official_code = '',
166
        $language = '',
167
        $phone = '',
168
        $picture_uri = '',
169
        $authSource = null,
170
        $expirationDate = null,
171
        $active = 1,
172
        $hr_dept_id = 0,
173
        $extra = [],
174
        $encrypt_method = '',
175
        $send_mail = false,
176
        $isAdmin = false,
177
        $address = '',
178
        $sendEmailToAllAdmins = false,
179
        $form = null,
180
        $creatorId = 0,
181
        $emailTemplate = [],
182
        $redirectToURLAfterLogin = '',
183
        $addUserToNode = true,
184
        $addUserToUrl = true
185
    ) {
186
        $authSource = !empty($authSource) ? $authSource : PLATFORM_AUTH_SOURCE;
187
        $creatorId = empty($creatorId) ? api_get_user_id() : 0;
188
        $creatorInfo = api_get_user_info($creatorId);
189
        $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
190
191
        // First check wether the login already exists
192
        if (!self::is_username_available($loginName)) {
193
            Display::addFlash(
194
                Display::return_message(get_lang('This login is already taken !'))
195
            );
196
197
            return false;
198
        }
199
200
        global $_configuration;
201
        $original_password = $password;
202
203
        $access_url_id = 1;
204
        if (api_get_multiple_access_url()) {
205
            $access_url_id = api_get_current_access_url_id();
206
        } else {
207
            // In some cases, the first access_url ID might be different from 1
208
            // for example when using a DB cluster or hacking the DB manually.
209
            // In this case, we want the first row, not necessarily "1".
210
            $accessUrlRepository = Container::getAccessUrlRepository();
211
            $accessUrl = $accessUrlRepository->getFirstId();
212
            if (!empty($accessUrl[0]) && !empty($accessUrl[0][1])) {
213
                $access_url_id = $accessUrl[0][1];
214
            }
215
        }
216
217
        if (isset($_configuration[$access_url_id]) &&
218
            is_array($_configuration[$access_url_id]) &&
219
            isset($_configuration[$access_url_id]['hosting_limit_users']) &&
220
            $_configuration[$access_url_id]['hosting_limit_users'] > 0) {
221
            $num = self::get_number_of_users();
222
            if ($num >= $_configuration[$access_url_id]['hosting_limit_users']) {
223
                api_warn_hosting_contact('hosting_limit_users');
224
                Display::addFlash(
225
                    Display::return_message(
226
                        get_lang('Sorry, this installation has a users limit, which has now been reached. To increase the number of users allowed on this Chamilo installation, please contact your hosting provider or, if available, upgrade to a superior hosting plan.'),
227
                        'warning'
228
                    )
229
                );
230
231
                return false;
232
            }
233
        }
234
235
        if (1 === $status &&
236
            isset($_configuration[$access_url_id]) &&
237
            is_array($_configuration[$access_url_id]) &&
238
            isset($_configuration[$access_url_id]['hosting_limit_teachers']) &&
239
            $_configuration[$access_url_id]['hosting_limit_teachers'] > 0
240
        ) {
241
            $num = self::get_number_of_users(1);
242
            if ($num >= $_configuration[$access_url_id]['hosting_limit_teachers']) {
243
                Display::addFlash(
244
                    Display::return_message(
245
                        get_lang('Sorry, this installation has a teachers limit, which has now been reached. To increase the number of teachers allowed on this Chamilo installation, please contact your hosting provider or, if available, upgrade to a superior hosting plan.'),
246
                        'warning'
247
                    )
248
                );
249
                api_warn_hosting_contact('hosting_limit_teachers');
250
251
                return false;
252
            }
253
        }
254
255
        if (empty($password)) {
256
            if (PLATFORM_AUTH_SOURCE === $authSource) {
257
                Display::addFlash(
258
                    Display::return_message(
259
                        get_lang('Required field').': '.get_lang(
260
                            'Password'
261
                        ),
262
                        'warning'
263
                    )
264
                );
265
266
                return false;
267
            }
268
269
            // We use the authSource as password.
270
            // The real validation will be by processed by the auth
271
            // source not Chamilo
272
            $password = $authSource;
273
        }
274
275
        // Checking the user language
276
        $languages = api_get_languages();
277
        $language = strtolower($language);
278
279
        // Default to english
280
        if (!in_array($language, $languages)) {
281
            $language = 'en';
282
        }
283
284
        $currentDate = api_get_utc_datetime();
285
        $now = new DateTime();
286
287
        if (empty($expirationDate) || '0000-00-00 00:00:00' === $expirationDate) {
288
            $expirationDate = null;
289
        // Default expiration date
290
            // if there is a default duration of a valid account then
291
            // we have to change the expiration_date accordingly
292
            // Accept 0000-00-00 00:00:00 as a null value to avoid issues with
293
            // third party code using this method with the previous (pre-1.10)
294
            // value of 0000...
295
            /*if ('' != api_get_setting('account_valid_duration')) {
296
                $expirationDate = new DateTime($currentDate);
297
                $days = (int) api_get_setting('account_valid_duration');
298
                $expirationDate->modify('+'.$days.' day');
299
            }*/
300
        } else {
301
            $expirationDate = api_get_utc_datetime($expirationDate);
302
            $expirationDate = new \DateTime($expirationDate, new DateTimeZone('UTC'));
303
        }
304
305
        $user = new User();
306
        $user
307
            ->setLastname($lastName)
308
            ->setFirstname($firstName)
309
            ->setUsername($loginName)
310
            ->setStatus($status)
311
            ->setPlainPassword($password)
312
            ->setEmail($email)
313
            ->setOfficialCode($official_code)
314
            ->setCreatorId($creatorId)
315
            ->setAuthSource($authSource)
316
            ->setPhone($phone)
317
            ->setAddress($address)
318
            ->setLanguage($language)
319
            ->setRegistrationDate($now)
320
            ->setHrDeptId($hr_dept_id)
321
            ->setActive($active)
322
            ->setEnabled($active)
323
            ->setTimezone(api_get_timezone())
324
        ;
325
326
        if (!empty($expirationDate)) {
327
            $user->setExpirationDate($expirationDate);
328
        }
329
330
        $em = Database::getManager();
331
        $repo = Container::$container->get('Chamilo\CoreBundle\Repository\UserRepository');
332
        $repo->updateUser($user, false);
333
334
        // Add user as a node
335
        if ($addUserToNode) {
336
            $resourceNode = new ResourceNode();
337
            $resourceNode
338
                ->setTitle($loginName)
339
                ->setCreator(api_get_user_entity($creatorId))
340
                ->setResourceType($repo->getResourceType())
341
            ;
342
            $em->persist($resourceNode);
343
            $user->setResourceNode($resourceNode);
344
        }
345
346
        $em->persist($user);
347
        $em->flush();
348
349
        $userId = $user->getId();
350
351
        // Add user to a group
352
        $statusToGroup = [
353
            COURSEMANAGER => 'TEACHER',
354
            STUDENT => 'STUDENT',
355
            DRH => 'RRHH',
356
            SESSIONADMIN => 'SESSION_ADMIN',
357
            STUDENT_BOSS => 'STUDENT_BOSS',
358
            INVITEE => 'INVITEE',
359
        ];
360
361
        if (isset($statusToGroup[$status])) {
362
            $group = Container::$container->get('Chamilo\CoreBundle\Repository\GroupRepository')->findOneBy(['code' => $statusToGroup[$status]]);
363
            if ($group) {
364
                $user->addGroup($group);
365
                $repo->updateUser($user);
366
            }
367
        }
368
369
        $em->flush();
370
371
        if (!empty($userId)) {
372
            if ($isAdmin) {
373
                self::add_user_as_admin($user);
0 ignored issues
show
Bug introduced by
The method add_user_as_admin() does not exist on UserManager. ( Ignorable by Annotation )

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

373
                self::/** @scrutinizer ignore-call */ 
374
                      add_user_as_admin($user);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
374
            }
375
376
            if ($addUserToUrl) {
377
                if (api_get_multiple_access_url()) {
378
                    UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
379
                } else {
380
                    //we are adding by default the access_url_user table with access_url_id = 1
381
                    UrlManager::add_user_to_url($userId, 1);
382
                }
383
            }
384
385
            if (is_array($extra) && count($extra) > 0) {
386
                $extra['item_id'] = $userId;
387
                $courseFieldValue = new ExtraFieldValue('user');
388
                $courseFieldValue->saveFieldValues($extra);
389
            } else {
390
                // Create notify settings by default
391
                self::update_extra_field_value(
392
                    $userId,
393
                    'mail_notify_invitation',
394
                    '1'
395
                );
396
                self::update_extra_field_value(
397
                    $userId,
398
                    'mail_notify_message',
399
                    '1'
400
                );
401
                self::update_extra_field_value(
402
                    $userId,
403
                    'mail_notify_group_message',
404
                    '1'
405
                );
406
            }
407
408
            self::update_extra_field_value(
409
                $userId,
410
                'already_logged_in',
411
                'false'
412
            );
413
414
            if (!empty($redirectToURLAfterLogin) && api_get_configuration_value('plugin_redirection_enabled')) {
415
                RedirectionPlugin::insert($userId, $redirectToURLAfterLogin);
416
            }
417
418
            if (!empty($email) && $send_mail) {
419
                $recipient_name = api_get_person_name(
420
                    $firstName,
421
                    $lastName,
422
                    null,
423
                    PERSON_NAME_EMAIL_ADDRESS
424
                );
425
                $tplSubject = new Template(
426
                    null,
427
                    false,
428
                    false,
429
                    false,
430
                    false,
431
                    false
432
                );
433
                $layoutSubject = $tplSubject->get_template('mail/subject_registration_platform.tpl');
434
                $emailSubject = $tplSubject->fetch($layoutSubject);
435
                $sender_name = api_get_person_name(
436
                    api_get_setting('administratorName'),
437
                    api_get_setting('administratorSurname'),
438
                    null,
439
                    PERSON_NAME_EMAIL_ADDRESS
440
                );
441
                $email_admin = api_get_setting('emailAdministrator');
442
443
                $url = api_get_path(WEB_PATH);
444
                if (api_is_multiple_url_enabled()) {
445
                    $access_url_id = api_get_current_access_url_id();
446
                    if (-1 != $access_url_id) {
447
                        $urlInfo = api_get_access_url($access_url_id);
448
                        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...
449
                            $url = $urlInfo['url'];
450
                        }
451
                    }
452
                }
453
454
                $tplContent = new Template(
455
                    null,
456
                    false,
457
                    false,
458
                    false,
459
                    false,
460
                    false
461
                );
462
                // variables for the default template
463
                $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
464
                $tplContent->assign('login_name', $loginName);
465
                $tplContent->assign('original_password', stripslashes($original_password));
466
                $tplContent->assign('mailWebPath', $url);
467
                $tplContent->assign('new_user', $user);
468
469
                $layoutContent = $tplContent->get_template('mail/content_registration_platform.tpl');
470
                $emailBody = $tplContent->fetch($layoutContent);
471
472
                $userInfo = api_get_user_info($userId);
473
                $mailTemplateManager = new MailTemplateManager();
474
475
                $phoneNumber = isset($extra['mobile_phone_number']) ? $extra['mobile_phone_number'] : null;
476
477
                $additionalParameters = [
478
                    'smsType' => SmsPlugin::WELCOME_LOGIN_PASSWORD,
479
                    'userId' => $userId,
480
                    'mobilePhoneNumber' => $phoneNumber,
481
                    'password' => $original_password,
482
                ];
483
484
                $emailBodyTemplate = '';
485
                if (!empty($emailTemplate)) {
486
                    if (isset($emailTemplate['content_registration_platform.tpl']) &&
487
                        !empty($emailTemplate['content_registration_platform.tpl'])
488
                    ) {
489
                        $emailBodyTemplate = $mailTemplateManager->parseTemplate(
490
                            $emailTemplate['content_registration_platform.tpl'],
491
                            $userInfo
492
                        );
493
                    }
494
                }
495
496
                $twoEmail = api_get_configuration_value('send_two_inscription_confirmation_mail');
497
                if (true === $twoEmail) {
498
                    $layoutContent = $tplContent->get_template('mail/new_user_first_email_confirmation.tpl');
499
                    $emailBody = $tplContent->fetch($layoutContent);
500
501
                    if (!empty($emailBodyTemplate) &&
502
                        isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
503
                        !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
504
                    ) {
505
                        $emailBody = $mailTemplateManager->parseTemplate(
506
                            $emailTemplate['new_user_first_email_confirmation.tpl'],
507
                            $userInfo
508
                        );
509
                    }
510
511
                    api_mail_html(
512
                        $recipient_name,
513
                        $email,
514
                        $emailSubject,
515
                        $emailBody,
516
                        $sender_name,
517
                        $email_admin,
518
                        null,
519
                        null,
520
                        null,
521
                        $additionalParameters,
522
                        $creatorEmail
523
                    );
524
525
                    $layoutContent = $tplContent->get_template('mail/new_user_second_email_confirmation.tpl');
526
                    $emailBody = $tplContent->fetch($layoutContent);
527
528
                    if (!empty($emailBodyTemplate) &&
529
                        isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
530
                        !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
531
                    ) {
532
                        $emailBody = $mailTemplateManager->parseTemplate(
533
                            $emailTemplate['new_user_second_email_confirmation.tpl'],
534
                            $userInfo
535
                        );
536
                    }
537
538
                    api_mail_html(
539
                        $recipient_name,
540
                        $email,
541
                        $emailSubject,
542
                        $emailBody,
543
                        $sender_name,
544
                        $email_admin,
545
                        null,
546
                        null,
547
                        null,
548
                        $additionalParameters,
549
                        $creatorEmail
550
                    );
551
                } else {
552
                    if (!empty($emailBodyTemplate)) {
553
                        $emailBody = $emailBodyTemplate;
554
                    }
555
                    $sendToInbox = api_get_configuration_value('send_inscription_msg_to_inbox');
556
                    if ($sendToInbox) {
557
                        $adminList = self::get_all_administrators();
558
                        $senderId = 1;
559
                        if (!empty($adminList)) {
560
                            $adminInfo = current($adminList);
561
                            $senderId = $adminInfo['user_id'];
562
                        }
563
564
                        MessageManager::send_message_simple(
565
                            $userId,
566
                            $emailSubject,
567
                            $emailBody,
568
                            $senderId
569
                        );
570
                    } else {
571
                        api_mail_html(
572
                            $recipient_name,
573
                            $email,
574
                            $emailSubject,
575
                            $emailBody,
576
                            $sender_name,
577
                            $email_admin,
578
                            null,
579
                            null,
580
                            null,
581
                            $additionalParameters,
582
                            $creatorEmail
583
                        );
584
                    }
585
                }
586
587
                $notification = api_get_configuration_value('send_notification_when_user_added');
588
                if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
589
                    foreach ($notification['admins'] as $adminId) {
590
                        $emailSubjectToAdmin = get_lang('The user has been added').': '.api_get_person_name($firstName, $lastName);
591
                        MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
592
                    }
593
                }
594
595
                if ($sendEmailToAllAdmins) {
596
                    $adminList = self::get_all_administrators();
597
598
                    $tplContent = new Template(
599
                        null,
600
                        false,
601
                        false,
602
                        false,
603
                        false,
604
                        false
605
                    );
606
                    // variables for the default template
607
                    $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
608
                    $tplContent->assign('user_added', $user);
609
                    $renderer = FormValidator::getDefaultRenderer();
610
                    // Form template
611
                    $elementTemplate = ' {label}: {element} <br />';
612
                    $renderer->setElementTemplate($elementTemplate);
613
                    /** @var FormValidator $form */
614
                    $form->freeze(null, $elementTemplate);
615
                    $form->removeElement('submit');
616
                    $formData = $form->returnForm();
617
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
618
                    $tplContent->assign('link', Display::url($url, $url));
619
                    $tplContent->assign('form', $formData);
620
621
                    $layoutContent = $tplContent->get_template('mail/content_registration_platform_to_admin.tpl');
622
                    $emailBody = $tplContent->fetch($layoutContent);
623
624
                    if (!empty($emailBodyTemplate) &&
625
                        isset($emailTemplate['content_registration_platform_to_admin.tpl']) &&
626
                        !empty($emailTemplate['content_registration_platform_to_admin.tpl'])
627
                    ) {
628
                        $emailBody = $mailTemplateManager->parseTemplate(
629
                            $emailTemplate['content_registration_platform_to_admin.tpl'],
630
                            $userInfo
631
                        );
632
                    }
633
634
                    $subject = get_lang('The user has been added');
635
636
                    foreach ($adminList as $adminId => $data) {
637
                        MessageManager::send_message_simple(
638
                            $adminId,
639
                            $subject,
640
                            $emailBody,
641
                            $userId
642
                        );
643
                    }
644
                }
645
                /* ENDS MANAGE EVENT WITH MAIL */
646
            }
647
            Event::addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId, null, $creatorId);
0 ignored issues
show
Bug introduced by
The method addEvent() does not exist on Event. ( Ignorable by Annotation )

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

647
            Event::/** @scrutinizer ignore-call */ 
648
                   addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId, null, $creatorId);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
648
        } else {
649
            Display::addFlash(
650
                Display::return_message(get_lang('There happened an unknown error. Please contact the platform administrator.'))
651
            );
652
653
            return false;
654
        }
655
656
        return $userId;
657
    }
658
659
    /**
660
     * Can user be deleted? This function checks whether there's a course
661
     * in which the given user is the
662
     * only course administrator. If that is the case, the user can't be
663
     * deleted because the course would remain without a course admin.
664
     *
665
     * @param int $user_id The user id
666
     *
667
     * @return bool true if user can be deleted
668
     *
669
     * @assert (null) === false
670
     * @assert (-1) === false
671
     * @assert ('abc') === false
672
     */
673
    public static function canDeleteUser($user_id)
674
    {
675
        $deny = api_get_configuration_value('deny_delete_users');
676
677
        if ($deny) {
678
            return false;
679
        }
680
681
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
682
        $user_id = (int) $user_id;
683
684
        if (empty($user_id)) {
685
            return false;
686
        }
687
688
        $sql = "SELECT * FROM $table_course_user
689
                WHERE status = 1 AND user_id = ".$user_id;
690
        $res = Database::query($sql);
691
        while ($course = Database::fetch_object($res)) {
692
            $sql = "SELECT id FROM $table_course_user
693
                    WHERE status=1 AND c_id = ".intval($course->c_id);
694
            $res2 = Database::query($sql);
695
            if (1 == Database::num_rows($res2)) {
696
                return false;
697
            }
698
        }
699
700
        return true;
701
    }
702
703
    /**
704
     * Delete a user from the platform, and all its belongings. This is a
705
     * very dangerous function that should only be accessible by
706
     * super-admins. Other roles should only be able to disable a user,
707
     * which removes access to the platform but doesn't delete anything.
708
     *
709
     * @param int The ID of th user to be deleted
710
     *
711
     * @throws Exception
712
     *
713
     * @return bool true if user is successfully deleted, false otherwise
714
     * @assert (null) === false
715
     * @assert ('abc') === false
716
     */
717
    public static function delete_user($user_id)
718
    {
719
        $user_id = (int) $user_id;
720
721
        if (empty($user_id)) {
722
            return false;
723
        }
724
725
        if (!self::canDeleteUser($user_id)) {
726
            return false;
727
        }
728
729
        $usergroup_rel_user = Database::get_main_table(TABLE_USERGROUP_REL_USER);
730
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
731
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
732
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
733
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
734
        $table_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
735
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
736
        $table_group = Database::get_course_table(TABLE_GROUP_USER);
737
        $table_work = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
738
739
        $userInfo = api_get_user_info($user_id);
740
        $repository = Container::getUserRepository();
741
742
        /** @var User $user */
743
        $user = $repository->find($user_id);
744
745
        $repository->deleteUser($user);
746
747
        // Unsubscribe the user from all groups in all his courses
748
        $sql = "SELECT c.id
749
                FROM $table_course c
750
                INNER JOIN $table_course_user cu
751
                ON (c.id = cu.c_id)
752
                WHERE
753
                    cu.user_id = '".$user_id."' AND
754
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
755
                ";
756
757
        $res = Database::query($sql);
758
        while ($course = Database::fetch_object($res)) {
759
            $sql = "DELETE FROM $table_group
760
                    WHERE c_id = {$course->id} AND user_id = $user_id";
761
            Database::query($sql);
762
        }
763
764
        // Unsubscribe user from usergroup_rel_user
765
        $sql = "DELETE FROM $usergroup_rel_user WHERE user_id = '".$user_id."'";
766
        Database::query($sql);
767
768
        // Unsubscribe user from all courses
769
        $sql = "DELETE FROM $table_course_user WHERE user_id = '".$user_id."'";
770
        Database::query($sql);
771
772
        // Unsubscribe user from all courses in sessions
773
        $sql = "DELETE FROM $table_session_course_user WHERE user_id = '".$user_id."'";
774
        Database::query($sql);
775
776
        // If the user was added as a id_coach then set the current admin as coach see BT#
777
        $currentUserId = api_get_user_id();
778
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
779
                WHERE id_coach = '".$user_id."'";
780
        Database::query($sql);
781
782
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
783
                WHERE session_admin_id = '".$user_id."'";
784
        Database::query($sql);
785
786
        // Unsubscribe user from all sessions
787
        $sql = "DELETE FROM $table_session_user
788
                WHERE user_id = '".$user_id."'";
789
        Database::query($sql);
790
791
        if (api_get_configuration_value('plugin_redirection_enabled')) {
792
            RedirectionPlugin::deleteUserRedirection($user_id);
793
        }
794
795
        // Delete the personal course categories
796
        $course_cat_table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
797
        $sql = "DELETE FROM $course_cat_table WHERE user_id = '".$user_id."'";
798
        Database::query($sql);
799
800
        // Delete user from the admin table
801
        $sql = "DELETE FROM $table_admin WHERE user_id = '".$user_id."'";
802
        Database::query($sql);
803
804
        // Delete the personal agenda-items from this user
805
        $agenda_table = Database::get_main_table(TABLE_PERSONAL_AGENDA);
806
        $sql = "DELETE FROM $agenda_table WHERE user = '".$user_id."'";
807
        Database::query($sql);
808
809
        $gradebook_results_table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_RESULT);
810
        $sql = 'DELETE FROM '.$gradebook_results_table.' WHERE user_id = '.$user_id;
811
        Database::query($sql);
812
813
        $extraFieldValue = new ExtraFieldValue('user');
814
        $extraFieldValue->deleteValuesByItem($user_id);
815
816
        UrlManager::deleteUserFromAllUrls($user_id);
817
        //UrlManager::deleteUserFromAllUrls($user_id);
818
819
        if ('true' === api_get_setting('allow_social_tool')) {
820
            $userGroup = new UserGroup();
821
            //Delete user from portal groups
822
            $group_list = $userGroup->get_groups_by_user($user_id);
823
            if (!empty($group_list)) {
824
                foreach ($group_list as $group_id => $data) {
825
                    $userGroup->delete_user_rel_group($user_id, $group_id);
826
                }
827
            }
828
829
            // Delete user from friend lists
830
            SocialManager::remove_user_rel_user($user_id, true);
831
        }
832
833
        // Removing survey invitation
834
        SurveyManager::delete_all_survey_invitations_by_user($user_id);
835
836
        // Delete students works
837
        $sql = "DELETE FROM $table_work WHERE user_id = $user_id AND c_id <> 0";
838
        Database::query($sql);
839
840
        /*$sql = "UPDATE c_item_property SET to_user_id = NULL
841
                WHERE to_user_id = '".$user_id."'";
842
        Database::query($sql);
843
844
        $sql = "UPDATE c_item_property SET insert_user_id = NULL
845
                WHERE insert_user_id = '".$user_id."'";
846
        Database::query($sql);
847
848
        $sql = "UPDATE c_item_property SET lastedit_user_id = NULL
849
                WHERE lastedit_user_id = '".$user_id."'";
850
        Database::query($sql);*/
851
852
        // Skills
853
        $em = Database::getManager();
854
855
        $criteria = ['user' => $user_id];
856
        $skills = $em->getRepository(SkillRelUser::class)->findBy($criteria);
857
        if ($skills) {
858
            /** @var SkillRelUser $skill */
859
            foreach ($skills as $skill) {
860
                $comments = $skill->getComments();
861
                if ($comments) {
862
                    /** @var SkillRelUserComment $comment */
863
                    foreach ($comments as $comment) {
864
                        $em->remove($comment);
865
                    }
866
                }
867
                $em->remove($skill);
868
            }
869
        }
870
871
        // ExtraFieldSavedSearch
872
        $criteria = ['user' => $user_id];
873
        $searchList = $em->getRepository(ExtraFieldSavedSearch::class)->findBy($criteria);
874
        if ($searchList) {
875
            foreach ($searchList as $search) {
876
                $em->remove($search);
877
            }
878
        }
879
880
        $connection = Database::getManager()->getConnection();
881
        $tableExists = $connection->getSchemaManager()->tablesExist(['plugin_bbb_room']);
882
        if ($tableExists) {
883
            // Delete user from database
884
            $sql = "DELETE FROM plugin_bbb_room WHERE participant_id = $user_id";
885
            Database::query($sql);
886
        }
887
888
        // Delete user/ticket relationships :(
889
        $tableExists = $connection->getSchemaManager()->tablesExist(['ticket_ticket']);
890
        if ($tableExists) {
891
            TicketManager::deleteUserFromTicketSystem($user_id);
892
        }
893
894
        $tableExists = $connection->getSchemaManager()->tablesExist(['c_lp_category_user']);
895
        if ($tableExists) {
896
            $sql = "DELETE FROM c_lp_category_user WHERE user_id = $user_id";
897
            Database::query($sql);
898
        }
899
900
        $app_plugin = new AppPlugin();
901
        $app_plugin->performActionsWhenDeletingItem('user', $user_id);
902
903
        // Add event to system log
904
        $authorId = api_get_user_id();
905
906
        Event::addEvent(
907
            LOG_USER_DELETE,
908
            LOG_USER_ID,
909
            $user_id,
910
            api_get_utc_datetime(),
911
            $authorId
912
        );
913
914
        Event::addEvent(
915
            LOG_USER_DELETE,
916
            LOG_USER_OBJECT,
917
            $userInfo,
918
            api_get_utc_datetime(),
919
            $authorId
920
        );
921
922
        return true;
923
    }
924
925
    /**
926
     * Deletes users completely. Can be called either as:
927
     * - UserManager::delete_users(1, 2, 3); or
928
     * - UserManager::delete_users(array(1, 2, 3));.
929
     *
930
     * @param array|int $ids
931
     *
932
     * @return bool True if at least one user was successfuly deleted. False otherwise.
933
     *
934
     * @author Laurent Opprecht
935
     *
936
     * @uses \UserManager::delete_user() to actually delete each user
937
     * @assert (null) === false
938
     * @assert (-1) === false
939
     * @assert (array(-1)) === false
940
     */
941
    public static function delete_users($ids = [])
942
    {
943
        $result = false;
944
        $ids = is_array($ids) ? $ids : func_get_args();
945
        if (!is_array($ids) || 0 == count($ids)) {
946
            return false;
947
        }
948
        $ids = array_map('intval', $ids);
949
        foreach ($ids as $id) {
950
            if (empty($id) || $id < 1) {
951
                continue;
952
            }
953
            $deleted = self::delete_user($id);
954
            $result = $deleted || $result;
955
        }
956
957
        return $result;
958
    }
959
960
    /**
961
     * Disable users. Can be called either as:
962
     * - UserManager::deactivate_users(1, 2, 3);
963
     * - UserManager::deactivate_users(array(1, 2, 3));.
964
     *
965
     * @param array|int $ids
966
     *
967
     * @return bool
968
     *
969
     * @author Laurent Opprecht
970
     * @assert (null) === false
971
     * @assert (array(-1)) === false
972
     */
973
    public static function deactivate_users($ids = [])
974
    {
975
        if (empty($ids)) {
976
            return false;
977
        }
978
979
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
980
981
        $ids = is_array($ids) ? $ids : func_get_args();
982
        $ids = array_map('intval', $ids);
983
        $ids = implode(',', $ids);
984
985
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
986
        $r = Database::query($sql);
987
        if (false !== $r) {
988
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
989
990
            return true;
991
        }
992
993
        return false;
994
    }
995
996
    /**
997
     * Enable users. Can be called either as:
998
     * - UserManager::activate_users(1, 2, 3);
999
     * - UserManager::activate_users(array(1, 2, 3));.
1000
     *
1001
     * @param array|int IDs of the users to enable
1002
     *
1003
     * @return bool
1004
     *
1005
     * @author Laurent Opprecht
1006
     * @assert (null) === false
1007
     * @assert (array(-1)) === false
1008
     */
1009
    public static function activate_users($ids = [])
1010
    {
1011
        if (empty($ids)) {
1012
            return false;
1013
        }
1014
1015
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1016
1017
        $ids = is_array($ids) ? $ids : func_get_args();
1018
        $ids = array_map('intval', $ids);
1019
        $ids = implode(',', $ids);
1020
1021
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
1022
        $r = Database::query($sql);
1023
        if (false !== $r) {
1024
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
1025
1026
            return true;
1027
        }
1028
1029
        return false;
1030
    }
1031
1032
    /**
1033
     * Update user information with all the parameters passed to this function.
1034
     *
1035
     * @param int    $user_id         The ID of the user to be updated
1036
     * @param string $firstname       The user's firstname
1037
     * @param string $lastname        The user's lastname
1038
     * @param string $username        The user's username (login)
1039
     * @param string $password        The user's password
1040
     * @param string $auth_source     The authentication source (default: "platform")
1041
     * @param string $email           The user's e-mail address
1042
     * @param int    $status          The user's status
1043
     * @param string $official_code   The user's official code (usually just an internal institutional code)
1044
     * @param string $phone           The user's phone number
1045
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
1046
     * @param string $expiration_date The date at which this user will be automatically disabled
1047
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
1048
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
1049
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
1050
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
1051
     * @param string $language        The language to which the user account will be set
1052
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
1053
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
1054
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
1055
     * @param string $address
1056
     * @param array  $emailTemplate
1057
     *
1058
     * @return bool|int False on error, or the user ID if the user information was updated
1059
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
1060
     */
1061
    public static function update_user(
1062
        $user_id,
1063
        $firstname,
1064
        $lastname,
1065
        $username,
1066
        $password = null,
1067
        $auth_source = null,
1068
        $email,
1069
        $status,
1070
        $official_code,
1071
        $phone,
1072
        $picture_uri,
1073
        $expiration_date,
1074
        $active,
1075
        $creator_id = null,
1076
        $hr_dept_id = 0,
1077
        $extra = null,
1078
        $language = 'english',
1079
        $encrypt_method = '',
1080
        $send_email = false,
1081
        $reset_password = 0,
1082
        $address = null,
1083
        $emailTemplate = []
1084
    ) {
1085
        $original_password = $password;
1086
        $user_id = (int) $user_id;
1087
        $creator_id = (int) $creator_id;
1088
1089
        if (empty($user_id)) {
1090
            return false;
1091
        }
1092
1093
        $userManager = self::getManager();
1094
        $user = api_get_user_entity($user_id);
1095
1096
        if (empty($user)) {
1097
            return false;
1098
        }
1099
1100
        if (0 == $reset_password) {
1101
            $password = null;
1102
            $auth_source = $user->getAuthSource();
1103
        } elseif (1 == $reset_password) {
1104
            $original_password = $password = api_generate_password();
1105
            $auth_source = PLATFORM_AUTH_SOURCE;
1106
        } elseif (2 == $reset_password) {
1107
            $password = $password;
1108
            $auth_source = PLATFORM_AUTH_SOURCE;
1109
        } elseif (3 == $reset_password) {
1110
            $password = $password;
1111
            $auth_source = $auth_source;
1112
        }
1113
1114
        // Checking the user language
1115
        $languages = array_keys(api_get_languages());
1116
        if (!in_array($language, $languages)) {
1117
            $language = api_get_setting('platformLanguage');
1118
        }
1119
1120
        $change_active = 0;
1121
        $isUserActive = $user->getActive();
1122
        if ($isUserActive != $active) {
1123
            $change_active = 1;
1124
        }
1125
1126
        $originalUsername = $user->getUsername();
1127
1128
        // If username is different from original then check if it exists.
1129
        if ($originalUsername !== $username) {
1130
            $available = self::is_username_available($username);
1131
            if (false === $available) {
1132
                return false;
1133
            }
1134
        }
1135
1136
        if (!empty($expiration_date)) {
1137
            $expiration_date = api_get_utc_datetime($expiration_date);
1138
            $expiration_date = new \DateTime(
1139
                $expiration_date,
1140
                new DateTimeZone('UTC')
1141
            );
1142
        }
1143
1144
        $user
1145
            ->setLastname($lastname)
1146
            ->setFirstname($firstname)
1147
            ->setUsername($username)
1148
            ->setStatus($status)
1149
            ->setAuthSource($auth_source)
1150
            ->setLanguage($language)
1151
            ->setLocale($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
        $statusToGroup = [
1168
            COURSEMANAGER => 'TEACHER',
1169
            STUDENT => 'STUDENT',
1170
            DRH => 'RRHH',
1171
            SESSIONADMIN => 'SESSION_ADMIN',
1172
            STUDENT_BOSS => 'STUDENT_BOSS',
1173
            INVITEE => 'INVITEE',
1174
        ];
1175
1176
        $group = Container::$container->get('Chamilo\CoreBundle\Repository\GroupRepository')->findOneBy(['code' => $statusToGroup[$status]]);
1177
        if ($group) {
1178
            $user->addGroup($group);
1179
        }
1180
1181
        $userManager->updateUser($user, true);
1182
        Event::addEvent(LOG_USER_UPDATE, LOG_USER_ID, $user_id);
1183
1184
        if (1 == $change_active) {
1185
            if (1 == $active) {
1186
                $event_title = LOG_USER_ENABLE;
1187
            } else {
1188
                $event_title = LOG_USER_DISABLE;
1189
            }
1190
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
1191
        }
1192
1193
        if (is_array($extra) && count($extra) > 0) {
1194
            $res = true;
1195
            foreach ($extra as $fname => $fvalue) {
1196
                $res = $res && self::update_extra_field_value(
1197
                    $user_id,
1198
                    $fname,
1199
                    $fvalue
1200
                );
1201
            }
1202
        }
1203
1204
        if (!empty($email) && $send_email) {
1205
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
1206
            $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
1207
            $sender_name = api_get_person_name(
1208
                api_get_setting('administratorName'),
1209
                api_get_setting('administratorSurname'),
1210
                null,
1211
                PERSON_NAME_EMAIL_ADDRESS
1212
            );
1213
            $email_admin = api_get_setting('emailAdministrator');
1214
            $url = api_get_path(WEB_PATH);
1215
            if (api_is_multiple_url_enabled()) {
1216
                $access_url_id = api_get_current_access_url_id();
1217
                if (-1 != $access_url_id) {
1218
                    $url = api_get_access_url($access_url_id);
1219
                    $url = $url['url'];
1220
                }
1221
            }
1222
1223
            $tplContent = new Template(
1224
                null,
1225
                false,
1226
                false,
1227
                false,
1228
                false,
1229
                false
1230
            );
1231
            // variables for the default template
1232
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
1233
            $tplContent->assign('login_name', $username);
1234
1235
            $originalPassword = '';
1236
            if ($reset_password > 0) {
1237
                $originalPassword = stripslashes($original_password);
1238
            }
1239
            $tplContent->assign('original_password', $originalPassword);
1240
            $tplContent->assign('portal_url', $url);
1241
1242
            $layoutContent = $tplContent->get_template('mail/user_edit_content.tpl');
1243
            $emailBody = $tplContent->fetch($layoutContent);
1244
1245
            $mailTemplateManager = new MailTemplateManager();
1246
1247
            if (!empty($emailTemplate) &&
1248
                isset($emailTemplate['user_edit_content.tpl']) &&
1249
                !empty($emailTemplate['user_edit_content.tpl'])
1250
            ) {
1251
                $userInfo = api_get_user_info($user_id);
1252
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], $userInfo);
1253
            }
1254
1255
            $creatorInfo = api_get_user_info($creator_id);
1256
            $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
1257
1258
            api_mail_html(
1259
                $recipient_name,
1260
                $email,
1261
                $emailsubject,
1262
                $emailBody,
1263
                $sender_name,
1264
                $email_admin,
1265
                null,
1266
                null,
1267
                null,
1268
                null,
1269
                $creatorEmail
1270
            );
1271
        }
1272
1273
        $cacheAvailable = api_get_configuration_value('apc');
1274
        if (true === $cacheAvailable) {
1275
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1276
            if (apcu_exists($apcVar)) {
1277
                apcu_delete($apcVar);
1278
            }
1279
        }
1280
1281
        return $user->getId();
1282
    }
1283
1284
    /**
1285
     * Disables a user.
1286
     *
1287
     * @param int User id
1288
     *
1289
     * @return bool
1290
     *
1291
     * @uses \UserManager::change_active_state() to actually disable the user
1292
     * @assert (0) === false
1293
     */
1294
    public static function disable($user_id)
1295
    {
1296
        if (empty($user_id)) {
1297
            return false;
1298
        }
1299
        self::change_active_state($user_id, 0);
1300
1301
        return true;
1302
    }
1303
1304
    /**
1305
     * Enable a user.
1306
     *
1307
     * @param int User id
1308
     *
1309
     * @return bool
1310
     *
1311
     * @uses \UserManager::change_active_state() to actually disable the user
1312
     * @assert (0) === false
1313
     */
1314
    public static function enable($user_id)
1315
    {
1316
        if (empty($user_id)) {
1317
            return false;
1318
        }
1319
        self::change_active_state($user_id, 1);
1320
1321
        return true;
1322
    }
1323
1324
    /**
1325
     * Returns the user's id based on the original id and field name in
1326
     * the extra fields. Returns 0 if no user was found. This function is
1327
     * mostly useful in the context of a web services-based sinchronization.
1328
     *
1329
     * @param string Original user id
1330
     * @param string Original field name
1331
     *
1332
     * @return int User id
1333
     * @assert ('0','---') === 0
1334
     */
1335
    public static function get_user_id_from_original_id(
1336
        $original_user_id_value,
1337
        $original_user_id_name
1338
    ) {
1339
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1340
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1341
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1342
1343
        $original_user_id_name = Database::escape_string($original_user_id_name);
1344
        $original_user_id_value = Database::escape_string($original_user_id_value);
1345
1346
        $sql = "SELECT item_id as user_id
1347
                FROM $t_uf uf
1348
                INNER JOIN $t_ufv ufv
1349
                ON ufv.field_id = uf.id
1350
                WHERE
1351
                    variable = '$original_user_id_name' AND
1352
                    value = '$original_user_id_value' AND
1353
                    extra_field_type = $extraFieldType
1354
                ";
1355
        $res = Database::query($sql);
1356
        $row = Database::fetch_object($res);
1357
        if ($row) {
1358
            return $row->user_id;
1359
        }
1360
1361
        return 0;
1362
    }
1363
1364
    /**
1365
     * Check if a username is available.
1366
     *
1367
     * @param string $username the wanted username
1368
     *
1369
     * @return bool true if the wanted username is available
1370
     * @assert ('') === false
1371
     * @assert ('xyzxyzxyz') === true
1372
     */
1373
    public static function is_username_available($username)
1374
    {
1375
        if (empty($username)) {
1376
            return false;
1377
        }
1378
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1379
        $sql = "SELECT username FROM $table_user
1380
                WHERE username = '".Database::escape_string($username)."'";
1381
        $res = Database::query($sql);
1382
1383
        return 0 == Database::num_rows($res);
1384
    }
1385
1386
    /**
1387
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1388
     *
1389
     * @param string $firstname the first name of the user
1390
     * @param string $lastname  the last name of the user
1391
     *
1392
     * @return string suggests a username that contains only ASCII-letters and digits,
1393
     *                without check for uniqueness within the system
1394
     *
1395
     * @author Julio Montoya Armas
1396
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1397
     * @assert ('','') === false
1398
     * @assert ('a','b') === 'ab'
1399
     */
1400
    public static function create_username($firstname, $lastname)
1401
    {
1402
        if (empty($firstname) && empty($lastname)) {
1403
            return false;
1404
        }
1405
1406
        // The first letter only.
1407
        $firstname = api_substr(
1408
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1409
            0,
1410
            1
1411
        );
1412
        //Looking for a space in the lastname
1413
        $pos = api_strpos($lastname, ' ');
1414
        if (false !== $pos) {
1415
            $lastname = api_substr($lastname, 0, $pos);
1416
        }
1417
1418
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1419
        $username = $firstname.$lastname;
1420
        if (empty($username)) {
1421
            $username = 'user';
1422
        }
1423
1424
        $username = URLify::transliterate($username);
1425
1426
        return strtolower(substr($username, 0, USERNAME_MAX_LENGTH - 3));
1427
    }
1428
1429
    /**
1430
     * Creates a unique username, using:
1431
     * 1. the first name and the last name of a user;
1432
     * 2. an already created username but not checked for uniqueness yet.
1433
     *
1434
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1435
     *                          parameter is treated as username which is to be checked f
1436
     *                          or uniqueness and to be modified when it is necessary.
1437
     * @param string $lastname  the last name of the user
1438
     *
1439
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1440
     *                Note: When the method is called several times with same parameters,
1441
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1442
     *
1443
     * @author Ivan Tcholakov, 2009
1444
     */
1445
    public static function create_unique_username($firstname, $lastname = null)
1446
    {
1447
        if (is_null($lastname)) {
1448
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1449
            // For making this method tolerant of mistakes,
1450
            // let us transliterate and purify the suggested input username anyway.
1451
            // So, instead of the sentence $username = $firstname; we place the following:
1452
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1453
        } else {
1454
            $username = self::create_username($firstname, $lastname);
1455
        }
1456
        if (!self::is_username_available($username)) {
1457
            $i = 2;
1458
            $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1459
            while (!self::is_username_available($temp_username)) {
1460
                $i++;
1461
                $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1462
            }
1463
            $username = $temp_username;
1464
        }
1465
1466
        $username = URLify::transliterate($username);
1467
1468
        return $username;
1469
    }
1470
1471
    /**
1472
     * Modifies a given username accordingly to the specification for valid characters and length.
1473
     *
1474
     * @param $username string          The input username
1475
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1476
     *                     otherwise compliance may be partial. The default value is FALSE.
1477
     *
1478
     * @return string the resulting purified username
1479
     */
1480
    public static function purify_username($username, $strict = false)
1481
    {
1482
        if ($strict) {
1483
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1484
            // into ASCII letters in order they not to be totally removed.
1485
            // 2. Applying the strict purifier.
1486
            // 3. Length limitation.
1487
            $return = 'true' === api_get_setting('login_is_email') ? substr(preg_replace(USERNAME_PURIFIER_MAIL, '', $username), 0, USERNAME_MAX_LENGTH) : substr(preg_replace(USERNAME_PURIFIER, '', $username), 0, USERNAME_MAX_LENGTH);
1488
            $return = URLify::transliterate($return);
1489
1490
            return $return;
1491
        }
1492
1493
        // 1. Applying the shallow purifier.
1494
        // 2. Length limitation.
1495
        return substr(
1496
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1497
            0,
1498
            USERNAME_MAX_LENGTH
1499
        );
1500
    }
1501
1502
    /**
1503
     * Checks whether the user id exists in the database.
1504
     *
1505
     * @param int $userId User id
1506
     *
1507
     * @return bool True if user id was found, false otherwise
1508
     */
1509
    public static function is_user_id_valid($userId)
1510
    {
1511
        $resultData = Database::select(
1512
            'COUNT(1) AS count',
1513
            Database::get_main_table(TABLE_MAIN_USER),
1514
            [
1515
                'where' => ['id = ?' => (int) $userId],
1516
            ],
1517
            'first'
1518
        );
1519
1520
        if (false === $resultData) {
1521
            return false;
1522
        }
1523
1524
        return $resultData['count'] > 0;
1525
    }
1526
1527
    /**
1528
     * Checks whether a given username matches to the specification strictly.
1529
     * The empty username is assumed here as invalid.
1530
     * Mostly this function is to be used in the user interface built-in validation routines
1531
     * for providing feedback while usernames are enterd manually.
1532
     *
1533
     * @param string $username the input username
1534
     *
1535
     * @return bool returns TRUE if the username is valid, FALSE otherwise
1536
     */
1537
    public static function is_username_valid($username)
1538
    {
1539
        return !empty($username) && $username == self::purify_username($username, true);
1540
    }
1541
1542
    /**
1543
     * Checks whether a username is empty. If the username contains whitespace characters,
1544
     * such as spaces, tabulators, newlines, etc.,
1545
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
1546
     *
1547
     * @param string $username the given username
1548
     *
1549
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1550
     */
1551
    public static function is_username_empty($username)
1552
    {
1553
        return 0 == strlen(self::purify_username($username, false));
1554
    }
1555
1556
    /**
1557
     * Checks whether a username is too long or not.
1558
     *
1559
     * @param string $username the given username, it should contain only ASCII-letters and digits
1560
     *
1561
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1562
     */
1563
    public static function is_username_too_long($username)
1564
    {
1565
        return strlen($username) > USERNAME_MAX_LENGTH;
1566
    }
1567
1568
    /**
1569
     * Get the users by ID.
1570
     *
1571
     * @param array  $ids    student ids
1572
     * @param string $active
1573
     * @param string $order
1574
     * @param string $limit
1575
     *
1576
     * @return array $result student information
1577
     */
1578
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
1579
    {
1580
        if (empty($ids)) {
1581
            return [];
1582
        }
1583
1584
        $ids = is_array($ids) ? $ids : [$ids];
1585
        $ids = array_map('intval', $ids);
1586
        $ids = implode(',', $ids);
1587
1588
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
1589
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
1590
        if (!is_null($active)) {
1591
            $sql .= ' AND active='.($active ? '1' : '0');
1592
        }
1593
1594
        if (!is_null($order)) {
1595
            $order = Database::escape_string($order);
1596
            $sql .= ' ORDER BY '.$order;
1597
        }
1598
1599
        if (!is_null($limit)) {
1600
            $limit = Database::escape_string($limit);
1601
            $sql .= ' LIMIT '.$limit;
1602
        }
1603
1604
        $rs = Database::query($sql);
1605
        $result = [];
1606
        while ($row = Database::fetch_array($rs)) {
1607
            $result[] = $row;
1608
        }
1609
1610
        return $result;
1611
    }
1612
1613
    /**
1614
     * Get a list of users of which the given conditions match with an = 'cond'.
1615
     *
1616
     * @param array $conditions a list of condition (example : status=>STUDENT)
1617
     * @param array $order_by   a list of fields on which sort
1618
     *
1619
     * @return array an array with all users of the platform
1620
     *
1621
     * @todo security filter order by
1622
     */
1623
    public static function get_user_list(
1624
        $conditions = [],
1625
        $order_by = [],
1626
        $limit_from = false,
1627
        $limit_to = false,
1628
        $idCampus = null
1629
    ) {
1630
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1631
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1632
        $return_array = [];
1633
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1634
1635
        if (api_is_multiple_url_enabled()) {
1636
            if ($idCampus) {
1637
                $urlId = $idCampus;
1638
            } else {
1639
                $urlId = api_get_current_access_url_id();
1640
            }
1641
            $sql .= " INNER JOIN $userUrlTable url_user
1642
                      ON (user.id = url_user.user_id)
1643
                      WHERE url_user.access_url_id = $urlId";
1644
        } else {
1645
            $sql .= " WHERE 1=1 ";
1646
        }
1647
1648
        if (count($conditions) > 0) {
1649
            foreach ($conditions as $field => $value) {
1650
                $field = Database::escape_string($field);
1651
                $value = Database::escape_string($value);
1652
                $sql .= " AND $field = '$value'";
1653
            }
1654
        }
1655
1656
        if (count($order_by) > 0) {
1657
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1658
        }
1659
1660
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1661
            $limit_from = (int) $limit_from;
1662
            $limit_to = (int) $limit_to;
1663
            $sql .= " LIMIT $limit_from, $limit_to";
1664
        }
1665
        $sql_result = Database::query($sql);
1666
        while ($result = Database::fetch_array($sql_result)) {
1667
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1668
            $return_array[] = $result;
1669
        }
1670
1671
        return $return_array;
1672
    }
1673
1674
    public static function getUserListExtraConditions(
1675
        $conditions = [],
1676
        $order_by = [],
1677
        $limit_from = false,
1678
        $limit_to = false,
1679
        $idCampus = null,
1680
        $extraConditions = '',
1681
        $getCount = false
1682
    ) {
1683
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1684
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1685
        $return_array = [];
1686
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1687
1688
        if ($getCount) {
1689
            $sql = "SELECT count(user.id) count FROM $user_table user ";
1690
        }
1691
1692
        if (api_is_multiple_url_enabled()) {
1693
            if ($idCampus) {
1694
                $urlId = $idCampus;
1695
            } else {
1696
                $urlId = api_get_current_access_url_id();
1697
            }
1698
            $sql .= " INNER JOIN $userUrlTable url_user
1699
                      ON (user.user_id = url_user.user_id)
1700
                      WHERE url_user.access_url_id = $urlId";
1701
        } else {
1702
            $sql .= " WHERE 1=1 ";
1703
        }
1704
1705
        $sql .= " AND status <> ".ANONYMOUS." ";
1706
1707
        if (count($conditions) > 0) {
1708
            foreach ($conditions as $field => $value) {
1709
                $field = Database::escape_string($field);
1710
                $value = Database::escape_string($value);
1711
                $sql .= " AND $field = '$value'";
1712
            }
1713
        }
1714
1715
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
1716
1717
        if (!empty($order_by) && count($order_by) > 0) {
1718
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1719
        }
1720
1721
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1722
            $limit_from = (int) $limit_from;
1723
            $limit_to = (int) $limit_to;
1724
            $sql .= " LIMIT $limit_from, $limit_to";
1725
        }
1726
1727
        $sql_result = Database::query($sql);
1728
1729
        if ($getCount) {
1730
            $result = Database::fetch_array($sql_result);
1731
1732
            return $result['count'];
1733
        }
1734
1735
        while ($result = Database::fetch_array($sql_result)) {
1736
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1737
            $return_array[] = $result;
1738
        }
1739
1740
        return $return_array;
1741
    }
1742
1743
    /**
1744
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
1745
     *
1746
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
1747
     * @param array  $order_by         a list of fields on which sort
1748
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
1749
     * @param string $condition        Whether we want the filters to be combined by AND or OR
1750
     * @param array  $onlyThisUserList
1751
     *
1752
     * @return array an array with all users of the platform
1753
     *
1754
     * @todo optional course code parameter, optional sorting parameters...
1755
     * @todo security filter order_by
1756
     */
1757
    public static function getUserListLike(
1758
        $conditions = [],
1759
        $order_by = [],
1760
        $simple_like = false,
1761
        $condition = 'AND',
1762
        $onlyThisUserList = []
1763
    ) {
1764
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1765
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1766
        $return_array = [];
1767
        $sql_query = "SELECT user.id FROM $user_table user ";
1768
1769
        if (api_is_multiple_url_enabled()) {
1770
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
1771
        }
1772
1773
        $sql_query .= ' WHERE 1 = 1 ';
1774
        if (count($conditions) > 0) {
1775
            $temp_conditions = [];
1776
            foreach ($conditions as $field => $value) {
1777
                $field = Database::escape_string($field);
1778
                $value = Database::escape_string($value);
1779
                if ($simple_like) {
1780
                    $temp_conditions[] = $field." LIKE '$value%'";
1781
                } else {
1782
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
1783
                }
1784
            }
1785
            if (!empty($temp_conditions)) {
1786
                $sql_query .= ' AND '.implode(' '.$condition.' ', $temp_conditions);
1787
            }
1788
1789
            if (api_is_multiple_url_enabled()) {
1790
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1791
            }
1792
        } else {
1793
            if (api_is_multiple_url_enabled()) {
1794
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1795
            }
1796
        }
1797
1798
        if (!empty($onlyThisUserList)) {
1799
            $onlyThisUserListToString = implode("','", $onlyThisUserList);
1800
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
1801
        }
1802
1803
        if (count($order_by) > 0) {
1804
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1805
        }
1806
1807
        $sql_result = Database::query($sql_query);
1808
        while ($result = Database::fetch_array($sql_result)) {
1809
            $userInfo = api_get_user_info($result['id']);
1810
            $return_array[] = $userInfo;
1811
        }
1812
1813
        return $return_array;
1814
    }
1815
1816
    /**
1817
     * Get user picture URL or path from user ID (returns an array).
1818
     * The return format is a complete path, enabling recovery of the directory
1819
     * with dirname() or the file with basename(). This also works for the
1820
     * functions dealing with the user's productions, as they are located in
1821
     * the same directory.
1822
     *
1823
     * @deprecated use Resources.
1824
     *
1825
     * @param int    $id       User ID
1826
     * @param string $type     Type of path to return (can be 'system', 'web')
1827
     * @param array  $userInfo user information to avoid query the DB
1828
     *                         returns the /main/img/unknown.jpg image set it at true
1829
     *
1830
     * @return array Array of 2 elements: 'dir' and 'file' which contain
1831
     *               the dir and file as the name implies if image does not exist it will
1832
     *               return the unknow image if anonymous parameter is true if not it returns an empty array
1833
     */
1834
    public static function get_user_picture_path_by_id(
1835
        $id,
1836
        $type = 'web',
1837
        $userInfo = []
1838
    ) {
1839
        switch ($type) {
1840
            case 'system': // Base: absolute system path.
1841
                $base = api_get_path(SYS_CODE_PATH);
1842
                break;
1843
            case 'web': // Base: absolute web path.
1844
            default:
1845
                $base = api_get_path(WEB_CODE_PATH);
1846
                break;
1847
        }
1848
1849
        $anonymousPath = [
1850
            'dir' => $base.'img/',
1851
            'file' => 'unknown.jpg',
1852
            'email' => '',
1853
        ];
1854
1855
        if (empty($id) || empty($type)) {
1856
            return $anonymousPath;
1857
        }
1858
1859
        $id = (int) $id;
1860
        if (empty($userInfo)) {
1861
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
1862
            $sql = "SELECT email, picture_uri FROM $user_table
1863
                    WHERE id = ".$id;
1864
            $res = Database::query($sql);
1865
1866
            if (!Database::num_rows($res)) {
1867
                return $anonymousPath;
1868
            }
1869
            $user = Database::fetch_array($res);
1870
            if (empty($user['picture_uri'])) {
1871
                return $anonymousPath;
1872
            }
1873
        } else {
1874
            $user = $userInfo;
1875
        }
1876
1877
        $pictureFilename = trim($user['picture_uri']);
1878
1879
        $dir = self::getUserPathById($id, $type);
0 ignored issues
show
Deprecated Code introduced by
The function UserManager::getUserPathById() has been deprecated: use Resources ( Ignorable by Annotation )

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

1879
        $dir = /** @scrutinizer ignore-deprecated */ self::getUserPathById($id, $type);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1880
1881
        return [
1882
            'dir' => $dir,
1883
            'file' => $pictureFilename,
1884
            'email' => $user['email'],
1885
        ];
1886
    }
1887
1888
    /**
1889
     * *** READ BEFORE REVIEW THIS FUNCTION ***
1890
     * This function is a exact copy from get_user_picture_path_by_id() and it was create it to avoid
1891
     * a recursive calls for get_user_picture_path_by_id() in another functions when you update a user picture
1892
     * in same script, so you can find this function usage in update_user_picture() function.
1893
     *
1894
     * @param int    $id       User ID
1895
     * @param string $type     Type of path to return (can be 'system', 'web')
1896
     * @param array  $userInfo user information to avoid query the DB
1897
     *                         returns the /main/img/unknown.jpg image set it at true
1898
     *
1899
     * @return array Array of 2 elements: 'dir' and 'file' which contain
1900
     *               the dir and file as the name implies if image does not exist it will
1901
     *               return the unknown image if anonymous parameter is true if not it returns an empty array
1902
     */
1903
    public static function getUserPicturePathById($id, $type = 'web', $userInfo = [])
1904
    {
1905
        switch ($type) {
1906
            case 'system': // Base: absolute system path.
1907
                $base = api_get_path(SYS_CODE_PATH);
1908
                break;
1909
            case 'web': // Base: absolute web path.
1910
            default:
1911
                $base = api_get_path(WEB_CODE_PATH);
1912
                break;
1913
        }
1914
1915
        $anonymousPath = [
1916
            'dir' => $base.'img/',
1917
            'file' => 'unknown.jpg',
1918
            'email' => '',
1919
        ];
1920
1921
        if (empty($id) || empty($type)) {
1922
            return $anonymousPath;
1923
        }
1924
1925
        $id = (int) $id;
1926
        if (empty($userInfo)) {
1927
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
1928
            $sql = "SELECT email, picture_uri FROM $user_table WHERE id = $id";
1929
            $res = Database::query($sql);
1930
1931
            if (!Database::num_rows($res)) {
1932
                return $anonymousPath;
1933
            }
1934
            $user = Database::fetch_array($res);
1935
1936
            if (empty($user['picture_uri'])) {
1937
                return $anonymousPath;
1938
            }
1939
        } else {
1940
            $user = $userInfo;
1941
        }
1942
1943
        $pictureFilename = trim($user['picture_uri']);
1944
        $dir = self::getUserPathById($id, $type);
0 ignored issues
show
Deprecated Code introduced by
The function UserManager::getUserPathById() has been deprecated: use Resources ( Ignorable by Annotation )

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

1944
        $dir = /** @scrutinizer ignore-deprecated */ self::getUserPathById($id, $type);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1945
1946
        return [
1947
            'dir' => $dir,
1948
            'file' => $pictureFilename,
1949
            'email' => $user['email'],
1950
        ];
1951
    }
1952
1953
    /**
1954
     * Get user path from user ID (returns an array).
1955
     * The return format is a complete path to a folder ending with "/"
1956
     * In case the first level of subdirectory of users/ does not exist, the
1957
     * function will attempt to create it. Probably not the right place to do it
1958
     * but at least it avoids headaches in many other places.
1959
     *
1960
     * @deprecated use Resources
1961
     *
1962
     * @param int    $id   User ID
1963
     * @param string $type Type of path to return (can be 'system', 'web', 'last')
1964
     *
1965
     * @return string User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
1966
     */
1967
    public static function getUserPathById($id, $type)
1968
    {
1969
        return null;
1970
        $id = (int) $id;
0 ignored issues
show
Unused Code introduced by
$id = (int)$id is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1971
        if (!$id) {
1972
            return null;
1973
        }
1974
1975
        $userPath = "users/$id/";
1976
        if ('true' === api_get_setting('split_users_upload_directory')) {
1977
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
1978
            // In exceptional cases, on some portals, the intermediate base user
1979
            // directory might not have been created. Make sure it is before
1980
            // going further.
1981
1982
            $rootPath = api_get_path(SYS_UPLOAD_PATH).'users/'.substr((string) $id, 0, 1);
1983
            if (!is_dir($rootPath)) {
1984
                $perm = api_get_permissions_for_new_directories();
1985
                try {
1986
                    mkdir($rootPath, $perm);
1987
                } catch (Exception $e) {
1988
                    error_log($e->getMessage());
1989
                }
1990
            }
1991
        }
1992
        switch ($type) {
1993
            case 'system': // Base: absolute system path.
1994
                $userPath = api_get_path(SYS_UPLOAD_PATH).$userPath;
1995
                break;
1996
            case 'web': // Base: absolute web path.
1997
                $userPath = api_get_path(WEB_UPLOAD_PATH).$userPath;
1998
                break;
1999
            case 'last': // Only the last part starting with users/
2000
                break;
2001
        }
2002
2003
        return $userPath;
2004
    }
2005
2006
    /**
2007
     * Gets the current user image.
2008
     *
2009
     * @param string $user_id
2010
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
2011
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
2012
     * @param bool   $addRandomId
2013
     * @param array  $userInfo    to avoid query the DB
2014
     *
2015
     * @todo use resources to get the user picture
2016
     *
2017
     * @return string
2018
     */
2019
    public static function getUserPicture(
2020
        $user_id,
2021
        $size = USER_IMAGE_SIZE_MEDIUM,
2022
        $addRandomId = true,
2023
        $userInfo = []
2024
    ) {
2025
        // Make sure userInfo is defined. Otherwise, define it!
2026
        if (empty($userInfo) || !is_array($userInfo) || 0 == count($userInfo)) {
2027
            if (empty($user_id)) {
2028
                return '';
2029
            } else {
2030
                $userInfo = api_get_user_info($user_id);
2031
            }
2032
        }
2033
2034
        $imageWebPath = self::get_user_picture_path_by_id(
0 ignored issues
show
Deprecated Code introduced by
The function UserManager::get_user_picture_path_by_id() has been deprecated: use Resources. ( Ignorable by Annotation )

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

2034
        $imageWebPath = /** @scrutinizer ignore-deprecated */ self::get_user_picture_path_by_id(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
2035
            $user_id,
2036
            'web',
2037
            $userInfo
2038
        );
2039
        $pictureWebFile = $imageWebPath['file'];
2040
        $pictureWebDir = $imageWebPath['dir'];
2041
2042
        $pictureAnonymousSize = '128';
2043
        $gravatarSize = 22;
2044
        $realSizeName = 'small_';
2045
2046
        switch ($size) {
2047
            case USER_IMAGE_SIZE_SMALL:
2048
                $pictureAnonymousSize = '32';
2049
                $realSizeName = 'small_';
2050
                $gravatarSize = 32;
2051
                break;
2052
            case USER_IMAGE_SIZE_MEDIUM:
2053
                $pictureAnonymousSize = '64';
2054
                $realSizeName = 'medium_';
2055
                $gravatarSize = 64;
2056
                break;
2057
            case USER_IMAGE_SIZE_ORIGINAL:
2058
                $pictureAnonymousSize = '128';
2059
                $realSizeName = '';
2060
                $gravatarSize = 128;
2061
                break;
2062
            case USER_IMAGE_SIZE_BIG:
2063
                $pictureAnonymousSize = '128';
2064
                $realSizeName = 'big_';
2065
                $gravatarSize = 128;
2066
                break;
2067
        }
2068
2069
        $gravatarEnabled = api_get_setting('gravatar_enabled');
2070
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
2071
        if ('unknown.jpg' == $pictureWebFile || empty($pictureWebFile)) {
2072
            if ('true' === $gravatarEnabled) {
2073
                $file = self::getGravatar(
2074
                    $imageWebPath['email'],
2075
                    $gravatarSize,
2076
                    api_get_setting('gravatar_type')
2077
                );
2078
2079
                if ($addRandomId) {
2080
                    $file .= '&rand='.uniqid();
2081
                }
2082
2083
                return $file;
2084
            }
2085
2086
            return $anonymousPath;
2087
        }
2088
2089
        $pictureSysPath = self::get_user_picture_path_by_id($user_id, 'system');
0 ignored issues
show
Deprecated Code introduced by
The function UserManager::get_user_picture_path_by_id() has been deprecated: use Resources. ( Ignorable by Annotation )

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

2089
        $pictureSysPath = /** @scrutinizer ignore-deprecated */ self::get_user_picture_path_by_id($user_id, 'system');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
2090
        $file = $pictureSysPath['dir'].$realSizeName.$pictureWebFile;
2091
        $picture = '';
2092
        if (file_exists($file)) {
2093
            $picture = $pictureWebDir.$realSizeName.$pictureWebFile;
2094
        } else {
2095
            $file = $pictureSysPath['dir'].$pictureWebFile;
2096
            if (file_exists($file) && !is_dir($file)) {
2097
                $picture = $pictureWebFile['dir'].$pictureWebFile;
2098
            }
2099
        }
2100
2101
        if (empty($picture)) {
2102
            return $anonymousPath;
2103
        }
2104
2105
        if ($addRandomId) {
2106
            $picture .= '?rand='.uniqid();
2107
        }
2108
2109
        return $picture;
2110
    }
2111
2112
    /**
2113
     * Creates new user photos in various sizes of a user, or deletes user photos.
2114
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2115
     *
2116
     * @param int    $user_id the user internal identification number
2117
     * @param string $file    The common file name for the newly created photos.
2118
     *                        It will be checked and modified for compatibility with the file system.
2119
     *                        If full name is provided, path component is ignored.
2120
     *                        If an empty name is provided, then old user photos are deleted only,
2121
     *
2122
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
2123
     *
2124
     * @param string $source_file    the full system name of the image from which user photos will be created
2125
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
2126
     *
2127
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
2128
     *              When deletion is requested returns empty string.
2129
     *              In case of internal error or negative validation returns FALSE.
2130
     */
2131
    public static function update_user_picture(
2132
        $user_id,
2133
        $file = null,
2134
        $source_file = null,
2135
        $cropParameters = ''
2136
    ) {
2137
        if (empty($user_id)) {
2138
            return false;
2139
        }
2140
        $delete = empty($file);
2141
        if (empty($source_file)) {
2142
            $source_file = $file;
2143
        }
2144
2145
        // User-reserved directory where photos have to be placed.
2146
        $path_info = self::getUserPicturePathById($user_id, 'system');
2147
        $path = $path_info['dir'];
2148
2149
        // If this directory does not exist - we create it.
2150
        if (!file_exists($path)) {
2151
            mkdir($path, api_get_permissions_for_new_directories(), true);
2152
        }
2153
2154
        // The old photos (if any).
2155
        $old_file = $path_info['file'];
2156
2157
        // Let us delete them.
2158
        if ('unknown.jpg' != $old_file) {
2159
            if ('true' == api_get_setting('platform.keep_old_images_after_delete')) {
2160
                $prefix = 'saved_'.date('Y_m_d_H_i_s').'_'.uniqid('').'_';
2161
                @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

2161
                /** @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...
2162
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
2163
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
2164
                @rename($path.$old_file, $path.$prefix.$old_file);
2165
            } else {
2166
                @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

2166
                /** @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...
2167
                @unlink($path.'medium_'.$old_file);
2168
                @unlink($path.'big_'.$old_file);
2169
                @unlink($path.$old_file);
2170
            }
2171
        }
2172
2173
        // Exit if only deletion has been requested. Return an empty picture name.
2174
        if ($delete) {
2175
            return '';
2176
        }
2177
2178
        // Validation 2.
2179
        $allowed_types = api_get_supported_image_extensions();
2180
        $file = str_replace('\\', '/', $file);
2181
        $filename = (false !== ($pos = strrpos($file, '/'))) ? substr($file, $pos + 1) : $file;
2182
        $extension = strtolower(substr(strrchr($filename, '.'), 1));
2183
        if (!in_array($extension, $allowed_types)) {
2184
            return false;
2185
        }
2186
2187
        // This is the common name for the new photos.
2188
        if ('unknown.jpg' != $old_file) {
2189
            $old_extension = strtolower(substr(strrchr($old_file, '.'), 1));
2190
            $filename = in_array($old_extension, $allowed_types) ? substr($old_file, 0, -strlen($old_extension)) : $old_file;
2191
            $filename = ('.' == substr($filename, -1)) ? $filename.$extension : $filename.'.'.$extension;
2192
        } else {
2193
            $filename = api_replace_dangerous_char($filename);
2194
            $filename = uniqid('').'_'.$filename;
2195
            // We always prefix user photos with user ids, so on setting
2196
            // api_get_setting('split_users_upload_directory') === 'true'
2197
            // the correspondent directories to be found successfully.
2198
            $filename = $user_id.'_'.$filename;
2199
        }
2200
2201
        //Crop the image to adjust 1:1 ratio
2202
        $image = new Image($source_file);
0 ignored issues
show
Deprecated Code introduced by
The class Image has been deprecated. ( Ignorable by Annotation )

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

2202
        $image = /** @scrutinizer ignore-deprecated */ new Image($source_file);
Loading history...
2203
        $image->crop($cropParameters);
2204
2205
        // Storing the new photos in 4 versions with various sizes.
2206
        $userPath = self::getUserPathById($user_id, 'system');
0 ignored issues
show
Deprecated Code introduced by
The function UserManager::getUserPathById() has been deprecated: use Resources ( Ignorable by Annotation )

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

2206
        $userPath = /** @scrutinizer ignore-deprecated */ self::getUserPathById($user_id, 'system');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
2207
2208
        // If this path does not exist - we create it.
2209
        if (!file_exists($userPath)) {
2210
            mkdir($userPath, api_get_permissions_for_new_directories(), true);
2211
        }
2212
        $small = new Image($source_file);
0 ignored issues
show
Deprecated Code introduced by
The class Image has been deprecated. ( Ignorable by Annotation )

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

2212
        $small = /** @scrutinizer ignore-deprecated */ new Image($source_file);
Loading history...
2213
        $small->resize(32);
2214
        $small->send_image($userPath.'small_'.$filename);
2215
        $medium = new Image($source_file);
0 ignored issues
show
Deprecated Code introduced by
The class Image has been deprecated. ( Ignorable by Annotation )

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

2215
        $medium = /** @scrutinizer ignore-deprecated */ new Image($source_file);
Loading history...
2216
        $medium->resize(85);
2217
        $medium->send_image($userPath.'medium_'.$filename);
2218
        $normal = new Image($source_file);
0 ignored issues
show
Deprecated Code introduced by
The class Image has been deprecated. ( Ignorable by Annotation )

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

2218
        $normal = /** @scrutinizer ignore-deprecated */ new Image($source_file);
Loading history...
2219
        $normal->resize(200);
2220
        $normal->send_image($userPath.$filename);
2221
2222
        $big = new Image($source_file); // This is the original picture.
0 ignored issues
show
Deprecated Code introduced by
The class Image has been deprecated. ( Ignorable by Annotation )

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

2222
        $big = /** @scrutinizer ignore-deprecated */ new Image($source_file); // This is the original picture.
Loading history...
2223
        $big->send_image($userPath.'big_'.$filename);
2224
2225
        $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...
2226
2227
        return $result ? $filename : false;
2228
    }
2229
2230
    /**
2231
     * Update User extra field file type into {user_folder}/{$extra_field}.
2232
     *
2233
     * @param int    $user_id     The user internal identification number
2234
     * @param string $extra_field The $extra_field The extra field name
2235
     * @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...
2236
     * @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...
2237
     *
2238
     * @return bool|null return filename if success, but false
2239
     */
2240
    public static function update_user_extra_file(
2241
        $user_id,
2242
        $extra_field = '',
2243
        $file = null,
2244
        $source_file = null
2245
    ) {
2246
        // Add Filter
2247
        $source_file = Security::filter_filename($source_file);
2248
        $file = Security::filter_filename($file);
2249
2250
        if (empty($user_id)) {
2251
            return false;
2252
        }
2253
2254
        if (empty($source_file)) {
2255
            $source_file = $file;
2256
        }
2257
2258
        // User-reserved directory where extra file have to be placed.
2259
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
0 ignored issues
show
Deprecated Code introduced by
The function UserManager::get_user_picture_path_by_id() has been deprecated: use Resources. ( Ignorable by Annotation )

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

2259
        $path_info = /** @scrutinizer ignore-deprecated */ self::get_user_picture_path_by_id($user_id, 'system');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
2260
        $path = $path_info['dir'];
2261
        if (!empty($extra_field)) {
2262
            $path .= $extra_field.'/';
2263
        }
2264
        // If this directory does not exist - we create it.
2265
        if (!file_exists($path)) {
2266
            @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

2266
            /** @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...
2267
        }
2268
2269
        if (filter_extension($file)) {
2270
            if (@move_uploaded_file($source_file, $path.$file)) {
2271
                if ($extra_field) {
2272
                    return $extra_field.'/'.$file;
2273
                } else {
2274
                    return $file;
2275
                }
2276
            }
2277
        }
2278
2279
        return false; // this should be returned if anything went wrong with the upload
2280
    }
2281
2282
    /**
2283
     * Deletes user photos.
2284
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2285
     *
2286
     * @param int $userId the user internal identification number
2287
     *
2288
     * @return mixed returns empty string on success, FALSE on error
2289
     */
2290
    public static function deleteUserPicture($userId)
2291
    {
2292
        return self::update_user_picture($userId);
2293
    }
2294
2295
    /**
2296
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
2297
     * doesn't have any.
2298
     *
2299
     * If there has been a request to remove a production, the function will return
2300
     * without building the list unless forced to do so by the optional second
2301
     * parameter. This increases performance by avoiding to read through the
2302
     * productions on the filesystem before the removal request has been carried
2303
     * out because they'll have to be re-read afterwards anyway.
2304
     *
2305
     * @param int  $user_id    User id
2306
     * @param bool $force      Optional parameter to force building after a removal request
2307
     * @param bool $showDelete
2308
     *
2309
     * @return string A string containing the XHTML code to display the production list, or FALSE
2310
     */
2311
    public static function build_production_list($user_id, $force = false, $showDelete = false)
2312
    {
2313
        if (!$force && !empty($_POST['remove_production'])) {
2314
            return true; // postpone reading from the filesystem
2315
        }
2316
2317
        $productions = self::get_user_productions($user_id);
2318
2319
        if (empty($productions)) {
2320
            return false;
2321
        }
2322
2323
        $production_dir = self::getUserPathById($user_id, 'web');
0 ignored issues
show
Deprecated Code introduced by
The function UserManager::getUserPathById() has been deprecated: use Resources ( Ignorable by Annotation )

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

2323
        $production_dir = /** @scrutinizer ignore-deprecated */ self::getUserPathById($user_id, 'web');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
2324
        $del_image = Display::returnIconPath('delete.png');
2325
        $add_image = Display::returnIconPath('archive.png');
2326
        $del_text = get_lang('Delete');
2327
        $production_list = '';
2328
        if (count($productions) > 0) {
2329
            $production_list = '<div class="files-production"><ul id="productions">';
2330
            foreach ($productions as $file) {
2331
                $production_list .= '<li>
2332
                    <img src="'.$add_image.'" />
2333
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2334
                        '.htmlentities($file).'
2335
                    </a>';
2336
                if ($showDelete) {
2337
                    $production_list .= '&nbsp;&nbsp;
2338
                        <input
2339
                            style="width:16px;"
2340
                            type="image"
2341
                            name="remove_production['.urlencode($file).']"
2342
                            src="'.$del_image.'"
2343
                            alt="'.$del_text.'"
2344
                            title="'.$del_text.' '.htmlentities($file).'"
2345
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2346
                }
2347
            }
2348
            $production_list .= '</ul></div>';
2349
        }
2350
2351
        return $production_list;
2352
    }
2353
2354
    /**
2355
     * Returns an array with the user's productions.
2356
     *
2357
     * @param int $user_id User id
2358
     *
2359
     * @return array An array containing the user's productions
2360
     */
2361
    public static function get_user_productions($user_id)
2362
    {
2363
        $production_repository = self::getUserPathById($user_id, 'system');
0 ignored issues
show
Deprecated Code introduced by
The function UserManager::getUserPathById() has been deprecated: use Resources ( Ignorable by Annotation )

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

2363
        $production_repository = /** @scrutinizer ignore-deprecated */ self::getUserPathById($user_id, 'system');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
2364
        $productions = [];
2365
2366
        if (is_dir($production_repository)) {
2367
            $handle = opendir($production_repository);
2368
            while ($file = readdir($handle)) {
2369
                if ('.' == $file ||
2370
                    '..' == $file ||
2371
                    '.htaccess' == $file ||
2372
                    is_dir($production_repository.$file)
2373
                ) {
2374
                    // skip current/parent directory and .htaccess
2375
                    continue;
2376
                }
2377
2378
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2379
                    // User's photos should not be listed as productions.
2380
                    continue;
2381
                }
2382
                $productions[] = $file;
2383
            }
2384
        }
2385
2386
        return $productions;
2387
    }
2388
2389
    /**
2390
     * Remove a user production.
2391
     *
2392
     * @param int    $user_id    User id
2393
     * @param string $production The production to remove
2394
     *
2395
     * @return bool
2396
     */
2397
    public static function remove_user_production($user_id, $production)
2398
    {
2399
        $production_path = self::get_user_picture_path_by_id($user_id, 'system');
0 ignored issues
show
Deprecated Code introduced by
The function UserManager::get_user_picture_path_by_id() has been deprecated: use Resources. ( Ignorable by Annotation )

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

2399
        $production_path = /** @scrutinizer ignore-deprecated */ self::get_user_picture_path_by_id($user_id, 'system');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
2400
        $production_file = $production_path['dir'].$production;
2401
        if (is_file($production_file)) {
2402
            unlink($production_file);
2403
2404
            return true;
2405
        }
2406
2407
        return false;
2408
    }
2409
2410
    /**
2411
     * Update an extra field value for a given user.
2412
     *
2413
     * @param int    $userId   User ID
2414
     * @param string $variable Field variable name
2415
     * @param string $value    Field value
2416
     *
2417
     * @return bool true if field updated, false otherwise
2418
     */
2419
    public static function update_extra_field_value($userId, $variable, $value = '')
2420
    {
2421
        $extraFieldValue = new ExtraFieldValue('user');
2422
        $params = [
2423
            'item_id' => $userId,
2424
            'variable' => $variable,
2425
            'value' => $value,
2426
        ];
2427
2428
        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...
2429
    }
2430
2431
    /**
2432
     * Get an array of extra fields with field details (type, default value and options).
2433
     *
2434
     * @param    int    Offset (from which row)
2435
     * @param    int    Number of items
2436
     * @param    int    Column on which sorting is made
2437
     * @param    string    Sorting direction
2438
     * @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...
2439
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2440
     *
2441
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2442
     */
2443
    public static function get_extra_fields(
2444
        $from = 0,
2445
        $number_of_items = 0,
2446
        $column = 5,
2447
        $direction = 'ASC',
2448
        $all_visibility = true,
2449
        $field_filter = null
2450
    ) {
2451
        $fields = [];
2452
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2453
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2454
        $columns = [
2455
            'id',
2456
            'variable',
2457
            'field_type',
2458
            'display_text',
2459
            'default_value',
2460
            'field_order',
2461
            'filter',
2462
        ];
2463
        $column = (int) $column;
2464
        $sort_direction = '';
2465
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2466
            $sort_direction = strtoupper($direction);
2467
        }
2468
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2469
        $sqlf = "SELECT * FROM $t_uf WHERE extra_field_type = $extraFieldType ";
2470
        if (!$all_visibility) {
2471
            $sqlf .= " AND visible_to_self = 1 ";
2472
        }
2473
        if (!is_null($field_filter)) {
2474
            $field_filter = (int) $field_filter;
2475
            $sqlf .= " AND filter = $field_filter ";
2476
        }
2477
        $sqlf .= " ORDER BY ".$columns[$column]." $sort_direction ";
2478
        if (0 != $number_of_items) {
2479
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2480
        }
2481
        $resf = Database::query($sqlf);
2482
        if (Database::num_rows($resf) > 0) {
2483
            while ($rowf = Database::fetch_array($resf)) {
2484
                $fields[$rowf['id']] = [
2485
                    0 => $rowf['id'],
2486
                    1 => $rowf['variable'],
2487
                    2 => $rowf['field_type'],
2488
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2489
                    4 => $rowf['default_value'],
2490
                    5 => $rowf['field_order'],
2491
                    6 => $rowf['visible_to_self'],
2492
                    7 => $rowf['changeable'],
2493
                    8 => $rowf['filter'],
2494
                    9 => [],
2495
                    10 => '<a name="'.$rowf['id'].'"></a>',
2496
                ];
2497
2498
                $sqlo = "SELECT * FROM $t_ufo
2499
                         WHERE field_id = ".$rowf['id']."
2500
                         ORDER BY option_order ASC";
2501
                $reso = Database::query($sqlo);
2502
                if (Database::num_rows($reso) > 0) {
2503
                    while ($rowo = Database::fetch_array($reso)) {
2504
                        $fields[$rowf['id']][9][$rowo['id']] = [
2505
                            0 => $rowo['id'],
2506
                            1 => $rowo['option_value'],
2507
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2508
                            3 => $rowo['option_order'],
2509
                        ];
2510
                    }
2511
                }
2512
            }
2513
        }
2514
2515
        return $fields;
2516
    }
2517
2518
    /**
2519
     * Get valid filenames in $user_folder/{$extra_field}/.
2520
     *
2521
     * @param $user_id
2522
     * @param $extra_field
2523
     * @param bool $full_path
2524
     *
2525
     * @return array
2526
     */
2527
    public static function get_user_extra_files($user_id, $extra_field, $full_path = false)
2528
    {
2529
        if (!$full_path) {
2530
            // Nothing to do
2531
        } else {
2532
            $path_info = self::get_user_picture_path_by_id($user_id, 'system');
0 ignored issues
show
Deprecated Code introduced by
The function UserManager::get_user_picture_path_by_id() has been deprecated: use Resources. ( Ignorable by Annotation )

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

2532
            $path_info = /** @scrutinizer ignore-deprecated */ self::get_user_picture_path_by_id($user_id, 'system');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
2533
            $path = $path_info['dir'];
2534
        }
2535
        $extra_data = self::get_extra_user_data_by_field($user_id, $extra_field);
2536
        $extra_files = $extra_data[$extra_field];
2537
2538
        $files = [];
2539
        if (is_array($extra_files)) {
2540
            foreach ($extra_files as $key => $value) {
2541
                if (!$full_path) {
2542
                    // Relative path from user folder
2543
                    $files[] = $value;
2544
                } else {
2545
                    $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...
2546
                }
2547
            }
2548
        } elseif (!empty($extra_files)) {
2549
            if (!$full_path) {
2550
                // Relative path from user folder
2551
                $files[] = $extra_files;
2552
            } else {
2553
                $files[] = $path.$extra_files;
2554
            }
2555
        }
2556
2557
        return $files; // can be an empty array
2558
    }
2559
2560
    /**
2561
     * Creates a new extra field.
2562
     *
2563
     * @param string $variable    Field's internal variable name
2564
     * @param int    $fieldType   Field's type
2565
     * @param string $displayText Field's language var name
2566
     * @param string $default     Field's default value
2567
     *
2568
     * @return int
2569
     */
2570
    public static function create_extra_field(
2571
        $variable,
2572
        $fieldType,
2573
        $displayText,
2574
        $default
2575
    ) {
2576
        $extraField = new ExtraField('user');
2577
        $params = [
2578
            'variable' => $variable,
2579
            'field_type' => $fieldType,
2580
            'display_text' => $displayText,
2581
            'default_value' => $default,
2582
        ];
2583
2584
        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...
2585
    }
2586
2587
    /**
2588
     * Check if a field is available.
2589
     *
2590
     * @param string $variable
2591
     *
2592
     * @return bool
2593
     */
2594
    public static function is_extra_field_available($variable)
2595
    {
2596
        $extraField = new ExtraField('user');
2597
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2598
2599
        return !empty($data) ? true : false;
2600
    }
2601
2602
    /**
2603
     * Gets user extra fields data.
2604
     *
2605
     * @param    int    User ID
2606
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2607
     * @param    bool    Whether to return invisible fields as well
2608
     * @param    bool    Whether to split multiple-selection fields or not
2609
     *
2610
     * @return array Array of fields => value for the given user
2611
     */
2612
    public static function get_extra_user_data(
2613
        $user_id,
2614
        $prefix = false,
2615
        $allVisibility = true,
2616
        $splitMultiple = false,
2617
        $fieldFilter = null
2618
    ) {
2619
        $user_id = (int) $user_id;
2620
2621
        if (empty($user_id)) {
2622
            return [];
2623
        }
2624
2625
        $extra_data = [];
2626
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2627
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2628
        $user_id = (int) $user_id;
2629
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2630
                FROM $t_uf f
2631
                WHERE
2632
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
2633
                ";
2634
        $filter_cond = '';
2635
2636
        if (!$allVisibility) {
2637
            if (isset($fieldFilter)) {
2638
                $fieldFilter = (int) $fieldFilter;
2639
                $filter_cond .= " AND filter = $fieldFilter ";
2640
            }
2641
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2642
        } else {
2643
            if (isset($fieldFilter)) {
2644
                $fieldFilter = (int) $fieldFilter;
2645
                $sql .= " AND filter = $fieldFilter ";
2646
            }
2647
        }
2648
2649
        $sql .= ' ORDER BY f.field_order';
2650
2651
        $res = Database::query($sql);
2652
        if (Database::num_rows($res) > 0) {
2653
            while ($row = Database::fetch_array($res)) {
2654
                if (self::USER_FIELD_TYPE_TAG == $row['type']) {
2655
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2656
                    $extra_data['extra_'.$row['fvar']] = $tags;
2657
                } else {
2658
                    $sqlu = "SELECT value as fval
2659
                            FROM $t_ufv
2660
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2661
                    $resu = Database::query($sqlu);
2662
                    // get default value
2663
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2664
                               WHERE id=".$row['id'];
2665
                    $res_df = Database::query($sql_df);
2666
2667
                    if (Database::num_rows($resu) > 0) {
2668
                        $rowu = Database::fetch_array($resu);
2669
                        $fval = $rowu['fval'];
2670
                        if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2671
                            $fval = explode(';', $rowu['fval']);
2672
                        }
2673
                    } else {
2674
                        $row_df = Database::fetch_array($res_df);
2675
                        $fval = $row_df['fval_df'];
2676
                    }
2677
                    // We get here (and fill the $extra_data array) even if there
2678
                    // is no user with data (we fill it with default values)
2679
                    if ($prefix) {
2680
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2681
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2682
                        } else {
2683
                            $extra_data['extra_'.$row['fvar']] = $fval;
2684
                        }
2685
                    } else {
2686
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2687
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2688
                        } else {
2689
                            $extra_data[$row['fvar']] = $fval;
2690
                        }
2691
                    }
2692
                }
2693
            }
2694
        }
2695
2696
        return $extra_data;
2697
    }
2698
2699
    /** Get extra user data by field.
2700
     * @param int    user ID
2701
     * @param string the internal variable name of the field
2702
     *
2703
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2704
     */
2705
    public static function get_extra_user_data_by_field(
2706
        $user_id,
2707
        $field_variable,
2708
        $prefix = false,
2709
        $all_visibility = true,
2710
        $splitmultiple = false
2711
    ) {
2712
        $user_id = (int) $user_id;
2713
2714
        if (empty($user_id)) {
2715
            return [];
2716
        }
2717
2718
        $extra_data = [];
2719
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2720
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2721
2722
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2723
                FROM $t_uf f
2724
                WHERE f.variable = '$field_variable' ";
2725
2726
        if (!$all_visibility) {
2727
            $sql .= " AND f.visible_to_self = 1 ";
2728
        }
2729
2730
        $sql .= " AND extra_field_type = ".EntityExtraField::USER_FIELD_TYPE;
2731
        $sql .= " ORDER BY f.field_order ";
2732
2733
        $res = Database::query($sql);
2734
        if (Database::num_rows($res) > 0) {
2735
            while ($row = Database::fetch_array($res)) {
2736
                $sqlu = "SELECT value as fval FROM $t_ufv v
2737
                         INNER JOIN $t_uf f
2738
                         ON (v.field_id = f.id)
2739
                         WHERE
2740
                            extra_field_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2741
                            field_id = ".$row['id']." AND
2742
                            item_id = ".$user_id;
2743
                $resu = Database::query($sqlu);
2744
                $fval = '';
2745
                if (Database::num_rows($resu) > 0) {
2746
                    $rowu = Database::fetch_array($resu);
2747
                    $fval = $rowu['fval'];
2748
                    if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2749
                        $fval = explode(';', $rowu['fval']);
2750
                    }
2751
                }
2752
                if ($prefix) {
2753
                    $extra_data['extra_'.$row['fvar']] = $fval;
2754
                } else {
2755
                    $extra_data[$row['fvar']] = $fval;
2756
                }
2757
            }
2758
        }
2759
2760
        return $extra_data;
2761
    }
2762
2763
    /**
2764
     * Get the extra field information for a certain field (the options as well).
2765
     *
2766
     * @param int $variable The name of the field we want to know everything about
2767
     *
2768
     * @return array Array containing all the information about the extra profile field
2769
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2770
     *               as returned by the database)
2771
     *
2772
     * @author Julio Montoya
2773
     *
2774
     * @since v1.8.6
2775
     */
2776
    public static function get_extra_field_information_by_name($variable)
2777
    {
2778
        $extraField = new ExtraField('user');
2779
2780
        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...
2781
    }
2782
2783
    /**
2784
     * Get the extra field information for user tag (the options as well).
2785
     *
2786
     * @param int $variable The name of the field we want to know everything about
2787
     *
2788
     * @return array Array containing all the information about the extra profile field
2789
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2790
     *               as returned by the database)
2791
     *
2792
     * @author José Loguercio
2793
     *
2794
     * @since v1.11.0
2795
     */
2796
    public static function get_extra_field_tags_information_by_name($variable)
2797
    {
2798
        $extraField = new ExtraField('user');
2799
2800
        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...
2801
    }
2802
2803
    /**
2804
     * Get all the extra field information of a certain field (also the options).
2805
     *
2806
     * @param int $fieldId the ID of the field we want to know everything of
2807
     *
2808
     * @return array $return containing all th information about the extra profile field
2809
     *
2810
     * @author Julio Montoya
2811
     *
2812
     * @deprecated
2813
     * @since v1.8.6
2814
     */
2815
    public static function get_extra_field_information($fieldId)
2816
    {
2817
        $extraField = new ExtraField('user');
2818
2819
        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...
2820
    }
2821
2822
    /**
2823
     * Get extra user data by value.
2824
     *
2825
     * @param string $variable       the internal variable name of the field
2826
     * @param string $value          the internal value of the field
2827
     * @param bool   $all_visibility
2828
     *
2829
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2830
     */
2831
    public static function get_extra_user_data_by_value($variable, $value, $all_visibility = true)
2832
    {
2833
        $extraFieldValue = new ExtraFieldValue('user');
2834
        $extraField = new ExtraField('user');
2835
2836
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2837
2838
        if (false === $info) {
2839
            return [];
2840
        }
2841
2842
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2843
            $variable,
2844
            $value,
2845
            false,
2846
            false,
2847
            true
2848
        );
2849
2850
        $result = [];
2851
        if (!empty($data)) {
2852
            foreach ($data as $item) {
2853
                $result[] = $item['item_id'];
2854
            }
2855
        }
2856
2857
        return $result;
2858
    }
2859
2860
    /**
2861
     * Get extra user data by tags value.
2862
     *
2863
     * @param int    $fieldId the ID of the field we want to know everything of
2864
     * @param string $tag     the tag name for search
2865
     *
2866
     * @return array with extra data info of a user
2867
     *
2868
     * @author José Loguercio
2869
     *
2870
     * @since v1.11.0
2871
     */
2872
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2873
    {
2874
        $extraField = new ExtraField('user');
2875
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2876
        $array = [];
2877
        foreach ($result as $index => $user) {
2878
            $array[] = $user['user_id'];
2879
        }
2880
2881
        return $array;
2882
    }
2883
2884
    /**
2885
     * Get extra user data by field variable.
2886
     *
2887
     * @param string $variable field variable
2888
     *
2889
     * @return array data
2890
     */
2891
    public static function get_extra_user_data_by_field_variable($variable)
2892
    {
2893
        $extraInfo = self::get_extra_field_information_by_name($variable);
2894
        $field_id = (int) $extraInfo['id'];
2895
2896
        $extraField = new ExtraFieldValue('user');
2897
        $data = $extraField->getValuesByFieldId($field_id);
2898
2899
        if (!empty($data)) {
2900
            foreach ($data as $row) {
2901
                $user_id = $row['item_id'];
2902
                $data[$user_id] = $row;
2903
            }
2904
        }
2905
2906
        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...
2907
    }
2908
2909
    /**
2910
     * Get extra user data tags by field variable.
2911
     *
2912
     * @param string $variable field variable
2913
     *
2914
     * @return array
2915
     */
2916
    public static function get_extra_user_data_for_tags($variable)
2917
    {
2918
        $data = self::get_extra_field_tags_information_by_name($variable);
2919
2920
        return $data;
2921
    }
2922
2923
    /**
2924
     * Gives a list of [session_category][session_id] for the current user.
2925
     *
2926
     * @param int  $user_id
2927
     * @param bool $is_time_over                 whether to fill the first element or not
2928
     *                                           (to give space for courses out of categories)
2929
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2930
     * @param bool $ignoreTimeLimit              ignore time start/end
2931
     * @param bool $getCount
2932
     *
2933
     * @return array list of statuses [session_category][session_id]
2934
     *
2935
     * @todo ensure multiple access urls are managed correctly
2936
     */
2937
    public static function get_sessions_by_category(
2938
        $user_id,
2939
        $is_time_over = true,
2940
        $ignore_visibility_for_admins = false,
2941
        $ignoreTimeLimit = false,
2942
        $getCount = false
2943
    ) {
2944
        $user_id = (int) $user_id;
2945
2946
        if (empty($user_id)) {
2947
            return [];
2948
        }
2949
2950
        $allowOrder = api_get_configuration_value('session_list_order');
2951
        $position = '';
2952
        if ($allowOrder) {
2953
            $position = ', s.position AS position ';
2954
        }
2955
2956
        // Get the list of sessions per user
2957
        $now = new DateTime('now', new DateTimeZone('UTC'));
2958
2959
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2960
        // join would not catch session-courses where the user is general
2961
        // session coach but which do not have students nor coaches registered
2962
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
2963
2964
        if (!$getCount) {
2965
            $dqlSelect = " DISTINCT
2966
                s.id,
2967
                s.name,
2968
                s.accessStartDate AS access_start_date,
2969
                s.accessEndDate AS access_end_date,
2970
                s.duration,
2971
                sc.id AS session_category_id,
2972
                sc.name AS session_category_name,
2973
                sc.dateStart AS session_category_date_start,
2974
                sc.dateEnd AS session_category_date_end,
2975
                s.coachAccessStartDate AS coach_access_start_date,
2976
                s.coachAccessEndDate AS coach_access_end_date,
2977
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
2978
                $position
2979
            ";
2980
        }
2981
2982
        $dql = "SELECT $dqlSelect
2983
                FROM ChamiloCoreBundle:Session AS s
2984
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2985
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2986
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc ";
2987
2988
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
2989
        // is awfully inefficient for large sets of data (1m25s for 58K
2990
        // sessions, BT#14115) but executing a similar query twice and grouping
2991
        // the results afterwards in PHP takes about 1/1000th of the time
2992
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
2993
        $dqlStudent = $dql." WHERE scu.user = :user AND url.url = :url ";
2994
        $dqlCoach = $dql." WHERE s.generalCoach = :user AND url.url = :url ";
2995
2996
        // Default order
2997
        $order = 'ORDER BY sc.name, s.name';
2998
2999
        // Order by date if showing all sessions
3000
        $showAllSessions = true === api_get_configuration_value('show_all_sessions_on_my_course_page');
3001
        if ($showAllSessions) {
3002
            $order = 'ORDER BY s.accessStartDate';
3003
        }
3004
3005
        // Order by position
3006
        if ($allowOrder) {
3007
            $order = 'ORDER BY s.position';
3008
        }
3009
3010
        // Order by dates according to settings
3011
        $orderBySettings = api_get_configuration_value('my_courses_session_order');
3012
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
3013
            $field = $orderBySettings['field'];
3014
            $orderSetting = $orderBySettings['order'];
3015
            switch ($field) {
3016
                case 'start_date':
3017
                    $order = " ORDER BY s.accessStartDate $orderSetting";
3018
                    break;
3019
                case 'end_date':
3020
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
3021
                    if ('asc' == $orderSetting) {
3022
                        // Put null values at the end
3023
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
3024
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
3025
                    }
3026
                    break;
3027
                case 'name':
3028
                    $order = " ORDER BY s.name $orderSetting ";
3029
                    break;
3030
            }
3031
        }
3032
3033
        $dqlStudent .= $order;
3034
        $dqlCoach .= $order;
3035
3036
        $accessUrlId = api_get_current_access_url_id();
3037
        $dqlStudent = Database::getManager()
3038
            ->createQuery($dqlStudent)
3039
            ->setParameters(
3040
                ['user' => $user_id, 'url' => $accessUrlId]
3041
            )
3042
        ;
3043
        $dqlCoach = Database::getManager()
3044
            ->createQuery($dqlCoach)
3045
            ->setParameters(
3046
                ['user' => $user_id, 'url' => $accessUrlId]
3047
            )
3048
        ;
3049
3050
        if ($getCount) {
3051
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
3052
        }
3053
3054
        $sessionDataStudent = $dqlStudent->getResult();
3055
        $sessionDataCoach = $dqlCoach->getResult();
3056
3057
        $sessionData = [];
3058
        // First fill $sessionData with student sessions
3059
        if (!empty($sessionDataStudent)) {
3060
        foreach ($sessionDataStudent as $row) {
3061
            $sessionData[$row['id']] = $row;
3062
        }
3063
        }
3064
        // Overwrite session data of the user as a student with session data
3065
        // of the user as a coach.
3066
        // There shouldn't be such duplicate rows, but just in case...
3067
        if (!empty($sessionDataCoach)) {
3068
        foreach ($sessionDataCoach as $row) {
3069
            $sessionData[$row['id']] = $row;
3070
            }
3071
        }
3072
3073
        $collapsable = api_get_configuration_value('allow_user_session_collapsable');
3074
        $extraField = new ExtraFieldValue('session');
3075
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
3076
3077
        if (empty($sessionData)) {
3078
            return [];
3079
        }
3080
        $categories = [];
3081
        foreach ($sessionData as $row) {
3082
            $session_id = $row['id'];
3083
            $coachList = SessionManager::getCoachesBySession($session_id);
3084
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
3085
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
3086
            $courseList = self::get_courses_list_by_session($user_id, $session_id);
3087
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
3088
3089
            // User portal filters:
3090
            if (false === $ignoreTimeLimit) {
3091
                if ($is_time_over) {
3092
                    // History
3093
                    if ($row['duration']) {
3094
                        if ($daysLeft >= 0) {
3095
                            continue;
3096
                        }
3097
                    } else {
3098
                        if (empty($row['access_end_date'])) {
3099
                            continue;
3100
                        } else {
3101
                            if ($row['access_end_date'] > $now) {
3102
                                continue;
3103
                            }
3104
                        }
3105
                    }
3106
                } else {
3107
                    // Current user portal
3108
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
3109
                    $isCoachOfCourse = in_array($user_id, $coachList);
3110
3111
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
3112
                        // Teachers can access the session depending in the access_coach date
3113
                    } else {
3114
                        if ($row['duration']) {
3115
                            if ($daysLeft <= 0) {
3116
                                continue;
3117
                            }
3118
                        } else {
3119
                            if (isset($row['access_end_date']) &&
3120
                                !empty($row['access_end_date'])
3121
                            ) {
3122
                                if ($row['access_end_date'] <= $now) {
3123
                                    continue;
3124
                                }
3125
                            }
3126
                        }
3127
                    }
3128
                }
3129
            }
3130
3131
            $categories[$row['session_category_id']]['session_category'] = [
3132
                'id' => $row['session_category_id'],
3133
                'name' => $row['session_category_name'],
3134
                'date_start' => $categoryStart,
3135
                'date_end' => $categoryEnd,
3136
            ];
3137
3138
            $visibility = api_get_session_visibility(
3139
                $session_id,
3140
                null,
3141
                $ignore_visibility_for_admins
3142
            );
3143
3144
            if (SESSION_VISIBLE != $visibility) {
3145
                // Course Coach session visibility.
3146
                $blockedCourseCount = 0;
3147
                $closedVisibilityList = [
3148
                    COURSE_VISIBILITY_CLOSED,
3149
                    COURSE_VISIBILITY_HIDDEN,
3150
                ];
3151
3152
                foreach ($courseList as $course) {
3153
                    // Checking session visibility
3154
                    $sessionCourseVisibility = api_get_session_visibility(
3155
                        $session_id,
3156
                        $course['real_id'],
3157
                        $ignore_visibility_for_admins
3158
                    );
3159
3160
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
3161
                    if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
3162
                        $blockedCourseCount++;
3163
                    }
3164
                }
3165
3166
                // If all courses are blocked then no show in the list.
3167
                if ($blockedCourseCount === count($courseList)) {
3168
                    $visibility = SESSION_INVISIBLE;
3169
                } else {
3170
                    $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...
3171
                }
3172
            }
3173
3174
            switch ($visibility) {
3175
                case SESSION_VISIBLE_READ_ONLY:
3176
                case SESSION_VISIBLE:
3177
                case SESSION_AVAILABLE:
3178
                    break;
3179
                case SESSION_INVISIBLE:
3180
                    if (false === $ignore_visibility_for_admins) {
3181
                        continue 2;
3182
                    }
3183
            }
3184
3185
            $collapsed = '';
3186
            $collapsedAction = '';
3187
            if ($collapsable) {
3188
                $collapsableData = SessionManager::getCollapsableData(
3189
                    $user_id,
3190
                    $session_id,
3191
                    $extraField,
3192
                    $collapsableLink
3193
                );
3194
                $collapsed = $collapsableData['collapsed'];
3195
                $collapsedAction = $collapsableData['collapsable_link'];
3196
            }
3197
3198
            $categories[$row['session_category_id']]['sessions'][] = [
3199
                'session_name' => $row['name'],
3200
                'session_id' => $row['id'],
3201
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
3202
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
3203
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
3204
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
3205
                'courses' => $courseList,
3206
                'collapsed' => $collapsed,
3207
                'collapsable_link' => $collapsedAction,
3208
                'duration' => $row['duration'],
3209
            ];
3210
        }
3211
3212
        return $categories;
3213
    }
3214
3215
    /**
3216
     * Gives a list of [session_id-course_code] => [status] for the current user.
3217
     *
3218
     * @param int $user_id
3219
     * @param int $sessionLimit
3220
     *
3221
     * @return array list of statuses (session_id-course_code => status)
3222
     */
3223
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
3224
    {
3225
        // Database Table Definitions
3226
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3227
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
3228
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3229
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3230
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3231
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3232
        $tblCourseCategory = Database::get_main_table(TABLE_MAIN_CATEGORY);
3233
3234
        $user_id = (int) $user_id;
3235
3236
        if (empty($user_id)) {
3237
            return [];
3238
        }
3239
3240
        // We filter the courses from the URL
3241
        $join_access_url = $where_access_url = '';
3242
        if (api_get_multiple_access_url()) {
3243
            $access_url_id = api_get_current_access_url_id();
3244
            if (-1 != $access_url_id) {
3245
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3246
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
3247
                $where_access_url = " AND access_url_id = $access_url_id ";
3248
            }
3249
        }
3250
3251
        // Courses in which we subscribed out of any session
3252
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3253
3254
        $sql = "SELECT
3255
                    course.code,
3256
                    course_rel_user.status course_rel_status,
3257
                    course_rel_user.sort sort,
3258
                    course_rel_user.user_course_cat user_course_cat
3259
                 FROM $tbl_course_user course_rel_user
3260
                 LEFT JOIN $tbl_course course
3261
                 ON course.id = course_rel_user.c_id
3262
                 LEFT JOIN $tbl_user_course_category user_course_category
3263
                 ON course_rel_user.user_course_cat = user_course_category.id
3264
                 $join_access_url
3265
                 WHERE
3266
                    course_rel_user.user_id = '".$user_id."' AND
3267
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
3268
                    $where_access_url
3269
                 ORDER BY user_course_category.sort, course_rel_user.sort, course.title ASC";
3270
3271
        $course_list_sql_result = Database::query($sql);
3272
3273
        $personal_course_list = [];
3274
        if (Database::num_rows($course_list_sql_result) > 0) {
3275
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3276
                $course_info = api_get_course_info($result_row['code']);
3277
                $result_row['course_info'] = $course_info;
3278
                $personal_course_list[] = $result_row;
3279
            }
3280
        }
3281
3282
        $coachCourseConditions = '';
3283
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
3284
        if (api_is_allowed_to_create_course()) {
3285
            $sessionListFromCourseCoach = [];
3286
            $sql = " SELECT DISTINCT session_id
3287
                    FROM $tbl_session_course_user
3288
                    WHERE user_id = $user_id AND status = 2 ";
3289
3290
            $result = Database::query($sql);
3291
            if (Database::num_rows($result)) {
3292
                $result = Database::store_result($result);
3293
                foreach ($result as $session) {
3294
                    $sessionListFromCourseCoach[] = $session['session_id'];
3295
                }
3296
            }
3297
            if (!empty($sessionListFromCourseCoach)) {
3298
                $condition = implode("','", $sessionListFromCourseCoach);
3299
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
3300
            }
3301
        }
3302
3303
        // Get the list of sessions where the user is subscribed
3304
        // This is divided into two different queries
3305
        $sessions = [];
3306
        $sessionLimitRestriction = '';
3307
        if (!empty($sessionLimit)) {
3308
            $sessionLimit = (int) $sessionLimit;
3309
            $sessionLimitRestriction = "LIMIT $sessionLimit";
3310
        }
3311
3312
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
3313
                FROM $tbl_session_user su INNER JOIN $tbl_session s
3314
                ON (s.id = su.session_id)
3315
                WHERE (
3316
                    su.user_id = $user_id AND
3317
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
3318
                )
3319
                $coachCourseConditions
3320
                ORDER BY access_start_date, access_end_date, name
3321
                $sessionLimitRestriction
3322
        ";
3323
3324
        $result = Database::query($sql);
3325
        if (Database::num_rows($result) > 0) {
3326
            while ($row = Database::fetch_assoc($result)) {
3327
                $sessions[$row['id']] = $row;
3328
            }
3329
        }
3330
3331
        $sql = "SELECT DISTINCT
3332
                id, name, access_start_date, access_end_date
3333
                FROM $tbl_session s
3334
                WHERE (
3335
                    id_coach = $user_id
3336
                )
3337
                $coachCourseConditions
3338
                ORDER BY access_start_date, access_end_date, name";
3339
3340
        $result = Database::query($sql);
3341
        if (Database::num_rows($result) > 0) {
3342
            while ($row = Database::fetch_assoc($result)) {
3343
                if (empty($sessions[$row['id']])) {
3344
                    $sessions[$row['id']] = $row;
3345
                }
3346
            }
3347
        }
3348
3349
        if (api_is_allowed_to_create_course()) {
3350
            foreach ($sessions as $enreg) {
3351
                $session_id = $enreg['id'];
3352
                $session_visibility = api_get_session_visibility($session_id);
3353
3354
                if (SESSION_INVISIBLE == $session_visibility) {
3355
                    continue;
3356
                }
3357
3358
                // This query is horribly slow when more than a few thousand
3359
                // users and just a few sessions to which they are subscribed
3360
                $sql = "SELECT DISTINCT
3361
                        course.code code,
3362
                        course.title i,
3363
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
3364
                        email, course.course_language l,
3365
                        1 sort,
3366
                        course_category.code user_course_cat,
3367
                        access_start_date,
3368
                        access_end_date,
3369
                        session.id as session_id,
3370
                        session.name as session_name
3371
                    FROM $tbl_session_course_user as session_course_user
3372
                    INNER JOIN $tbl_course AS course
3373
                        ON course.id = session_course_user.c_id
3374
                    LEFT JOIN $tblCourseCategory course_category ON course.category_id = course_category.id
3375
                    INNER JOIN $tbl_session as session
3376
                        ON session.id = session_course_user.session_id
3377
                    LEFT JOIN $tbl_user as user
3378
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
3379
                    WHERE
3380
                        session_course_user.session_id = $session_id AND (
3381
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
3382
                            OR session.id_coach = $user_id
3383
                        )
3384
                    ORDER BY i";
3385
                $course_list_sql_result = Database::query($sql);
3386
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3387
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3388
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3389
                    $personal_course_list[$key] = $result_row;
3390
                }
3391
            }
3392
        }
3393
3394
        foreach ($sessions as $enreg) {
3395
            $session_id = $enreg['id'];
3396
            $session_visibility = api_get_session_visibility($session_id);
3397
            if (SESSION_INVISIBLE == $session_visibility) {
3398
                continue;
3399
            }
3400
3401
            /* This query is very similar to the above query,
3402
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3403
            $sql = "SELECT DISTINCT
3404
                course.code code,
3405
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
3406
                email,
3407
                course.course_language l,
3408
                1 sort,
3409
                course_category.code user_course_cat,
3410
                access_start_date,
3411
                access_end_date,
3412
                session.id as session_id,
3413
                session.name as session_name,
3414
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
3415
            FROM $tbl_session_course_user as session_course_user
3416
            INNER JOIN $tbl_course AS course
3417
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
3418
            LEFT JOIN $tblCourseCategory course_category ON course.category_id = course_category.id
3419
            INNER JOIN $tbl_session as session
3420
            ON session_course_user.session_id = session.id
3421
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
3422
            WHERE session_course_user.user_id = $user_id
3423
            ORDER BY i";
3424
3425
            $course_list_sql_result = Database::query($sql);
3426
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3427
                $result_row['course_info'] = api_get_course_info($result_row['code']);
3428
                $key = $result_row['session_id'].' - '.$result_row['code'];
3429
                if (!isset($personal_course_list[$key])) {
3430
                    $personal_course_list[$key] = $result_row;
3431
                }
3432
            }
3433
        }
3434
3435
        return $personal_course_list;
3436
    }
3437
3438
    /**
3439
     * Gives a list of courses for the given user in the given session.
3440
     *
3441
     * @param int $user_id
3442
     * @param int $session_id
3443
     *
3444
     * @return array list of statuses (session_id-course_code => status)
3445
     */
3446
    public static function get_courses_list_by_session($user_id, $session_id)
3447
    {
3448
        // Database Table Definitions
3449
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3450
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
3451
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3452
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3453
3454
        $user_id = (int) $user_id;
3455
        $session_id = (int) $session_id;
3456
        // We filter the courses from the URL
3457
        $join_access_url = $where_access_url = '';
3458
        if (api_get_multiple_access_url()) {
3459
            $urlId = api_get_current_access_url_id();
3460
            if (-1 != $urlId) {
3461
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3462
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
3463
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
3464
            }
3465
        }
3466
3467
        /* This query is very similar to the query below, but it will check the
3468
        session_rel_course_user table if there are courses registered
3469
        to our user or not */
3470
        $sql = "SELECT DISTINCT
3471
                    c.title,
3472
                    c.visibility,
3473
                    c.id as real_id,
3474
                    c.code as course_code,
3475
                    sc.position,
3476
                    c.unsubscribe
3477
                FROM $tbl_session_course_user as scu
3478
                INNER JOIN $tbl_session_course sc
3479
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3480
                INNER JOIN $tableCourse as c
3481
                ON (scu.c_id = c.id)
3482
                $join_access_url
3483
                WHERE
3484
                    scu.user_id = $user_id AND
3485
                    scu.session_id = $session_id
3486
                    $where_access_url
3487
                ORDER BY sc.position ASC";
3488
3489
        $myCourseList = [];
3490
        $courses = [];
3491
        $result = Database::query($sql);
3492
        if (Database::num_rows($result) > 0) {
3493
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3494
                $result_row['status'] = 5;
3495
                if (!in_array($result_row['real_id'], $courses)) {
3496
                    $position = $result_row['position'];
3497
                    if (!isset($myCourseList[$position])) {
3498
                        $myCourseList[$position] = $result_row;
3499
                    } else {
3500
                        $myCourseList[] = $result_row;
3501
                    }
3502
                    $courses[] = $result_row['real_id'];
3503
                }
3504
            }
3505
        }
3506
3507
        if (api_is_allowed_to_create_course()) {
3508
            $sql = "SELECT DISTINCT
3509
                        c.title,
3510
                        c.visibility,
3511
                        c.id as real_id,
3512
                        c.code as course_code,
3513
                        sc.position,
3514
                        c.unsubscribe
3515
                    FROM $tbl_session_course_user as scu
3516
                    INNER JOIN $tbl_session as s
3517
                    ON (scu.session_id = s.id)
3518
                    INNER JOIN $tbl_session_course sc
3519
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3520
                    INNER JOIN $tableCourse as c
3521
                    ON (scu.c_id = c.id)
3522
                    $join_access_url
3523
                    WHERE
3524
                      s.id = $session_id AND
3525
                      (
3526
                        (scu.user_id = $user_id AND scu.status = 2) OR
3527
                        s.id_coach = $user_id
3528
                      )
3529
                    $where_access_url
3530
                    ORDER BY sc.position ASC";
3531
            $result = Database::query($sql);
3532
3533
            if (Database::num_rows($result) > 0) {
3534
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3535
                    $result_row['status'] = 2;
3536
                    if (!in_array($result_row['real_id'], $courses)) {
3537
                        $position = $result_row['position'];
3538
                        if (!isset($myCourseList[$position])) {
3539
                            $myCourseList[$position] = $result_row;
3540
                        } else {
3541
                            $myCourseList[] = $result_row;
3542
                        }
3543
                        $courses[] = $result_row['real_id'];
3544
                    }
3545
                }
3546
            }
3547
        }
3548
3549
        if (api_is_drh()) {
3550
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3551
            $sessionList = array_keys($sessionList);
3552
            if (in_array($session_id, $sessionList)) {
3553
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3554
                if (!empty($courseList)) {
3555
                    foreach ($courseList as $course) {
3556
                        if (!in_array($course['id'], $courses)) {
3557
                            $position = $course['position'];
3558
                            if (!isset($myCourseList[$position])) {
3559
                                $myCourseList[$position] = $course;
3560
                            } else {
3561
                                $myCourseList[] = $course;
3562
                            }
3563
                        }
3564
                    }
3565
                }
3566
            }
3567
        } else {
3568
            //check if user is general coach for this session
3569
            $sessionInfo = api_get_session_info($session_id);
3570
            if ($sessionInfo['id_coach'] == $user_id) {
3571
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3572
                if (!empty($courseList)) {
3573
                    foreach ($courseList as $course) {
3574
                        if (!in_array($course['id'], $courses)) {
3575
                            $position = $course['position'];
3576
                            if (!isset($myCourseList[$position])) {
3577
                                $myCourseList[$position] = $course;
3578
                            } else {
3579
                                $myCourseList[] = $course;
3580
                            }
3581
                        }
3582
                    }
3583
                }
3584
            }
3585
        }
3586
3587
        if (!empty($myCourseList)) {
3588
            ksort($myCourseList);
3589
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
3590
            if (empty($checkPosition)) {
3591
                // The session course list doesn't have any position,
3592
                // then order the course list by course code
3593
                $list = array_column($myCourseList, 'course_code');
3594
                array_multisort($myCourseList, SORT_ASC, $list);
3595
            }
3596
        }
3597
3598
        return $myCourseList;
3599
    }
3600
3601
    /**
3602
     * Get user id from a username.
3603
     *
3604
     * @param string $username
3605
     *
3606
     * @return int User ID (or false if not found)
3607
     */
3608
    public static function get_user_id_from_username($username)
3609
    {
3610
        if (empty($username)) {
3611
            return false;
3612
        }
3613
        $username = trim($username);
3614
        $username = Database::escape_string($username);
3615
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3616
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3617
        $res = Database::query($sql);
3618
3619
        if (false === $res) {
3620
            return false;
3621
        }
3622
        if (1 !== Database::num_rows($res)) {
3623
            return false;
3624
        }
3625
        $row = Database::fetch_array($res);
3626
3627
        return $row['id'];
3628
    }
3629
3630
    /**
3631
     * Get the users files upload from his share_folder.
3632
     *
3633
     * @param string $user_id      User ID
3634
     * @param string $course       course directory
3635
     * @param string $resourceType resource type: images, all
3636
     *
3637
     * @return string
3638
     */
3639
    public static function get_user_upload_files_by_course(
3640
        $user_id,
3641
        $course,
3642
        $resourceType = 'all'
3643
    ) {
3644
        $return = '';
3645
        $user_id = (int) $user_id;
3646
3647
        if (!empty($user_id) && !empty($course)) {
3648
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
0 ignored issues
show
Bug introduced by
The constant SYS_COURSE_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3649
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3650
            $file_list = [];
3651
3652
            if (is_dir($path)) {
3653
                $handle = opendir($path);
3654
                while ($file = readdir($handle)) {
3655
                    if ('.' == $file || '..' == $file || '.htaccess' == $file || is_dir($path.$file)) {
3656
                        continue; // skip current/parent directory and .htaccess
3657
                    }
3658
                    $file_list[] = $file;
3659
                }
3660
                if (count($file_list) > 0) {
3661
                    $return = "<h4>$course</h4>";
3662
                    $return .= '<ul class="thumbnails">';
3663
                }
3664
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
3665
                foreach ($file_list as $file) {
3666
                    if ('all' == $resourceType) {
3667
                        $return .= '<li>
3668
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
3669
                    } elseif ('images' == $resourceType) {
3670
                        //get extension
3671
                        $ext = explode('.', $file);
3672
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
3673
                            $return .= '<li class="span2">
3674
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
3675
                                                <img src="'.$web_path.urlencode($file).'" >
3676
                                            </a>
3677
                                        </li>';
3678
                        }
3679
                    }
3680
                }
3681
                if (count($file_list) > 0) {
3682
                    $return .= '</ul>';
3683
                }
3684
            }
3685
        }
3686
3687
        return $return;
3688
    }
3689
3690
    /**
3691
     * Gets the API key (or keys) and return them into an array.
3692
     *
3693
     * @param int     Optional user id (defaults to the result of api_get_user_id())
3694
     * @param string $api_service
3695
     *
3696
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
3697
     */
3698
    public static function get_api_keys($user_id = null, $api_service = 'dokeos')
3699
    {
3700
        if ($user_id != strval(intval($user_id))) {
3701
            return false;
3702
        }
3703
        if (empty($user_id)) {
3704
            $user_id = api_get_user_id();
3705
        }
3706
        if (false === $user_id) {
3707
            return false;
3708
        }
3709
        $service_name = Database::escape_string($api_service);
3710
        if (false === is_string($service_name)) {
3711
            return false;
3712
        }
3713
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3714
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
3715
        $res = Database::query($sql);
3716
        if (false === $res) {
3717
            return false;
3718
        } //error during query
3719
        $num = Database::num_rows($res);
3720
        if (0 == $num) {
3721
            return false;
3722
        }
3723
        $list = [];
3724
        while ($row = Database::fetch_array($res)) {
3725
            $list[$row['id']] = $row['api_key'];
3726
        }
3727
3728
        return $list;
3729
    }
3730
3731
    /**
3732
     * Adds a new API key to the users' account.
3733
     *
3734
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
3735
     * @param string $api_service
3736
     *
3737
     * @return bool True on success, false on failure
3738
     */
3739
    public static function add_api_key($user_id = null, $api_service = 'dokeos')
3740
    {
3741
        if ($user_id != strval(intval($user_id))) {
3742
            return false;
3743
        }
3744
        if (empty($user_id)) {
3745
            $user_id = api_get_user_id();
3746
        }
3747
        if (false === $user_id) {
3748
            return false;
3749
        }
3750
        $service_name = Database::escape_string($api_service);
3751
        if (false === is_string($service_name)) {
3752
            return false;
3753
        }
3754
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3755
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
3756
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
3757
        $res = Database::query($sql);
3758
        if (false === $res) {
3759
            return false;
3760
        } //error during query
3761
        $num = Database::insert_id();
3762
3763
        return 0 == $num ? false : $num;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 0 == $num ? false : $num also could return the type string which is incompatible with the documented return type boolean.
Loading history...
3764
    }
3765
3766
    /**
3767
     * Deletes an API key from the user's account.
3768
     *
3769
     * @param   int     API key's internal ID
3770
     *
3771
     * @return bool True on success, false on failure
3772
     */
3773
    public static function delete_api_key($key_id)
3774
    {
3775
        if ($key_id != strval(intval($key_id))) {
3776
            return false;
3777
        }
3778
        if (false === $key_id) {
3779
            return false;
3780
        }
3781
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3782
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
3783
        $res = Database::query($sql);
3784
        if (false === $res) {
3785
            return false;
3786
        } //error during query
3787
        $num = Database::num_rows($res);
3788
        if (1 !== $num) {
3789
            return false;
3790
        }
3791
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
3792
        $res = Database::query($sql);
3793
        if (false === $res) {
3794
            return false;
3795
        } //error during query
3796
3797
        return true;
3798
    }
3799
3800
    /**
3801
     * Regenerate an API key from the user's account.
3802
     *
3803
     * @param   int     user ID (defaults to the results of api_get_user_id())
3804
     * @param   string  API key's internal ID
3805
     *
3806
     * @return int num
3807
     */
3808
    public static function update_api_key($user_id, $api_service)
3809
    {
3810
        if ($user_id != strval(intval($user_id))) {
3811
            return false;
3812
        }
3813
        if (false === $user_id) {
3814
            return false;
3815
        }
3816
        $service_name = Database::escape_string($api_service);
3817
        if (false === is_string($service_name)) {
3818
            return false;
3819
        }
3820
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3821
        $sql = "SELECT id FROM $t_api
3822
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3823
        $res = Database::query($sql);
3824
        $num = Database::num_rows($res);
3825
        if (1 == $num) {
3826
            $id_key = Database::fetch_array($res, 'ASSOC');
3827
            self::delete_api_key($id_key['id']);
3828
            $num = self::add_api_key($user_id, $api_service);
3829
        } elseif (0 == $num) {
3830
            $num = self::add_api_key($user_id, $api_service);
3831
        }
3832
3833
        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...
3834
    }
3835
3836
    /**
3837
     * @param   int     user ID (defaults to the results of api_get_user_id())
3838
     * @param   string    API key's internal ID
3839
     *
3840
     * @return int row ID, or return false if not found
3841
     */
3842
    public static function get_api_key_id($user_id, $api_service)
3843
    {
3844
        if ($user_id != strval(intval($user_id))) {
3845
            return false;
3846
        }
3847
        if (false === $user_id) {
3848
            return false;
3849
        }
3850
        if (empty($api_service)) {
3851
            return false;
3852
        }
3853
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3854
        $api_service = Database::escape_string($api_service);
3855
        $sql = "SELECT id FROM $t_api
3856
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3857
        $res = Database::query($sql);
3858
        if (Database::num_rows($res) < 1) {
3859
            return false;
3860
        }
3861
        $row = Database::fetch_array($res, 'ASSOC');
3862
3863
        return $row['id'];
3864
    }
3865
3866
    /**
3867
     * Checks if a user_id is platform admin.
3868
     *
3869
     * @param   int user ID
3870
     *
3871
     * @return bool True if is admin, false otherwise
3872
     *
3873
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
3874
     */
3875
    public static function is_admin($user_id)
3876
    {
3877
        $user_id = (int) $user_id;
3878
        if (empty($user_id)) {
3879
            return false;
3880
        }
3881
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3882
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3883
        $res = Database::query($sql);
3884
3885
        return 1 === Database::num_rows($res);
3886
    }
3887
3888
    /**
3889
     * Get the total count of users.
3890
     *
3891
     * @param int $status        Status of users to be counted
3892
     * @param int $access_url_id Access URL ID (optional)
3893
     * @param int $active
3894
     *
3895
     * @return mixed Number of users or false on error
3896
     */
3897
    public static function get_number_of_users($status = 0, $access_url_id = 1, $active = null)
3898
    {
3899
        $t_u = Database::get_main_table(TABLE_MAIN_USER);
3900
        $t_a = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3901
3902
        if (api_is_multiple_url_enabled()) {
3903
            $sql = "SELECT count(u.id)
3904
                    FROM $t_u u
3905
                    INNER JOIN $t_a url_user
3906
                    ON (u.id = url_user.user_id)
3907
                    WHERE url_user.access_url_id = $access_url_id
3908
            ";
3909
        } else {
3910
            $sql = "SELECT count(u.id)
3911
                    FROM $t_u u
3912
                    WHERE 1 = 1 ";
3913
        }
3914
3915
        if (is_int($status) && $status > 0) {
3916
            $status = (int) $status;
3917
            $sql .= " AND u.status = $status ";
3918
        }
3919
3920
        if (null !== $active) {
3921
            $active = (int) $active;
3922
            $sql .= " AND u.active = $active ";
3923
        }
3924
3925
        $res = Database::query($sql);
3926
        if (1 === Database::num_rows($res)) {
3927
            return (int) Database::result($res, 0, 0);
3928
        }
3929
3930
        return false;
3931
    }
3932
3933
    /**
3934
     * Gets the tags of a specific field_id
3935
     * USER TAGS.
3936
     *
3937
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
3938
     *
3939
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
3940
     *    Called it "books" for example.
3941
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
3942
     * 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
3943
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
3944
     * 5. Test and enjoy.
3945
     *
3946
     * @param string $tag
3947
     * @param int    $field_id      field_id
3948
     * @param string $return_format how we are going to result value in array or in a string (json)
3949
     * @param $limit
3950
     *
3951
     * @return mixed
3952
     */
3953
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
3954
    {
3955
        // database table definition
3956
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3957
        $field_id = (int) $field_id;
3958
        $limit = (int) $limit;
3959
        $tag = trim(Database::escape_string($tag));
3960
3961
        // all the information of the field
3962
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
3963
                WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag LIMIT $limit";
3964
        $result = Database::query($sql);
3965
        $return = [];
3966
        if (Database::num_rows($result) > 0) {
3967
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3968
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
3969
            }
3970
        }
3971
        if ('json' === $return_format) {
3972
            $return = json_encode($return);
3973
        }
3974
3975
        return $return;
3976
    }
3977
3978
    /**
3979
     * @param int $field_id
3980
     * @param int $limit
3981
     *
3982
     * @return array
3983
     */
3984
    public static function get_top_tags($field_id, $limit = 100)
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
        $limit = (int) $limit;
3991
        // all the information of the field
3992
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
3993
                INNER JOIN $table_user_tag ut
3994
                ON (ut.id = uv.tag_id)
3995
                WHERE field_id = $field_id
3996
                GROUP BY tag_id
3997
                ORDER BY count DESC
3998
                LIMIT $limit";
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;
4004
            }
4005
        }
4006
4007
        return $return;
4008
    }
4009
4010
    /**
4011
     * Get user's tags.
4012
     *
4013
     * @param int $user_id
4014
     * @param int $field_id
4015
     *
4016
     * @return array
4017
     */
4018
    public static function get_user_tags($user_id, $field_id)
4019
    {
4020
        // database table definition
4021
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4022
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4023
        $field_id = (int) $field_id;
4024
        $user_id = (int) $user_id;
4025
4026
        // all the information of the field
4027
        $sql = "SELECT ut.id, tag, count
4028
                FROM $table_user_tag ut
4029
                INNER JOIN $table_user_tag_values uv
4030
                ON (uv.tag_id=ut.ID)
4031
                WHERE field_id = $field_id AND user_id = $user_id
4032
                ORDER BY tag";
4033
        $result = Database::query($sql);
4034
        $return = [];
4035
        if (Database::num_rows($result) > 0) {
4036
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4037
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
4038
            }
4039
        }
4040
4041
        return $return;
4042
    }
4043
4044
    /**
4045
     * Get user's tags.
4046
     *
4047
     * @param int  $user_id
4048
     * @param int  $field_id
4049
     * @param bool $show_links show links or not
4050
     *
4051
     * @return string
4052
     */
4053
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
4054
    {
4055
        // database table definition
4056
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4057
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4058
        $field_id = (int) $field_id;
4059
        $user_id = (int) $user_id;
4060
4061
        // all the information of the field
4062
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
4063
                INNER JOIN $table_user_tag_values uv
4064
                ON (uv.tag_id = ut.id)
4065
                WHERE field_id = $field_id AND user_id = $user_id
4066
                ORDER BY tag";
4067
4068
        $result = Database::query($sql);
4069
        $return = [];
4070
        if (Database::num_rows($result) > 0) {
4071
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4072
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
4073
            }
4074
        }
4075
        $user_tags = $return;
4076
        $tag_tmp = [];
4077
        foreach ($user_tags as $tag) {
4078
            if ($show_links) {
4079
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
4080
                    $tag['tag'].
4081
                '</a>';
4082
            } else {
4083
                $tag_tmp[] = $tag['tag'];
4084
            }
4085
        }
4086
4087
        if (is_array($user_tags) && count($user_tags) > 0) {
4088
            return implode(', ', $tag_tmp);
4089
        } else {
4090
            return '';
4091
        }
4092
    }
4093
4094
    /**
4095
     * Get the tag id.
4096
     *
4097
     * @param int $tag
4098
     * @param int $field_id
4099
     *
4100
     * @return int returns 0 if fails otherwise the tag id
4101
     */
4102
    public static function get_tag_id($tag, $field_id)
4103
    {
4104
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4105
        $tag = Database::escape_string($tag);
4106
        $field_id = (int) $field_id;
4107
        //with COLLATE latin1_bin to select query in a case sensitive mode
4108
        $sql = "SELECT id FROM $table_user_tag
4109
                WHERE tag LIKE '$tag' AND field_id = $field_id";
4110
        $result = Database::query($sql);
4111
        if (Database::num_rows($result) > 0) {
4112
            $row = Database::fetch_array($result, 'ASSOC');
4113
4114
            return $row['id'];
4115
        } else {
4116
            return 0;
4117
        }
4118
    }
4119
4120
    /**
4121
     * Get the tag id.
4122
     *
4123
     * @param int $tag_id
4124
     * @param int $field_id
4125
     *
4126
     * @return int 0 if fails otherwise the tag id
4127
     */
4128
    public static function get_tag_id_from_id($tag_id, $field_id)
4129
    {
4130
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4131
        $tag_id = (int) $tag_id;
4132
        $field_id = (int) $field_id;
4133
        $sql = "SELECT id FROM $table_user_tag
4134
                WHERE id = '$tag_id' AND field_id = $field_id";
4135
        $result = Database::query($sql);
4136
        if (Database::num_rows($result) > 0) {
4137
            $row = Database::fetch_array($result, 'ASSOC');
4138
4139
            return $row['id'];
4140
        } else {
4141
            return false;
4142
        }
4143
    }
4144
4145
    /**
4146
     * Adds a user-tag value.
4147
     *
4148
     * @param mixed $tag
4149
     * @param int   $user_id
4150
     * @param int   $field_id field id of the tag
4151
     *
4152
     * @return bool True if the tag was inserted or updated. False otherwise.
4153
     *              The return value doesn't take into account *values* added to the tag.
4154
     *              Only the creation/update of the tag field itself.
4155
     */
4156
    public static function add_tag($tag, $user_id, $field_id)
4157
    {
4158
        // database table definition
4159
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4160
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4161
        $tag = trim(Database::escape_string($tag));
4162
        $user_id = (int) $user_id;
4163
        $field_id = (int) $field_id;
4164
4165
        $tag_id = self::get_tag_id($tag, $field_id);
4166
4167
        /* IMPORTANT
4168
         *  @todo we don't create tags with numbers
4169
         *
4170
         */
4171
        if (is_numeric($tag)) {
4172
            //the form is sending an id this means that the user select it from the list so it MUST exists
4173
            /* $new_tag_id = self::get_tag_id_from_id($tag,$field_id);
4174
              if ($new_tag_id !== false) {
4175
              $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $new_tag_id";
4176
              $result = Database::query($sql);
4177
              $last_insert_id = $new_tag_id;
4178
              } else {
4179
              $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
4180
              $result = Database::query($sql);
4181
              $last_insert_id = Database::insert_id();
4182
              } */
4183
        }
4184
4185
        //this is a new tag
4186
        if (0 == $tag_id) {
4187
            //the tag doesn't exist
4188
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
4189
            Database::query($sql);
4190
            $last_insert_id = Database::insert_id();
4191
        } else {
4192
            //the tag exists we update it
4193
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
4194
            Database::query($sql);
4195
            $last_insert_id = $tag_id;
4196
        }
4197
4198
        if (!empty($last_insert_id) && (0 != $last_insert_id)) {
4199
            //we insert the relationship user-tag
4200
            $sql = "SELECT tag_id FROM $table_user_tag_values
4201
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
4202
            $result = Database::query($sql);
4203
            //if the relationship does not exist we create it
4204
            if (0 == Database::num_rows($result)) {
4205
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
4206
                Database::query($sql);
4207
            }
4208
4209
            return true;
4210
        }
4211
4212
        return false;
4213
    }
4214
4215
    /**
4216
     * Deletes an user tag.
4217
     *
4218
     * @param int $user_id
4219
     * @param int $field_id
4220
     */
4221
    public static function delete_user_tags($user_id, $field_id)
4222
    {
4223
        // database table definition
4224
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4225
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4226
        $user_id = (int) $user_id;
4227
4228
        $tags = self::get_user_tags($user_id, $field_id);
4229
        if (is_array($tags) && count($tags) > 0) {
4230
            foreach ($tags as $key => $tag) {
4231
                if ($tag['count'] > '0') {
4232
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
4233
                    Database::query($sql);
4234
                }
4235
                $sql = "DELETE FROM $table_user_tag_values
4236
                        WHERE user_id = $user_id AND tag_id = $key";
4237
                Database::query($sql);
4238
            }
4239
        }
4240
    }
4241
4242
    /**
4243
     * Process the tag list comes from the UserManager::update_extra_field_value() function.
4244
     *
4245
     * @param array $tags     the tag list that will be added
4246
     * @param int   $user_id
4247
     * @param int   $field_id
4248
     *
4249
     * @return bool
4250
     */
4251
    public static function process_tags($tags, $user_id, $field_id)
4252
    {
4253
        // We loop the tags and add it to the DB
4254
        if (is_array($tags)) {
4255
            foreach ($tags as $tag) {
4256
                self::add_tag($tag, $user_id, $field_id);
4257
            }
4258
        } else {
4259
            self::add_tag($tags, $user_id, $field_id);
4260
        }
4261
4262
        return true;
4263
    }
4264
4265
    /**
4266
     * Returns a list of all administrators.
4267
     *
4268
     * @return array
4269
     */
4270
    public static function get_all_administrators()
4271
    {
4272
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4273
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
4274
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4275
        $access_url_id = api_get_current_access_url_id();
4276
        if (api_get_multiple_access_url()) {
4277
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
4278
                    FROM $tbl_url_rel_user as url
4279
                    INNER JOIN $table_admin as admin
4280
                    ON (admin.user_id=url.user_id)
4281
                    INNER JOIN $table_user u
4282
                    ON (u.id=admin.user_id)
4283
                    WHERE access_url_id ='".$access_url_id."'";
4284
        } else {
4285
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
4286
                    FROM $table_admin as admin
4287
                    INNER JOIN $table_user u
4288
                    ON (u.id=admin.user_id)";
4289
        }
4290
        $result = Database::query($sql);
4291
        $return = [];
4292
        if (Database::num_rows($result) > 0) {
4293
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4294
                $return[$row['user_id']] = $row;
4295
            }
4296
        }
4297
4298
        return $return;
4299
    }
4300
4301
    /**
4302
     * Search an user (tags, first name, last name and email ).
4303
     *
4304
     * @param string $tag
4305
     * @param int    $field_id        field id of the tag
4306
     * @param int    $from            where to start in the query
4307
     * @param int    $number_of_items
4308
     * @param bool   $getCount        get count or not
4309
     *
4310
     * @return array
4311
     */
4312
    public static function get_all_user_tags(
4313
        $tag,
4314
        $field_id = 0,
4315
        $from = 0,
4316
        $number_of_items = 10,
4317
        $getCount = false
4318
    ) {
4319
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
4320
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4321
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4322
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4323
4324
        $field_id = intval($field_id);
4325
        $from = intval($from);
4326
        $number_of_items = intval($number_of_items);
4327
4328
        $where_field = "";
4329
        $where_extra_fields = self::get_search_form_where_extra_fields();
4330
        if (0 != $field_id) {
4331
            $where_field = " field_id = $field_id AND ";
4332
        }
4333
4334
        // all the information of the field
4335
        if ($getCount) {
4336
            $select = "SELECT count(DISTINCT u.id) count";
4337
        } else {
4338
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, tag, picture_uri";
4339
        }
4340
4341
        $sql = " $select
4342
                FROM $user_table u
4343
                INNER JOIN $access_url_rel_user_table url_rel_user
4344
                ON (u.id = url_rel_user.user_id)
4345
                LEFT JOIN $table_user_tag_values uv
4346
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
4347
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
4348
                WHERE
4349
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
4350
                    (
4351
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
4352
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
4353
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
4354
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
4355
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
4356
                     )
4357
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
4358
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
4359
4360
        $keyword_active = true;
4361
        // only active users
4362
        if ($keyword_active) {
4363
            $sql .= " AND u.active='1'";
4364
        }
4365
        // avoid anonymous
4366
        $sql .= " AND u.status <> 6 ";
4367
        $sql .= " ORDER BY username";
4368
        $sql .= " LIMIT $from , $number_of_items";
4369
4370
        $result = Database::query($sql);
4371
        $return = [];
4372
4373
        if (Database::num_rows($result) > 0) {
4374
            if ($getCount) {
4375
                $row = Database::fetch_array($result, 'ASSOC');
4376
4377
                return $row['count'];
4378
            }
4379
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4380
                $return[$row['id']] = $row;
4381
            }
4382
        }
4383
4384
        return $return;
4385
    }
4386
4387
    /**
4388
     * Get extra filterable user fields (only type select).
4389
     *
4390
     * @return array Array of extra fields as [int => ['name' => ..., 'variable' => ..., 'data' => ...]] (
4391
     *               or empty array if no extra field)
4392
     */
4393
    public static function getExtraFilterableFields()
4394
    {
4395
        $extraFieldList = self::get_extra_fields();
4396
        $fields = [];
4397
        if (is_array($extraFieldList)) {
4398
            foreach ($extraFieldList as $extraField) {
4399
                // If is enabled to filter and is a "<select>" field type
4400
                if (1 == $extraField[8] && 4 == $extraField[2]) {
4401
                    $fields[] = [
4402
                        'name' => $extraField[3],
4403
                        'variable' => $extraField[1],
4404
                        'data' => $extraField[9],
4405
                    ];
4406
                }
4407
            }
4408
        }
4409
4410
        return $fields;
4411
    }
4412
4413
    /**
4414
     * Get extra where clauses for finding users based on extra filterable user fields (type select).
4415
     *
4416
     * @return string With AND clauses based on user's ID which have the values to search in extra user fields
4417
     *                (or empty if no extra field exists)
4418
     */
4419
    public static function get_search_form_where_extra_fields()
4420
    {
4421
        $useExtraFields = false;
4422
        $extraFields = self::getExtraFilterableFields();
4423
        $extraFieldResult = [];
4424
        if (is_array($extraFields) && count($extraFields) > 0) {
4425
            foreach ($extraFields as $extraField) {
4426
                $varName = 'field_'.$extraField['variable'];
4427
                if (self::is_extra_field_available($extraField['variable'])) {
4428
                    if (isset($_GET[$varName]) && '0' != $_GET[$varName]) {
4429
                        $useExtraFields = true;
4430
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
4431
                            $extraField['variable'],
4432
                            $_GET[$varName]
4433
                        );
4434
                    }
4435
                }
4436
            }
4437
        }
4438
4439
        if ($useExtraFields) {
4440
            $finalResult = [];
4441
            if (count($extraFieldResult) > 1) {
4442
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
4443
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
4444
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
4445
                    }
4446
                }
4447
            } else {
4448
                $finalResult = $extraFieldResult[0];
4449
            }
4450
4451
            if (is_array($finalResult) && count($finalResult) > 0) {
4452
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
4453
            } else {
4454
                //no results
4455
                $whereFilter = " AND u.id  = -1 ";
4456
            }
4457
4458
            return $whereFilter;
4459
        }
4460
4461
        return '';
4462
    }
4463
4464
    /**
4465
     * Show the search form.
4466
     *
4467
     * @param string $query the value of the search box
4468
     *
4469
     * @throws Exception
4470
     *
4471
     * @return string HTML form
4472
     */
4473
    public static function get_search_form($query, $defaultParams = [])
4474
    {
4475
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
4476
        $form = new FormValidator(
4477
            'search_user',
4478
            'get',
4479
            api_get_path(WEB_PATH).'main/social/search.php',
4480
            '',
4481
            [],
4482
            FormValidator::LAYOUT_HORIZONTAL
4483
        );
4484
4485
        $query = Security::remove_XSS($query);
4486
4487
        if (!empty($query)) {
4488
            $form->addHeader(get_lang('Results and feedback').' "'.$query.'"');
4489
        }
4490
4491
        $form->addText(
4492
            'q',
4493
            get_lang('Users, Groups'),
4494
            false,
4495
            [
4496
                'id' => 'q',
4497
            ]
4498
        );
4499
        $options = [
4500
            0 => get_lang('Select'),
4501
            1 => get_lang('User'),
4502
            2 => get_lang('Group'),
4503
        ];
4504
        $form->addSelect(
4505
            'search_type',
4506
            get_lang('Type'),
4507
            $options,
4508
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
4509
        );
4510
4511
        // Extra fields
4512
        $extraFields = self::getExtraFilterableFields();
4513
        $defaults = [];
4514
        if (is_array($extraFields) && count($extraFields) > 0) {
4515
            foreach ($extraFields as $extraField) {
4516
                $varName = 'field_'.$extraField['variable'];
4517
                $options = [
4518
                    0 => get_lang('Select'),
4519
                ];
4520
                foreach ($extraField['data'] as $option) {
4521
                    if (isset($_GET[$varName])) {
4522
                        if ($_GET[$varName] == $option[1]) {
4523
                            $defaults[$option[1]] = true;
4524
                        }
4525
                    }
4526
4527
                    $options[$option[1]] = $option[1];
4528
                }
4529
                $form->addSelect($varName, $extraField['name'], $options);
4530
            }
4531
        }
4532
4533
        $defaults['search_type'] = (int) $searchType;
4534
        $defaults['q'] = $query;
4535
4536
        if (!empty($defaultParams)) {
4537
            $defaults = array_merge($defaults, $defaultParams);
4538
        }
4539
        $form->setDefaults($defaults);
4540
        $form->addButtonSearch(get_lang('Search'));
4541
4542
        $js = '<script>
4543
        extra_field_toogle();
4544
        function extra_field_toogle() {
4545
            if (jQuery("select[name=search_type]").val() != "1") {
4546
                jQuery(".extra_field").hide();
4547
            } else {
4548
                jQuery(".extra_field").show();
4549
            }
4550
        }
4551
        </script>';
4552
4553
        return $js.$form->returnForm();
4554
    }
4555
4556
    /**
4557
     * Shows the user menu.
4558
     */
4559
    public static function show_menu()
4560
    {
4561
        echo '<div class="actions">';
4562
        echo '<a href="/main/auth/profile.php">'.
4563
            Display::return_icon('profile.png').' '.get_lang('Profile').'</a>';
4564
        echo '<a href="/main/messages/inbox.php">'.
4565
            Display::return_icon('inbox.png').' '.get_lang('Inbox').'</a>';
4566
        echo '<a href="/main/messages/outbox.php">'.
4567
            Display::return_icon('outbox.png').' '.get_lang('Outbox').'</a>';
4568
        echo '<span style="float:right; padding-top:7px;">'.
4569
        '<a href="/main/auth/profile.php?show=1">'.
4570
            Display::return_icon('edit.gif').' '.get_lang('Configuration').'</a>';
4571
        echo '</span>';
4572
        echo '</div>';
4573
    }
4574
4575
    /**
4576
     * Allow to register contact to social network.
4577
     *
4578
     * @param int $friend_id     user friend id
4579
     * @param int $my_user_id    user id
4580
     * @param int $relation_type relation between users see constants definition
4581
     *
4582
     * @return bool
4583
     */
4584
    public static function relate_users($friend_id, $my_user_id, $relation_type)
4585
    {
4586
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4587
4588
        $friend_id = (int) $friend_id;
4589
        $my_user_id = (int) $my_user_id;
4590
        $relation_type = (int) $relation_type;
4591
4592
        $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
4593
                WHERE
4594
                    friend_user_id='.$friend_id.' AND
4595
                    user_id='.$my_user_id.' AND
4596
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
4597
        $result = Database::query($sql);
4598
        $row = Database::fetch_array($result, 'ASSOC');
4599
        $current_date = api_get_utc_datetime();
4600
4601
        if (0 == $row['count']) {
4602
            $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
4603
                    VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
4604
            Database::query($sql);
4605
4606
            return true;
4607
        }
4608
4609
        $sql = 'SELECT COUNT(*) as count, relation_type  FROM '.$tbl_my_friend.'
4610
                WHERE
4611
                    friend_user_id='.$friend_id.' AND
4612
                    user_id='.$my_user_id.' AND
4613
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
4614
        $result = Database::query($sql);
4615
        $row = Database::fetch_array($result, 'ASSOC');
4616
4617
        if (1 == $row['count']) {
4618
            //only for the case of a RRHH or a Student BOSS
4619
            if ($row['relation_type'] != $relation_type &&
4620
                (USER_RELATION_TYPE_RRHH == $relation_type || USER_RELATION_TYPE_BOSS == $relation_type)
4621
            ) {
4622
                $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
4623
                        VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
4624
            } else {
4625
                $sql = 'UPDATE '.$tbl_my_friend.' SET relation_type='.$relation_type.'
4626
                        WHERE friend_user_id='.$friend_id.' AND user_id='.$my_user_id;
4627
            }
4628
            Database::query($sql);
4629
4630
            return true;
4631
        }
4632
4633
        return false;
4634
    }
4635
4636
    /**
4637
     * Deletes a contact.
4638
     *
4639
     * @param bool   $friend_id
4640
     * @param bool   $real_removed          true will delete ALL friends relationship
4641
     * @param string $with_status_condition
4642
     *
4643
     * @author isaac flores paz <[email protected]>
4644
     * @author Julio Montoya <[email protected]> Cleaning code
4645
     */
4646
    public static function remove_user_rel_user(
4647
        $friend_id,
4648
        $real_removed = false,
4649
        $with_status_condition = ''
4650
    ) {
4651
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4652
        $tbl_my_message = Database::get_main_table(TABLE_MESSAGE);
4653
        $friend_id = (int) $friend_id;
4654
        $user_id = api_get_user_id();
4655
4656
        if ($real_removed) {
4657
            $extra_condition = '';
4658
            if ('' != $with_status_condition) {
4659
                $extra_condition = ' AND relation_type = '.intval($with_status_condition);
4660
            }
4661
            $sql = 'DELETE FROM '.$tbl_my_friend.'
4662
                    WHERE
4663
                        relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
4664
                        friend_user_id='.$friend_id.' '.$extra_condition;
4665
            Database::query($sql);
4666
            $sql = 'DELETE FROM '.$tbl_my_friend.'
4667
                   WHERE
4668
                    relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
4669
                    user_id='.$friend_id.' '.$extra_condition;
4670
            Database::query($sql);
4671
        } else {
4672
            $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
4673
                    WHERE
4674
                        user_id='.$user_id.' AND
4675
                        relation_type NOT IN('.USER_RELATION_TYPE_DELETED.', '.USER_RELATION_TYPE_RRHH.') AND
4676
                        friend_user_id='.$friend_id;
4677
            $result = Database::query($sql);
4678
            $row = Database::fetch_array($result, 'ASSOC');
4679
            if (1 == $row['count']) {
4680
                //Delete user rel user
4681
                $sql_i = 'UPDATE '.$tbl_my_friend.' SET relation_type='.USER_RELATION_TYPE_DELETED.'
4682
                          WHERE user_id='.$user_id.' AND friend_user_id='.$friend_id;
4683
4684
                $sql_j = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
4685
                          WHERE
4686
                                user_receiver_id='.$user_id.' AND
4687
                                user_sender_id='.$friend_id.' AND update_date="0000-00-00 00:00:00" ';
4688
                // Delete user
4689
                $sql_ij = 'UPDATE '.$tbl_my_friend.'  SET relation_type='.USER_RELATION_TYPE_DELETED.'
4690
                           WHERE user_id='.$friend_id.' AND friend_user_id='.$user_id;
4691
                $sql_ji = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
4692
                           WHERE
4693
                                user_receiver_id='.$friend_id.' AND
4694
                                user_sender_id='.$user_id.' AND
4695
                                update_date="0000-00-00 00:00:00" ';
4696
                Database::query($sql_i);
4697
                Database::query($sql_j);
4698
                Database::query($sql_ij);
4699
                Database::query($sql_ji);
4700
            }
4701
        }
4702
4703
        // Delete accepted invitations
4704
        $sql = "DELETE FROM $tbl_my_message
4705
                WHERE
4706
                    msg_status = ".MESSAGE_STATUS_INVITATION_ACCEPTED." AND
4707
                    (
4708
                        user_receiver_id = $user_id AND
4709
                        user_sender_id = $friend_id
4710
                    ) OR
4711
                    (
4712
                        user_sender_id = $user_id AND
4713
                        user_receiver_id = $friend_id
4714
                    )
4715
        ";
4716
        Database::query($sql);
4717
    }
4718
4719
    /**
4720
     * @param int $userId
4721
     *
4722
     * @return array
4723
     */
4724
    public static function getDrhListFromUser($userId)
4725
    {
4726
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4727
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4728
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4729
        $userId = (int) $userId;
4730
4731
        $orderBy = null;
4732
        if (api_is_western_name_order()) {
4733
            $orderBy .= ' ORDER BY firstname, lastname ';
4734
        } else {
4735
            $orderBy .= ' ORDER BY lastname, firstname ';
4736
        }
4737
4738
        $sql = "SELECT u.id, username, u.firstname, u.lastname
4739
                FROM $tblUser u
4740
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
4741
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
4742
                WHERE
4743
                    access_url_id = ".api_get_current_access_url_id()." AND
4744
                    uru.user_id = '$userId' AND
4745
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4746
                $orderBy
4747
                ";
4748
        $result = Database::query($sql);
4749
4750
        return Database::store_result($result);
4751
    }
4752
4753
    /**
4754
     * get users followed by human resource manager.
4755
     *
4756
     * @param int    $userId
4757
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
4758
     * @param bool   $getOnlyUserId
4759
     * @param bool   $getSql
4760
     * @param bool   $getCount
4761
     * @param int    $from
4762
     * @param int    $numberItems
4763
     * @param int    $column
4764
     * @param string $direction
4765
     * @param int    $active
4766
     * @param string $lastConnectionDate
4767
     *
4768
     * @return array users
4769
     */
4770
    public static function get_users_followed_by_drh(
4771
        $userId,
4772
        $userStatus = 0,
4773
        $getOnlyUserId = false,
4774
        $getSql = false,
4775
        $getCount = false,
4776
        $from = null,
4777
        $numberItems = null,
4778
        $column = null,
4779
        $direction = null,
4780
        $active = null,
4781
        $lastConnectionDate = null
4782
    ) {
4783
        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...
4784
            $userId,
4785
            $userStatus,
4786
            $getOnlyUserId,
4787
            $getSql,
4788
            $getCount,
4789
            $from,
4790
            $numberItems,
4791
            $column,
4792
            $direction,
4793
            $active,
4794
            $lastConnectionDate,
4795
            DRH
4796
        );
4797
    }
4798
4799
    /**
4800
     * Get users followed by human resource manager.
4801
     *
4802
     * @param int    $userId
4803
     * @param int    $userStatus         Filter users by status (STUDENT, COURSEMANAGER, etc)
4804
     * @param bool   $getOnlyUserId
4805
     * @param bool   $getSql
4806
     * @param bool   $getCount
4807
     * @param int    $from
4808
     * @param int    $numberItems
4809
     * @param int    $column
4810
     * @param string $direction
4811
     * @param int    $active
4812
     * @param string $lastConnectionDate
4813
     * @param int    $status             the function is called by who? COURSEMANAGER, DRH?
4814
     * @param string $keyword
4815
     *
4816
     * @return mixed Users list (array) or the SQL query if $getSQL was set to true
4817
     */
4818
    public static function getUsersFollowedByUser(
4819
        $userId,
4820
        $userStatus = null,
4821
        $getOnlyUserId = false,
4822
        $getSql = false,
4823
        $getCount = false,
4824
        $from = null,
4825
        $numberItems = null,
4826
        $column = null,
4827
        $direction = null,
4828
        $active = null,
4829
        $lastConnectionDate = null,
4830
        $status = null,
4831
        $keyword = null
4832
    ) {
4833
        // Database Table Definitions
4834
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4835
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4836
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4837
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4838
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4839
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4840
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4841
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4842
4843
        $userId = (int) $userId;
4844
        $limitCondition = '';
4845
4846
        if (isset($from) && isset($numberItems)) {
4847
            $from = (int) $from;
4848
            $numberItems = (int) $numberItems;
4849
            $limitCondition = "LIMIT $from, $numberItems";
4850
        }
4851
4852
        $column = Database::escape_string($column);
4853
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
4854
4855
        $userConditions = '';
4856
        if (!empty($userStatus)) {
4857
            $userConditions .= ' AND u.status = '.intval($userStatus);
4858
        }
4859
4860
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
4861
        if ($getOnlyUserId) {
4862
            $select = " SELECT DISTINCT u.id user_id";
4863
        }
4864
4865
        $masterSelect = "SELECT DISTINCT * FROM ";
4866
4867
        if ($getCount) {
4868
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
4869
            $select = " SELECT DISTINCT(u.id) user_id";
4870
        }
4871
4872
        if (!is_null($active)) {
4873
            $active = intval($active);
4874
            $userConditions .= " AND u.active = $active ";
4875
        }
4876
4877
        if (!empty($keyword)) {
4878
            $keyword = Database::escape_string($keyword);
4879
            $userConditions .= " AND (
4880
                u.username LIKE '%$keyword%' OR
4881
                u.firstname LIKE '%$keyword%' OR
4882
                u.lastname LIKE '%$keyword%' OR
4883
                u.official_code LIKE '%$keyword%' OR
4884
                u.email LIKE '%$keyword%'
4885
            )";
4886
        }
4887
4888
        if (!empty($lastConnectionDate)) {
4889
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
4890
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
4891
        }
4892
4893
        $sessionConditionsCoach = null;
4894
        $sessionConditionsTeacher = null;
4895
        $drhConditions = null;
4896
        $teacherSelect = null;
4897
4898
        switch ($status) {
4899
            case DRH:
4900
                $drhConditions .= " AND
4901
                    friend_user_id = '$userId' AND
4902
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4903
                ";
4904
                break;
4905
            case COURSEMANAGER:
4906
                $drhConditions .= " AND
4907
                    friend_user_id = '$userId' AND
4908
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4909
                ";
4910
4911
                $sessionConditionsCoach .= " AND
4912
                    (s.id_coach = '$userId')
4913
                ";
4914
4915
                $sessionConditionsTeacher .= " AND
4916
                    (scu.status = 2 AND scu.user_id = '$userId')
4917
                ";
4918
4919
                $teacherSelect =
4920
                "UNION ALL (
4921
                        $select
4922
                        FROM $tbl_user u
4923
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
4924
                        WHERE
4925
                            (
4926
                                sru.session_id IN (
4927
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
4928
                                    $tbl_session_rel_access_url session_rel_access_rel_user
4929
                                    ON session_rel_access_rel_user.session_id = s.id
4930
                                    WHERE access_url_id = ".api_get_current_access_url_id()."
4931
                                    $sessionConditionsCoach
4932
                                ) OR sru.session_id IN (
4933
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
4934
                                    INNER JOIN $tbl_session_rel_access_url url
4935
                                    ON (url.session_id = s.id)
4936
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
4937
                                    ON (scu.session_id = s.id)
4938
                                    WHERE access_url_id = ".api_get_current_access_url_id()."
4939
                                    $sessionConditionsTeacher
4940
                                )
4941
                            )
4942
                            $userConditions
4943
                    )
4944
                    UNION ALL(
4945
                        $select
4946
                        FROM $tbl_user u
4947
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
4948
                        WHERE cu.c_id IN (
4949
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
4950
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
4951
                        )
4952
                        $userConditions
4953
                    )"
4954
                ;
4955
                break;
4956
            case STUDENT_BOSS:
4957
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
4958
                break;
4959
            case HRM_REQUEST:
4960
                $drhConditions .= " AND
4961
                    friend_user_id = '$userId' AND
4962
                    relation_type = '".USER_RELATION_TYPE_HRM_REQUEST."'
4963
                ";
4964
                break;
4965
        }
4966
4967
        $join = null;
4968
        $sql = " $masterSelect
4969
                (
4970
                    (
4971
                        $select
4972
                        FROM $tbl_user u
4973
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
4974
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
4975
                        $join
4976
                        WHERE
4977
                            access_url_id = ".api_get_current_access_url_id()."
4978
                            $drhConditions
4979
                            $userConditions
4980
                    )
4981
                    $teacherSelect
4982
4983
                ) as t1";
4984
4985
        if ($getSql) {
4986
            return $sql;
4987
        }
4988
        if ($getCount) {
4989
            $result = Database::query($sql);
4990
            $row = Database::fetch_array($result);
4991
4992
            return $row['count'];
4993
        }
4994
4995
        $orderBy = null;
4996
        if (false == $getOnlyUserId) {
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...
4997
            if (api_is_western_name_order()) {
4998
                $orderBy .= " ORDER BY firstname, lastname ";
4999
            } else {
5000
                $orderBy .= " ORDER BY lastname, firstname ";
5001
            }
5002
5003
            if (!empty($column) && !empty($direction)) {
5004
                // Fixing order due the UNIONs
5005
                $column = str_replace('u.', '', $column);
5006
                $orderBy = " ORDER BY $column $direction ";
5007
            }
5008
        }
5009
5010
        $sql .= $orderBy;
5011
        $sql .= $limitCondition;
5012
5013
        $result = Database::query($sql);
5014
        $users = [];
5015
        if (Database::num_rows($result) > 0) {
5016
            while ($row = Database::fetch_array($result)) {
5017
                $users[$row['user_id']] = $row;
5018
            }
5019
        }
5020
5021
        return $users;
5022
    }
5023
5024
    /**
5025
     * Subscribes users to human resource manager (Dashboard feature).
5026
     *
5027
     * @param int   $hr_dept_id
5028
     * @param array $users_id
5029
     * @param bool  $deleteOtherAssignedUsers
5030
     *
5031
     * @return int
5032
     */
5033
    public static function subscribeUsersToHRManager(
5034
        $hr_dept_id,
5035
        $users_id,
5036
        $deleteOtherAssignedUsers = true
5037
    ) {
5038
        return self::subscribeUsersToUser(
5039
            $hr_dept_id,
5040
            $users_id,
5041
            USER_RELATION_TYPE_RRHH,
5042
            false,
5043
            $deleteOtherAssignedUsers
5044
        );
5045
    }
5046
5047
    /**
5048
     * Register request to assign users to HRM.
5049
     *
5050
     * @param int   $hrmId   The HRM ID
5051
     * @param array $usersId The users IDs
5052
     *
5053
     * @return int
5054
     */
5055
    public static function requestUsersToHRManager($hrmId, $usersId)
5056
    {
5057
        return self::subscribeUsersToUser(
5058
            $hrmId,
5059
            $usersId,
5060
            USER_RELATION_TYPE_HRM_REQUEST,
5061
            false,
5062
            false
5063
        );
5064
    }
5065
5066
    /**
5067
     * Remove the requests for assign a user to a HRM.
5068
     *
5069
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
5070
     */
5071
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
5072
    {
5073
        $users = implode(', ', $usersId);
5074
        Database::getManager()
5075
            ->createQuery('
5076
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
5077
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
5078
            ')
5079
            ->execute(['hrm_id' => $hrmId, 'relation_type' => USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
5080
    }
5081
5082
    /**
5083
     * Add subscribed users to a user by relation type.
5084
     *
5085
     * @param int    $userId                   The user id
5086
     * @param array  $subscribedUsersId        The id of subscribed users
5087
     * @param string $relationType             The relation type
5088
     * @param bool   $deleteUsersBeforeInsert
5089
     * @param bool   $deleteOtherAssignedUsers
5090
     *
5091
     * @return int
5092
     */
5093
    public static function subscribeUsersToUser(
5094
        $userId,
5095
        $subscribedUsersId,
5096
        $relationType,
5097
        $deleteUsersBeforeInsert = false,
5098
        $deleteOtherAssignedUsers = true
5099
    ) {
5100
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5101
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5102
5103
        $userId = (int) $userId;
5104
        $relationType = (int) $relationType;
5105
        $affectedRows = 0;
5106
5107
        if ($deleteOtherAssignedUsers) {
5108
            if (api_get_multiple_access_url()) {
5109
                // Deleting assigned users to hrm_id
5110
                $sql = "SELECT s.user_id
5111
                        FROM $userRelUserTable s
5112
                        INNER JOIN $userRelAccessUrlTable a
5113
                        ON (a.user_id = s.user_id)
5114
                        WHERE
5115
                            friend_user_id = $userId AND
5116
                            relation_type = $relationType AND
5117
                            access_url_id = ".api_get_current_access_url_id();
5118
            } else {
5119
                $sql = "SELECT user_id
5120
                        FROM $userRelUserTable
5121
                        WHERE
5122
                            friend_user_id = $userId AND
5123
                            relation_type = $relationType";
5124
            }
5125
            $result = Database::query($sql);
5126
5127
            if (Database::num_rows($result) > 0) {
5128
                while ($row = Database::fetch_array($result)) {
5129
                    $sql = "DELETE FROM $userRelUserTable
5130
                            WHERE
5131
                                user_id = {$row['user_id']} AND
5132
                                friend_user_id = $userId AND
5133
                                relation_type = $relationType";
5134
                    Database::query($sql);
5135
                }
5136
            }
5137
        }
5138
5139
        if ($deleteUsersBeforeInsert) {
5140
            $sql = "DELETE FROM $userRelUserTable
5141
                    WHERE
5142
                        user_id = $userId AND
5143
                        relation_type = $relationType";
5144
            Database::query($sql);
5145
        }
5146
5147
        // Inserting new user list
5148
        if (is_array($subscribedUsersId)) {
5149
            foreach ($subscribedUsersId as $subscribedUserId) {
5150
                $subscribedUserId = (int) $subscribedUserId;
5151
                $sql = "SELECT id
5152
                        FROM $userRelUserTable
5153
                        WHERE
5154
                            user_id = $subscribedUserId AND
5155
                            friend_user_id = $userId AND
5156
                            relation_type = $relationType";
5157
5158
                $result = Database::query($sql);
5159
                $num = Database::num_rows($result);
5160
                if (0 === $num) {
5161
                    $date = api_get_utc_datetime();
5162
                    $sql = "INSERT INTO $userRelUserTable (user_id, friend_user_id, relation_type, last_edit)
5163
                            VALUES ($subscribedUserId, $userId, $relationType, '$date')";
5164
                    $result = Database::query($sql);
5165
                    $affectedRows += Database::affected_rows($result);
5166
                }
5167
            }
5168
        }
5169
5170
        return $affectedRows;
5171
    }
5172
5173
    /**
5174
     * This function check if an user is followed by human resources manager.
5175
     *
5176
     * @param int $user_id
5177
     * @param int $hr_dept_id Human resources manager
5178
     *
5179
     * @return bool
5180
     */
5181
    public static function is_user_followed_by_drh($user_id, $hr_dept_id)
5182
    {
5183
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5184
        $user_id = (int) $user_id;
5185
        $hr_dept_id = (int) $hr_dept_id;
5186
        $result = false;
5187
5188
        $sql = "SELECT user_id FROM $tbl_user_rel_user
5189
                WHERE
5190
                    user_id = $user_id AND
5191
                    friend_user_id = $hr_dept_id AND
5192
                    relation_type = ".USER_RELATION_TYPE_RRHH;
5193
        $rs = Database::query($sql);
5194
        if (Database::num_rows($rs) > 0) {
5195
            $result = true;
5196
        }
5197
5198
        return $result;
5199
    }
5200
5201
    /**
5202
     * Return the user id of teacher or session administrator.
5203
     *
5204
     * @param array $courseInfo
5205
     *
5206
     * @return mixed The user id, or false if the session ID was negative
5207
     */
5208
    public static function get_user_id_of_course_admin_or_session_admin($courseInfo)
5209
    {
5210
        $session = api_get_session_id();
5211
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5212
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5213
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5214
5215
        if (empty($courseInfo)) {
5216
            return false;
5217
        }
5218
5219
        $courseId = $courseInfo['real_id'];
5220
5221
        if (0 == $session || is_null($session)) {
5222
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
5223
                    INNER JOIN '.$table_course_user.' ru
5224
                    ON ru.user_id = u.id
5225
                    WHERE
5226
                        ru.status = 1 AND
5227
                        ru.c_id = "'.$courseId.'" ';
5228
            $rs = Database::query($sql);
5229
            $num_rows = Database::num_rows($rs);
5230
            if (1 == $num_rows) {
5231
                $row = Database::fetch_array($rs);
5232
5233
                return $row['uid'];
5234
            } else {
5235
                $my_num_rows = $num_rows;
5236
                $my_user_id = Database::result($rs, $my_num_rows - 1, 'uid');
5237
5238
                return $my_user_id;
5239
            }
5240
        } elseif ($session > 0) {
5241
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
5242
                    INNER JOIN '.$table_session_course_user.' sru
5243
                    ON sru.user_id=u.id
5244
                    WHERE
5245
                        sru.c_id="'.$courseId.'" AND
5246
                        sru.status=2';
5247
            $rs = Database::query($sql);
5248
            $row = Database::fetch_array($rs);
5249
5250
            return $row['uid'];
5251
        }
5252
5253
        return false;
5254
    }
5255
5256
    /**
5257
     * Determines if a user is a gradebook certified.
5258
     *
5259
     * @param int $cat_id  The category id of gradebook
5260
     * @param int $user_id The user id
5261
     *
5262
     * @return bool
5263
     */
5264
    public static function is_user_certified($cat_id, $user_id)
5265
    {
5266
        $cat_id = (int) $cat_id;
5267
        $user_id = (int) $user_id;
5268
5269
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
5270
        $sql = 'SELECT path_certificate
5271
                FROM '.$table.'
5272
                WHERE
5273
                    cat_id = "'.$cat_id.'" AND
5274
                    user_id = "'.$user_id.'"';
5275
        $rs = Database::query($sql);
5276
        $row = Database::fetch_array($rs);
5277
5278
        if ('' == $row['path_certificate'] || is_null($row['path_certificate'])) {
5279
            return false;
5280
        }
5281
5282
        return true;
5283
    }
5284
5285
    /**
5286
     * Gets the info about a gradebook certificate for a user by course.
5287
     *
5288
     * @param array $course_info The course code
5289
     * @param int   $session_id
5290
     * @param int   $user_id     The user id
5291
     *
5292
     * @return array if there is not information return false
5293
     */
5294
    public static function get_info_gradebook_certificate($course_info, $session_id, $user_id)
5295
    {
5296
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
5297
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
5298
        $session_id = (int) $session_id;
5299
        $user_id = (int) $user_id;
5300
        $courseId = $course_info['real_id'];
5301
5302
        if (empty($session_id)) {
5303
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
5304
        } else {
5305
            $session_condition = " AND session_id = $session_id";
5306
        }
5307
5308
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.'
5309
                WHERE cat_id = (
5310
                    SELECT id FROM '.$tbl_grade_category.'
5311
                    WHERE
5312
                        c_id = "'.$courseId.'" '.$session_condition.'
5313
                    LIMIT 1
5314
                ) AND user_id='.$user_id;
5315
5316
        $rs = Database::query($sql);
5317
        if (Database::num_rows($rs) > 0) {
5318
            $row = Database::fetch_array($rs, 'ASSOC');
5319
            $score = $row['score_certificate'];
5320
            $category_id = $row['cat_id'];
5321
            $cat = Category::load($category_id);
5322
            $displayscore = ScoreDisplay::instance();
5323
            if (isset($cat) && $displayscore->is_custom()) {
5324
                $grade = $displayscore->display_score(
5325
                    [$score, $cat[0]->get_weight()],
5326
                    SCORE_DIV_PERCENT_WITH_CUSTOM
5327
                );
5328
            } else {
5329
                $grade = $displayscore->display_score(
5330
                    [$score, $cat[0]->get_weight()]
5331
                );
5332
            }
5333
            $row['grade'] = $grade;
5334
5335
            return $row;
5336
        }
5337
5338
        return false;
5339
    }
5340
5341
    /**
5342
     * This function check if the user is a coach inside session course.
5343
     *
5344
     * @param int $user_id    User id
5345
     * @param int $courseId
5346
     * @param int $session_id
5347
     *
5348
     * @return bool True if the user is a coach
5349
     */
5350
    public static function is_session_course_coach($user_id, $courseId, $session_id)
5351
    {
5352
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5353
        // Protect data
5354
        $user_id = intval($user_id);
5355
        $courseId = intval($courseId);
5356
        $session_id = intval($session_id);
5357
        $result = false;
5358
5359
        $sql = "SELECT session_id FROM $table
5360
                WHERE
5361
                  session_id = $session_id AND
5362
                  c_id = $courseId AND
5363
                  user_id = $user_id AND
5364
                  status = 2 ";
5365
        $res = Database::query($sql);
5366
5367
        if (Database::num_rows($res) > 0) {
5368
            $result = true;
5369
        }
5370
5371
        return $result;
5372
    }
5373
5374
    /**
5375
     * This function returns an icon path that represents the favicon of the website of which the url given.
5376
     * Defaults to the current Chamilo favicon.
5377
     *
5378
     * @param string $url1 URL of website where to look for favicon.ico
5379
     * @param string $url2 Optional second URL of website where to look for favicon.ico
5380
     *
5381
     * @return string Path of icon to load
5382
     */
5383
    public static function get_favicon_from_url($url1, $url2 = null)
5384
    {
5385
        $icon_link = '';
5386
        $url = $url1;
5387
        if (empty($url1)) {
5388
            $url = $url2;
5389
            if (empty($url)) {
5390
                $url = api_get_access_url(api_get_current_access_url_id());
5391
                $url = $url[0];
5392
            }
5393
        }
5394
        if (!empty($url)) {
5395
            $pieces = parse_url($url);
5396
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
5397
        }
5398
5399
        return $icon_link;
5400
    }
5401
5402
    public static function addUserAsAdmin(User $user)
5403
    {
5404
        if ($user) {
0 ignored issues
show
introduced by
$user is of type Chamilo\CoreBundle\Entity\User, thus it always evaluated to true.
Loading history...
5405
            $userId = $user->getId();
5406
5407
            if (!self::is_admin($userId)) {
5408
                $table = Database::get_main_table(TABLE_MAIN_ADMIN);
5409
                $sql = "INSERT INTO $table SET user_id = $userId";
5410
                Database::query($sql);
5411
            }
5412
5413
            $user->addRole('ROLE_SUPER_ADMIN');
5414
            self::getManager()->updateUser($user, true);
5415
        }
5416
    }
5417
5418
    public static function removeUserAdmin(User $user)
5419
    {
5420
        $userId = (int) $user->getId();
5421
        if (self::is_admin($userId)) {
5422
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
5423
            $sql = "DELETE FROM $table WHERE user_id = $userId";
5424
            Database::query($sql);
5425
            $user->removeRole('ROLE_SUPER_ADMIN');
5426
            self::getManager()->updateUser($user, true);
5427
        }
5428
    }
5429
5430
    /**
5431
     * @param string $from
5432
     * @param string $to
5433
     */
5434
    public static function update_all_user_languages($from, $to)
5435
    {
5436
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5437
        $from = Database::escape_string($from);
5438
        $to = Database::escape_string($to);
5439
5440
        if (!empty($to) && !empty($from)) {
5441
            $sql = "UPDATE $table_user SET language = '$to'
5442
                    WHERE language = '$from'";
5443
            Database::query($sql);
5444
        }
5445
    }
5446
5447
    /**
5448
     * Subscribe boss to students.
5449
     *
5450
     * @param int   $bossId  The boss id
5451
     * @param array $usersId The users array
5452
     *
5453
     * @return int Affected rows
5454
     */
5455
    public static function subscribeBossToUsers($bossId, $usersId)
5456
    {
5457
        return self::subscribeUsersToUser(
5458
            $bossId,
5459
            $usersId,
5460
            USER_RELATION_TYPE_BOSS
5461
        );
5462
    }
5463
5464
    /**
5465
     * @param int $userId
5466
     *
5467
     * @return bool
5468
     */
5469
    public static function removeAllBossFromStudent($userId)
5470
    {
5471
        $userId = (int) $userId;
5472
5473
        if (empty($userId)) {
5474
            return false;
5475
        }
5476
5477
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5478
        $sql = "DELETE FROM $userRelUserTable
5479
                WHERE user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
5480
        Database::query($sql);
5481
5482
        return true;
5483
    }
5484
5485
    /**
5486
     * Subscribe boss to students, if $bossList is empty then the boss list will be empty too.
5487
     *
5488
     * @param int   $studentId
5489
     * @param array $bossList
5490
     * @param bool  $sendNotification
5491
     *
5492
     * @return mixed Affected rows or false on failure
5493
     */
5494
    public static function subscribeUserToBossList(
5495
        $studentId,
5496
        $bossList,
5497
        $sendNotification = false
5498
    ) {
5499
        $inserted = 0;
5500
        if (!empty($bossList)) {
5501
            sort($bossList);
5502
            $studentId = (int) $studentId;
5503
            $studentInfo = api_get_user_info($studentId);
5504
5505
            if (empty($studentInfo)) {
5506
                return false;
5507
            }
5508
5509
            $previousBossList = self::getStudentBossList($studentId);
5510
            $previousBossList = !empty($previousBossList) ? array_column($previousBossList, 'boss_id') : [];
5511
            sort($previousBossList);
5512
5513
            // Boss list is the same, nothing changed.
5514
            if ($bossList == $previousBossList) {
5515
                return false;
5516
            }
5517
5518
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5519
            self::removeAllBossFromStudent($studentId);
5520
5521
            foreach ($bossList as $bossId) {
5522
                $bossId = (int) $bossId;
5523
                $bossInfo = api_get_user_info($bossId);
5524
5525
                if (empty($bossInfo)) {
5526
                    continue;
5527
                }
5528
5529
                $bossLanguage = $bossInfo['language'];
5530
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
5531
                        VALUES ($studentId, $bossId, ".USER_RELATION_TYPE_BOSS.")";
5532
                $insertId = Database::query($sql);
5533
5534
                if ($insertId) {
5535
                    if ($sendNotification) {
5536
                        $name = $studentInfo['complete_name'];
5537
                        $url = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?student='.$studentId;
5538
                        $url = Display::url($url, $url);
5539
                        $subject = sprintf(get_lang('You have been assigned the learner %s'), $name);
5540
                        $message = sprintf(get_lang('You have been assigned the learner %sWithUrlX'), $name, $url);
5541
                        MessageManager::send_message_simple(
5542
                            $bossId,
5543
                            $subject,
5544
                            $message
5545
                        );
5546
                    }
5547
                    $inserted++;
5548
                }
5549
            }
5550
        } else {
5551
            self::removeAllBossFromStudent($studentId);
5552
        }
5553
5554
        return $inserted;
5555
    }
5556
5557
    /**
5558
     * Get users followed by student boss.
5559
     *
5560
     * @param int    $userId
5561
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
5562
     * @param bool   $getOnlyUserId
5563
     * @param bool   $getSql
5564
     * @param bool   $getCount
5565
     * @param int    $from
5566
     * @param int    $numberItems
5567
     * @param int    $column
5568
     * @param string $direction
5569
     * @param int    $active
5570
     * @param string $lastConnectionDate
5571
     *
5572
     * @return array users
5573
     */
5574
    public static function getUsersFollowedByStudentBoss(
5575
        $userId,
5576
        $userStatus = 0,
5577
        $getOnlyUserId = false,
5578
        $getSql = false,
5579
        $getCount = false,
5580
        $from = null,
5581
        $numberItems = null,
5582
        $column = null,
5583
        $direction = null,
5584
        $active = null,
5585
        $lastConnectionDate = null
5586
    ) {
5587
        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...
5588
            $userId,
5589
            $userStatus,
5590
            $getOnlyUserId,
5591
            $getSql,
5592
            $getCount,
5593
            $from,
5594
            $numberItems,
5595
            $column,
5596
            $direction,
5597
            $active,
5598
            $lastConnectionDate,
5599
            STUDENT_BOSS
5600
        );
5601
    }
5602
5603
    /**
5604
     * @return array
5605
     */
5606
    public static function getOfficialCodeGrouped()
5607
    {
5608
        $user = Database::get_main_table(TABLE_MAIN_USER);
5609
        $sql = "SELECT DISTINCT official_code
5610
                FROM $user
5611
                GROUP BY official_code";
5612
        $result = Database::query($sql);
5613
        $values = Database::store_result($result, 'ASSOC');
5614
        $result = [];
5615
        foreach ($values as $value) {
5616
            $result[$value['official_code']] = $value['official_code'];
5617
        }
5618
5619
        return $result;
5620
    }
5621
5622
    /**
5623
     * @param string $officialCode
5624
     *
5625
     * @return array
5626
     */
5627
    public static function getUsersByOfficialCode($officialCode)
5628
    {
5629
        $user = Database::get_main_table(TABLE_MAIN_USER);
5630
        $officialCode = Database::escape_string($officialCode);
5631
5632
        $sql = "SELECT DISTINCT id
5633
                FROM $user
5634
                WHERE official_code = '$officialCode'
5635
                ";
5636
        $result = Database::query($sql);
5637
5638
        $users = [];
5639
        while ($row = Database::fetch_array($result)) {
5640
            $users[] = $row['id'];
5641
        }
5642
5643
        return $users;
5644
    }
5645
5646
    /**
5647
     * Calc the expended time (in seconds) by a user in a course.
5648
     *
5649
     * @param int    $userId    The user id
5650
     * @param int    $courseId  The course id
5651
     * @param int    $sessionId Optional. The session id
5652
     * @param string $from      Optional. From date
5653
     * @param string $until     Optional. Until date
5654
     *
5655
     * @return int The time
5656
     */
5657
    public static function getTimeSpentInCourses(
5658
        $userId,
5659
        $courseId,
5660
        $sessionId = 0,
5661
        $from = '',
5662
        $until = ''
5663
    ) {
5664
        $userId = (int) $userId;
5665
        $sessionId = (int) $sessionId;
5666
5667
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5668
        $whereConditions = [
5669
            'user_id = ? ' => $userId,
5670
            'AND c_id = ? ' => $courseId,
5671
            'AND session_id = ? ' => $sessionId,
5672
        ];
5673
5674
        if (!empty($from) && !empty($until)) {
5675
            $whereConditions["AND (login_course_date >= '?' "] = $from;
5676
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
5677
        }
5678
5679
        $trackResult = Database::select(
5680
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
5681
            $trackCourseAccessTable,
5682
            [
5683
                'where' => $whereConditions,
5684
            ],
5685
            'first'
5686
        );
5687
5688
        if (false != $trackResult) {
5689
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
5690
        }
5691
5692
        return 0;
5693
    }
5694
5695
    /**
5696
     * Get the boss user ID from a followed user id.
5697
     *
5698
     * @param $userId
5699
     *
5700
     * @return bool
5701
     */
5702
    public static function getFirstStudentBoss($userId)
5703
    {
5704
        $userId = (int) $userId;
5705
        if ($userId > 0) {
5706
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5707
            $row = Database::select(
5708
                'DISTINCT friend_user_id AS boss_id',
5709
                $userRelTable,
5710
                [
5711
                    'where' => [
5712
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
5713
                            $userId,
5714
                            USER_RELATION_TYPE_BOSS,
5715
                        ],
5716
                    ],
5717
                ]
5718
            );
5719
            if (!empty($row)) {
5720
                return $row[0]['boss_id'];
5721
            }
5722
        }
5723
5724
        return false;
5725
    }
5726
5727
    /**
5728
     * Get the boss user ID from a followed user id.
5729
     *
5730
     * @param int $userId student id
5731
     *
5732
     * @return array
5733
     */
5734
    public static function getStudentBossList($userId)
5735
    {
5736
        $userId = (int) $userId;
5737
5738
        if ($userId > 0) {
5739
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5740
            $result = Database::select(
5741
                'DISTINCT friend_user_id AS boss_id',
5742
                $userRelTable,
5743
                [
5744
                    'where' => [
5745
                        'user_id = ? AND relation_type = ? ' => [
5746
                            $userId,
5747
                            USER_RELATION_TYPE_BOSS,
5748
                        ],
5749
                    ],
5750
                ]
5751
            );
5752
5753
            return $result;
5754
        }
5755
5756
        return [];
5757
    }
5758
5759
    /**
5760
     * @param int $bossId
5761
     * @param int $studentId
5762
     *
5763
     * @return bool
5764
     */
5765
    public static function userIsBossOfStudent($bossId, $studentId)
5766
    {
5767
        $result = false;
5768
        $bossList = self::getStudentBossList($studentId);
5769
        if (!empty($bossList)) {
5770
            $bossList = array_column($bossList, 'boss_id');
5771
            if (in_array($bossId, $bossList)) {
5772
                $result = true;
5773
            }
5774
        }
5775
5776
        return $result;
5777
    }
5778
5779
    /**
5780
     * Displays the name of the user and makes the link to the user profile.
5781
     *
5782
     * @param array $userInfo
5783
     *
5784
     * @return string
5785
     */
5786
    public static function getUserProfileLink($userInfo)
5787
    {
5788
        if (isset($userInfo) && isset($userInfo['user_id'])) {
5789
            return Display::url(
5790
                $userInfo['complete_name_with_username'],
5791
                $userInfo['profile_url']
5792
            );
5793
        }
5794
5795
        return get_lang('Anonymous');
5796
    }
5797
5798
    /**
5799
     * Get users whose name matches $firstname and $lastname.
5800
     *
5801
     * @param string $firstname Firstname to search
5802
     * @param string $lastname  Lastname to search
5803
     *
5804
     * @return array The user list
5805
     */
5806
    public static function getUsersByName($firstname, $lastname)
5807
    {
5808
        $firstname = Database::escape_string($firstname);
5809
        $lastname = Database::escape_string($lastname);
5810
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
5811
5812
        $sql = <<<SQL
5813
            SELECT id, username, lastname, firstname
5814
            FROM $userTable
5815
            WHERE
5816
                firstname LIKE '$firstname%' AND
5817
                lastname LIKE '$lastname%'
5818
SQL;
5819
        $result = Database::query($sql);
5820
        $users = [];
5821
        while ($resultData = Database::fetch_object($result)) {
5822
            $users[] = $resultData;
5823
        }
5824
5825
        return $users;
5826
    }
5827
5828
    /**
5829
     * @param int $optionSelected
5830
     *
5831
     * @return string
5832
     */
5833
    public static function getUserSubscriptionTab($optionSelected = 1)
5834
    {
5835
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
5836
        if (('true' === $allowAdmin && api_is_allowed_to_edit()) ||
5837
            api_is_platform_admin()
5838
        ) {
5839
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
5840
5841
            $headers = [
5842
                [
5843
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
5844
                    'content' => get_lang('Learners'),
5845
                ],
5846
                [
5847
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
5848
                    'content' => get_lang('Trainers'),
5849
                ],
5850
                /*[
5851
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
5852
                    'content' => get_lang('Learners'),
5853
                ],
5854
                [
5855
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
5856
                    'content' => get_lang('Trainers'),
5857
                ],*/
5858
                [
5859
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
5860
                    'content' => get_lang('Groups'),
5861
                ],
5862
                [
5863
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
5864
                    'content' => get_lang('Classes'),
5865
                ],
5866
            ];
5867
5868
            return Display::tabsOnlyLink($headers, $optionSelected);
5869
        }
5870
5871
        return '';
5872
    }
5873
5874
    /**
5875
     * Make sure this function is protected because it does NOT check password!
5876
     *
5877
     * This function defines globals.
5878
     *
5879
     * @param int  $userId
5880
     * @param bool $checkIfUserCanLoginAs
5881
     *
5882
     * @return bool
5883
     *
5884
     * @author Evie Embrechts
5885
     * @author Yannick Warnier <[email protected]>
5886
     */
5887
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
5888
    {
5889
        $userId = (int) $userId;
5890
        $userInfo = api_get_user_info($userId);
5891
5892
        // Check if the user is allowed to 'login_as'
5893
        $canLoginAs = true;
5894
        if ($checkIfUserCanLoginAs) {
5895
            $canLoginAs = api_can_login_as($userId);
5896
        }
5897
5898
        if (!$canLoginAs || empty($userInfo)) {
5899
            return false;
5900
        }
5901
5902
        if ($userId) {
5903
            $logInfo = [
5904
                'tool' => 'logout',
5905
                'tool_id' => 0,
5906
                'tool_id_detail' => 0,
5907
                'action' => '',
5908
                'info' => 'Change user (login as)',
5909
            ];
5910
            Event::registerLog($logInfo);
0 ignored issues
show
Bug introduced by
The method registerLog() does not exist on Event. ( Ignorable by Annotation )

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

5910
            Event::/** @scrutinizer ignore-call */ 
5911
                   registerLog($logInfo);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
5911
5912
            // Logout the current user
5913
            self::loginDelete(api_get_user_id());
5914
5915
            return true;
5916
5917
            Session::erase('_user');
0 ignored issues
show
Unused Code introduced by
ChamiloSession::erase('_user') is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
5918
            Session::erase('is_platformAdmin');
5919
            Session::erase('is_allowedCreateCourse');
5920
            Session::erase('_uid');
5921
5922
            // Cleaning session variables
5923
            $_user['firstName'] = $userInfo['firstname'];
5924
            $_user['lastName'] = $userInfo['lastname'];
5925
            $_user['mail'] = $userInfo['email'];
5926
            $_user['official_code'] = $userInfo['official_code'];
5927
            $_user['picture_uri'] = $userInfo['picture_uri'];
5928
            $_user['user_id'] = $userId;
5929
            $_user['id'] = $userId;
5930
            $_user['status'] = $userInfo['status'];
5931
5932
            // Filling session variables with new data
5933
            Session::write('_uid', $userId);
5934
            Session::write('_user', $userInfo);
5935
            Session::write('is_platformAdmin', (bool) self::is_admin($userId));
5936
            Session::write('is_allowedCreateCourse', 1 == $userInfo['status']);
5937
            // will be useful later to know if the user is actually an admin or not (example reporting)
5938
            Session::write('login_as', true);
5939
            $logInfo = [
5940
                'tool' => 'login',
5941
                'tool_id' => 0,
5942
                'tool_id_detail' => 0,
5943
                'info' => $userId,
5944
            ];
5945
            Event::registerLog($logInfo);
5946
5947
            return true;
5948
        }
5949
5950
        return false;
5951
    }
5952
5953
    /**
5954
     * Remove all login records from the track_e_online stats table,
5955
     * for the given user ID.
5956
     *
5957
     * @param int $userId User ID
5958
     */
5959
    public static function loginDelete($userId)
5960
    {
5961
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5962
        $userId = (int) $userId;
5963
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
5964
        Database::query($query);
5965
    }
5966
5967
    /**
5968
     * Login as first admin user registered in the platform.
5969
     *
5970
     * @return array
5971
     */
5972
    public static function logInAsFirstAdmin()
5973
    {
5974
        $adminList = self::get_all_administrators();
5975
5976
        if (!empty($adminList)) {
5977
            $userInfo = current($adminList);
5978
            if (!empty($userInfo)) {
5979
                $result = self::loginAsUser($userInfo['user_id'], false);
5980
                if ($result && api_is_platform_admin()) {
5981
                    return api_get_user_info();
0 ignored issues
show
Bug Best Practice introduced by
The expression return api_get_user_info() could also return false which is incompatible with the documented return type array. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
5982
                }
5983
            }
5984
        }
5985
5986
        return [];
5987
    }
5988
5989
    /**
5990
     * Check if user is teacher of a student based in their courses.
5991
     *
5992
     * @param $teacherId
5993
     * @param $studentId
5994
     *
5995
     * @return array
5996
     */
5997
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
5998
    {
5999
        $courses = CourseManager::getCoursesFollowedByUser(
6000
            $teacherId,
6001
            COURSEMANAGER
6002
        );
6003
        if (empty($courses)) {
6004
            return false;
6005
        }
6006
6007
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
6008
        if (empty($coursesFromUser)) {
6009
            return false;
6010
        }
6011
6012
        $coursesCodeList = array_column($courses, 'code');
6013
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
6014
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
6015
        $commonCourses = array_filter($commonCourses);
6016
6017
        if (!empty($commonCourses)) {
6018
            return $commonCourses;
6019
        }
6020
6021
        return [];
6022
    }
6023
6024
    /**
6025
     * @param int $teacherId
6026
     * @param int $studentId
6027
     *
6028
     * @return bool
6029
     */
6030
    public static function isTeacherOfStudent($teacherId, $studentId)
6031
    {
6032
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
6033
            $teacherId,
6034
            $studentId
6035
        );
6036
6037
        if (!empty($courses)) {
6038
            return true;
6039
        }
6040
6041
        return false;
6042
    }
6043
6044
    /**
6045
     * Send user confirmation mail.
6046
     *
6047
     * @throws Exception
6048
     */
6049
    public static function sendUserConfirmationMail(User $user)
6050
    {
6051
        $uniqueId = api_get_unique_id();
6052
        $user->setConfirmationToken($uniqueId);
6053
6054
        Database::getManager()->persist($user);
6055
        Database::getManager()->flush();
6056
6057
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
6058
6059
        // Check if the user was originally set for an automated subscription to a course or session
6060
        $courseCodeToRedirect = Session::read('course_redirect');
6061
        $sessionToRedirect = Session::read('session_redirect');
6062
        if (!empty($courseCodeToRedirect)) {
6063
            $url .= '&c='.$courseCodeToRedirect;
6064
        }
6065
        if (!empty($sessionToRedirect)) {
6066
            $url .= '&s='.$sessionToRedirect;
6067
        }
6068
        $mailSubject = get_lang('Registration confirmation');
6069
        $mailBody = get_lang('Registration confirmationEmailMessage')
6070
            .PHP_EOL
6071
            .Display::url($url, $url);
6072
6073
        api_mail_html(
6074
            self::formatUserFullName($user),
6075
            $user->getEmail(),
6076
            $mailSubject,
6077
            $mailBody
6078
        );
6079
        Display::addFlash(Display::return_message(get_lang('Check your e-mail and follow the instructions.')));
6080
    }
6081
6082
    /**
6083
     * Anonymize a user. Replace personal info by anonymous info.
6084
     *
6085
     * @param int  $userId   User id
6086
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
6087
     *
6088
     * @throws \Exception
6089
     *
6090
     * @return bool
6091
     * @assert (0) === false
6092
     */
6093
    public static function anonymize($userId, $deleteIP = true)
6094
    {
6095
        global $debug;
6096
6097
        $userId = (int) $userId;
6098
6099
        if (empty($userId)) {
6100
            return false;
6101
        }
6102
6103
        $em = Database::getManager();
6104
        $user = api_get_user_entity($userId);
6105
        $uniqueId = uniqid('anon', true);
6106
        $user
6107
            ->setFirstname($uniqueId)
6108
            ->setLastname($uniqueId)
6109
            ->setBiography('')
6110
            ->setAddress('')
6111
            ->setCurriculumItems(null)
6112
            ->setDateOfBirth(null)
6113
            ->setCompetences('')
6114
            ->setDiplomas('')
6115
            ->setOpenarea('')
6116
            ->setTeach('')
6117
            ->setProductions(null)
6118
            ->setOpenid('')
6119
            ->setEmailCanonical($uniqueId.'@example.com')
6120
            ->setEmail($uniqueId.'@example.com')
6121
            ->setUsername($uniqueId)
6122
            ->setUsernameCanonical($uniqueId)
6123
            ->setPhone('')
6124
            ->setOfficialCode('')
6125
        ;
6126
6127
        self::deleteUserPicture($userId);
6128
        self::cleanUserRequestsOfRemoval($userId);
6129
6130
        // The IP address is a border-case personal data, as it does
6131
        // not directly allow for personal identification (it is not
6132
        // a completely safe value in most countries - the IP could
6133
        // be used by neighbours and crackers)
6134
        if ($deleteIP) {
6135
            $substitute = '127.0.0.1';
6136
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
6137
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
6138
            $res = Database::query($sql);
6139
            if (false === $res && $debug > 0) {
6140
                error_log("Could not anonymize IP address for user $userId ($sql)");
6141
            }
6142
6143
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6144
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6145
            $res = Database::query($sql);
6146
            if (false === $res && $debug > 0) {
6147
                error_log("Could not anonymize IP address for user $userId ($sql)");
6148
            }
6149
6150
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6151
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE exe_user_id = $userId";
6152
            $res = Database::query($sql);
6153
            if (false === $res && $debug > 0) {
6154
                error_log("Could not anonymize IP address for user $userId ($sql)");
6155
            }
6156
6157
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6158
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6159
            $res = Database::query($sql);
6160
            if (false === $res && $debug > 0) {
6161
                error_log("Could not anonymize IP address for user $userId ($sql)");
6162
            }
6163
6164
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6165
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6166
            $res = Database::query($sql);
6167
            if (false === $res && $debug > 0) {
6168
                error_log("Could not anonymize IP address for user $userId ($sql)");
6169
            }
6170
6171
            $table = Database::get_course_table(TABLE_WIKI);
6172
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6173
            $res = Database::query($sql);
6174
            if (false === $res && $debug > 0) {
6175
                error_log("Could not anonymize IP address for user $userId ($sql)");
6176
            }
6177
6178
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
6179
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
6180
            $res = Database::query($sql);
6181
            if (false === $res && $debug > 0) {
6182
                error_log("Could not anonymize IP address for user $userId ($sql)");
6183
            }
6184
6185
            $table = Database::get_course_table(TABLE_WIKI);
6186
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6187
            $res = Database::query($sql);
6188
            if (false === $res && $debug > 0) {
6189
                error_log("Could not anonymize IP address for user $userId ($sql)");
6190
            }
6191
        }
6192
        $em->persist($user);
6193
        $em->flush();
6194
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
6195
6196
        return true;
6197
    }
6198
6199
    /**
6200
     * @param int $userId
6201
     *
6202
     * @throws Exception
6203
     *
6204
     * @return string
6205
     */
6206
    public static function anonymizeUserWithVerification($userId)
6207
    {
6208
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
6209
6210
        $message = '';
6211
        if (api_is_platform_admin() ||
6212
            ($allowDelete && api_is_session_admin())
6213
        ) {
6214
            $userToUpdateInfo = api_get_user_info($userId);
6215
            $currentUserId = api_get_user_id();
6216
6217
            if ($userToUpdateInfo &&
6218
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
6219
            ) {
6220
                if ($userId != $currentUserId &&
6221
                    self::anonymize($userId)
6222
                ) {
6223
                    $message = Display::return_message(
6224
                        sprintf(get_lang('User %s information anonymized.'), $userToUpdateInfo['complete_name_with_username']),
6225
                        'confirmation'
6226
                    );
6227
                } else {
6228
                    $message = Display::return_message(
6229
                        sprintf(get_lang('We could not anonymize user %s information. Please try again or check the logs.'), $userToUpdateInfo['complete_name_with_username']),
6230
                        'error'
6231
                    );
6232
                }
6233
            } else {
6234
                $message = Display::return_message(
6235
                    sprintf(get_lang('You don\'t have permissions to anonymize user %s. You need the same permissions as to delete users.'), $userToUpdateInfo['complete_name_with_username']),
6236
                    'error'
6237
                );
6238
            }
6239
        }
6240
6241
        return $message;
6242
    }
6243
6244
    /**
6245
     * @param int $userId
6246
     *
6247
     * @throws Exception
6248
     *
6249
     * @return string
6250
     */
6251
    public static function deleteUserWithVerification($userId)
6252
    {
6253
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
6254
        $message = Display::return_message(get_lang('You cannot delete this user'), 'error');
6255
        $userToUpdateInfo = api_get_user_info($userId);
6256
6257
        // User must exist.
6258
        if (empty($userToUpdateInfo)) {
6259
            return $message;
6260
        }
6261
6262
        $currentUserId = api_get_user_id();
6263
6264
        // Cannot delete myself.
6265
        if ($userId == $currentUserId) {
6266
            return $message;
6267
        }
6268
6269
        if (api_is_platform_admin() ||
6270
            ($allowDelete && api_is_session_admin())
6271
        ) {
6272
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
6273
                if (self::delete_user($userId)) {
6274
                    $message = Display::return_message(
6275
                        get_lang('The user has been deleted').': '.$userToUpdateInfo['complete_name_with_username'],
6276
                        'confirmation'
6277
                    );
6278
                } else {
6279
                    $message = Display::return_message(get_lang('You cannot delete this userBecauseOwnsCourse'), 'error');
6280
                }
6281
            }
6282
        }
6283
6284
        return $message;
6285
    }
6286
6287
    /**
6288
     * @return array
6289
     */
6290
    public static function createDataPrivacyExtraFields()
6291
    {
6292
        self::create_extra_field(
6293
            'request_for_legal_agreement_consent_removal_justification',
6294
            1, //text
6295
            'Request for legal agreement consent removal justification	',
6296
            ''
6297
        );
6298
6299
        self::create_extra_field(
6300
            'request_for_delete_account_justification',
6301
            1, //text
6302
            'Request for delete account justification',
6303
            ''
6304
        );
6305
6306
        $extraFieldId = self::create_extra_field(
6307
            'request_for_legal_agreement_consent_removal',
6308
            1, //text
6309
            'Request for legal agreement consent removal',
6310
            ''
6311
        );
6312
6313
        $extraFieldIdDeleteAccount = self::create_extra_field(
6314
            'request_for_delete_account',
6315
            1, //text
6316
            'Request for delete user account',
6317
            ''
6318
        );
6319
6320
        return [
6321
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
6322
            'delete_legal' => $extraFieldId,
6323
        ];
6324
    }
6325
6326
    /**
6327
     * @param int $userId
6328
     */
6329
    public static function cleanUserRequestsOfRemoval($userId)
6330
    {
6331
        $userId = (int) $userId;
6332
6333
        $extraFieldValue = new ExtraFieldValue('user');
6334
        $extraFieldsToDelete = [
6335
            'legal_accept',
6336
            'request_for_legal_agreement_consent_removal',
6337
            'request_for_legal_agreement_consent_removal_justification',
6338
            'request_for_delete_account_justification', // just in case delete also this
6339
            'request_for_delete_account',
6340
        ];
6341
6342
        foreach ($extraFieldsToDelete as $variable) {
6343
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
6344
                $userId,
6345
                $variable
6346
            );
6347
            if ($value && isset($value['id'])) {
6348
                $extraFieldValue->delete($value['id']);
6349
            }
6350
        }
6351
    }
6352
6353
    /**
6354
     * @param int $searchYear
6355
     *
6356
     * @throws Exception
6357
     *
6358
     * @return array
6359
     */
6360
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
6361
    {
6362
        $timezone = new DateTimeZone(api_get_timezone());
6363
6364
        $sessions = [];
6365
        if (DRH == $userInfo['status']) {
6366
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
6367
        } elseif (api_is_platform_admin(true)) {
6368
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
6369
        } else {
6370
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
6371
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
6372
6373
            foreach ($sessionsByCategory as $sessionsInCategory) {
6374
                $sessions = array_merge($sessions, $sessionsInCategory);
6375
            }
6376
        }
6377
6378
        $sessions = array_map(
6379
            function ($sessionInfo) {
6380
                if (!isset($sessionInfo['session_id'])) {
6381
                    $sessionInfo['session_id'] = $sessionInfo['id'];
6382
                }
6383
                if (!isset($sessionInfo['session_name'])) {
6384
                    $sessionInfo['session_name'] = $sessionInfo['name'];
6385
                }
6386
6387
                return $sessionInfo;
6388
            },
6389
            $sessions
6390
        );
6391
6392
        $calendarSessions = [];
6393
6394
        foreach ($sessions as $sessionInfo) {
6395
            if (!empty($sessionInfo['duration'])) {
6396
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
6397
                    $sessionInfo['session_id'],
6398
                    $userInfo['id']
6399
                );
6400
6401
                if (empty($courseAccess)) {
6402
                    continue;
6403
                }
6404
6405
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
6406
                $lastAccessDate = clone $firstAcessDate;
6407
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
6408
6409
                $firstAccessYear = (int) $firstAcessDate->format('Y');
6410
                $lastAccessYear = (int) $lastAccessDate->format('Y');
6411
6412
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
6413
                    $calendarSessions[$sessionInfo['session_id']] = [
6414
                        'name' => $sessionInfo['session_name'],
6415
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
6416
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
6417
                    ];
6418
                }
6419
6420
                continue;
6421
            }
6422
6423
            $accessStartDate = !empty($sessionInfo['access_start_date'])
6424
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6425
                : null;
6426
            $accessEndDate = !empty($sessionInfo['access_end_date'])
6427
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6428
                : null;
6429
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
6430
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
6431
6432
            $isValid = false;
6433
6434
            if ($accessStartYear && $accessEndYear) {
6435
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
6436
                    $isValid = true;
6437
                }
6438
            }
6439
6440
            if ($accessStartYear && !$accessEndYear) {
6441
                if ($accessStartYear == $searchYear) {
6442
                    $isValid = true;
6443
                }
6444
            }
6445
6446
            if (!$accessStartYear && $accessEndYear) {
6447
                if ($accessEndYear == $searchYear) {
6448
                    $isValid = true;
6449
                }
6450
            }
6451
6452
            if ($isValid) {
6453
                $calendarSessions[$sessionInfo['session_id']] = [
6454
                    'name' => $sessionInfo['session_name'],
6455
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
6456
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
6457
                ];
6458
            }
6459
        }
6460
6461
        return $calendarSessions;
6462
    }
6463
6464
    /**
6465
     * Get sessions info for planification calendar.
6466
     *
6467
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
6468
     * @param int   $searchYear
6469
     *
6470
     * @throws Exception
6471
     *
6472
     * @return array
6473
     */
6474
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
6475
    {
6476
        $timezone = new DateTimeZone(api_get_timezone());
6477
        $calendar = [];
6478
6479
        foreach ($sessionsList as $sessionId => $sessionInfo) {
6480
            $startDate = $sessionInfo['access_start_date']
6481
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6482
                : null;
6483
            $endDate = $sessionInfo['access_end_date']
6484
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6485
                : null;
6486
6487
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
6488
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
6489
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
6490
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
6491
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
6492
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
6493
6494
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
6495
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
6496
6497
            $calendar[] = [
6498
                'id' => $sessionId,
6499
                'name' => $sessionInfo['name'],
6500
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
6501
                'start_in_last_year' => $startYear < $searchYear,
6502
                'end_in_next_year' => $endYear > $searchYear,
6503
                'no_start' => !$startWeek,
6504
                'no_end' => !$endWeek,
6505
                'start' => $start,
6506
                'duration' => $duration > 0 ? $duration : 1,
6507
            ];
6508
        }
6509
6510
        usort(
6511
            $calendar,
6512
            function ($sA, $sB) {
6513
                if ($sA['start'] == $sB['start']) {
6514
                    return 0;
6515
                }
6516
6517
                if ($sA['start'] < $sB['start']) {
6518
                    return -1;
6519
                }
6520
6521
                return 1;
6522
            }
6523
        );
6524
6525
        return $calendar;
6526
    }
6527
6528
    /**
6529
     * Return the user's full name. Optionally with the username.
6530
     *
6531
     * @param bool $includeUsername Optional. By default username is not included.
6532
     */
6533
    public static function formatUserFullName(User $user, $includeUsername = false): string
6534
    {
6535
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
6536
6537
        if ($includeUsername && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
6538
            $username = $user->getUsername();
6539
6540
            return "$fullName ($username)";
6541
        }
6542
6543
        return $fullName;
6544
    }
6545
6546
    /**
6547
     * @param int $userId
6548
     *
6549
     * @return array
6550
     */
6551
    public static function getUserCareers($userId)
6552
    {
6553
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6554
        $tableCareer = Database::get_main_table(TABLE_CAREER);
6555
        $userId = (int) $userId;
6556
6557
        $sql = "SELECT c.id, c.name
6558
                FROM $table uc
6559
                INNER JOIN $tableCareer c
6560
                ON uc.career_id = c.id
6561
                WHERE user_id = $userId
6562
                ORDER BY uc.created_at
6563
                ";
6564
        $result = Database::query($sql);
6565
6566
        return Database::store_result($result, 'ASSOC');
6567
    }
6568
6569
    /**
6570
     * @param int $userId
6571
     * @param int $careerId
6572
     */
6573
    public static function addUserCareer($userId, $careerId)
6574
    {
6575
        if (!api_get_configuration_value('allow_career_users')) {
6576
            return false;
6577
        }
6578
6579
        if (false === self::userHasCareer($userId, $careerId)) {
6580
            $params = ['user_id' => $userId, 'career_id' => $careerId, 'created_at' => api_get_utc_datetime(), 'updated_at' => api_get_utc_datetime()];
6581
            $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6582
            Database::insert($table, $params);
6583
        }
6584
6585
        return true;
6586
    }
6587
6588
    /**
6589
     * @param int   $userCareerId
6590
     * @param array $data
6591
     *
6592
     * @return bool
6593
     */
6594
    public static function updateUserCareer($userCareerId, $data)
6595
    {
6596
        if (!api_get_configuration_value('allow_career_users')) {
6597
            return false;
6598
        }
6599
6600
        $params = ['extra_data' => $data, 'updated_at' => api_get_utc_datetime()];
6601
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6602
        Database::update(
6603
            $table,
6604
            $params,
6605
            ['id = ?' => (int) $userCareerId]
6606
        );
6607
6608
        return true;
6609
    }
6610
6611
    /**
6612
     * @param int $userId
6613
     * @param int $careerId
6614
     *
6615
     * @return array
6616
     */
6617
    public static function getUserCareer($userId, $careerId)
6618
    {
6619
        $userId = (int) $userId;
6620
        $careerId = (int) $careerId;
6621
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6622
6623
        $sql = "SELECT * FROM $table WHERE user_id = $userId AND career_id = $careerId";
6624
        $result = Database::query($sql);
6625
6626
        return Database::fetch_array($result, 'ASSOC');
6627
    }
6628
6629
    /**
6630
     * @param int $userId
6631
     * @param int $careerId
6632
     *
6633
     * @return bool
6634
     */
6635
    public static function userHasCareer($userId, $careerId)
6636
    {
6637
        $userId = (int) $userId;
6638
        $careerId = (int) $careerId;
6639
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6640
6641
        $sql = "SELECT id FROM $table WHERE user_id = $userId AND career_id = $careerId";
6642
        $result = Database::query($sql);
6643
6644
        return Database::num_rows($result) > 0;
6645
    }
6646
6647
    /**
6648
     * @return EncoderFactory
6649
     */
6650
    private static function getEncoderFactory()
6651
    {
6652
        $encryption = self::getPasswordEncryption();
6653
        $encoders = [
6654
            'Chamilo\\CoreBundle\\Entity\\User' => new \Chamilo\CoreBundle\Security\Encoder($encryption),
6655
        ];
6656
6657
        $encoderFactory = new \Symfony\Component\Security\Core\Encoder\EncoderFactory($encoders);
6658
6659
        return $encoderFactory;
6660
    }
6661
6662
    /**
6663
     * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface
6664
     */
6665
    private static function getEncoder(User $user)
6666
    {
6667
        $encoderFactory = self::getEncoderFactory();
6668
6669
        return $encoderFactory->getEncoder($user);
6670
    }
6671
6672
    /**
6673
     * Disables or enables a user.
6674
     *
6675
     * @param int $user_id
6676
     * @param int $active  Enable or disable
6677
     *
6678
     * @return bool True on success, false on failure
6679
     * @assert (-1,0) === false
6680
     * @assert (1,1) === true
6681
     */
6682
    private static function change_active_state($user_id, $active)
6683
    {
6684
        $user_id = (int) $user_id;
6685
        $active = (int) $active;
6686
6687
        if (empty($user_id)) {
6688
            return false;
6689
        }
6690
6691
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6692
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
6693
        $r = Database::query($sql);
6694
        $ev = LOG_USER_DISABLE;
6695
        if (1 == $active) {
6696
            $ev = LOG_USER_ENABLE;
6697
        }
6698
        if (false !== $r) {
6699
            Event::addEvent($ev, LOG_USER_ID, $user_id);
6700
        }
6701
6702
        return $r;
6703
    }
6704
6705
    /**
6706
     * Get either a Gravatar URL or complete image tag for a specified email address.
6707
     *
6708
     * @param string $email The email address
6709
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
6710
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
6711
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
6712
     * @param bool   $img   True to return a complete IMG tag False for just the URL
6713
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
6714
     *
6715
     * @return string containing either just a URL or a complete image tag
6716
     * @source http://gravatar.com/site/implement/images/php/
6717
     */
6718
    private static function getGravatar(
6719
        $email,
6720
        $s = 80,
6721
        $d = 'mm',
6722
        $r = 'g',
6723
        $img = false,
6724
        $atts = []
6725
    ) {
6726
        $url = 'http://www.gravatar.com/avatar/';
6727
        if (!empty($_SERVER['HTTPS'])) {
6728
            $url = 'https://secure.gravatar.com/avatar/';
6729
        }
6730
        $url .= md5(strtolower(trim($email)));
6731
        $url .= "?s=$s&d=$d&r=$r";
6732
        if ($img) {
6733
            $url = '<img src="'.$url.'"';
6734
            foreach ($atts as $key => $val) {
6735
                $url .= ' '.$key.'="'.$val.'"';
6736
            }
6737
            $url .= ' />';
6738
        }
6739
6740
        return $url;
6741
    }
6742
}
6743