Passed
Push — master ( 684a17...b0814c )
by Julito
09:43
created

UserManager::requestUsersToHRManager()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 2
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
5
use Chamilo\CoreBundle\Entity\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\GroupRepository;
12
use Chamilo\CoreBundle\Repository\Node\UserRepository;
13
use ChamiloSession as Session;
14
use Symfony\Component\HttpFoundation\File\UploadedFile;
15
16
/**
17
 * Class UserManager.
18
 *
19
 * This library provides functions for user management.
20
 * Include/require it in your code to use its functionality.
21
 *
22
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
23
 */
24
class UserManager
25
{
26
    // This constants are deprecated use the constants located in ExtraField
27
    public const USER_FIELD_TYPE_TEXT = 1;
28
    public const USER_FIELD_TYPE_TEXTAREA = 2;
29
    public const USER_FIELD_TYPE_RADIO = 3;
30
    public const USER_FIELD_TYPE_SELECT = 4;
31
    public const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
32
    public const USER_FIELD_TYPE_DATE = 6;
33
    public const USER_FIELD_TYPE_DATETIME = 7;
34
    public const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
35
    public const USER_FIELD_TYPE_DIVIDER = 9;
36
    public const USER_FIELD_TYPE_TAG = 10;
37
    public const USER_FIELD_TYPE_TIMEZONE = 11;
38
    public const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
39
    public const USER_FIELD_TYPE_FILE = 13;
40
    public const USER_FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
41
42
    private static $encryptionMethod;
43
44
    /**
45
     * Constructor.
46
     *
47
     * @assert () === null
48
     */
49
    public function __construct()
50
    {
51
    }
52
53
    /**
54
     * Repository is use to query the DB, selects, etc.
55
     *
56
     * @return UserRepository
57
     */
58
    public static function getRepository()
59
    {
60
        return Container::$container->get(UserRepository::class);
61
    }
62
63
    /**
64
     * Create/update/delete methods are available in the UserManager
65
     * (based in the Sonata\UserBundle\Entity\UserManager).
66
     */
67
    public static function getManager()
68
    {
69
        return Container::getUserManager();
70
    }
71
72
    /**
73
     * @param string $encryptionMethod
74
     */
75
    public static function setPasswordEncryption($encryptionMethod)
76
    {
77
        self::$encryptionMethod = $encryptionMethod;
78
    }
79
80
    /**
81
     * @return bool|mixed
82
     */
83
    public static function getPasswordEncryption()
84
    {
85
        $encryptionMethod = self::$encryptionMethod;
86
        if (empty($encryptionMethod)) {
87
            $encryptionMethod = api_get_configuration_value('password_encryption');
88
        }
89
90
        return $encryptionMethod;
91
    }
92
93
    /**
94
     * Validates the password.
95
     *
96
     * @param $encoded
97
     * @param $raw
98
     * @param $salt
99
     *
100
     * @return bool
101
     */
102
    public static function isPasswordValid($encoded, $raw, $salt)
103
    {
104
        $encoder = new \Chamilo\CoreBundle\Security\Encoder(self::getPasswordEncryption());
105
        $validPassword = $encoder->isPasswordValid($encoded, $raw, $salt);
106
107
        return $validPassword;
108
    }
109
110
    /**
111
     * @param int    $userId
112
     * @param string $password
113
     */
114
    public static function updatePassword($userId, $password)
115
    {
116
        $user = api_get_user_entity($userId);
117
        $userManager = self::getManager();
118
        $user->setPlainPassword($password);
119
        $userManager->updateUser($user, true);
120
    }
121
122
    /**
123
     * Creates a new user for the platform.
124
     *
125
     * @author Hugues Peeters <[email protected]>,
126
     * @author Roan Embrechts <[email protected]>
127
     *
128
     * @param string        $firstName
129
     * @param string        $lastName
130
     * @param int           $status                  (1 for course tutor, 5 for student, 6 for anonymous)
131
     * @param string        $email
132
     * @param string        $loginName
133
     * @param string        $password
134
     * @param string        $official_code           Any official code (optional)
135
     * @param string        $language                User language    (optional)
136
     * @param string        $phone                   Phone number    (optional)
137
     * @param string        $picture_uri             Picture URI        (optional)
138
     * @param string        $authSource              Authentication source (defaults to 'platform', dependind on constant)
139
     * @param string        $expirationDate          Account expiration date (optional, defaults to null)
140
     * @param int           $active                  Whether the account is enabled or disabled by default
141
     * @param int           $hr_dept_id              The department of HR in which the user is registered (defaults to 0)
142
     * @param array         $extra                   Extra fields
143
     * @param string        $encrypt_method          Used if password is given encrypted. Set to an empty string by default
144
     * @param bool          $send_mail
145
     * @param bool          $isAdmin
146
     * @param string        $address
147
     * @param bool          $sendEmailToAllAdmins
148
     * @param FormValidator $form
149
     * @param int           $creatorId
150
     * @param array         $emailTemplate
151
     * @param string        $redirectToURLAfterLogin
152
     * @param bool          $addUserToNode
153
     *
154
     * @return mixed new user id - if the new user creation succeeds, false otherwise
155
     * @desc The function tries to retrieve user id from the session.
156
     * If it exists, the current user id is the creator id. If a problem arises,
157
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
158
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
159
     */
160
    public static function create_user(
161
        $firstName,
162
        $lastName,
163
        $status,
164
        $email,
165
        $loginName,
166
        $password,
167
        $official_code = '',
168
        $language = '',
169
        $phone = '',
170
        $picture_uri = '',
171
        $authSource = null,
172
        $expirationDate = null,
173
        $active = 1,
174
        $hr_dept_id = 0,
175
        $extra = [],
176
        $encrypt_method = '',
177
        $send_mail = false,
178
        $isAdmin = false,
179
        $address = '',
180
        $sendEmailToAllAdmins = false,
181
        $form = null,
182
        $creatorId = 0,
183
        $emailTemplate = [],
184
        $redirectToURLAfterLogin = '',
185
        $addUserToNode = true,
186
        $addUserToUrl = true
187
    ) {
188
        $authSource = !empty($authSource) ? $authSource : PLATFORM_AUTH_SOURCE;
189
        $creatorId = empty($creatorId) ? api_get_user_id() : 0;
190
        $creatorInfo = api_get_user_info($creatorId);
191
        $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
192
193
        // First check wether the login already exists
194
        if (!self::is_username_available($loginName)) {
195
            Display::addFlash(
196
                Display::return_message(get_lang('This login is already taken !'))
197
            );
198
199
            return false;
200
        }
201
202
        global $_configuration;
203
        $original_password = $password;
204
205
        $access_url_id = 1;
206
        if (api_get_multiple_access_url()) {
207
            $access_url_id = api_get_current_access_url_id();
208
        } else {
209
            // In some cases, the first access_url ID might be different from 1
210
            // for example when using a DB cluster or hacking the DB manually.
211
            // In this case, we want the first row, not necessarily "1".
212
            $accessUrlRepository = Container::getAccessUrlRepository();
213
            $accessUrl = $accessUrlRepository->getFirstId();
214
            if (!empty($accessUrl[0]) && !empty($accessUrl[0][1])) {
215
                $access_url_id = $accessUrl[0][1];
216
            }
217
        }
218
219
        if (isset($_configuration[$access_url_id]) &&
220
            is_array($_configuration[$access_url_id]) &&
221
            isset($_configuration[$access_url_id]['hosting_limit_users']) &&
222
            $_configuration[$access_url_id]['hosting_limit_users'] > 0) {
223
            $num = self::get_number_of_users();
224
            if ($num >= $_configuration[$access_url_id]['hosting_limit_users']) {
225
                api_warn_hosting_contact('hosting_limit_users');
226
                Display::addFlash(
227
                    Display::return_message(
228
                        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.'),
229
                        'warning'
230
                    )
231
                );
232
233
                return false;
234
            }
235
        }
236
237
        if (1 === $status &&
238
            isset($_configuration[$access_url_id]) &&
239
            is_array($_configuration[$access_url_id]) &&
240
            isset($_configuration[$access_url_id]['hosting_limit_teachers']) &&
241
            $_configuration[$access_url_id]['hosting_limit_teachers'] > 0
242
        ) {
243
            $num = self::get_number_of_users(1);
244
            if ($num >= $_configuration[$access_url_id]['hosting_limit_teachers']) {
245
                Display::addFlash(
246
                    Display::return_message(
247
                        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.'),
248
                        'warning'
249
                    )
250
                );
251
                api_warn_hosting_contact('hosting_limit_teachers');
252
253
                return false;
254
            }
255
        }
256
257
        if (empty($password)) {
258
            if (PLATFORM_AUTH_SOURCE === $authSource) {
259
                Display::addFlash(
260
                    Display::return_message(
261
                        get_lang('Required field').': '.get_lang(
262
                            'Password'
263
                        ),
264
                        'warning'
265
                    )
266
                );
267
268
                return false;
269
            }
270
271
            // We use the authSource as password.
272
            // The real validation will be by processed by the auth
273
            // source not Chamilo
274
            $password = $authSource;
275
        }
276
277
        // Checking the user language
278
        $languages = api_get_languages();
279
        $language = strtolower($language);
280
281
        // Default to english
282
        if (!in_array($language, array_keys($languages), true)) {
283
            $language = 'en';
284
        }
285
286
        $currentDate = api_get_utc_datetime();
287
        $now = new DateTime();
288
289
        if (empty($expirationDate) || '0000-00-00 00:00:00' === $expirationDate) {
290
            $expirationDate = null;
291
        // Default expiration date
292
            // if there is a default duration of a valid account then
293
            // we have to change the expiration_date accordingly
294
            // Accept 0000-00-00 00:00:00 as a null value to avoid issues with
295
            // third party code using this method with the previous (pre-1.10)
296
            // value of 0000...
297
            /*if ('' != api_get_setting('account_valid_duration')) {
298
                $expirationDate = new DateTime($currentDate);
299
                $days = (int) api_get_setting('account_valid_duration');
300
                $expirationDate->modify('+'.$days.' day');
301
            }*/
302
        } else {
303
            $expirationDate = api_get_utc_datetime($expirationDate);
304
            $expirationDate = new \DateTime($expirationDate, new DateTimeZone('UTC'));
305
        }
306
307
        $user = new User();
308
        $user
309
            ->setLastname($lastName)
310
            ->setFirstname($firstName)
311
            ->setUsername($loginName)
312
            ->setStatus($status)
313
            ->setPlainPassword($password)
314
            ->setEmail($email)
315
            ->setOfficialCode($official_code)
316
            ->setCreatorId($creatorId)
317
            ->setAuthSource($authSource)
318
            ->setPhone($phone)
319
            ->setAddress($address)
320
            ->setLanguage($language)
321
            ->setRegistrationDate($now)
322
            ->setHrDeptId($hr_dept_id)
323
            ->setActive($active)
324
            ->setEnabled($active)
325
            ->setTimezone(api_get_timezone())
326
        ;
327
328
        if (!empty($expirationDate)) {
329
            $user->setExpirationDate($expirationDate);
330
        }
331
332
        $em = Database::getManager();
333
        $repo = Container::getUserRepository();
334
        $repo->updateUser($user, false);
335
336
        // Add user as a node
337
        if ($addUserToNode) {
338
            $resourceNode = new ResourceNode();
339
            $resourceNode
340
                ->setTitle($loginName)
341
                ->setCreator(api_get_user_entity($creatorId))
342
                ->setResourceType($repo->getResourceType())
343
            ;
344
            $em->persist($resourceNode);
345
            $user->setResourceNode($resourceNode);
346
        }
347
348
        $em->persist($user);
349
        $em->flush();
350
351
        $userId = $user->getId();
352
353
        // Add user to a group
354
        $statusToGroup = [
355
            COURSEMANAGER => 'TEACHER',
356
            STUDENT => 'STUDENT',
357
            DRH => 'RRHH',
358
            SESSIONADMIN => 'SESSION_ADMIN',
359
            STUDENT_BOSS => 'STUDENT_BOSS',
360
            INVITEE => 'INVITEE',
361
        ];
362
363
        if (isset($statusToGroup[$status])) {
364
            $group = Container::$container->get(GroupRepository::class)->findOneBy(['code' => $statusToGroup[$status]]);
365
            if ($group) {
366
                $user->addGroup($group);
367
                $repo->updateUser($user);
368
            }
369
        }
370
371
        $em->flush();
372
373
        if (!empty($userId)) {
374
            if ($isAdmin) {
375
                self::addUserAsAdmin($user);
376
            }
377
378
            if ($addUserToUrl) {
379
                if (api_get_multiple_access_url()) {
380
                    UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
381
                } else {
382
                    //we are adding by default the access_url_user table with access_url_id = 1
383
                    UrlManager::add_user_to_url($userId, 1);
384
                }
385
            }
386
387
            if (is_array($extra) && count($extra) > 0) {
388
                $extra['item_id'] = $userId;
389
                $courseFieldValue = new ExtraFieldValue('user');
390
                $courseFieldValue->saveFieldValues($extra);
391
            } else {
392
                // Create notify settings by default
393
                self::update_extra_field_value(
394
                    $userId,
395
                    'mail_notify_invitation',
396
                    '1'
397
                );
398
                self::update_extra_field_value(
399
                    $userId,
400
                    'mail_notify_message',
401
                    '1'
402
                );
403
                self::update_extra_field_value(
404
                    $userId,
405
                    'mail_notify_group_message',
406
                    '1'
407
                );
408
            }
409
410
            self::update_extra_field_value(
411
                $userId,
412
                'already_logged_in',
413
                'false'
414
            );
415
416
            if (!empty($redirectToURLAfterLogin) && api_get_configuration_value('plugin_redirection_enabled')) {
417
                RedirectionPlugin::insert($userId, $redirectToURLAfterLogin);
418
            }
419
420
            if (!empty($email) && $send_mail) {
421
                $recipient_name = api_get_person_name(
422
                    $firstName,
423
                    $lastName,
424
                    null,
425
                    PERSON_NAME_EMAIL_ADDRESS
426
                );
427
                $tplSubject = new Template(
428
                    null,
429
                    false,
430
                    false,
431
                    false,
432
                    false,
433
                    false
434
                );
435
                $layoutSubject = $tplSubject->get_template('mail/subject_registration_platform.tpl');
436
                $emailSubject = $tplSubject->fetch($layoutSubject);
437
                $sender_name = api_get_person_name(
438
                    api_get_setting('administratorName'),
439
                    api_get_setting('administratorSurname'),
440
                    null,
441
                    PERSON_NAME_EMAIL_ADDRESS
442
                );
443
                $email_admin = api_get_setting('emailAdministrator');
444
445
                $url = api_get_path(WEB_PATH);
446
                if (api_is_multiple_url_enabled()) {
447
                    $access_url_id = api_get_current_access_url_id();
448
                    if (-1 != $access_url_id) {
449
                        $urlInfo = api_get_access_url($access_url_id);
450
                        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...
451
                            $url = $urlInfo['url'];
452
                        }
453
                    }
454
                }
455
456
                $tplContent = new Template(
457
                    null,
458
                    false,
459
                    false,
460
                    false,
461
                    false,
462
                    false
463
                );
464
                // variables for the default template
465
                $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
466
                $tplContent->assign('login_name', $loginName);
467
                $tplContent->assign('original_password', stripslashes($original_password));
468
                $tplContent->assign('mailWebPath', $url);
469
                $tplContent->assign('new_user', $user);
470
471
                $layoutContent = $tplContent->get_template('mail/content_registration_platform.tpl');
472
                $emailBody = $tplContent->fetch($layoutContent);
473
474
                $userInfo = api_get_user_info($userId);
475
                $mailTemplateManager = new MailTemplateManager();
476
477
                $phoneNumber = isset($extra['mobile_phone_number']) ? $extra['mobile_phone_number'] : null;
478
479
                $additionalParameters = [
480
                    'smsType' => SmsPlugin::WELCOME_LOGIN_PASSWORD,
481
                    'userId' => $userId,
482
                    'mobilePhoneNumber' => $phoneNumber,
483
                    'password' => $original_password,
484
                ];
485
486
                $emailBodyTemplate = '';
487
                if (!empty($emailTemplate)) {
488
                    if (isset($emailTemplate['content_registration_platform.tpl']) &&
489
                        !empty($emailTemplate['content_registration_platform.tpl'])
490
                    ) {
491
                        $emailBodyTemplate = $mailTemplateManager->parseTemplate(
492
                            $emailTemplate['content_registration_platform.tpl'],
493
                            $userInfo
494
                        );
495
                    }
496
                }
497
498
                $twoEmail = api_get_configuration_value('send_two_inscription_confirmation_mail');
499
                if (true === $twoEmail) {
500
                    $layoutContent = $tplContent->get_template('mail/new_user_first_email_confirmation.tpl');
501
                    $emailBody = $tplContent->fetch($layoutContent);
502
503
                    if (!empty($emailBodyTemplate) &&
504
                        isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
505
                        !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
506
                    ) {
507
                        $emailBody = $mailTemplateManager->parseTemplate(
508
                            $emailTemplate['new_user_first_email_confirmation.tpl'],
509
                            $userInfo
510
                        );
511
                    }
512
513
                    api_mail_html(
514
                        $recipient_name,
515
                        $email,
516
                        $emailSubject,
517
                        $emailBody,
518
                        $sender_name,
519
                        $email_admin,
520
                        null,
521
                        null,
522
                        null,
523
                        $additionalParameters,
524
                        $creatorEmail
525
                    );
526
527
                    $layoutContent = $tplContent->get_template('mail/new_user_second_email_confirmation.tpl');
528
                    $emailBody = $tplContent->fetch($layoutContent);
529
530
                    if (!empty($emailBodyTemplate) &&
531
                        isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
532
                        !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
533
                    ) {
534
                        $emailBody = $mailTemplateManager->parseTemplate(
535
                            $emailTemplate['new_user_second_email_confirmation.tpl'],
536
                            $userInfo
537
                        );
538
                    }
539
540
                    api_mail_html(
541
                        $recipient_name,
542
                        $email,
543
                        $emailSubject,
544
                        $emailBody,
545
                        $sender_name,
546
                        $email_admin,
547
                        null,
548
                        null,
549
                        null,
550
                        $additionalParameters,
551
                        $creatorEmail
552
                    );
553
                } else {
554
                    if (!empty($emailBodyTemplate)) {
555
                        $emailBody = $emailBodyTemplate;
556
                    }
557
                    $sendToInbox = api_get_configuration_value('send_inscription_msg_to_inbox');
558
                    if ($sendToInbox) {
559
                        $adminList = self::get_all_administrators();
560
                        $senderId = 1;
561
                        if (!empty($adminList)) {
562
                            $adminInfo = current($adminList);
563
                            $senderId = $adminInfo['user_id'];
564
                        }
565
566
                        MessageManager::send_message_simple(
567
                            $userId,
568
                            $emailSubject,
569
                            $emailBody,
570
                            $senderId
571
                        );
572
                    } else {
573
                        api_mail_html(
574
                            $recipient_name,
575
                            $email,
576
                            $emailSubject,
577
                            $emailBody,
578
                            $sender_name,
579
                            $email_admin,
580
                            null,
581
                            null,
582
                            null,
583
                            $additionalParameters,
584
                            $creatorEmail
585
                        );
586
                    }
587
                }
588
589
                $notification = api_get_configuration_value('send_notification_when_user_added');
590
                if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
591
                    foreach ($notification['admins'] as $adminId) {
592
                        $emailSubjectToAdmin = get_lang('The user has been added').': '.api_get_person_name($firstName, $lastName);
593
                        MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
594
                    }
595
                }
596
597
                if ($sendEmailToAllAdmins) {
598
                    $adminList = self::get_all_administrators();
599
600
                    $tplContent = new Template(
601
                        null,
602
                        false,
603
                        false,
604
                        false,
605
                        false,
606
                        false
607
                    );
608
                    // variables for the default template
609
                    $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
610
                    $tplContent->assign('user_added', $user);
611
                    $renderer = FormValidator::getDefaultRenderer();
612
                    // Form template
613
                    $elementTemplate = ' {label}: {element} <br />';
614
                    $renderer->setElementTemplate($elementTemplate);
615
                    /** @var FormValidator $form */
616
                    $form->freeze(null, $elementTemplate);
617
                    $form->removeElement('submit');
618
                    $formData = $form->returnForm();
619
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
620
                    $tplContent->assign('link', Display::url($url, $url));
621
                    $tplContent->assign('form', $formData);
622
623
                    $layoutContent = $tplContent->get_template('mail/content_registration_platform_to_admin.tpl');
624
                    $emailBody = $tplContent->fetch($layoutContent);
625
626
                    if (!empty($emailBodyTemplate) &&
627
                        isset($emailTemplate['content_registration_platform_to_admin.tpl']) &&
628
                        !empty($emailTemplate['content_registration_platform_to_admin.tpl'])
629
                    ) {
630
                        $emailBody = $mailTemplateManager->parseTemplate(
631
                            $emailTemplate['content_registration_platform_to_admin.tpl'],
632
                            $userInfo
633
                        );
634
                    }
635
636
                    $subject = get_lang('The user has been added');
637
638
                    foreach ($adminList as $adminId => $data) {
639
                        MessageManager::send_message_simple(
640
                            $adminId,
641
                            $subject,
642
                            $emailBody,
643
                            $userId
644
                        );
645
                    }
646
                }
647
                /* ENDS MANAGE EVENT WITH MAIL */
648
            }
649
            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

649
            Event::/** @scrutinizer ignore-call */ 
650
                   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...
650
        } else {
651
            Display::addFlash(
652
                Display::return_message(get_lang('There happened an unknown error. Please contact the platform administrator.'))
653
            );
654
655
            return false;
656
        }
657
658
        return $userId;
659
    }
660
661
    /**
662
     * Can user be deleted? This function checks whether there's a course
663
     * in which the given user is the
664
     * only course administrator. If that is the case, the user can't be
665
     * deleted because the course would remain without a course admin.
666
     *
667
     * @param int $user_id The user id
668
     *
669
     * @return bool true if user can be deleted
670
     *
671
     * @assert (null) === false
672
     * @assert (-1) === false
673
     * @assert ('abc') === false
674
     */
675
    public static function canDeleteUser($user_id)
676
    {
677
        $deny = api_get_configuration_value('deny_delete_users');
678
679
        if ($deny) {
680
            return false;
681
        }
682
683
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
684
        $user_id = (int) $user_id;
685
686
        if (empty($user_id)) {
687
            return false;
688
        }
689
690
        $sql = "SELECT * FROM $table_course_user
691
                WHERE status = 1 AND user_id = ".$user_id;
692
        $res = Database::query($sql);
693
        while ($course = Database::fetch_object($res)) {
694
            $sql = "SELECT id FROM $table_course_user
695
                    WHERE status=1 AND c_id = ".intval($course->c_id);
696
            $res2 = Database::query($sql);
697
            if (1 == Database::num_rows($res2)) {
698
                return false;
699
            }
700
        }
701
702
        return true;
703
    }
704
705
    /**
706
     * Delete a user from the platform, and all its belongings. This is a
707
     * very dangerous function that should only be accessible by
708
     * super-admins. Other roles should only be able to disable a user,
709
     * which removes access to the platform but doesn't delete anything.
710
     *
711
     * @param int The ID of th user to be deleted
712
     *
713
     * @throws Exception
714
     *
715
     * @return bool true if user is successfully deleted, false otherwise
716
     * @assert (null) === false
717
     * @assert ('abc') === false
718
     */
719
    public static function delete_user($user_id)
720
    {
721
        $user_id = (int) $user_id;
722
723
        if (empty($user_id)) {
724
            return false;
725
        }
726
727
        if (!self::canDeleteUser($user_id)) {
728
            return false;
729
        }
730
731
        $usergroup_rel_user = Database::get_main_table(TABLE_USERGROUP_REL_USER);
732
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
733
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
734
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
735
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
736
        $table_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
737
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
738
        $table_group = Database::get_course_table(TABLE_GROUP_USER);
739
        $table_work = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
740
741
        $userInfo = api_get_user_info($user_id);
742
        $repository = Container::getUserRepository();
743
744
        /** @var User $user */
745
        $user = $repository->find($user_id);
746
747
        $repository->deleteUser($user);
748
749
        // Unsubscribe the user from all groups in all his courses
750
        $sql = "SELECT c.id
751
                FROM $table_course c
752
                INNER JOIN $table_course_user cu
753
                ON (c.id = cu.c_id)
754
                WHERE
755
                    cu.user_id = '".$user_id."' AND
756
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
757
                ";
758
759
        $res = Database::query($sql);
760
        while ($course = Database::fetch_object($res)) {
761
            $sql = "DELETE FROM $table_group
762
                    WHERE c_id = {$course->id} AND user_id = $user_id";
763
            Database::query($sql);
764
        }
765
766
        // Unsubscribe user from usergroup_rel_user
767
        $sql = "DELETE FROM $usergroup_rel_user WHERE user_id = '".$user_id."'";
768
        Database::query($sql);
769
770
        // Unsubscribe user from all courses
771
        $sql = "DELETE FROM $table_course_user WHERE user_id = '".$user_id."'";
772
        Database::query($sql);
773
774
        // Unsubscribe user from all courses in sessions
775
        $sql = "DELETE FROM $table_session_course_user WHERE user_id = '".$user_id."'";
776
        Database::query($sql);
777
778
        // If the user was added as a id_coach then set the current admin as coach see BT#
779
        $currentUserId = api_get_user_id();
780
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
781
                WHERE id_coach = '".$user_id."'";
782
        Database::query($sql);
783
784
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
785
                WHERE session_admin_id = '".$user_id."'";
786
        Database::query($sql);
787
788
        // Unsubscribe user from all sessions
789
        $sql = "DELETE FROM $table_session_user
790
                WHERE user_id = '".$user_id."'";
791
        Database::query($sql);
792
793
        if (api_get_configuration_value('plugin_redirection_enabled')) {
794
            RedirectionPlugin::deleteUserRedirection($user_id);
795
        }
796
797
        // Delete the personal course categories
798
        $course_cat_table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
799
        $sql = "DELETE FROM $course_cat_table WHERE user_id = '".$user_id."'";
800
        Database::query($sql);
801
802
        // Delete user from the admin table
803
        $sql = "DELETE FROM $table_admin WHERE user_id = '".$user_id."'";
804
        Database::query($sql);
805
806
        // Delete the personal agenda-items from this user
807
        $agenda_table = Database::get_main_table(TABLE_PERSONAL_AGENDA);
808
        $sql = "DELETE FROM $agenda_table WHERE user = '".$user_id."'";
809
        Database::query($sql);
810
811
        $gradebook_results_table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_RESULT);
812
        $sql = 'DELETE FROM '.$gradebook_results_table.' WHERE user_id = '.$user_id;
813
        Database::query($sql);
814
815
        $extraFieldValue = new ExtraFieldValue('user');
816
        $extraFieldValue->deleteValuesByItem($user_id);
817
818
        UrlManager::deleteUserFromAllUrls($user_id);
819
        //UrlManager::deleteUserFromAllUrls($user_id);
820
821
        if ('true' === api_get_setting('allow_social_tool')) {
822
            $userGroup = new UserGroup();
823
            //Delete user from portal groups
824
            $group_list = $userGroup->get_groups_by_user($user_id);
825
            if (!empty($group_list)) {
826
                foreach ($group_list as $group_id => $data) {
827
                    $userGroup->delete_user_rel_group($user_id, $group_id);
828
                }
829
            }
830
831
            // Delete user from friend lists
832
            SocialManager::remove_user_rel_user($user_id, true);
833
        }
834
835
        // Removing survey invitation
836
        SurveyManager::delete_all_survey_invitations_by_user($user_id);
837
838
        // Delete students works
839
        $sql = "DELETE FROM $table_work WHERE user_id = $user_id AND c_id <> 0";
840
        Database::query($sql);
841
842
        /*$sql = "UPDATE c_item_property SET to_user_id = NULL
843
                WHERE to_user_id = '".$user_id."'";
844
        Database::query($sql);
845
846
        $sql = "UPDATE c_item_property SET insert_user_id = NULL
847
                WHERE insert_user_id = '".$user_id."'";
848
        Database::query($sql);
849
850
        $sql = "UPDATE c_item_property SET lastedit_user_id = NULL
851
                WHERE lastedit_user_id = '".$user_id."'";
852
        Database::query($sql);*/
853
854
        // Skills
855
        $em = Database::getManager();
856
857
        $criteria = ['user' => $user_id];
858
        $skills = $em->getRepository(SkillRelUser::class)->findBy($criteria);
859
        if ($skills) {
860
            /** @var SkillRelUser $skill */
861
            foreach ($skills as $skill) {
862
                $comments = $skill->getComments();
863
                if ($comments) {
864
                    /** @var SkillRelUserComment $comment */
865
                    foreach ($comments as $comment) {
866
                        $em->remove($comment);
867
                    }
868
                }
869
                $em->remove($skill);
870
            }
871
        }
872
873
        // ExtraFieldSavedSearch
874
        $criteria = ['user' => $user_id];
875
        $searchList = $em->getRepository(ExtraFieldSavedSearch::class)->findBy($criteria);
876
        if ($searchList) {
877
            foreach ($searchList as $search) {
878
                $em->remove($search);
879
            }
880
        }
881
882
        $connection = Database::getManager()->getConnection();
883
        $tableExists = $connection->getSchemaManager()->tablesExist(['plugin_bbb_room']);
884
        if ($tableExists) {
885
            // Delete user from database
886
            $sql = "DELETE FROM plugin_bbb_room WHERE participant_id = $user_id";
887
            Database::query($sql);
888
        }
889
890
        // Delete user/ticket relationships :(
891
        $tableExists = $connection->getSchemaManager()->tablesExist(['ticket_ticket']);
892
        if ($tableExists) {
893
            TicketManager::deleteUserFromTicketSystem($user_id);
894
        }
895
896
        $tableExists = $connection->getSchemaManager()->tablesExist(['c_lp_category_user']);
897
        if ($tableExists) {
898
            $sql = "DELETE FROM c_lp_category_user WHERE user_id = $user_id";
899
            Database::query($sql);
900
        }
901
902
        $app_plugin = new AppPlugin();
903
        $app_plugin->performActionsWhenDeletingItem('user', $user_id);
904
905
        // Add event to system log
906
        $authorId = api_get_user_id();
907
908
        Event::addEvent(
909
            LOG_USER_DELETE,
910
            LOG_USER_ID,
911
            $user_id,
912
            api_get_utc_datetime(),
913
            $authorId
914
        );
915
916
        Event::addEvent(
917
            LOG_USER_DELETE,
918
            LOG_USER_OBJECT,
919
            $userInfo,
920
            api_get_utc_datetime(),
921
            $authorId
922
        );
923
924
        return true;
925
    }
926
927
    /**
928
     * Deletes users completely. Can be called either as:
929
     * - UserManager::delete_users(1, 2, 3); or
930
     * - UserManager::delete_users(array(1, 2, 3));.
931
     *
932
     * @param array|int $ids
933
     *
934
     * @return bool True if at least one user was successfuly deleted. False otherwise.
935
     *
936
     * @author Laurent Opprecht
937
     *
938
     * @uses \UserManager::delete_user() to actually delete each user
939
     * @assert (null) === false
940
     * @assert (-1) === false
941
     * @assert (array(-1)) === false
942
     */
943
    public static function delete_users($ids = [])
944
    {
945
        $result = false;
946
        $ids = is_array($ids) ? $ids : func_get_args();
947
        if (!is_array($ids) || 0 == count($ids)) {
948
            return false;
949
        }
950
        $ids = array_map('intval', $ids);
951
        foreach ($ids as $id) {
952
            if (empty($id) || $id < 1) {
953
                continue;
954
            }
955
            $deleted = self::delete_user($id);
956
            $result = $deleted || $result;
957
        }
958
959
        return $result;
960
    }
961
962
    /**
963
     * Disable users. Can be called either as:
964
     * - UserManager::deactivate_users(1, 2, 3);
965
     * - UserManager::deactivate_users(array(1, 2, 3));.
966
     *
967
     * @param array|int $ids
968
     *
969
     * @return bool
970
     *
971
     * @author Laurent Opprecht
972
     * @assert (null) === false
973
     * @assert (array(-1)) === false
974
     */
975
    public static function deactivate_users($ids = [])
976
    {
977
        if (empty($ids)) {
978
            return false;
979
        }
980
981
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
982
983
        $ids = is_array($ids) ? $ids : func_get_args();
984
        $ids = array_map('intval', $ids);
985
        $ids = implode(',', $ids);
986
987
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
988
        $r = Database::query($sql);
989
        if (false !== $r) {
990
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
991
992
            return true;
993
        }
994
995
        return false;
996
    }
997
998
    /**
999
     * Enable users. Can be called either as:
1000
     * - UserManager::activate_users(1, 2, 3);
1001
     * - UserManager::activate_users(array(1, 2, 3));.
1002
     *
1003
     * @param array|int IDs of the users to enable
1004
     *
1005
     * @return bool
1006
     *
1007
     * @author Laurent Opprecht
1008
     * @assert (null) === false
1009
     * @assert (array(-1)) === false
1010
     */
1011
    public static function activate_users($ids = [])
1012
    {
1013
        if (empty($ids)) {
1014
            return false;
1015
        }
1016
1017
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1018
1019
        $ids = is_array($ids) ? $ids : func_get_args();
1020
        $ids = array_map('intval', $ids);
1021
        $ids = implode(',', $ids);
1022
1023
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
1024
        $r = Database::query($sql);
1025
        if (false !== $r) {
1026
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
1027
1028
            return true;
1029
        }
1030
1031
        return false;
1032
    }
1033
1034
    /**
1035
     * Update user information with all the parameters passed to this function.
1036
     *
1037
     * @param int    $user_id         The ID of the user to be updated
1038
     * @param string $firstname       The user's firstname
1039
     * @param string $lastname        The user's lastname
1040
     * @param string $username        The user's username (login)
1041
     * @param string $password        The user's password
1042
     * @param string $auth_source     The authentication source (default: "platform")
1043
     * @param string $email           The user's e-mail address
1044
     * @param int    $status          The user's status
1045
     * @param string $official_code   The user's official code (usually just an internal institutional code)
1046
     * @param string $phone           The user's phone number
1047
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
1048
     * @param string $expiration_date The date at which this user will be automatically disabled
1049
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
1050
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
1051
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
1052
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
1053
     * @param string $language        The language to which the user account will be set
1054
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
1055
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
1056
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
1057
     * @param string $address
1058
     * @param array  $emailTemplate
1059
     *
1060
     * @return bool|int False on error, or the user ID if the user information was updated
1061
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
1062
     */
1063
    public static function update_user(
1064
        $user_id,
1065
        $firstname,
1066
        $lastname,
1067
        $username,
1068
        $password = null,
1069
        $auth_source = null,
1070
        $email,
1071
        $status,
1072
        $official_code,
1073
        $phone,
1074
        $picture_uri,
1075
        $expiration_date,
1076
        $active,
1077
        $creator_id = null,
1078
        $hr_dept_id = 0,
1079
        $extra = null,
1080
        $language = 'english',
1081
        $encrypt_method = '',
1082
        $send_email = false,
1083
        $reset_password = 0,
1084
        $address = null,
1085
        $emailTemplate = []
1086
    ) {
1087
        $original_password = $password;
1088
        $user_id = (int) $user_id;
1089
        $creator_id = (int) $creator_id;
1090
1091
        if (empty($user_id)) {
1092
            return false;
1093
        }
1094
1095
        $userManager = self::getManager();
1096
        $user = api_get_user_entity($user_id);
1097
1098
        if (empty($user)) {
1099
            return false;
1100
        }
1101
1102
        if (0 == $reset_password) {
1103
            $password = null;
1104
            $auth_source = $user->getAuthSource();
1105
        } elseif (1 == $reset_password) {
1106
            $original_password = $password = api_generate_password();
1107
            $auth_source = PLATFORM_AUTH_SOURCE;
1108
        } elseif (2 == $reset_password) {
1109
            $password = $password;
1110
            $auth_source = PLATFORM_AUTH_SOURCE;
1111
        } elseif (3 == $reset_password) {
1112
            $password = $password;
1113
            $auth_source = $auth_source;
1114
        }
1115
1116
        // Checking the user language
1117
        $languages = array_keys(api_get_languages());
1118
        if (!in_array($language, $languages)) {
1119
            $language = api_get_setting('platformLanguage');
1120
        }
1121
1122
        $change_active = 0;
1123
        $isUserActive = $user->getActive();
1124
        if ($isUserActive != $active) {
1125
            $change_active = 1;
1126
        }
1127
1128
        $originalUsername = $user->getUsername();
1129
1130
        // If username is different from original then check if it exists.
1131
        if ($originalUsername !== $username) {
1132
            $available = self::is_username_available($username);
1133
            if (false === $available) {
1134
                return false;
1135
            }
1136
        }
1137
1138
        if (!empty($expiration_date)) {
1139
            $expiration_date = api_get_utc_datetime($expiration_date);
1140
            $expiration_date = new \DateTime(
1141
                $expiration_date,
1142
                new DateTimeZone('UTC')
1143
            );
1144
        }
1145
1146
        $user
1147
            ->setLastname($lastname)
1148
            ->setFirstname($firstname)
1149
            ->setUsername($username)
1150
            ->setStatus($status)
1151
            ->setAuthSource($auth_source)
1152
            ->setLanguage($language)
1153
            ->setLocale($language)
1154
            ->setEmail($email)
1155
            ->setOfficialCode($official_code)
1156
            ->setPhone($phone)
1157
            ->setAddress($address)
1158
            //->setPictureUri($picture_uri)
1159
            ->setExpirationDate($expiration_date)
1160
            ->setActive($active)
1161
            ->setEnabled($active)
1162
            ->setHrDeptId($hr_dept_id)
1163
        ;
1164
1165
        if (!is_null($password)) {
1166
            $user->setPlainPassword($password);
1167
        }
1168
1169
        $statusToGroup = [
1170
            COURSEMANAGER => 'TEACHER',
1171
            STUDENT => 'STUDENT',
1172
            DRH => 'RRHH',
1173
            SESSIONADMIN => 'SESSION_ADMIN',
1174
            STUDENT_BOSS => 'STUDENT_BOSS',
1175
            INVITEE => 'INVITEE',
1176
        ];
1177
1178
        $group = Container::$container->get(GroupRepository::class)->findOneBy(['code' => $statusToGroup[$status]]);
1179
        if ($group) {
1180
            $user->addGroup($group);
1181
        }
1182
1183
        $userManager->updateUser($user, true);
1184
        Event::addEvent(LOG_USER_UPDATE, LOG_USER_ID, $user_id);
1185
1186
        if (1 == $change_active) {
1187
            if (1 == $active) {
1188
                $event_title = LOG_USER_ENABLE;
1189
            } else {
1190
                $event_title = LOG_USER_DISABLE;
1191
            }
1192
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
1193
        }
1194
1195
        if (is_array($extra) && count($extra) > 0) {
1196
            $res = true;
1197
            foreach ($extra as $fname => $fvalue) {
1198
                $res = $res && self::update_extra_field_value(
1199
                    $user_id,
1200
                    $fname,
1201
                    $fvalue
1202
                );
1203
            }
1204
        }
1205
1206
        if (!empty($email) && $send_email) {
1207
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
1208
            $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
1209
            $sender_name = api_get_person_name(
1210
                api_get_setting('administratorName'),
1211
                api_get_setting('administratorSurname'),
1212
                null,
1213
                PERSON_NAME_EMAIL_ADDRESS
1214
            );
1215
            $email_admin = api_get_setting('emailAdministrator');
1216
            $url = api_get_path(WEB_PATH);
1217
            if (api_is_multiple_url_enabled()) {
1218
                $access_url_id = api_get_current_access_url_id();
1219
                if (-1 != $access_url_id) {
1220
                    $url = api_get_access_url($access_url_id);
1221
                    $url = $url['url'];
1222
                }
1223
            }
1224
1225
            $tplContent = new Template(
1226
                null,
1227
                false,
1228
                false,
1229
                false,
1230
                false,
1231
                false
1232
            );
1233
            // variables for the default template
1234
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
1235
            $tplContent->assign('login_name', $username);
1236
1237
            $originalPassword = '';
1238
            if ($reset_password > 0) {
1239
                $originalPassword = stripslashes($original_password);
1240
            }
1241
            $tplContent->assign('original_password', $originalPassword);
1242
            $tplContent->assign('portal_url', $url);
1243
1244
            $layoutContent = $tplContent->get_template('mail/user_edit_content.tpl');
1245
            $emailBody = $tplContent->fetch($layoutContent);
1246
1247
            $mailTemplateManager = new MailTemplateManager();
1248
1249
            if (!empty($emailTemplate) &&
1250
                isset($emailTemplate['user_edit_content.tpl']) &&
1251
                !empty($emailTemplate['user_edit_content.tpl'])
1252
            ) {
1253
                $userInfo = api_get_user_info($user_id);
1254
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], $userInfo);
1255
            }
1256
1257
            $creatorInfo = api_get_user_info($creator_id);
1258
            $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
1259
1260
            api_mail_html(
1261
                $recipient_name,
1262
                $email,
1263
                $emailsubject,
1264
                $emailBody,
1265
                $sender_name,
1266
                $email_admin,
1267
                null,
1268
                null,
1269
                null,
1270
                null,
1271
                $creatorEmail
1272
            );
1273
        }
1274
1275
        $cacheAvailable = api_get_configuration_value('apc');
1276
        if (true === $cacheAvailable) {
1277
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1278
            if (apcu_exists($apcVar)) {
1279
                apcu_delete($apcVar);
1280
            }
1281
        }
1282
1283
        return $user->getId();
1284
    }
1285
1286
    /**
1287
     * Disables a user.
1288
     *
1289
     * @param int User id
1290
     *
1291
     * @return bool
1292
     *
1293
     * @uses \UserManager::change_active_state() to actually disable the user
1294
     * @assert (0) === false
1295
     */
1296
    public static function disable($user_id)
1297
    {
1298
        if (empty($user_id)) {
1299
            return false;
1300
        }
1301
        self::change_active_state($user_id, 0);
1302
1303
        return true;
1304
    }
1305
1306
    /**
1307
     * Enable a user.
1308
     *
1309
     * @param int User id
1310
     *
1311
     * @return bool
1312
     *
1313
     * @uses \UserManager::change_active_state() to actually disable the user
1314
     * @assert (0) === false
1315
     */
1316
    public static function enable($user_id)
1317
    {
1318
        if (empty($user_id)) {
1319
            return false;
1320
        }
1321
        self::change_active_state($user_id, 1);
1322
1323
        return true;
1324
    }
1325
1326
    /**
1327
     * Returns the user's id based on the original id and field name in
1328
     * the extra fields. Returns 0 if no user was found. This function is
1329
     * mostly useful in the context of a web services-based sinchronization.
1330
     *
1331
     * @param string Original user id
1332
     * @param string Original field name
1333
     *
1334
     * @return int User id
1335
     * @assert ('0','---') === 0
1336
     */
1337
    public static function get_user_id_from_original_id(
1338
        $original_user_id_value,
1339
        $original_user_id_name
1340
    ) {
1341
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1342
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1343
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1344
1345
        $original_user_id_name = Database::escape_string($original_user_id_name);
1346
        $original_user_id_value = Database::escape_string($original_user_id_value);
1347
1348
        $sql = "SELECT item_id as user_id
1349
                FROM $t_uf uf
1350
                INNER JOIN $t_ufv ufv
1351
                ON ufv.field_id = uf.id
1352
                WHERE
1353
                    variable = '$original_user_id_name' AND
1354
                    value = '$original_user_id_value' AND
1355
                    extra_field_type = $extraFieldType
1356
                ";
1357
        $res = Database::query($sql);
1358
        $row = Database::fetch_object($res);
1359
        if ($row) {
1360
            return $row->user_id;
1361
        }
1362
1363
        return 0;
1364
    }
1365
1366
    /**
1367
     * Check if a username is available.
1368
     *
1369
     * @param string $username the wanted username
1370
     *
1371
     * @return bool true if the wanted username is available
1372
     * @assert ('') === false
1373
     * @assert ('xyzxyzxyz') === true
1374
     */
1375
    public static function is_username_available($username)
1376
    {
1377
        if (empty($username)) {
1378
            return false;
1379
        }
1380
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1381
        $sql = "SELECT username FROM $table_user
1382
                WHERE username = '".Database::escape_string($username)."'";
1383
        $res = Database::query($sql);
1384
1385
        return 0 == Database::num_rows($res);
1386
    }
1387
1388
    /**
1389
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1390
     *
1391
     * @param string $firstname the first name of the user
1392
     * @param string $lastname  the last name of the user
1393
     *
1394
     * @return string suggests a username that contains only ASCII-letters and digits,
1395
     *                without check for uniqueness within the system
1396
     *
1397
     * @author Julio Montoya Armas
1398
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1399
     * @assert ('','') === false
1400
     * @assert ('a','b') === 'ab'
1401
     */
1402
    public static function create_username($firstname, $lastname)
1403
    {
1404
        if (empty($firstname) && empty($lastname)) {
1405
            return false;
1406
        }
1407
1408
        // The first letter only.
1409
        $firstname = api_substr(
1410
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1411
            0,
1412
            1
1413
        );
1414
        //Looking for a space in the lastname
1415
        $pos = api_strpos($lastname, ' ');
1416
        if (false !== $pos) {
1417
            $lastname = api_substr($lastname, 0, $pos);
1418
        }
1419
1420
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1421
        $username = $firstname.$lastname;
1422
        if (empty($username)) {
1423
            $username = 'user';
1424
        }
1425
1426
        $username = URLify::transliterate($username);
1427
1428
        return strtolower(substr($username, 0, USERNAME_MAX_LENGTH - 3));
1429
    }
1430
1431
    /**
1432
     * Creates a unique username, using:
1433
     * 1. the first name and the last name of a user;
1434
     * 2. an already created username but not checked for uniqueness yet.
1435
     *
1436
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1437
     *                          parameter is treated as username which is to be checked f
1438
     *                          or uniqueness and to be modified when it is necessary.
1439
     * @param string $lastname  the last name of the user
1440
     *
1441
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1442
     *                Note: When the method is called several times with same parameters,
1443
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1444
     *
1445
     * @author Ivan Tcholakov, 2009
1446
     */
1447
    public static function create_unique_username($firstname, $lastname = null)
1448
    {
1449
        if (is_null($lastname)) {
1450
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1451
            // For making this method tolerant of mistakes,
1452
            // let us transliterate and purify the suggested input username anyway.
1453
            // So, instead of the sentence $username = $firstname; we place the following:
1454
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1455
        } else {
1456
            $username = self::create_username($firstname, $lastname);
1457
        }
1458
        if (!self::is_username_available($username)) {
1459
            $i = 2;
1460
            $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1461
            while (!self::is_username_available($temp_username)) {
1462
                $i++;
1463
                $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1464
            }
1465
            $username = $temp_username;
1466
        }
1467
1468
        $username = URLify::transliterate($username);
1469
1470
        return $username;
1471
    }
1472
1473
    /**
1474
     * Modifies a given username accordingly to the specification for valid characters and length.
1475
     *
1476
     * @param $username string          The input username
1477
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1478
     *                     otherwise compliance may be partial. The default value is FALSE.
1479
     *
1480
     * @return string the resulting purified username
1481
     */
1482
    public static function purify_username($username, $strict = false)
1483
    {
1484
        if ($strict) {
1485
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1486
            // into ASCII letters in order they not to be totally removed.
1487
            // 2. Applying the strict purifier.
1488
            // 3. Length limitation.
1489
            $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);
1490
            $return = URLify::transliterate($return);
1491
1492
            return $return;
1493
        }
1494
1495
        // 1. Applying the shallow purifier.
1496
        // 2. Length limitation.
1497
        return substr(
1498
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1499
            0,
1500
            USERNAME_MAX_LENGTH
1501
        );
1502
    }
1503
1504
    /**
1505
     * Checks whether the user id exists in the database.
1506
     *
1507
     * @param int $userId User id
1508
     *
1509
     * @return bool True if user id was found, false otherwise
1510
     */
1511
    public static function is_user_id_valid($userId)
1512
    {
1513
        $resultData = Database::select(
1514
            'COUNT(1) AS count',
1515
            Database::get_main_table(TABLE_MAIN_USER),
1516
            [
1517
                'where' => ['id = ?' => (int) $userId],
1518
            ],
1519
            'first'
1520
        );
1521
1522
        if (false === $resultData) {
1523
            return false;
1524
        }
1525
1526
        return $resultData['count'] > 0;
1527
    }
1528
1529
    /**
1530
     * Checks whether a given username matches to the specification strictly.
1531
     * The empty username is assumed here as invalid.
1532
     * Mostly this function is to be used in the user interface built-in validation routines
1533
     * for providing feedback while usernames are enterd manually.
1534
     *
1535
     * @param string $username the input username
1536
     *
1537
     * @return bool returns TRUE if the username is valid, FALSE otherwise
1538
     */
1539
    public static function is_username_valid($username)
1540
    {
1541
        return !empty($username) && $username == self::purify_username($username, true);
1542
    }
1543
1544
    /**
1545
     * Checks whether a username is empty. If the username contains whitespace characters,
1546
     * such as spaces, tabulators, newlines, etc.,
1547
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
1548
     *
1549
     * @param string $username the given username
1550
     *
1551
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1552
     */
1553
    public static function is_username_empty($username)
1554
    {
1555
        return 0 == strlen(self::purify_username($username, false));
1556
    }
1557
1558
    /**
1559
     * Checks whether a username is too long or not.
1560
     *
1561
     * @param string $username the given username, it should contain only ASCII-letters and digits
1562
     *
1563
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1564
     */
1565
    public static function is_username_too_long($username)
1566
    {
1567
        return strlen($username) > USERNAME_MAX_LENGTH;
1568
    }
1569
1570
    /**
1571
     * Get the users by ID.
1572
     *
1573
     * @param array  $ids    student ids
1574
     * @param string $active
1575
     * @param string $order
1576
     * @param string $limit
1577
     *
1578
     * @return array $result student information
1579
     */
1580
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
1581
    {
1582
        if (empty($ids)) {
1583
            return [];
1584
        }
1585
1586
        $ids = is_array($ids) ? $ids : [$ids];
1587
        $ids = array_map('intval', $ids);
1588
        $ids = implode(',', $ids);
1589
1590
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
1591
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
1592
        if (!is_null($active)) {
1593
            $sql .= ' AND active='.($active ? '1' : '0');
1594
        }
1595
1596
        if (!is_null($order)) {
1597
            $order = Database::escape_string($order);
1598
            $sql .= ' ORDER BY '.$order;
1599
        }
1600
1601
        if (!is_null($limit)) {
1602
            $limit = Database::escape_string($limit);
1603
            $sql .= ' LIMIT '.$limit;
1604
        }
1605
1606
        $rs = Database::query($sql);
1607
        $result = [];
1608
        while ($row = Database::fetch_array($rs)) {
1609
            $result[] = $row;
1610
        }
1611
1612
        return $result;
1613
    }
1614
1615
    /**
1616
     * Get a list of users of which the given conditions match with an = 'cond'.
1617
     *
1618
     * @param array $conditions a list of condition (example : status=>STUDENT)
1619
     * @param array $order_by   a list of fields on which sort
1620
     *
1621
     * @return array an array with all users of the platform
1622
     *
1623
     * @todo security filter order by
1624
     */
1625
    public static function get_user_list(
1626
        $conditions = [],
1627
        $order_by = [],
1628
        $limit_from = false,
1629
        $limit_to = false,
1630
        $idCampus = null
1631
    ) {
1632
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1633
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1634
        $return_array = [];
1635
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1636
1637
        if (api_is_multiple_url_enabled()) {
1638
            if ($idCampus) {
1639
                $urlId = $idCampus;
1640
            } else {
1641
                $urlId = api_get_current_access_url_id();
1642
            }
1643
            $sql .= " INNER JOIN $userUrlTable url_user
1644
                      ON (user.id = url_user.user_id)
1645
                      WHERE url_user.access_url_id = $urlId";
1646
        } else {
1647
            $sql .= " WHERE 1=1 ";
1648
        }
1649
1650
        if (count($conditions) > 0) {
1651
            foreach ($conditions as $field => $value) {
1652
                $field = Database::escape_string($field);
1653
                $value = Database::escape_string($value);
1654
                $sql .= " AND $field = '$value'";
1655
            }
1656
        }
1657
1658
        if (count($order_by) > 0) {
1659
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1660
        }
1661
1662
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1663
            $limit_from = (int) $limit_from;
1664
            $limit_to = (int) $limit_to;
1665
            $sql .= " LIMIT $limit_from, $limit_to";
1666
        }
1667
        $sql_result = Database::query($sql);
1668
        while ($result = Database::fetch_array($sql_result)) {
1669
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1670
            $return_array[] = $result;
1671
        }
1672
1673
        return $return_array;
1674
    }
1675
1676
    public static function getUserListExtraConditions(
1677
        $conditions = [],
1678
        $order_by = [],
1679
        $limit_from = false,
1680
        $limit_to = false,
1681
        $idCampus = null,
1682
        $extraConditions = '',
1683
        $getCount = false
1684
    ) {
1685
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1686
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1687
        $return_array = [];
1688
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1689
1690
        if ($getCount) {
1691
            $sql = "SELECT count(user.id) count FROM $user_table user ";
1692
        }
1693
1694
        if (api_is_multiple_url_enabled()) {
1695
            if ($idCampus) {
1696
                $urlId = $idCampus;
1697
            } else {
1698
                $urlId = api_get_current_access_url_id();
1699
            }
1700
            $sql .= " INNER JOIN $userUrlTable url_user
1701
                      ON (user.user_id = url_user.user_id)
1702
                      WHERE url_user.access_url_id = $urlId";
1703
        } else {
1704
            $sql .= " WHERE 1=1 ";
1705
        }
1706
1707
        $sql .= " AND status <> ".ANONYMOUS." ";
1708
1709
        if (count($conditions) > 0) {
1710
            foreach ($conditions as $field => $value) {
1711
                $field = Database::escape_string($field);
1712
                $value = Database::escape_string($value);
1713
                $sql .= " AND $field = '$value'";
1714
            }
1715
        }
1716
1717
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
1718
1719
        if (!empty($order_by) && count($order_by) > 0) {
1720
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1721
        }
1722
1723
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1724
            $limit_from = (int) $limit_from;
1725
            $limit_to = (int) $limit_to;
1726
            $sql .= " LIMIT $limit_from, $limit_to";
1727
        }
1728
1729
        $sql_result = Database::query($sql);
1730
1731
        if ($getCount) {
1732
            $result = Database::fetch_array($sql_result);
1733
1734
            return $result['count'];
1735
        }
1736
1737
        while ($result = Database::fetch_array($sql_result)) {
1738
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1739
            $return_array[] = $result;
1740
        }
1741
1742
        return $return_array;
1743
    }
1744
1745
    /**
1746
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
1747
     *
1748
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
1749
     * @param array  $order_by         a list of fields on which sort
1750
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
1751
     * @param string $condition        Whether we want the filters to be combined by AND or OR
1752
     * @param array  $onlyThisUserList
1753
     *
1754
     * @return array an array with all users of the platform
1755
     *
1756
     * @todo optional course code parameter, optional sorting parameters...
1757
     * @todo security filter order_by
1758
     */
1759
    public static function getUserListLike(
1760
        $conditions = [],
1761
        $order_by = [],
1762
        $simple_like = false,
1763
        $condition = 'AND',
1764
        $onlyThisUserList = []
1765
    ) {
1766
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1767
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1768
        $return_array = [];
1769
        $sql_query = "SELECT user.id FROM $user_table user ";
1770
1771
        if (api_is_multiple_url_enabled()) {
1772
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
1773
        }
1774
1775
        $sql_query .= ' WHERE 1 = 1 ';
1776
        if (count($conditions) > 0) {
1777
            $temp_conditions = [];
1778
            foreach ($conditions as $field => $value) {
1779
                $field = Database::escape_string($field);
1780
                $value = Database::escape_string($value);
1781
                if ($simple_like) {
1782
                    $temp_conditions[] = $field." LIKE '$value%'";
1783
                } else {
1784
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
1785
                }
1786
            }
1787
            if (!empty($temp_conditions)) {
1788
                $sql_query .= ' AND '.implode(' '.$condition.' ', $temp_conditions);
1789
            }
1790
1791
            if (api_is_multiple_url_enabled()) {
1792
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1793
            }
1794
        } else {
1795
            if (api_is_multiple_url_enabled()) {
1796
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1797
            }
1798
        }
1799
1800
        if (!empty($onlyThisUserList)) {
1801
            $onlyThisUserListToString = implode("','", $onlyThisUserList);
1802
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
1803
        }
1804
1805
        if (count($order_by) > 0) {
1806
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1807
        }
1808
1809
        $sql_result = Database::query($sql_query);
1810
        while ($result = Database::fetch_array($sql_result)) {
1811
            $userInfo = api_get_user_info($result['id']);
1812
            $return_array[] = $userInfo;
1813
        }
1814
1815
        return $return_array;
1816
    }
1817
1818
    /**
1819
     * Gets the current user image.
1820
     *
1821
     * @param string $userId
1822
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
1823
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
1824
     * @param bool   $addRandomId
1825
     * @param array  $userInfo    to avoid query the DB
1826
     * @todo add gravatar support
1827
     * @todo replace $userId with User entity
1828
     *
1829
     * @return string
1830
     */
1831
    public static function getUserPicture(
1832
        $userId,
1833
        int $size = USER_IMAGE_SIZE_MEDIUM,
1834
        $addRandomId = true,
1835
        $userInfo = []
1836
    ) {
1837
        $user = api_get_user_entity($userId);
1838
        $illustrationRepo = Container::getIllustrationRepository();
1839
1840
        switch ($size) {
1841
            case USER_IMAGE_SIZE_SMALL:
1842
                $width = 32;
1843
                break;
1844
            case USER_IMAGE_SIZE_MEDIUM:
1845
                $width = 64;
1846
                break;
1847
            case USER_IMAGE_SIZE_BIG:
1848
                $width = 128;
1849
                break;
1850
            case USER_IMAGE_SIZE_ORIGINAL:
1851
            default:
1852
                $width = 0;
1853
                break;
1854
        }
1855
1856
        $url = $illustrationRepo->getIllustrationUrl($user);
1857
        if (!empty($width)) {
1858
            $params = ['w' => $width];
1859
        }
1860
1861
        if ($addRandomId) {
1862
            $params['rand'] = uniqid('u_', true);
1863
        }
1864
1865
        $paramsToString = '';
1866
        if (!empty($params)) {
1867
            $paramsToString = http_build_query($params);
1868
        }
1869
1870
        return $url.$paramsToString;
1871
1872
        /*
1873
        // Make sure userInfo is defined. Otherwise, define it!
1874
        if (empty($userInfo) || !is_array($userInfo) || 0 == count($userInfo)) {
1875
            if (empty($user_id)) {
1876
                return '';
1877
            } else {
1878
                $userInfo = api_get_user_info($user_id);
1879
            }
1880
        }
1881
1882
        $imageWebPath = self::get_user_picture_path_by_id(
1883
            $user_id,
1884
            'web',
1885
            $userInfo
1886
        );
1887
        $pictureWebFile = $imageWebPath['file'];
1888
        $pictureWebDir = $imageWebPath['dir'];
1889
1890
        $pictureAnonymousSize = '128';
1891
        $gravatarSize = 22;
1892
        $realSizeName = 'small_';
1893
1894
        switch ($size) {
1895
            case USER_IMAGE_SIZE_SMALL:
1896
                $pictureAnonymousSize = '32';
1897
                $realSizeName = 'small_';
1898
                $gravatarSize = 32;
1899
                break;
1900
            case USER_IMAGE_SIZE_MEDIUM:
1901
                $pictureAnonymousSize = '64';
1902
                $realSizeName = 'medium_';
1903
                $gravatarSize = 64;
1904
                break;
1905
            case USER_IMAGE_SIZE_ORIGINAL:
1906
                $pictureAnonymousSize = '128';
1907
                $realSizeName = '';
1908
                $gravatarSize = 128;
1909
                break;
1910
            case USER_IMAGE_SIZE_BIG:
1911
                $pictureAnonymousSize = '128';
1912
                $realSizeName = 'big_';
1913
                $gravatarSize = 128;
1914
                break;
1915
        }
1916
1917
        $gravatarEnabled = api_get_setting('gravatar_enabled');
1918
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
1919
        if ('unknown.jpg' == $pictureWebFile || empty($pictureWebFile)) {
1920
            if ('true' === $gravatarEnabled) {
1921
                $file = self::getGravatar(
1922
                    $imageWebPath['email'],
1923
                    $gravatarSize,
1924
                    api_get_setting('gravatar_type')
1925
                );
1926
1927
                if ($addRandomId) {
1928
                    $file .= '&rand='.uniqid();
1929
                }
1930
1931
                return $file;
1932
            }
1933
1934
            return $anonymousPath;
1935
        }
1936
1937
        if ($addRandomId) {
1938
            $picture .= '?rand='.uniqid();
1939
        }
1940
1941
        return $picture;*/
1942
    }
1943
1944
    /**
1945
     * Creates new user photos in various sizes of a user, or deletes user photos.
1946
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
1947
     *
1948
     * @param int    $user_id the user internal identification number
1949
     * @param string $file    The common file name for the newly created photos.
1950
     *                        It will be checked and modified for compatibility with the file system.
1951
     *                        If full name is provided, path component is ignored.
1952
     *                        If an empty name is provided, then old user photos are deleted only,
1953
     *
1954
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
1955
     *
1956
     * @param string $source_file    the full system name of the image from which user photos will be created
1957
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
1958
     *
1959
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
1960
     *              When deletion is requested returns empty string.
1961
     *              In case of internal error or negative validation returns FALSE.
1962
     */
1963
    public static function update_user_picture($userId, UploadedFile $file, $crop = '')
1964
    {
1965
        if (empty($userId) || empty($file)) {
1966
            return false;
1967
        }
1968
1969
        $repo = Container::getUserRepository();
1970
        $user = $repo->find($userId);
1971
        if ($user) {
1972
            $repoIllustration = Container::getIllustrationRepository();
1973
            $repoIllustration->addIllustration($user, $user, $file, $crop);
1974
        }
1975
1976
    }
1977
1978
    /**
1979
     * Deletes user photos.
1980
     *
1981
     * @param int $userId the user internal identification number
1982
     *
1983
     * @return mixed returns empty string on success, FALSE on error
1984
     */
1985
    public static function deleteUserPicture($userId)
1986
    {
1987
        $repo = Container::getUserRepository();
1988
        $user = $repo->find($userId);
1989
        if ($user) {
1990
            $illustrationRepo = Container::getIllustrationRepository();
1991
            $illustrationRepo->deleteIllustration($user);
1992
        }
1993
    }
1994
1995
    /**
1996
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
1997
     * doesn't have any.
1998
     *
1999
     * If there has been a request to remove a production, the function will return
2000
     * without building the list unless forced to do so by the optional second
2001
     * parameter. This increases performance by avoiding to read through the
2002
     * productions on the filesystem before the removal request has been carried
2003
     * out because they'll have to be re-read afterwards anyway.
2004
     *
2005
     * @param int  $user_id    User id
2006
     * @param bool $force      Optional parameter to force building after a removal request
2007
     * @param bool $showDelete
2008
     *
2009
     * @return string A string containing the XHTML code to display the production list, or FALSE
2010
     */
2011
    public static function build_production_list($user_id, $force = false, $showDelete = false)
2012
    {
2013
        if (!$force && !empty($_POST['remove_production'])) {
2014
            return true; // postpone reading from the filesystem
2015
        }
2016
2017
        $productions = self::get_user_productions($user_id);
2018
2019
        if (empty($productions)) {
2020
            return false;
2021
        }
2022
2023
        return false;
2024
2025
        $production_dir = self::getUserPathById($user_id, 'web');
0 ignored issues
show
Unused Code introduced by
$production_dir = self::...thById($user_id, 'web') 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...
2026
        $del_image = Display::returnIconPath('delete.png');
2027
        $add_image = Display::returnIconPath('archive.png');
2028
        $del_text = get_lang('Delete');
2029
        $production_list = '';
2030
        if (count($productions) > 0) {
2031
            $production_list = '<div class="files-production"><ul id="productions">';
2032
            foreach ($productions as $file) {
2033
                $production_list .= '<li>
2034
                    <img src="'.$add_image.'" />
2035
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2036
                        '.htmlentities($file).'
2037
                    </a>';
2038
                if ($showDelete) {
2039
                    $production_list .= '&nbsp;&nbsp;
2040
                        <input
2041
                            style="width:16px;"
2042
                            type="image"
2043
                            name="remove_production['.urlencode($file).']"
2044
                            src="'.$del_image.'"
2045
                            alt="'.$del_text.'"
2046
                            title="'.$del_text.' '.htmlentities($file).'"
2047
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2048
                }
2049
            }
2050
            $production_list .= '</ul></div>';
2051
        }
2052
2053
        return $production_list;
2054
    }
2055
2056
    /**
2057
     * Returns an array with the user's productions.
2058
     *
2059
     * @param int $user_id User id
2060
     *
2061
     * @return array An array containing the user's productions
2062
     */
2063
    public static function get_user_productions($user_id)
2064
    {
2065
        return [];
2066
2067
        $production_repository = self::getUserPathById($user_id, 'system');
0 ignored issues
show
Unused Code introduced by
$production_repository =...yId($user_id, 'system') 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...
2068
        $productions = [];
2069
2070
        if (is_dir($production_repository)) {
2071
            $handle = opendir($production_repository);
2072
            while ($file = readdir($handle)) {
2073
                if ('.' == $file ||
2074
                    '..' == $file ||
2075
                    '.htaccess' == $file ||
2076
                    is_dir($production_repository.$file)
2077
                ) {
2078
                    // skip current/parent directory and .htaccess
2079
                    continue;
2080
                }
2081
2082
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2083
                    // User's photos should not be listed as productions.
2084
                    continue;
2085
                }
2086
                $productions[] = $file;
2087
            }
2088
        }
2089
2090
        return $productions;
2091
    }
2092
2093
    /**
2094
     * Remove a user production.
2095
     *
2096
     * @param int    $user_id    User id
2097
     * @param string $production The production to remove
2098
     *
2099
     * @return bool
2100
     */
2101
    public static function remove_user_production($user_id, $production)
2102
    {
2103
        throw new Exception('remove_user_production');
2104
        /*$production_path = self::get_user_picture_path_by_id($user_id, 'system');
2105
        $production_file = $production_path['dir'].$production;
2106
        if (is_file($production_file)) {
2107
            unlink($production_file);
2108
2109
            return true;
2110
        }
2111
2112
        return false;*/
2113
    }
2114
2115
    /**
2116
     * Update an extra field value for a given user.
2117
     *
2118
     * @param int    $userId   User ID
2119
     * @param string $variable Field variable name
2120
     * @param string $value    Field value
2121
     *
2122
     * @return bool true if field updated, false otherwise
2123
     */
2124
    public static function update_extra_field_value($userId, $variable, $value = '')
2125
    {
2126
        $extraFieldValue = new ExtraFieldValue('user');
2127
        $params = [
2128
            'item_id' => $userId,
2129
            'variable' => $variable,
2130
            'value' => $value,
2131
        ];
2132
2133
        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...
2134
    }
2135
2136
    /**
2137
     * Get an array of extra fields with field details (type, default value and options).
2138
     *
2139
     * @param    int    Offset (from which row)
2140
     * @param    int    Number of items
2141
     * @param    int    Column on which sorting is made
2142
     * @param    string    Sorting direction
2143
     * @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...
2144
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2145
     *
2146
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2147
     */
2148
    public static function get_extra_fields(
2149
        $from = 0,
2150
        $number_of_items = 0,
2151
        $column = 5,
2152
        $direction = 'ASC',
2153
        $all_visibility = true,
2154
        $field_filter = null
2155
    ) {
2156
        $fields = [];
2157
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2158
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2159
        $columns = [
2160
            'id',
2161
            'variable',
2162
            'field_type',
2163
            'display_text',
2164
            'default_value',
2165
            'field_order',
2166
            'filter',
2167
        ];
2168
        $column = (int) $column;
2169
        $sort_direction = '';
2170
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2171
            $sort_direction = strtoupper($direction);
2172
        }
2173
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2174
        $sqlf = "SELECT * FROM $t_uf WHERE extra_field_type = $extraFieldType ";
2175
        if (!$all_visibility) {
2176
            $sqlf .= " AND visible_to_self = 1 ";
2177
        }
2178
        if (!is_null($field_filter)) {
2179
            $field_filter = (int) $field_filter;
2180
            $sqlf .= " AND filter = $field_filter ";
2181
        }
2182
        $sqlf .= " ORDER BY ".$columns[$column]." $sort_direction ";
2183
        if (0 != $number_of_items) {
2184
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2185
        }
2186
        $resf = Database::query($sqlf);
2187
        if (Database::num_rows($resf) > 0) {
2188
            while ($rowf = Database::fetch_array($resf)) {
2189
                $fields[$rowf['id']] = [
2190
                    0 => $rowf['id'],
2191
                    1 => $rowf['variable'],
2192
                    2 => $rowf['field_type'],
2193
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2194
                    4 => $rowf['default_value'],
2195
                    5 => $rowf['field_order'],
2196
                    6 => $rowf['visible_to_self'],
2197
                    7 => $rowf['changeable'],
2198
                    8 => $rowf['filter'],
2199
                    9 => [],
2200
                    10 => '<a name="'.$rowf['id'].'"></a>',
2201
                ];
2202
2203
                $sqlo = "SELECT * FROM $t_ufo
2204
                         WHERE field_id = ".$rowf['id']."
2205
                         ORDER BY option_order ASC";
2206
                $reso = Database::query($sqlo);
2207
                if (Database::num_rows($reso) > 0) {
2208
                    while ($rowo = Database::fetch_array($reso)) {
2209
                        $fields[$rowf['id']][9][$rowo['id']] = [
2210
                            0 => $rowo['id'],
2211
                            1 => $rowo['option_value'],
2212
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2213
                            3 => $rowo['option_order'],
2214
                        ];
2215
                    }
2216
                }
2217
            }
2218
        }
2219
2220
        return $fields;
2221
    }
2222
2223
    /**
2224
     * Creates a new extra field.
2225
     *
2226
     * @param string $variable    Field's internal variable name
2227
     * @param int    $fieldType   Field's type
2228
     * @param string $displayText Field's language var name
2229
     * @param string $default     Field's default value
2230
     *
2231
     * @return int
2232
     */
2233
    public static function create_extra_field(
2234
        $variable,
2235
        $fieldType,
2236
        $displayText,
2237
        $default
2238
    ) {
2239
        $extraField = new ExtraField('user');
2240
        $params = [
2241
            'variable' => $variable,
2242
            'field_type' => $fieldType,
2243
            'display_text' => $displayText,
2244
            'default_value' => $default,
2245
        ];
2246
2247
        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...
2248
    }
2249
2250
    /**
2251
     * Check if a field is available.
2252
     *
2253
     * @param string $variable
2254
     *
2255
     * @return bool
2256
     */
2257
    public static function is_extra_field_available($variable)
2258
    {
2259
        $extraField = new ExtraField('user');
2260
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2261
2262
        return !empty($data) ? true : false;
2263
    }
2264
2265
    /**
2266
     * Gets user extra fields data.
2267
     *
2268
     * @param    int    User ID
2269
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2270
     * @param    bool    Whether to return invisible fields as well
2271
     * @param    bool    Whether to split multiple-selection fields or not
2272
     *
2273
     * @return array Array of fields => value for the given user
2274
     */
2275
    public static function get_extra_user_data(
2276
        $user_id,
2277
        $prefix = false,
2278
        $allVisibility = true,
2279
        $splitMultiple = false,
2280
        $fieldFilter = null
2281
    ) {
2282
        $user_id = (int) $user_id;
2283
2284
        if (empty($user_id)) {
2285
            return [];
2286
        }
2287
2288
        $extra_data = [];
2289
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2290
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2291
        $user_id = (int) $user_id;
2292
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2293
                FROM $t_uf f
2294
                WHERE
2295
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
2296
                ";
2297
        $filter_cond = '';
2298
2299
        if (!$allVisibility) {
2300
            if (isset($fieldFilter)) {
2301
                $fieldFilter = (int) $fieldFilter;
2302
                $filter_cond .= " AND filter = $fieldFilter ";
2303
            }
2304
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2305
        } else {
2306
            if (isset($fieldFilter)) {
2307
                $fieldFilter = (int) $fieldFilter;
2308
                $sql .= " AND filter = $fieldFilter ";
2309
            }
2310
        }
2311
2312
        $sql .= ' ORDER BY f.field_order';
2313
2314
        $res = Database::query($sql);
2315
        if (Database::num_rows($res) > 0) {
2316
            while ($row = Database::fetch_array($res)) {
2317
                if (self::USER_FIELD_TYPE_TAG == $row['type']) {
2318
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2319
                    $extra_data['extra_'.$row['fvar']] = $tags;
2320
                } else {
2321
                    $sqlu = "SELECT value as fval
2322
                            FROM $t_ufv
2323
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2324
                    $resu = Database::query($sqlu);
2325
                    // get default value
2326
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2327
                               WHERE id=".$row['id'];
2328
                    $res_df = Database::query($sql_df);
2329
2330
                    if (Database::num_rows($resu) > 0) {
2331
                        $rowu = Database::fetch_array($resu);
2332
                        $fval = $rowu['fval'];
2333
                        if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2334
                            $fval = explode(';', $rowu['fval']);
2335
                        }
2336
                    } else {
2337
                        $row_df = Database::fetch_array($res_df);
2338
                        $fval = $row_df['fval_df'];
2339
                    }
2340
                    // We get here (and fill the $extra_data array) even if there
2341
                    // is no user with data (we fill it with default values)
2342
                    if ($prefix) {
2343
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2344
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2345
                        } else {
2346
                            $extra_data['extra_'.$row['fvar']] = $fval;
2347
                        }
2348
                    } else {
2349
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2350
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2351
                        } else {
2352
                            $extra_data[$row['fvar']] = $fval;
2353
                        }
2354
                    }
2355
                }
2356
            }
2357
        }
2358
2359
        return $extra_data;
2360
    }
2361
2362
    /** Get extra user data by field.
2363
     * @param int    user ID
2364
     * @param string the internal variable name of the field
2365
     *
2366
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2367
     */
2368
    public static function get_extra_user_data_by_field(
2369
        $user_id,
2370
        $field_variable,
2371
        $prefix = false,
2372
        $all_visibility = true,
2373
        $splitmultiple = false
2374
    ) {
2375
        $user_id = (int) $user_id;
2376
2377
        if (empty($user_id)) {
2378
            return [];
2379
        }
2380
2381
        $extra_data = [];
2382
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2383
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2384
2385
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2386
                FROM $t_uf f
2387
                WHERE f.variable = '$field_variable' ";
2388
2389
        if (!$all_visibility) {
2390
            $sql .= " AND f.visible_to_self = 1 ";
2391
        }
2392
2393
        $sql .= " AND extra_field_type = ".EntityExtraField::USER_FIELD_TYPE;
2394
        $sql .= " ORDER BY f.field_order ";
2395
2396
        $res = Database::query($sql);
2397
        if (Database::num_rows($res) > 0) {
2398
            while ($row = Database::fetch_array($res)) {
2399
                $sqlu = "SELECT value as fval FROM $t_ufv v
2400
                         INNER JOIN $t_uf f
2401
                         ON (v.field_id = f.id)
2402
                         WHERE
2403
                            extra_field_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2404
                            field_id = ".$row['id']." AND
2405
                            item_id = ".$user_id;
2406
                $resu = Database::query($sqlu);
2407
                $fval = '';
2408
                if (Database::num_rows($resu) > 0) {
2409
                    $rowu = Database::fetch_array($resu);
2410
                    $fval = $rowu['fval'];
2411
                    if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2412
                        $fval = explode(';', $rowu['fval']);
2413
                    }
2414
                }
2415
                if ($prefix) {
2416
                    $extra_data['extra_'.$row['fvar']] = $fval;
2417
                } else {
2418
                    $extra_data[$row['fvar']] = $fval;
2419
                }
2420
            }
2421
        }
2422
2423
        return $extra_data;
2424
    }
2425
2426
    /**
2427
     * Get the extra field information for a certain field (the options as well).
2428
     *
2429
     * @param int $variable The name of the field we want to know everything about
2430
     *
2431
     * @return array Array containing all the information about the extra profile field
2432
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2433
     *               as returned by the database)
2434
     *
2435
     * @author Julio Montoya
2436
     *
2437
     * @since v1.8.6
2438
     */
2439
    public static function get_extra_field_information_by_name($variable)
2440
    {
2441
        $extraField = new ExtraField('user');
2442
2443
        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...
2444
    }
2445
2446
    /**
2447
     * Get the extra field information for user tag (the options as well).
2448
     *
2449
     * @param int $variable The name of the field we want to know everything about
2450
     *
2451
     * @return array Array containing all the information about the extra profile field
2452
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2453
     *               as returned by the database)
2454
     *
2455
     * @author José Loguercio
2456
     *
2457
     * @since v1.11.0
2458
     */
2459
    public static function get_extra_field_tags_information_by_name($variable)
2460
    {
2461
        $extraField = new ExtraField('user');
2462
2463
        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...
2464
    }
2465
2466
    /**
2467
     * Get all the extra field information of a certain field (also the options).
2468
     *
2469
     * @param int $fieldId the ID of the field we want to know everything of
2470
     *
2471
     * @return array $return containing all th information about the extra profile field
2472
     *
2473
     * @author Julio Montoya
2474
     *
2475
     * @deprecated
2476
     * @since v1.8.6
2477
     */
2478
    public static function get_extra_field_information($fieldId)
2479
    {
2480
        $extraField = new ExtraField('user');
2481
2482
        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...
2483
    }
2484
2485
    /**
2486
     * Get extra user data by value.
2487
     *
2488
     * @param string $variable       the internal variable name of the field
2489
     * @param string $value          the internal value of the field
2490
     * @param bool   $all_visibility
2491
     *
2492
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2493
     */
2494
    public static function get_extra_user_data_by_value($variable, $value, $all_visibility = true)
2495
    {
2496
        $extraFieldValue = new ExtraFieldValue('user');
2497
        $extraField = new ExtraField('user');
2498
2499
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2500
2501
        if (false === $info) {
2502
            return [];
2503
        }
2504
2505
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2506
            $variable,
2507
            $value,
2508
            false,
2509
            false,
2510
            true
2511
        );
2512
2513
        $result = [];
2514
        if (!empty($data)) {
2515
            foreach ($data as $item) {
2516
                $result[] = $item['item_id'];
2517
            }
2518
        }
2519
2520
        return $result;
2521
    }
2522
2523
    /**
2524
     * Get extra user data by tags value.
2525
     *
2526
     * @param int    $fieldId the ID of the field we want to know everything of
2527
     * @param string $tag     the tag name for search
2528
     *
2529
     * @return array with extra data info of a user
2530
     *
2531
     * @author José Loguercio
2532
     *
2533
     * @since v1.11.0
2534
     */
2535
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2536
    {
2537
        $extraField = new ExtraField('user');
2538
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2539
        $array = [];
2540
        foreach ($result as $index => $user) {
2541
            $array[] = $user['user_id'];
2542
        }
2543
2544
        return $array;
2545
    }
2546
2547
    /**
2548
     * Get extra user data by field variable.
2549
     *
2550
     * @param string $variable field variable
2551
     *
2552
     * @return array data
2553
     */
2554
    public static function get_extra_user_data_by_field_variable($variable)
2555
    {
2556
        $extraInfo = self::get_extra_field_information_by_name($variable);
2557
        $field_id = (int) $extraInfo['id'];
2558
2559
        $extraField = new ExtraFieldValue('user');
2560
        $data = $extraField->getValuesByFieldId($field_id);
2561
2562
        if (!empty($data)) {
2563
            foreach ($data as $row) {
2564
                $user_id = $row['item_id'];
2565
                $data[$user_id] = $row;
2566
            }
2567
        }
2568
2569
        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...
2570
    }
2571
2572
    /**
2573
     * Get extra user data tags by field variable.
2574
     *
2575
     * @param string $variable field variable
2576
     *
2577
     * @return array
2578
     */
2579
    public static function get_extra_user_data_for_tags($variable)
2580
    {
2581
        $data = self::get_extra_field_tags_information_by_name($variable);
2582
2583
        return $data;
2584
    }
2585
2586
    /**
2587
     * Gives a list of [session_category][session_id] for the current user.
2588
     *
2589
     * @param int  $user_id
2590
     * @param bool $is_time_over                 whether to fill the first element or not
2591
     *                                           (to give space for courses out of categories)
2592
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2593
     * @param bool $ignoreTimeLimit              ignore time start/end
2594
     * @param bool $getCount
2595
     *
2596
     * @return array list of statuses [session_category][session_id]
2597
     *
2598
     * @todo ensure multiple access urls are managed correctly
2599
     */
2600
    public static function get_sessions_by_category(
2601
        $user_id,
2602
        $is_time_over = true,
2603
        $ignore_visibility_for_admins = false,
2604
        $ignoreTimeLimit = false,
2605
        $getCount = false
2606
    ) {
2607
        $user_id = (int) $user_id;
2608
2609
        if (empty($user_id)) {
2610
            return [];
2611
        }
2612
2613
        $allowOrder = api_get_configuration_value('session_list_order');
2614
        $position = '';
2615
        if ($allowOrder) {
2616
            $position = ', s.position AS position ';
2617
        }
2618
2619
        // Get the list of sessions per user
2620
        $now = new DateTime('now', new DateTimeZone('UTC'));
2621
2622
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2623
        // join would not catch session-courses where the user is general
2624
        // session coach but which do not have students nor coaches registered
2625
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
2626
2627
        if (!$getCount) {
2628
            $dqlSelect = " DISTINCT
2629
                s.id,
2630
                s.name,
2631
                s.accessStartDate AS access_start_date,
2632
                s.accessEndDate AS access_end_date,
2633
                s.duration,
2634
                sc.id AS session_category_id,
2635
                sc.name AS session_category_name,
2636
                sc.dateStart AS session_category_date_start,
2637
                sc.dateEnd AS session_category_date_end,
2638
                s.coachAccessStartDate AS coach_access_start_date,
2639
                s.coachAccessEndDate AS coach_access_end_date,
2640
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
2641
                $position
2642
            ";
2643
        }
2644
2645
        $dql = "SELECT $dqlSelect
2646
                FROM ChamiloCoreBundle:Session AS s
2647
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2648
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2649
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc ";
2650
2651
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
2652
        // is awfully inefficient for large sets of data (1m25s for 58K
2653
        // sessions, BT#14115) but executing a similar query twice and grouping
2654
        // the results afterwards in PHP takes about 1/1000th of the time
2655
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
2656
        $dqlStudent = $dql." WHERE scu.user = :user AND url.url = :url ";
2657
        $dqlCoach = $dql." WHERE s.generalCoach = :user AND url.url = :url ";
2658
2659
        // Default order
2660
        $order = 'ORDER BY sc.name, s.name';
2661
2662
        // Order by date if showing all sessions
2663
        $showAllSessions = true === api_get_configuration_value('show_all_sessions_on_my_course_page');
2664
        if ($showAllSessions) {
2665
            $order = 'ORDER BY s.accessStartDate';
2666
        }
2667
2668
        // Order by position
2669
        if ($allowOrder) {
2670
            $order = 'ORDER BY s.position';
2671
        }
2672
2673
        // Order by dates according to settings
2674
        $orderBySettings = api_get_configuration_value('my_courses_session_order');
2675
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
2676
            $field = $orderBySettings['field'];
2677
            $orderSetting = $orderBySettings['order'];
2678
            switch ($field) {
2679
                case 'start_date':
2680
                    $order = " ORDER BY s.accessStartDate $orderSetting";
2681
                    break;
2682
                case 'end_date':
2683
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
2684
                    if ('asc' == $orderSetting) {
2685
                        // Put null values at the end
2686
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
2687
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
2688
                    }
2689
                    break;
2690
                case 'name':
2691
                    $order = " ORDER BY s.name $orderSetting ";
2692
                    break;
2693
            }
2694
        }
2695
2696
        $dqlStudent .= $order;
2697
        $dqlCoach .= $order;
2698
2699
        $accessUrlId = api_get_current_access_url_id();
2700
        $dqlStudent = Database::getManager()
2701
            ->createQuery($dqlStudent)
2702
            ->setParameters(
2703
                ['user' => $user_id, 'url' => $accessUrlId]
2704
            )
2705
        ;
2706
        $dqlCoach = Database::getManager()
2707
            ->createQuery($dqlCoach)
2708
            ->setParameters(
2709
                ['user' => $user_id, 'url' => $accessUrlId]
2710
            )
2711
        ;
2712
2713
        if ($getCount) {
2714
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
2715
        }
2716
2717
        $sessionDataStudent = $dqlStudent->getResult();
2718
        $sessionDataCoach = $dqlCoach->getResult();
2719
2720
        $sessionData = [];
2721
        // First fill $sessionData with student sessions
2722
        if (!empty($sessionDataStudent)) {
2723
            foreach ($sessionDataStudent as $row) {
2724
                $sessionData[$row['id']] = $row;
2725
            }
2726
        }
2727
        // Overwrite session data of the user as a student with session data
2728
        // of the user as a coach.
2729
        // There shouldn't be such duplicate rows, but just in case...
2730
        if (!empty($sessionDataCoach)) {
2731
            foreach ($sessionDataCoach as $row) {
2732
                $sessionData[$row['id']] = $row;
2733
            }
2734
        }
2735
2736
        $collapsable = api_get_configuration_value('allow_user_session_collapsable');
2737
        $extraField = new ExtraFieldValue('session');
2738
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
2739
2740
        if (empty($sessionData)) {
2741
            return [];
2742
        }
2743
        $categories = [];
2744
        foreach ($sessionData as $row) {
2745
            $session_id = $row['id'];
2746
            $coachList = SessionManager::getCoachesBySession($session_id);
2747
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
2748
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
2749
            $courseList = self::get_courses_list_by_session($user_id, $session_id);
2750
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
2751
2752
            // User portal filters:
2753
            if (false === $ignoreTimeLimit) {
2754
                if ($is_time_over) {
2755
                    // History
2756
                    if ($row['duration']) {
2757
                        if ($daysLeft >= 0) {
2758
                            continue;
2759
                        }
2760
                    } else {
2761
                        if (empty($row['access_end_date'])) {
2762
                            continue;
2763
                        } else {
2764
                            if ($row['access_end_date'] > $now) {
2765
                                continue;
2766
                            }
2767
                        }
2768
                    }
2769
                } else {
2770
                    // Current user portal
2771
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
2772
                    $isCoachOfCourse = in_array($user_id, $coachList);
2773
2774
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
2775
                        // Teachers can access the session depending in the access_coach date
2776
                    } else {
2777
                        if ($row['duration']) {
2778
                            if ($daysLeft <= 0) {
2779
                                continue;
2780
                            }
2781
                        } else {
2782
                            if (isset($row['access_end_date']) &&
2783
                                !empty($row['access_end_date'])
2784
                            ) {
2785
                                if ($row['access_end_date'] <= $now) {
2786
                                    continue;
2787
                                }
2788
                            }
2789
                        }
2790
                    }
2791
                }
2792
            }
2793
2794
            $categories[$row['session_category_id']]['session_category'] = [
2795
                'id' => $row['session_category_id'],
2796
                'name' => $row['session_category_name'],
2797
                'date_start' => $categoryStart,
2798
                'date_end' => $categoryEnd,
2799
            ];
2800
2801
            $visibility = api_get_session_visibility(
2802
                $session_id,
2803
                null,
2804
                $ignore_visibility_for_admins
2805
            );
2806
2807
            if (SESSION_VISIBLE != $visibility) {
2808
                // Course Coach session visibility.
2809
                $blockedCourseCount = 0;
2810
                $closedVisibilityList = [
2811
                    COURSE_VISIBILITY_CLOSED,
2812
                    COURSE_VISIBILITY_HIDDEN,
2813
                ];
2814
2815
                foreach ($courseList as $course) {
2816
                    // Checking session visibility
2817
                    $sessionCourseVisibility = api_get_session_visibility(
2818
                        $session_id,
2819
                        $course['real_id'],
2820
                        $ignore_visibility_for_admins
2821
                    );
2822
2823
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
2824
                    if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
2825
                        $blockedCourseCount++;
2826
                    }
2827
                }
2828
2829
                // If all courses are blocked then no show in the list.
2830
                if ($blockedCourseCount === count($courseList)) {
2831
                    $visibility = SESSION_INVISIBLE;
2832
                } else {
2833
                    $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...
2834
                }
2835
            }
2836
2837
            switch ($visibility) {
2838
                case SESSION_VISIBLE_READ_ONLY:
2839
                case SESSION_VISIBLE:
2840
                case SESSION_AVAILABLE:
2841
                    break;
2842
                case SESSION_INVISIBLE:
2843
                    if (false === $ignore_visibility_for_admins) {
2844
                        continue 2;
2845
                    }
2846
            }
2847
2848
            $collapsed = '';
2849
            $collapsedAction = '';
2850
            if ($collapsable) {
2851
                $collapsableData = SessionManager::getCollapsableData(
2852
                    $user_id,
2853
                    $session_id,
2854
                    $extraField,
2855
                    $collapsableLink
2856
                );
2857
                $collapsed = $collapsableData['collapsed'];
2858
                $collapsedAction = $collapsableData['collapsable_link'];
2859
            }
2860
2861
            $categories[$row['session_category_id']]['sessions'][] = [
2862
                'session_name' => $row['name'],
2863
                'session_id' => $row['id'],
2864
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
2865
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
2866
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
2867
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
2868
                'courses' => $courseList,
2869
                'collapsed' => $collapsed,
2870
                'collapsable_link' => $collapsedAction,
2871
                'duration' => $row['duration'],
2872
            ];
2873
        }
2874
2875
        return $categories;
2876
    }
2877
2878
    /**
2879
     * Gives a list of [session_id-course_code] => [status] for the current user.
2880
     *
2881
     * @param int $user_id
2882
     * @param int $sessionLimit
2883
     *
2884
     * @return array list of statuses (session_id-course_code => status)
2885
     */
2886
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
2887
    {
2888
        // Database Table Definitions
2889
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2890
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2891
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2892
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
2893
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2894
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2895
        $tblCourseCategory = Database::get_main_table(TABLE_MAIN_CATEGORY);
2896
2897
        $user_id = (int) $user_id;
2898
2899
        if (empty($user_id)) {
2900
            return [];
2901
        }
2902
2903
        // We filter the courses from the URL
2904
        $join_access_url = $where_access_url = '';
2905
        if (api_get_multiple_access_url()) {
2906
            $access_url_id = api_get_current_access_url_id();
2907
            if (-1 != $access_url_id) {
2908
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2909
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
2910
                $where_access_url = " AND access_url_id = $access_url_id ";
2911
            }
2912
        }
2913
2914
        // Courses in which we subscribed out of any session
2915
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
2916
2917
        $sql = "SELECT
2918
                    course.code,
2919
                    course_rel_user.status course_rel_status,
2920
                    course_rel_user.sort sort,
2921
                    course_rel_user.user_course_cat user_course_cat
2922
                 FROM $tbl_course_user course_rel_user
2923
                 LEFT JOIN $tbl_course course
2924
                 ON course.id = course_rel_user.c_id
2925
                 LEFT JOIN $tbl_user_course_category user_course_category
2926
                 ON course_rel_user.user_course_cat = user_course_category.id
2927
                 $join_access_url
2928
                 WHERE
2929
                    course_rel_user.user_id = '".$user_id."' AND
2930
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
2931
                    $where_access_url
2932
                 ORDER BY user_course_category.sort, course_rel_user.sort, course.title ASC";
2933
2934
        $course_list_sql_result = Database::query($sql);
2935
2936
        $personal_course_list = [];
2937
        if (Database::num_rows($course_list_sql_result) > 0) {
2938
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
2939
                $course_info = api_get_course_info($result_row['code']);
2940
                $result_row['course_info'] = $course_info;
2941
                $personal_course_list[] = $result_row;
2942
            }
2943
        }
2944
2945
        $coachCourseConditions = '';
2946
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
2947
        if (api_is_allowed_to_create_course()) {
2948
            $sessionListFromCourseCoach = [];
2949
            $sql = " SELECT DISTINCT session_id
2950
                    FROM $tbl_session_course_user
2951
                    WHERE user_id = $user_id AND status = 2 ";
2952
2953
            $result = Database::query($sql);
2954
            if (Database::num_rows($result)) {
2955
                $result = Database::store_result($result);
2956
                foreach ($result as $session) {
2957
                    $sessionListFromCourseCoach[] = $session['session_id'];
2958
                }
2959
            }
2960
            if (!empty($sessionListFromCourseCoach)) {
2961
                $condition = implode("','", $sessionListFromCourseCoach);
2962
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
2963
            }
2964
        }
2965
2966
        // Get the list of sessions where the user is subscribed
2967
        // This is divided into two different queries
2968
        $sessions = [];
2969
        $sessionLimitRestriction = '';
2970
        if (!empty($sessionLimit)) {
2971
            $sessionLimit = (int) $sessionLimit;
2972
            $sessionLimitRestriction = "LIMIT $sessionLimit";
2973
        }
2974
2975
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
2976
                FROM $tbl_session_user su INNER JOIN $tbl_session s
2977
                ON (s.id = su.session_id)
2978
                WHERE (
2979
                    su.user_id = $user_id AND
2980
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
2981
                )
2982
                $coachCourseConditions
2983
                ORDER BY access_start_date, access_end_date, name
2984
                $sessionLimitRestriction
2985
        ";
2986
2987
        $result = Database::query($sql);
2988
        if (Database::num_rows($result) > 0) {
2989
            while ($row = Database::fetch_assoc($result)) {
2990
                $sessions[$row['id']] = $row;
2991
            }
2992
        }
2993
2994
        $sql = "SELECT DISTINCT
2995
                id, name, access_start_date, access_end_date
2996
                FROM $tbl_session s
2997
                WHERE (
2998
                    id_coach = $user_id
2999
                )
3000
                $coachCourseConditions
3001
                ORDER BY access_start_date, access_end_date, name";
3002
3003
        $result = Database::query($sql);
3004
        if (Database::num_rows($result) > 0) {
3005
            while ($row = Database::fetch_assoc($result)) {
3006
                if (empty($sessions[$row['id']])) {
3007
                    $sessions[$row['id']] = $row;
3008
                }
3009
            }
3010
        }
3011
3012
        if (api_is_allowed_to_create_course()) {
3013
            foreach ($sessions as $enreg) {
3014
                $session_id = $enreg['id'];
3015
                $session_visibility = api_get_session_visibility($session_id);
3016
3017
                if (SESSION_INVISIBLE == $session_visibility) {
3018
                    continue;
3019
                }
3020
3021
                // This query is horribly slow when more than a few thousand
3022
                // users and just a few sessions to which they are subscribed
3023
                $sql = "SELECT DISTINCT
3024
                        course.code code,
3025
                        course.title i,
3026
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
3027
                        email, course.course_language l,
3028
                        1 sort,
3029
                        course_category.code user_course_cat,
3030
                        access_start_date,
3031
                        access_end_date,
3032
                        session.id as session_id,
3033
                        session.name as session_name
3034
                    FROM $tbl_session_course_user as session_course_user
3035
                    INNER JOIN $tbl_course AS course
3036
                        ON course.id = session_course_user.c_id
3037
                    LEFT JOIN $tblCourseCategory course_category ON course.category_id = course_category.id
3038
                    INNER JOIN $tbl_session as session
3039
                        ON session.id = session_course_user.session_id
3040
                    LEFT JOIN $tbl_user as user
3041
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
3042
                    WHERE
3043
                        session_course_user.session_id = $session_id AND (
3044
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
3045
                            OR session.id_coach = $user_id
3046
                        )
3047
                    ORDER BY i";
3048
                $course_list_sql_result = Database::query($sql);
3049
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3050
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3051
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3052
                    $personal_course_list[$key] = $result_row;
3053
                }
3054
            }
3055
        }
3056
3057
        foreach ($sessions as $enreg) {
3058
            $session_id = $enreg['id'];
3059
            $session_visibility = api_get_session_visibility($session_id);
3060
            if (SESSION_INVISIBLE == $session_visibility) {
3061
                continue;
3062
            }
3063
3064
            /* This query is very similar to the above query,
3065
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3066
            $sql = "SELECT DISTINCT
3067
                course.code code,
3068
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
3069
                email,
3070
                course.course_language l,
3071
                1 sort,
3072
                course_category.code user_course_cat,
3073
                access_start_date,
3074
                access_end_date,
3075
                session.id as session_id,
3076
                session.name as session_name,
3077
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
3078
            FROM $tbl_session_course_user as session_course_user
3079
            INNER JOIN $tbl_course AS course
3080
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
3081
            LEFT JOIN $tblCourseCategory course_category ON course.category_id = course_category.id
3082
            INNER JOIN $tbl_session as session
3083
            ON session_course_user.session_id = session.id
3084
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
3085
            WHERE session_course_user.user_id = $user_id
3086
            ORDER BY i";
3087
3088
            $course_list_sql_result = Database::query($sql);
3089
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3090
                $result_row['course_info'] = api_get_course_info($result_row['code']);
3091
                $key = $result_row['session_id'].' - '.$result_row['code'];
3092
                if (!isset($personal_course_list[$key])) {
3093
                    $personal_course_list[$key] = $result_row;
3094
                }
3095
            }
3096
        }
3097
3098
        return $personal_course_list;
3099
    }
3100
3101
    /**
3102
     * Gives a list of courses for the given user in the given session.
3103
     *
3104
     * @param int $user_id
3105
     * @param int $session_id
3106
     *
3107
     * @return array list of statuses (session_id-course_code => status)
3108
     */
3109
    public static function get_courses_list_by_session($user_id, $session_id)
3110
    {
3111
        // Database Table Definitions
3112
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3113
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
3114
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3115
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3116
3117
        $user_id = (int) $user_id;
3118
        $session_id = (int) $session_id;
3119
        // We filter the courses from the URL
3120
        $join_access_url = $where_access_url = '';
3121
        if (api_get_multiple_access_url()) {
3122
            $urlId = api_get_current_access_url_id();
3123
            if (-1 != $urlId) {
3124
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3125
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
3126
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
3127
            }
3128
        }
3129
3130
        /* This query is very similar to the query below, but it will check the
3131
        session_rel_course_user table if there are courses registered
3132
        to our user or not */
3133
        $sql = "SELECT DISTINCT
3134
                    c.title,
3135
                    c.visibility,
3136
                    c.id as real_id,
3137
                    c.code as course_code,
3138
                    sc.position,
3139
                    c.unsubscribe
3140
                FROM $tbl_session_course_user as scu
3141
                INNER JOIN $tbl_session_course sc
3142
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3143
                INNER JOIN $tableCourse as c
3144
                ON (scu.c_id = c.id)
3145
                $join_access_url
3146
                WHERE
3147
                    scu.user_id = $user_id AND
3148
                    scu.session_id = $session_id
3149
                    $where_access_url
3150
                ORDER BY sc.position ASC";
3151
3152
        $myCourseList = [];
3153
        $courses = [];
3154
        $result = Database::query($sql);
3155
        if (Database::num_rows($result) > 0) {
3156
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3157
                $result_row['status'] = 5;
3158
                if (!in_array($result_row['real_id'], $courses)) {
3159
                    $position = $result_row['position'];
3160
                    if (!isset($myCourseList[$position])) {
3161
                        $myCourseList[$position] = $result_row;
3162
                    } else {
3163
                        $myCourseList[] = $result_row;
3164
                    }
3165
                    $courses[] = $result_row['real_id'];
3166
                }
3167
            }
3168
        }
3169
3170
        if (api_is_allowed_to_create_course()) {
3171
            $sql = "SELECT DISTINCT
3172
                        c.title,
3173
                        c.visibility,
3174
                        c.id as real_id,
3175
                        c.code as course_code,
3176
                        sc.position,
3177
                        c.unsubscribe
3178
                    FROM $tbl_session_course_user as scu
3179
                    INNER JOIN $tbl_session as s
3180
                    ON (scu.session_id = s.id)
3181
                    INNER JOIN $tbl_session_course sc
3182
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3183
                    INNER JOIN $tableCourse as c
3184
                    ON (scu.c_id = c.id)
3185
                    $join_access_url
3186
                    WHERE
3187
                      s.id = $session_id AND
3188
                      (
3189
                        (scu.user_id = $user_id AND scu.status = 2) OR
3190
                        s.id_coach = $user_id
3191
                      )
3192
                    $where_access_url
3193
                    ORDER BY sc.position ASC";
3194
            $result = Database::query($sql);
3195
3196
            if (Database::num_rows($result) > 0) {
3197
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3198
                    $result_row['status'] = 2;
3199
                    if (!in_array($result_row['real_id'], $courses)) {
3200
                        $position = $result_row['position'];
3201
                        if (!isset($myCourseList[$position])) {
3202
                            $myCourseList[$position] = $result_row;
3203
                        } else {
3204
                            $myCourseList[] = $result_row;
3205
                        }
3206
                        $courses[] = $result_row['real_id'];
3207
                    }
3208
                }
3209
            }
3210
        }
3211
3212
        if (api_is_drh()) {
3213
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3214
            $sessionList = array_keys($sessionList);
3215
            if (in_array($session_id, $sessionList)) {
3216
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3217
                if (!empty($courseList)) {
3218
                    foreach ($courseList as $course) {
3219
                        if (!in_array($course['id'], $courses)) {
3220
                            $position = $course['position'];
3221
                            if (!isset($myCourseList[$position])) {
3222
                                $myCourseList[$position] = $course;
3223
                            } else {
3224
                                $myCourseList[] = $course;
3225
                            }
3226
                        }
3227
                    }
3228
                }
3229
            }
3230
        } else {
3231
            //check if user is general coach for this session
3232
            $sessionInfo = api_get_session_info($session_id);
3233
            if ($sessionInfo['id_coach'] == $user_id) {
3234
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3235
                if (!empty($courseList)) {
3236
                    foreach ($courseList as $course) {
3237
                        if (!in_array($course['id'], $courses)) {
3238
                            $position = $course['position'];
3239
                            if (!isset($myCourseList[$position])) {
3240
                                $myCourseList[$position] = $course;
3241
                            } else {
3242
                                $myCourseList[] = $course;
3243
                            }
3244
                        }
3245
                    }
3246
                }
3247
            }
3248
        }
3249
3250
        if (!empty($myCourseList)) {
3251
            ksort($myCourseList);
3252
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
3253
            if (empty($checkPosition)) {
3254
                // The session course list doesn't have any position,
3255
                // then order the course list by course code
3256
                $list = array_column($myCourseList, 'course_code');
3257
                array_multisort($myCourseList, SORT_ASC, $list);
0 ignored issues
show
Bug introduced by
SORT_ASC cannot be passed to array_multisort() as the parameter $rest expects a reference. ( Ignorable by Annotation )

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

3257
                array_multisort($myCourseList, /** @scrutinizer ignore-type */ SORT_ASC, $list);
Loading history...
3258
            }
3259
        }
3260
3261
        return $myCourseList;
3262
    }
3263
3264
    /**
3265
     * Get user id from a username.
3266
     *
3267
     * @param string $username
3268
     *
3269
     * @return int User ID (or false if not found)
3270
     */
3271
    public static function get_user_id_from_username($username)
3272
    {
3273
        if (empty($username)) {
3274
            return false;
3275
        }
3276
        $username = trim($username);
3277
        $username = Database::escape_string($username);
3278
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3279
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3280
        $res = Database::query($sql);
3281
3282
        if (false === $res) {
3283
            return false;
3284
        }
3285
        if (1 !== Database::num_rows($res)) {
3286
            return false;
3287
        }
3288
        $row = Database::fetch_array($res);
3289
3290
        return $row['id'];
3291
    }
3292
3293
    /**
3294
     * Get the users files upload from his share_folder.
3295
     *
3296
     * @param string $user_id      User ID
3297
     * @param string $course       course directory
3298
     * @param string $resourceType resource type: images, all
3299
     *
3300
     * @return string
3301
     */
3302
    public static function get_user_upload_files_by_course(
3303
        $user_id,
3304
        $course,
3305
        $resourceType = 'all'
3306
    ) {
3307
        $return = '';
3308
        $user_id = (int) $user_id;
3309
3310
        if (!empty($user_id) && !empty($course)) {
3311
            $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...
3312
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3313
            $file_list = [];
3314
3315
            if (is_dir($path)) {
3316
                $handle = opendir($path);
3317
                while ($file = readdir($handle)) {
3318
                    if ('.' == $file || '..' == $file || '.htaccess' == $file || is_dir($path.$file)) {
3319
                        continue; // skip current/parent directory and .htaccess
3320
                    }
3321
                    $file_list[] = $file;
3322
                }
3323
                if (count($file_list) > 0) {
3324
                    $return = "<h4>$course</h4>";
3325
                    $return .= '<ul class="thumbnails">';
3326
                }
3327
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
3328
                foreach ($file_list as $file) {
3329
                    if ('all' == $resourceType) {
3330
                        $return .= '<li>
3331
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
3332
                    } elseif ('images' == $resourceType) {
3333
                        //get extension
3334
                        $ext = explode('.', $file);
3335
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
3336
                            $return .= '<li class="span2">
3337
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
3338
                                                <img src="'.$web_path.urlencode($file).'" >
3339
                                            </a>
3340
                                        </li>';
3341
                        }
3342
                    }
3343
                }
3344
                if (count($file_list) > 0) {
3345
                    $return .= '</ul>';
3346
                }
3347
            }
3348
        }
3349
3350
        return $return;
3351
    }
3352
3353
    /**
3354
     * Gets the API key (or keys) and return them into an array.
3355
     *
3356
     * @param int     Optional user id (defaults to the result of api_get_user_id())
3357
     * @param string $api_service
3358
     *
3359
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
3360
     */
3361
    public static function get_api_keys($user_id = null, $api_service = 'dokeos')
3362
    {
3363
        if ($user_id != strval(intval($user_id))) {
3364
            return false;
3365
        }
3366
        if (empty($user_id)) {
3367
            $user_id = api_get_user_id();
3368
        }
3369
        if (false === $user_id) {
3370
            return false;
3371
        }
3372
        $service_name = Database::escape_string($api_service);
3373
        if (false === is_string($service_name)) {
3374
            return false;
3375
        }
3376
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3377
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
3378
        $res = Database::query($sql);
3379
        if (false === $res) {
3380
            return false;
3381
        } //error during query
3382
        $num = Database::num_rows($res);
3383
        if (0 == $num) {
3384
            return false;
3385
        }
3386
        $list = [];
3387
        while ($row = Database::fetch_array($res)) {
3388
            $list[$row['id']] = $row['api_key'];
3389
        }
3390
3391
        return $list;
3392
    }
3393
3394
    /**
3395
     * Adds a new API key to the users' account.
3396
     *
3397
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
3398
     * @param string $api_service
3399
     *
3400
     * @return bool True on success, false on failure
3401
     */
3402
    public static function add_api_key($user_id = null, $api_service = 'dokeos')
3403
    {
3404
        if ($user_id != strval(intval($user_id))) {
3405
            return false;
3406
        }
3407
        if (empty($user_id)) {
3408
            $user_id = api_get_user_id();
3409
        }
3410
        if (false === $user_id) {
3411
            return false;
3412
        }
3413
        $service_name = Database::escape_string($api_service);
3414
        if (false === is_string($service_name)) {
3415
            return false;
3416
        }
3417
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3418
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
3419
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
3420
        $res = Database::query($sql);
3421
        if (false === $res) {
3422
            return false;
3423
        } //error during query
3424
        $num = Database::insert_id();
3425
3426
        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...
3427
    }
3428
3429
    /**
3430
     * Deletes an API key from the user's account.
3431
     *
3432
     * @param   int     API key's internal ID
3433
     *
3434
     * @return bool True on success, false on failure
3435
     */
3436
    public static function delete_api_key($key_id)
3437
    {
3438
        if ($key_id != strval(intval($key_id))) {
3439
            return false;
3440
        }
3441
        if (false === $key_id) {
3442
            return false;
3443
        }
3444
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3445
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
3446
        $res = Database::query($sql);
3447
        if (false === $res) {
3448
            return false;
3449
        } //error during query
3450
        $num = Database::num_rows($res);
3451
        if (1 !== $num) {
3452
            return false;
3453
        }
3454
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
3455
        $res = Database::query($sql);
3456
        if (false === $res) {
3457
            return false;
3458
        } //error during query
3459
3460
        return true;
3461
    }
3462
3463
    /**
3464
     * Regenerate an API key from the user's account.
3465
     *
3466
     * @param   int     user ID (defaults to the results of api_get_user_id())
3467
     * @param   string  API key's internal ID
3468
     *
3469
     * @return int num
3470
     */
3471
    public static function update_api_key($user_id, $api_service)
3472
    {
3473
        if ($user_id != strval(intval($user_id))) {
3474
            return false;
3475
        }
3476
        if (false === $user_id) {
3477
            return false;
3478
        }
3479
        $service_name = Database::escape_string($api_service);
3480
        if (false === is_string($service_name)) {
3481
            return false;
3482
        }
3483
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3484
        $sql = "SELECT id FROM $t_api
3485
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3486
        $res = Database::query($sql);
3487
        $num = Database::num_rows($res);
3488
        if (1 == $num) {
3489
            $id_key = Database::fetch_array($res, 'ASSOC');
3490
            self::delete_api_key($id_key['id']);
3491
            $num = self::add_api_key($user_id, $api_service);
3492
        } elseif (0 == $num) {
3493
            $num = self::add_api_key($user_id, $api_service);
3494
        }
3495
3496
        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...
3497
    }
3498
3499
    /**
3500
     * @param   int     user ID (defaults to the results of api_get_user_id())
3501
     * @param   string    API key's internal ID
3502
     *
3503
     * @return int row ID, or return false if not found
3504
     */
3505
    public static function get_api_key_id($user_id, $api_service)
3506
    {
3507
        if ($user_id != strval(intval($user_id))) {
3508
            return false;
3509
        }
3510
        if (false === $user_id) {
3511
            return false;
3512
        }
3513
        if (empty($api_service)) {
3514
            return false;
3515
        }
3516
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3517
        $api_service = Database::escape_string($api_service);
3518
        $sql = "SELECT id FROM $t_api
3519
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3520
        $res = Database::query($sql);
3521
        if (Database::num_rows($res) < 1) {
3522
            return false;
3523
        }
3524
        $row = Database::fetch_array($res, 'ASSOC');
3525
3526
        return $row['id'];
3527
    }
3528
3529
    /**
3530
     * Checks if a user_id is platform admin.
3531
     *
3532
     * @param   int user ID
3533
     *
3534
     * @return bool True if is admin, false otherwise
3535
     *
3536
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
3537
     */
3538
    public static function is_admin($user_id)
3539
    {
3540
        $user_id = (int) $user_id;
3541
        if (empty($user_id)) {
3542
            return false;
3543
        }
3544
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3545
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3546
        $res = Database::query($sql);
3547
3548
        return 1 === Database::num_rows($res);
3549
    }
3550
3551
    /**
3552
     * Get the total count of users.
3553
     *
3554
     * @param int $status        Status of users to be counted
3555
     * @param int $access_url_id Access URL ID (optional)
3556
     * @param int $active
3557
     *
3558
     * @return mixed Number of users or false on error
3559
     */
3560
    public static function get_number_of_users($status = 0, $access_url_id = 1, $active = null)
3561
    {
3562
        $t_u = Database::get_main_table(TABLE_MAIN_USER);
3563
        $t_a = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3564
3565
        if (api_is_multiple_url_enabled()) {
3566
            $sql = "SELECT count(u.id)
3567
                    FROM $t_u u
3568
                    INNER JOIN $t_a url_user
3569
                    ON (u.id = url_user.user_id)
3570
                    WHERE url_user.access_url_id = $access_url_id
3571
            ";
3572
        } else {
3573
            $sql = "SELECT count(u.id)
3574
                    FROM $t_u u
3575
                    WHERE 1 = 1 ";
3576
        }
3577
3578
        if (is_int($status) && $status > 0) {
3579
            $status = (int) $status;
3580
            $sql .= " AND u.status = $status ";
3581
        }
3582
3583
        if (null !== $active) {
3584
            $active = (int) $active;
3585
            $sql .= " AND u.active = $active ";
3586
        }
3587
3588
        $res = Database::query($sql);
3589
        if (1 === Database::num_rows($res)) {
3590
            return (int) Database::result($res, 0, 0);
3591
        }
3592
3593
        return false;
3594
    }
3595
3596
    /**
3597
     * Gets the tags of a specific field_id
3598
     * USER TAGS.
3599
     *
3600
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
3601
     *
3602
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
3603
     *    Called it "books" for example.
3604
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
3605
     * 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
3606
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
3607
     * 5. Test and enjoy.
3608
     *
3609
     * @param string $tag
3610
     * @param int    $field_id      field_id
3611
     * @param string $return_format how we are going to result value in array or in a string (json)
3612
     * @param $limit
3613
     *
3614
     * @return mixed
3615
     */
3616
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
3617
    {
3618
        // database table definition
3619
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3620
        $field_id = (int) $field_id;
3621
        $limit = (int) $limit;
3622
        $tag = trim(Database::escape_string($tag));
3623
3624
        // all the information of the field
3625
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
3626
                WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag LIMIT $limit";
3627
        $result = Database::query($sql);
3628
        $return = [];
3629
        if (Database::num_rows($result) > 0) {
3630
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3631
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
3632
            }
3633
        }
3634
        if ('json' === $return_format) {
3635
            $return = json_encode($return);
3636
        }
3637
3638
        return $return;
3639
    }
3640
3641
    /**
3642
     * @param int $field_id
3643
     * @param int $limit
3644
     *
3645
     * @return array
3646
     */
3647
    public static function get_top_tags($field_id, $limit = 100)
3648
    {
3649
        // database table definition
3650
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3651
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3652
        $field_id = (int) $field_id;
3653
        $limit = (int) $limit;
3654
        // all the information of the field
3655
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
3656
                INNER JOIN $table_user_tag ut
3657
                ON (ut.id = uv.tag_id)
3658
                WHERE field_id = $field_id
3659
                GROUP BY tag_id
3660
                ORDER BY count DESC
3661
                LIMIT $limit";
3662
        $result = Database::query($sql);
3663
        $return = [];
3664
        if (Database::num_rows($result) > 0) {
3665
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3666
                $return[] = $row;
3667
            }
3668
        }
3669
3670
        return $return;
3671
    }
3672
3673
    /**
3674
     * Get user's tags.
3675
     *
3676
     * @param int $user_id
3677
     * @param int $field_id
3678
     *
3679
     * @return array
3680
     */
3681
    public static function get_user_tags($user_id, $field_id)
3682
    {
3683
        // database table definition
3684
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3685
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3686
        $field_id = (int) $field_id;
3687
        $user_id = (int) $user_id;
3688
3689
        // all the information of the field
3690
        $sql = "SELECT ut.id, tag, count
3691
                FROM $table_user_tag ut
3692
                INNER JOIN $table_user_tag_values uv
3693
                ON (uv.tag_id=ut.ID)
3694
                WHERE field_id = $field_id AND user_id = $user_id
3695
                ORDER BY tag";
3696
        $result = Database::query($sql);
3697
        $return = [];
3698
        if (Database::num_rows($result) > 0) {
3699
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3700
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3701
            }
3702
        }
3703
3704
        return $return;
3705
    }
3706
3707
    /**
3708
     * Get user's tags.
3709
     *
3710
     * @param int  $user_id
3711
     * @param int  $field_id
3712
     * @param bool $show_links show links or not
3713
     *
3714
     * @return string
3715
     */
3716
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
3717
    {
3718
        // database table definition
3719
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3720
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3721
        $field_id = (int) $field_id;
3722
        $user_id = (int) $user_id;
3723
3724
        // all the information of the field
3725
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
3726
                INNER JOIN $table_user_tag_values uv
3727
                ON (uv.tag_id = ut.id)
3728
                WHERE field_id = $field_id AND user_id = $user_id
3729
                ORDER BY tag";
3730
3731
        $result = Database::query($sql);
3732
        $return = [];
3733
        if (Database::num_rows($result) > 0) {
3734
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3735
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3736
            }
3737
        }
3738
        $user_tags = $return;
3739
        $tag_tmp = [];
3740
        foreach ($user_tags as $tag) {
3741
            if ($show_links) {
3742
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
3743
                    $tag['tag'].
3744
                '</a>';
3745
            } else {
3746
                $tag_tmp[] = $tag['tag'];
3747
            }
3748
        }
3749
3750
        if (is_array($user_tags) && count($user_tags) > 0) {
3751
            return implode(', ', $tag_tmp);
3752
        } else {
3753
            return '';
3754
        }
3755
    }
3756
3757
    /**
3758
     * Get the tag id.
3759
     *
3760
     * @param int $tag
3761
     * @param int $field_id
3762
     *
3763
     * @return int returns 0 if fails otherwise the tag id
3764
     */
3765
    public static function get_tag_id($tag, $field_id)
3766
    {
3767
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3768
        $tag = Database::escape_string($tag);
3769
        $field_id = (int) $field_id;
3770
        //with COLLATE latin1_bin to select query in a case sensitive mode
3771
        $sql = "SELECT id FROM $table_user_tag
3772
                WHERE tag LIKE '$tag' AND field_id = $field_id";
3773
        $result = Database::query($sql);
3774
        if (Database::num_rows($result) > 0) {
3775
            $row = Database::fetch_array($result, 'ASSOC');
3776
3777
            return $row['id'];
3778
        } else {
3779
            return 0;
3780
        }
3781
    }
3782
3783
    /**
3784
     * Get the tag id.
3785
     *
3786
     * @param int $tag_id
3787
     * @param int $field_id
3788
     *
3789
     * @return int 0 if fails otherwise the tag id
3790
     */
3791
    public static function get_tag_id_from_id($tag_id, $field_id)
3792
    {
3793
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3794
        $tag_id = (int) $tag_id;
3795
        $field_id = (int) $field_id;
3796
        $sql = "SELECT id FROM $table_user_tag
3797
                WHERE id = '$tag_id' AND field_id = $field_id";
3798
        $result = Database::query($sql);
3799
        if (Database::num_rows($result) > 0) {
3800
            $row = Database::fetch_array($result, 'ASSOC');
3801
3802
            return $row['id'];
3803
        } else {
3804
            return false;
3805
        }
3806
    }
3807
3808
    /**
3809
     * Adds a user-tag value.
3810
     *
3811
     * @param mixed $tag
3812
     * @param int   $user_id
3813
     * @param int   $field_id field id of the tag
3814
     *
3815
     * @return bool True if the tag was inserted or updated. False otherwise.
3816
     *              The return value doesn't take into account *values* added to the tag.
3817
     *              Only the creation/update of the tag field itself.
3818
     */
3819
    public static function add_tag($tag, $user_id, $field_id)
3820
    {
3821
        // database table definition
3822
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3823
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3824
        $tag = trim(Database::escape_string($tag));
3825
        $user_id = (int) $user_id;
3826
        $field_id = (int) $field_id;
3827
3828
        $tag_id = self::get_tag_id($tag, $field_id);
3829
3830
        /* IMPORTANT
3831
         *  @todo we don't create tags with numbers
3832
         *
3833
         */
3834
        if (is_numeric($tag)) {
3835
            //the form is sending an id this means that the user select it from the list so it MUST exists
3836
            /* $new_tag_id = self::get_tag_id_from_id($tag,$field_id);
3837
              if ($new_tag_id !== false) {
3838
              $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $new_tag_id";
3839
              $result = Database::query($sql);
3840
              $last_insert_id = $new_tag_id;
3841
              } else {
3842
              $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
3843
              $result = Database::query($sql);
3844
              $last_insert_id = Database::insert_id();
3845
              } */
3846
        }
3847
3848
        //this is a new tag
3849
        if (0 == $tag_id) {
3850
            //the tag doesn't exist
3851
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
3852
            Database::query($sql);
3853
            $last_insert_id = Database::insert_id();
3854
        } else {
3855
            //the tag exists we update it
3856
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
3857
            Database::query($sql);
3858
            $last_insert_id = $tag_id;
3859
        }
3860
3861
        if (!empty($last_insert_id) && (0 != $last_insert_id)) {
3862
            //we insert the relationship user-tag
3863
            $sql = "SELECT tag_id FROM $table_user_tag_values
3864
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
3865
            $result = Database::query($sql);
3866
            //if the relationship does not exist we create it
3867
            if (0 == Database::num_rows($result)) {
3868
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
3869
                Database::query($sql);
3870
            }
3871
3872
            return true;
3873
        }
3874
3875
        return false;
3876
    }
3877
3878
    /**
3879
     * Deletes an user tag.
3880
     *
3881
     * @param int $user_id
3882
     * @param int $field_id
3883
     */
3884
    public static function delete_user_tags($user_id, $field_id)
3885
    {
3886
        // database table definition
3887
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3888
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3889
        $user_id = (int) $user_id;
3890
3891
        $tags = self::get_user_tags($user_id, $field_id);
3892
        if (is_array($tags) && count($tags) > 0) {
3893
            foreach ($tags as $key => $tag) {
3894
                if ($tag['count'] > '0') {
3895
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
3896
                    Database::query($sql);
3897
                }
3898
                $sql = "DELETE FROM $table_user_tag_values
3899
                        WHERE user_id = $user_id AND tag_id = $key";
3900
                Database::query($sql);
3901
            }
3902
        }
3903
    }
3904
3905
    /**
3906
     * Process the tag list comes from the UserManager::update_extra_field_value() function.
3907
     *
3908
     * @param array $tags     the tag list that will be added
3909
     * @param int   $user_id
3910
     * @param int   $field_id
3911
     *
3912
     * @return bool
3913
     */
3914
    public static function process_tags($tags, $user_id, $field_id)
3915
    {
3916
        // We loop the tags and add it to the DB
3917
        if (is_array($tags)) {
3918
            foreach ($tags as $tag) {
3919
                self::add_tag($tag, $user_id, $field_id);
3920
            }
3921
        } else {
3922
            self::add_tag($tags, $user_id, $field_id);
3923
        }
3924
3925
        return true;
3926
    }
3927
3928
    /**
3929
     * Returns a list of all administrators.
3930
     *
3931
     * @return array
3932
     */
3933
    public static function get_all_administrators()
3934
    {
3935
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3936
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
3937
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3938
        $access_url_id = api_get_current_access_url_id();
3939
        if (api_get_multiple_access_url()) {
3940
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3941
                    FROM $tbl_url_rel_user as url
3942
                    INNER JOIN $table_admin as admin
3943
                    ON (admin.user_id=url.user_id)
3944
                    INNER JOIN $table_user u
3945
                    ON (u.id=admin.user_id)
3946
                    WHERE access_url_id ='".$access_url_id."'";
3947
        } else {
3948
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3949
                    FROM $table_admin as admin
3950
                    INNER JOIN $table_user u
3951
                    ON (u.id=admin.user_id)";
3952
        }
3953
        $result = Database::query($sql);
3954
        $return = [];
3955
        if (Database::num_rows($result) > 0) {
3956
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3957
                $return[$row['user_id']] = $row;
3958
            }
3959
        }
3960
3961
        return $return;
3962
    }
3963
3964
    /**
3965
     * Search an user (tags, first name, last name and email ).
3966
     *
3967
     * @param string $tag
3968
     * @param int    $field_id        field id of the tag
3969
     * @param int    $from            where to start in the query
3970
     * @param int    $number_of_items
3971
     * @param bool   $getCount        get count or not
3972
     *
3973
     * @return array
3974
     */
3975
    public static function get_all_user_tags(
3976
        $tag,
3977
        $field_id = 0,
3978
        $from = 0,
3979
        $number_of_items = 10,
3980
        $getCount = false
3981
    ) {
3982
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
3983
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3984
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3985
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3986
3987
        $field_id = intval($field_id);
3988
        $from = intval($from);
3989
        $number_of_items = intval($number_of_items);
3990
3991
        $where_field = "";
3992
        $where_extra_fields = self::get_search_form_where_extra_fields();
3993
        if (0 != $field_id) {
3994
            $where_field = " field_id = $field_id AND ";
3995
        }
3996
3997
        // all the information of the field
3998
        if ($getCount) {
3999
            $select = "SELECT count(DISTINCT u.id) count";
4000
        } else {
4001
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, tag, picture_uri";
4002
        }
4003
4004
        $sql = " $select
4005
                FROM $user_table u
4006
                INNER JOIN $access_url_rel_user_table url_rel_user
4007
                ON (u.id = url_rel_user.user_id)
4008
                LEFT JOIN $table_user_tag_values uv
4009
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
4010
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
4011
                WHERE
4012
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
4013
                    (
4014
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
4015
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
4016
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
4017
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
4018
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
4019
                     )
4020
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
4021
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
4022
4023
        $keyword_active = true;
4024
        // only active users
4025
        if ($keyword_active) {
4026
            $sql .= " AND u.active='1'";
4027
        }
4028
        // avoid anonymous
4029
        $sql .= " AND u.status <> 6 ";
4030
        $sql .= " ORDER BY username";
4031
        $sql .= " LIMIT $from , $number_of_items";
4032
4033
        $result = Database::query($sql);
4034
        $return = [];
4035
4036
        if (Database::num_rows($result) > 0) {
4037
            if ($getCount) {
4038
                $row = Database::fetch_array($result, 'ASSOC');
4039
4040
                return $row['count'];
4041
            }
4042
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4043
                $return[$row['id']] = $row;
4044
            }
4045
        }
4046
4047
        return $return;
4048
    }
4049
4050
    /**
4051
     * Get extra filterable user fields (only type select).
4052
     *
4053
     * @return array Array of extra fields as [int => ['name' => ..., 'variable' => ..., 'data' => ...]] (
4054
     *               or empty array if no extra field)
4055
     */
4056
    public static function getExtraFilterableFields()
4057
    {
4058
        $extraFieldList = self::get_extra_fields();
4059
        $fields = [];
4060
        if (is_array($extraFieldList)) {
4061
            foreach ($extraFieldList as $extraField) {
4062
                // If is enabled to filter and is a "<select>" field type
4063
                if (1 == $extraField[8] && 4 == $extraField[2]) {
4064
                    $fields[] = [
4065
                        'name' => $extraField[3],
4066
                        'variable' => $extraField[1],
4067
                        'data' => $extraField[9],
4068
                    ];
4069
                }
4070
            }
4071
        }
4072
4073
        return $fields;
4074
    }
4075
4076
    /**
4077
     * Get extra where clauses for finding users based on extra filterable user fields (type select).
4078
     *
4079
     * @return string With AND clauses based on user's ID which have the values to search in extra user fields
4080
     *                (or empty if no extra field exists)
4081
     */
4082
    public static function get_search_form_where_extra_fields()
4083
    {
4084
        $useExtraFields = false;
4085
        $extraFields = self::getExtraFilterableFields();
4086
        $extraFieldResult = [];
4087
        if (is_array($extraFields) && count($extraFields) > 0) {
4088
            foreach ($extraFields as $extraField) {
4089
                $varName = 'field_'.$extraField['variable'];
4090
                if (self::is_extra_field_available($extraField['variable'])) {
4091
                    if (isset($_GET[$varName]) && '0' != $_GET[$varName]) {
4092
                        $useExtraFields = true;
4093
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
4094
                            $extraField['variable'],
4095
                            $_GET[$varName]
4096
                        );
4097
                    }
4098
                }
4099
            }
4100
        }
4101
4102
        if ($useExtraFields) {
4103
            $finalResult = [];
4104
            if (count($extraFieldResult) > 1) {
4105
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
4106
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
4107
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
4108
                    }
4109
                }
4110
            } else {
4111
                $finalResult = $extraFieldResult[0];
4112
            }
4113
4114
            if (is_array($finalResult) && count($finalResult) > 0) {
4115
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
4116
            } else {
4117
                //no results
4118
                $whereFilter = " AND u.id  = -1 ";
4119
            }
4120
4121
            return $whereFilter;
4122
        }
4123
4124
        return '';
4125
    }
4126
4127
    /**
4128
     * Show the search form.
4129
     *
4130
     * @param string $query the value of the search box
4131
     *
4132
     * @throws Exception
4133
     *
4134
     * @return string HTML form
4135
     */
4136
    public static function get_search_form($query, $defaultParams = [])
4137
    {
4138
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
4139
        $form = new FormValidator(
4140
            'search_user',
4141
            'get',
4142
            api_get_path(WEB_PATH).'main/social/search.php',
4143
            '',
4144
            [],
4145
            FormValidator::LAYOUT_HORIZONTAL
4146
        );
4147
4148
        $query = Security::remove_XSS($query);
4149
4150
        if (!empty($query)) {
4151
            $form->addHeader(get_lang('Results and feedback').' "'.$query.'"');
4152
        }
4153
4154
        $form->addText(
4155
            'q',
4156
            get_lang('Users, Groups'),
4157
            false,
4158
            [
4159
                'id' => 'q',
4160
            ]
4161
        );
4162
        $options = [
4163
            0 => get_lang('Select'),
4164
            1 => get_lang('User'),
4165
            2 => get_lang('Group'),
4166
        ];
4167
        $form->addSelect(
4168
            'search_type',
4169
            get_lang('Type'),
4170
            $options,
4171
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
4172
        );
4173
4174
        // Extra fields
4175
        $extraFields = self::getExtraFilterableFields();
4176
        $defaults = [];
4177
        if (is_array($extraFields) && count($extraFields) > 0) {
4178
            foreach ($extraFields as $extraField) {
4179
                $varName = 'field_'.$extraField['variable'];
4180
                $options = [
4181
                    0 => get_lang('Select'),
4182
                ];
4183
                foreach ($extraField['data'] as $option) {
4184
                    if (isset($_GET[$varName])) {
4185
                        if ($_GET[$varName] == $option[1]) {
4186
                            $defaults[$option[1]] = true;
4187
                        }
4188
                    }
4189
4190
                    $options[$option[1]] = $option[1];
4191
                }
4192
                $form->addSelect($varName, $extraField['name'], $options);
4193
            }
4194
        }
4195
4196
        $defaults['search_type'] = (int) $searchType;
4197
        $defaults['q'] = $query;
4198
4199
        if (!empty($defaultParams)) {
4200
            $defaults = array_merge($defaults, $defaultParams);
4201
        }
4202
        $form->setDefaults($defaults);
4203
        $form->addButtonSearch(get_lang('Search'));
4204
4205
        $js = '<script>
4206
        extra_field_toogle();
4207
        function extra_field_toogle() {
4208
            if (jQuery("select[name=search_type]").val() != "1") {
4209
                jQuery(".extra_field").hide();
4210
            } else {
4211
                jQuery(".extra_field").show();
4212
            }
4213
        }
4214
        </script>';
4215
4216
        return $js.$form->returnForm();
4217
    }
4218
4219
    /**
4220
     * Shows the user menu.
4221
     */
4222
    public static function show_menu()
4223
    {
4224
        echo '<div class="actions">';
4225
        echo '<a href="/main/auth/profile.php">'.
4226
            Display::return_icon('profile.png').' '.get_lang('Profile').'</a>';
4227
        echo '<a href="/main/messages/inbox.php">'.
4228
            Display::return_icon('inbox.png').' '.get_lang('Inbox').'</a>';
4229
        echo '<a href="/main/messages/outbox.php">'.
4230
            Display::return_icon('outbox.png').' '.get_lang('Outbox').'</a>';
4231
        echo '<span style="float:right; padding-top:7px;">'.
4232
        '<a href="/main/auth/profile.php?show=1">'.
4233
            Display::return_icon('edit.gif').' '.get_lang('Configuration').'</a>';
4234
        echo '</span>';
4235
        echo '</div>';
4236
    }
4237
4238
    /**
4239
     * Allow to register contact to social network.
4240
     *
4241
     * @param int $friend_id     user friend id
4242
     * @param int $my_user_id    user id
4243
     * @param int $relation_type relation between users see constants definition
4244
     *
4245
     * @return bool
4246
     */
4247
    public static function relate_users($friend_id, $my_user_id, $relation_type)
4248
    {
4249
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4250
4251
        $friend_id = (int) $friend_id;
4252
        $my_user_id = (int) $my_user_id;
4253
        $relation_type = (int) $relation_type;
4254
4255
        $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
4256
                WHERE
4257
                    friend_user_id='.$friend_id.' AND
4258
                    user_id='.$my_user_id.' AND
4259
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
4260
        $result = Database::query($sql);
4261
        $row = Database::fetch_array($result, 'ASSOC');
4262
        $current_date = api_get_utc_datetime();
4263
4264
        if (0 == $row['count']) {
4265
            $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
4266
                    VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
4267
            Database::query($sql);
4268
4269
            return true;
4270
        }
4271
4272
        $sql = 'SELECT COUNT(*) as count, relation_type  FROM '.$tbl_my_friend.'
4273
                WHERE
4274
                    friend_user_id='.$friend_id.' AND
4275
                    user_id='.$my_user_id.' AND
4276
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
4277
        $result = Database::query($sql);
4278
        $row = Database::fetch_array($result, 'ASSOC');
4279
4280
        if (1 == $row['count']) {
4281
            //only for the case of a RRHH or a Student BOSS
4282
            if ($row['relation_type'] != $relation_type &&
4283
                (USER_RELATION_TYPE_RRHH == $relation_type || USER_RELATION_TYPE_BOSS == $relation_type)
4284
            ) {
4285
                $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
4286
                        VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
4287
            } else {
4288
                $sql = 'UPDATE '.$tbl_my_friend.' SET relation_type='.$relation_type.'
4289
                        WHERE friend_user_id='.$friend_id.' AND user_id='.$my_user_id;
4290
            }
4291
            Database::query($sql);
4292
4293
            return true;
4294
        }
4295
4296
        return false;
4297
    }
4298
4299
    /**
4300
     * Deletes a contact.
4301
     *
4302
     * @param bool   $friend_id
4303
     * @param bool   $real_removed          true will delete ALL friends relationship
4304
     * @param string $with_status_condition
4305
     *
4306
     * @author isaac flores paz <[email protected]>
4307
     * @author Julio Montoya <[email protected]> Cleaning code
4308
     */
4309
    public static function remove_user_rel_user(
4310
        $friend_id,
4311
        $real_removed = false,
4312
        $with_status_condition = ''
4313
    ) {
4314
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4315
        $tbl_my_message = Database::get_main_table(TABLE_MESSAGE);
4316
        $friend_id = (int) $friend_id;
4317
        $user_id = api_get_user_id();
4318
4319
        if ($real_removed) {
4320
            $extra_condition = '';
4321
            if ('' != $with_status_condition) {
4322
                $extra_condition = ' AND relation_type = '.intval($with_status_condition);
4323
            }
4324
            $sql = 'DELETE FROM '.$tbl_my_friend.'
4325
                    WHERE
4326
                        relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
4327
                        friend_user_id='.$friend_id.' '.$extra_condition;
4328
            Database::query($sql);
4329
            $sql = 'DELETE FROM '.$tbl_my_friend.'
4330
                   WHERE
4331
                    relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
4332
                    user_id='.$friend_id.' '.$extra_condition;
4333
            Database::query($sql);
4334
        } else {
4335
            $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
4336
                    WHERE
4337
                        user_id='.$user_id.' AND
4338
                        relation_type NOT IN('.USER_RELATION_TYPE_DELETED.', '.USER_RELATION_TYPE_RRHH.') AND
4339
                        friend_user_id='.$friend_id;
4340
            $result = Database::query($sql);
4341
            $row = Database::fetch_array($result, 'ASSOC');
4342
            if (1 == $row['count']) {
4343
                //Delete user rel user
4344
                $sql_i = 'UPDATE '.$tbl_my_friend.' SET relation_type='.USER_RELATION_TYPE_DELETED.'
4345
                          WHERE user_id='.$user_id.' AND friend_user_id='.$friend_id;
4346
4347
                $sql_j = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
4348
                          WHERE
4349
                                user_receiver_id='.$user_id.' AND
4350
                                user_sender_id='.$friend_id.' AND update_date="0000-00-00 00:00:00" ';
4351
                // Delete user
4352
                $sql_ij = 'UPDATE '.$tbl_my_friend.'  SET relation_type='.USER_RELATION_TYPE_DELETED.'
4353
                           WHERE user_id='.$friend_id.' AND friend_user_id='.$user_id;
4354
                $sql_ji = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
4355
                           WHERE
4356
                                user_receiver_id='.$friend_id.' AND
4357
                                user_sender_id='.$user_id.' AND
4358
                                update_date="0000-00-00 00:00:00" ';
4359
                Database::query($sql_i);
4360
                Database::query($sql_j);
4361
                Database::query($sql_ij);
4362
                Database::query($sql_ji);
4363
            }
4364
        }
4365
4366
        // Delete accepted invitations
4367
        $sql = "DELETE FROM $tbl_my_message
4368
                WHERE
4369
                    msg_status = ".MESSAGE_STATUS_INVITATION_ACCEPTED." AND
4370
                    (
4371
                        user_receiver_id = $user_id AND
4372
                        user_sender_id = $friend_id
4373
                    ) OR
4374
                    (
4375
                        user_sender_id = $user_id AND
4376
                        user_receiver_id = $friend_id
4377
                    )
4378
        ";
4379
        Database::query($sql);
4380
    }
4381
4382
    /**
4383
     * @param int $userId
4384
     *
4385
     * @return array
4386
     */
4387
    public static function getDrhListFromUser($userId)
4388
    {
4389
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4390
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4391
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4392
        $userId = (int) $userId;
4393
4394
        $orderBy = null;
4395
        if (api_is_western_name_order()) {
4396
            $orderBy .= ' ORDER BY firstname, lastname ';
4397
        } else {
4398
            $orderBy .= ' ORDER BY lastname, firstname ';
4399
        }
4400
4401
        $sql = "SELECT u.id, username, u.firstname, u.lastname
4402
                FROM $tblUser u
4403
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
4404
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
4405
                WHERE
4406
                    access_url_id = ".api_get_current_access_url_id()." AND
4407
                    uru.user_id = '$userId' AND
4408
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4409
                $orderBy
4410
                ";
4411
        $result = Database::query($sql);
4412
4413
        return Database::store_result($result);
4414
    }
4415
4416
    /**
4417
     * get users followed by human resource manager.
4418
     *
4419
     * @param int    $userId
4420
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
4421
     * @param bool   $getOnlyUserId
4422
     * @param bool   $getSql
4423
     * @param bool   $getCount
4424
     * @param int    $from
4425
     * @param int    $numberItems
4426
     * @param int    $column
4427
     * @param string $direction
4428
     * @param int    $active
4429
     * @param string $lastConnectionDate
4430
     *
4431
     * @return array users
4432
     */
4433
    public static function get_users_followed_by_drh(
4434
        $userId,
4435
        $userStatus = 0,
4436
        $getOnlyUserId = false,
4437
        $getSql = false,
4438
        $getCount = false,
4439
        $from = null,
4440
        $numberItems = null,
4441
        $column = null,
4442
        $direction = null,
4443
        $active = null,
4444
        $lastConnectionDate = null
4445
    ) {
4446
        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...
4447
            $userId,
4448
            $userStatus,
4449
            $getOnlyUserId,
4450
            $getSql,
4451
            $getCount,
4452
            $from,
4453
            $numberItems,
4454
            $column,
4455
            $direction,
4456
            $active,
4457
            $lastConnectionDate,
4458
            DRH
4459
        );
4460
    }
4461
4462
    /**
4463
     * Get users followed by human resource manager.
4464
     *
4465
     * @param int    $userId
4466
     * @param int    $userStatus         Filter users by status (STUDENT, COURSEMANAGER, etc)
4467
     * @param bool   $getOnlyUserId
4468
     * @param bool   $getSql
4469
     * @param bool   $getCount
4470
     * @param int    $from
4471
     * @param int    $numberItems
4472
     * @param int    $column
4473
     * @param string $direction
4474
     * @param int    $active
4475
     * @param string $lastConnectionDate
4476
     * @param int    $status             the function is called by who? COURSEMANAGER, DRH?
4477
     * @param string $keyword
4478
     *
4479
     * @return mixed Users list (array) or the SQL query if $getSQL was set to true
4480
     */
4481
    public static function getUsersFollowedByUser(
4482
        $userId,
4483
        $userStatus = null,
4484
        $getOnlyUserId = false,
4485
        $getSql = false,
4486
        $getCount = false,
4487
        $from = null,
4488
        $numberItems = null,
4489
        $column = null,
4490
        $direction = null,
4491
        $active = null,
4492
        $lastConnectionDate = null,
4493
        $status = null,
4494
        $keyword = null
4495
    ) {
4496
        // Database Table Definitions
4497
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4498
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4499
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4500
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4501
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4502
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4503
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4504
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4505
4506
        $userId = (int) $userId;
4507
        $limitCondition = '';
4508
4509
        if (isset($from) && isset($numberItems)) {
4510
            $from = (int) $from;
4511
            $numberItems = (int) $numberItems;
4512
            $limitCondition = "LIMIT $from, $numberItems";
4513
        }
4514
4515
        $column = Database::escape_string($column);
4516
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
4517
4518
        $userConditions = '';
4519
        if (!empty($userStatus)) {
4520
            $userConditions .= ' AND u.status = '.intval($userStatus);
4521
        }
4522
4523
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
4524
        if ($getOnlyUserId) {
4525
            $select = " SELECT DISTINCT u.id user_id";
4526
        }
4527
4528
        $masterSelect = "SELECT DISTINCT * FROM ";
4529
4530
        if ($getCount) {
4531
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
4532
            $select = " SELECT DISTINCT(u.id) user_id";
4533
        }
4534
4535
        if (!is_null($active)) {
4536
            $active = intval($active);
4537
            $userConditions .= " AND u.active = $active ";
4538
        }
4539
4540
        if (!empty($keyword)) {
4541
            $keyword = Database::escape_string($keyword);
4542
            $userConditions .= " AND (
4543
                u.username LIKE '%$keyword%' OR
4544
                u.firstname LIKE '%$keyword%' OR
4545
                u.lastname LIKE '%$keyword%' OR
4546
                u.official_code LIKE '%$keyword%' OR
4547
                u.email LIKE '%$keyword%'
4548
            )";
4549
        }
4550
4551
        if (!empty($lastConnectionDate)) {
4552
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
4553
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
4554
        }
4555
4556
        $sessionConditionsCoach = null;
4557
        $sessionConditionsTeacher = null;
4558
        $drhConditions = null;
4559
        $teacherSelect = null;
4560
4561
        switch ($status) {
4562
            case DRH:
4563
                $drhConditions .= " AND
4564
                    friend_user_id = '$userId' AND
4565
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4566
                ";
4567
                break;
4568
            case COURSEMANAGER:
4569
                $drhConditions .= " AND
4570
                    friend_user_id = '$userId' AND
4571
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4572
                ";
4573
4574
                $sessionConditionsCoach .= " AND
4575
                    (s.id_coach = '$userId')
4576
                ";
4577
4578
                $sessionConditionsTeacher .= " AND
4579
                    (scu.status = 2 AND scu.user_id = '$userId')
4580
                ";
4581
4582
                $teacherSelect =
4583
                "UNION ALL (
4584
                        $select
4585
                        FROM $tbl_user u
4586
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
4587
                        WHERE
4588
                            (
4589
                                sru.session_id IN (
4590
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
4591
                                    $tbl_session_rel_access_url session_rel_access_rel_user
4592
                                    ON session_rel_access_rel_user.session_id = s.id
4593
                                    WHERE access_url_id = ".api_get_current_access_url_id()."
4594
                                    $sessionConditionsCoach
4595
                                ) OR sru.session_id IN (
4596
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
4597
                                    INNER JOIN $tbl_session_rel_access_url url
4598
                                    ON (url.session_id = s.id)
4599
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
4600
                                    ON (scu.session_id = s.id)
4601
                                    WHERE access_url_id = ".api_get_current_access_url_id()."
4602
                                    $sessionConditionsTeacher
4603
                                )
4604
                            )
4605
                            $userConditions
4606
                    )
4607
                    UNION ALL(
4608
                        $select
4609
                        FROM $tbl_user u
4610
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
4611
                        WHERE cu.c_id IN (
4612
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
4613
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
4614
                        )
4615
                        $userConditions
4616
                    )"
4617
                ;
4618
                break;
4619
            case STUDENT_BOSS:
4620
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
4621
                break;
4622
            case HRM_REQUEST:
4623
                $drhConditions .= " AND
4624
                    friend_user_id = '$userId' AND
4625
                    relation_type = '".USER_RELATION_TYPE_HRM_REQUEST."'
4626
                ";
4627
                break;
4628
        }
4629
4630
        $join = null;
4631
        $sql = " $masterSelect
4632
                (
4633
                    (
4634
                        $select
4635
                        FROM $tbl_user u
4636
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
4637
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
4638
                        $join
4639
                        WHERE
4640
                            access_url_id = ".api_get_current_access_url_id()."
4641
                            $drhConditions
4642
                            $userConditions
4643
                    )
4644
                    $teacherSelect
4645
4646
                ) as t1";
4647
4648
        if ($getSql) {
4649
            return $sql;
4650
        }
4651
        if ($getCount) {
4652
            $result = Database::query($sql);
4653
            $row = Database::fetch_array($result);
4654
4655
            return $row['count'];
4656
        }
4657
4658
        $orderBy = null;
4659
        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...
4660
            if (api_is_western_name_order()) {
4661
                $orderBy .= " ORDER BY firstname, lastname ";
4662
            } else {
4663
                $orderBy .= " ORDER BY lastname, firstname ";
4664
            }
4665
4666
            if (!empty($column) && !empty($direction)) {
4667
                // Fixing order due the UNIONs
4668
                $column = str_replace('u.', '', $column);
4669
                $orderBy = " ORDER BY $column $direction ";
4670
            }
4671
        }
4672
4673
        $sql .= $orderBy;
4674
        $sql .= $limitCondition;
4675
4676
        $result = Database::query($sql);
4677
        $users = [];
4678
        if (Database::num_rows($result) > 0) {
4679
            while ($row = Database::fetch_array($result)) {
4680
                $users[$row['user_id']] = $row;
4681
            }
4682
        }
4683
4684
        return $users;
4685
    }
4686
4687
    /**
4688
     * Subscribes users to human resource manager (Dashboard feature).
4689
     *
4690
     * @param int   $hr_dept_id
4691
     * @param array $users_id
4692
     * @param bool  $deleteOtherAssignedUsers
4693
     *
4694
     * @return int
4695
     */
4696
    public static function subscribeUsersToHRManager(
4697
        $hr_dept_id,
4698
        $users_id,
4699
        $deleteOtherAssignedUsers = true
4700
    ) {
4701
        return self::subscribeUsersToUser(
4702
            $hr_dept_id,
4703
            $users_id,
4704
            USER_RELATION_TYPE_RRHH,
4705
            false,
4706
            $deleteOtherAssignedUsers
4707
        );
4708
    }
4709
4710
    /**
4711
     * Register request to assign users to HRM.
4712
     *
4713
     * @param int   $hrmId   The HRM ID
4714
     * @param array $usersId The users IDs
4715
     *
4716
     * @return int
4717
     */
4718
    public static function requestUsersToHRManager($hrmId, $usersId)
4719
    {
4720
        return self::subscribeUsersToUser(
4721
            $hrmId,
4722
            $usersId,
4723
            USER_RELATION_TYPE_HRM_REQUEST,
4724
            false,
4725
            false
4726
        );
4727
    }
4728
4729
    /**
4730
     * Remove the requests for assign a user to a HRM.
4731
     *
4732
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
4733
     */
4734
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
4735
    {
4736
        $users = implode(', ', $usersId);
4737
        Database::getManager()
4738
            ->createQuery('
4739
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
4740
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
4741
            ')
4742
            ->execute(['hrm_id' => $hrmId, 'relation_type' => USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
4743
    }
4744
4745
    /**
4746
     * Add subscribed users to a user by relation type.
4747
     *
4748
     * @param int    $userId                   The user id
4749
     * @param array  $subscribedUsersId        The id of subscribed users
4750
     * @param string $relationType             The relation type
4751
     * @param bool   $deleteUsersBeforeInsert
4752
     * @param bool   $deleteOtherAssignedUsers
4753
     *
4754
     * @return int
4755
     */
4756
    public static function subscribeUsersToUser(
4757
        $userId,
4758
        $subscribedUsersId,
4759
        $relationType,
4760
        $deleteUsersBeforeInsert = false,
4761
        $deleteOtherAssignedUsers = true
4762
    ) {
4763
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4764
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4765
4766
        $userId = (int) $userId;
4767
        $relationType = (int) $relationType;
4768
        $affectedRows = 0;
4769
4770
        if ($deleteOtherAssignedUsers) {
4771
            if (api_get_multiple_access_url()) {
4772
                // Deleting assigned users to hrm_id
4773
                $sql = "SELECT s.user_id
4774
                        FROM $userRelUserTable s
4775
                        INNER JOIN $userRelAccessUrlTable a
4776
                        ON (a.user_id = s.user_id)
4777
                        WHERE
4778
                            friend_user_id = $userId AND
4779
                            relation_type = $relationType AND
4780
                            access_url_id = ".api_get_current_access_url_id();
4781
            } else {
4782
                $sql = "SELECT user_id
4783
                        FROM $userRelUserTable
4784
                        WHERE
4785
                            friend_user_id = $userId AND
4786
                            relation_type = $relationType";
4787
            }
4788
            $result = Database::query($sql);
4789
4790
            if (Database::num_rows($result) > 0) {
4791
                while ($row = Database::fetch_array($result)) {
4792
                    $sql = "DELETE FROM $userRelUserTable
4793
                            WHERE
4794
                                user_id = {$row['user_id']} AND
4795
                                friend_user_id = $userId AND
4796
                                relation_type = $relationType";
4797
                    Database::query($sql);
4798
                }
4799
            }
4800
        }
4801
4802
        if ($deleteUsersBeforeInsert) {
4803
            $sql = "DELETE FROM $userRelUserTable
4804
                    WHERE
4805
                        user_id = $userId AND
4806
                        relation_type = $relationType";
4807
            Database::query($sql);
4808
        }
4809
4810
        // Inserting new user list
4811
        if (is_array($subscribedUsersId)) {
4812
            foreach ($subscribedUsersId as $subscribedUserId) {
4813
                $subscribedUserId = (int) $subscribedUserId;
4814
                $sql = "SELECT id
4815
                        FROM $userRelUserTable
4816
                        WHERE
4817
                            user_id = $subscribedUserId AND
4818
                            friend_user_id = $userId AND
4819
                            relation_type = $relationType";
4820
4821
                $result = Database::query($sql);
4822
                $num = Database::num_rows($result);
4823
                if (0 === $num) {
4824
                    $date = api_get_utc_datetime();
4825
                    $sql = "INSERT INTO $userRelUserTable (user_id, friend_user_id, relation_type, last_edit)
4826
                            VALUES ($subscribedUserId, $userId, $relationType, '$date')";
4827
                    $result = Database::query($sql);
4828
                    $affectedRows += Database::affected_rows($result);
4829
                }
4830
            }
4831
        }
4832
4833
        return $affectedRows;
4834
    }
4835
4836
    /**
4837
     * This function check if an user is followed by human resources manager.
4838
     *
4839
     * @param int $user_id
4840
     * @param int $hr_dept_id Human resources manager
4841
     *
4842
     * @return bool
4843
     */
4844
    public static function is_user_followed_by_drh($user_id, $hr_dept_id)
4845
    {
4846
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4847
        $user_id = (int) $user_id;
4848
        $hr_dept_id = (int) $hr_dept_id;
4849
        $result = false;
4850
4851
        $sql = "SELECT user_id FROM $tbl_user_rel_user
4852
                WHERE
4853
                    user_id = $user_id AND
4854
                    friend_user_id = $hr_dept_id AND
4855
                    relation_type = ".USER_RELATION_TYPE_RRHH;
4856
        $rs = Database::query($sql);
4857
        if (Database::num_rows($rs) > 0) {
4858
            $result = true;
4859
        }
4860
4861
        return $result;
4862
    }
4863
4864
    /**
4865
     * Return the user id of teacher or session administrator.
4866
     *
4867
     * @param array $courseInfo
4868
     *
4869
     * @return mixed The user id, or false if the session ID was negative
4870
     */
4871
    public static function get_user_id_of_course_admin_or_session_admin($courseInfo)
4872
    {
4873
        $session = api_get_session_id();
4874
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4875
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4876
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4877
4878
        if (empty($courseInfo)) {
4879
            return false;
4880
        }
4881
4882
        $courseId = $courseInfo['real_id'];
4883
4884
        if (0 == $session || is_null($session)) {
4885
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
4886
                    INNER JOIN '.$table_course_user.' ru
4887
                    ON ru.user_id = u.id
4888
                    WHERE
4889
                        ru.status = 1 AND
4890
                        ru.c_id = "'.$courseId.'" ';
4891
            $rs = Database::query($sql);
4892
            $num_rows = Database::num_rows($rs);
4893
            if (1 == $num_rows) {
4894
                $row = Database::fetch_array($rs);
4895
4896
                return $row['uid'];
4897
            } else {
4898
                $my_num_rows = $num_rows;
4899
                $my_user_id = Database::result($rs, $my_num_rows - 1, 'uid');
4900
4901
                return $my_user_id;
4902
            }
4903
        } elseif ($session > 0) {
4904
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
4905
                    INNER JOIN '.$table_session_course_user.' sru
4906
                    ON sru.user_id=u.id
4907
                    WHERE
4908
                        sru.c_id="'.$courseId.'" AND
4909
                        sru.status=2';
4910
            $rs = Database::query($sql);
4911
            $row = Database::fetch_array($rs);
4912
4913
            return $row['uid'];
4914
        }
4915
4916
        return false;
4917
    }
4918
4919
    /**
4920
     * Determines if a user is a gradebook certified.
4921
     *
4922
     * @param int $cat_id  The category id of gradebook
4923
     * @param int $user_id The user id
4924
     *
4925
     * @return bool
4926
     */
4927
    public static function is_user_certified($cat_id, $user_id)
4928
    {
4929
        $cat_id = (int) $cat_id;
4930
        $user_id = (int) $user_id;
4931
4932
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4933
        $sql = 'SELECT path_certificate
4934
                FROM '.$table.'
4935
                WHERE
4936
                    cat_id = "'.$cat_id.'" AND
4937
                    user_id = "'.$user_id.'"';
4938
        $rs = Database::query($sql);
4939
        $row = Database::fetch_array($rs);
4940
4941
        if ('' == $row['path_certificate'] || is_null($row['path_certificate'])) {
4942
            return false;
4943
        }
4944
4945
        return true;
4946
    }
4947
4948
    /**
4949
     * Gets the info about a gradebook certificate for a user by course.
4950
     *
4951
     * @param array $course_info The course code
4952
     * @param int   $session_id
4953
     * @param int   $user_id     The user id
4954
     *
4955
     * @return array if there is not information return false
4956
     */
4957
    public static function get_info_gradebook_certificate($course_info, $session_id, $user_id)
4958
    {
4959
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4960
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
4961
        $session_id = (int) $session_id;
4962
        $user_id = (int) $user_id;
4963
        $courseId = $course_info['real_id'];
4964
4965
        if (empty($session_id)) {
4966
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
4967
        } else {
4968
            $session_condition = " AND session_id = $session_id";
4969
        }
4970
4971
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.'
4972
                WHERE cat_id = (
4973
                    SELECT id FROM '.$tbl_grade_category.'
4974
                    WHERE
4975
                        c_id = "'.$courseId.'" '.$session_condition.'
4976
                    LIMIT 1
4977
                ) AND user_id='.$user_id;
4978
4979
        $rs = Database::query($sql);
4980
        if (Database::num_rows($rs) > 0) {
4981
            $row = Database::fetch_array($rs, 'ASSOC');
4982
            $score = $row['score_certificate'];
4983
            $category_id = $row['cat_id'];
4984
            $cat = Category::load($category_id);
4985
            $displayscore = ScoreDisplay::instance();
4986
            if (isset($cat) && $displayscore->is_custom()) {
4987
                $grade = $displayscore->display_score(
4988
                    [$score, $cat[0]->get_weight()],
4989
                    SCORE_DIV_PERCENT_WITH_CUSTOM
4990
                );
4991
            } else {
4992
                $grade = $displayscore->display_score(
4993
                    [$score, $cat[0]->get_weight()]
4994
                );
4995
            }
4996
            $row['grade'] = $grade;
4997
4998
            return $row;
4999
        }
5000
5001
        return false;
5002
    }
5003
5004
    /**
5005
     * This function check if the user is a coach inside session course.
5006
     *
5007
     * @param int $user_id    User id
5008
     * @param int $courseId
5009
     * @param int $session_id
5010
     *
5011
     * @return bool True if the user is a coach
5012
     */
5013
    public static function is_session_course_coach($user_id, $courseId, $session_id)
5014
    {
5015
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5016
        // Protect data
5017
        $user_id = intval($user_id);
5018
        $courseId = intval($courseId);
5019
        $session_id = intval($session_id);
5020
        $result = false;
5021
5022
        $sql = "SELECT session_id FROM $table
5023
                WHERE
5024
                  session_id = $session_id AND
5025
                  c_id = $courseId AND
5026
                  user_id = $user_id AND
5027
                  status = 2 ";
5028
        $res = Database::query($sql);
5029
5030
        if (Database::num_rows($res) > 0) {
5031
            $result = true;
5032
        }
5033
5034
        return $result;
5035
    }
5036
5037
    /**
5038
     * This function returns an icon path that represents the favicon of the website of which the url given.
5039
     * Defaults to the current Chamilo favicon.
5040
     *
5041
     * @param string $url1 URL of website where to look for favicon.ico
5042
     * @param string $url2 Optional second URL of website where to look for favicon.ico
5043
     *
5044
     * @return string Path of icon to load
5045
     */
5046
    public static function get_favicon_from_url($url1, $url2 = null)
5047
    {
5048
        $icon_link = '';
5049
        $url = $url1;
5050
        if (empty($url1)) {
5051
            $url = $url2;
5052
            if (empty($url)) {
5053
                $url = api_get_access_url(api_get_current_access_url_id());
5054
                $url = $url[0];
5055
            }
5056
        }
5057
        if (!empty($url)) {
5058
            $pieces = parse_url($url);
5059
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
5060
        }
5061
5062
        return $icon_link;
5063
    }
5064
5065
    public static function addUserAsAdmin(User $user)
5066
    {
5067
        if ($user) {
0 ignored issues
show
introduced by
$user is of type Chamilo\CoreBundle\Entity\User, thus it always evaluated to true.
Loading history...
5068
            $userId = $user->getId();
5069
5070
            if (!self::is_admin($userId)) {
5071
                $table = Database::get_main_table(TABLE_MAIN_ADMIN);
5072
                $sql = "INSERT INTO $table SET user_id = $userId";
5073
                Database::query($sql);
5074
            }
5075
5076
            $user->addRole('ROLE_SUPER_ADMIN');
5077
            self::getManager()->updateUser($user, true);
5078
        }
5079
    }
5080
5081
    public static function removeUserAdmin(User $user)
5082
    {
5083
        $userId = (int) $user->getId();
5084
        if (self::is_admin($userId)) {
5085
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
5086
            $sql = "DELETE FROM $table WHERE user_id = $userId";
5087
            Database::query($sql);
5088
            $user->removeRole('ROLE_SUPER_ADMIN');
5089
            self::getManager()->updateUser($user, true);
5090
        }
5091
    }
5092
5093
    /**
5094
     * @param string $from
5095
     * @param string $to
5096
     */
5097
    public static function update_all_user_languages($from, $to)
5098
    {
5099
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5100
        $from = Database::escape_string($from);
5101
        $to = Database::escape_string($to);
5102
5103
        if (!empty($to) && !empty($from)) {
5104
            $sql = "UPDATE $table_user SET language = '$to'
5105
                    WHERE language = '$from'";
5106
            Database::query($sql);
5107
        }
5108
    }
5109
5110
    /**
5111
     * Subscribe boss to students.
5112
     *
5113
     * @param int   $bossId  The boss id
5114
     * @param array $usersId The users array
5115
     *
5116
     * @return int Affected rows
5117
     */
5118
    public static function subscribeBossToUsers($bossId, $usersId)
5119
    {
5120
        return self::subscribeUsersToUser(
5121
            $bossId,
5122
            $usersId,
5123
            USER_RELATION_TYPE_BOSS
5124
        );
5125
    }
5126
5127
    /**
5128
     * @param int $userId
5129
     *
5130
     * @return bool
5131
     */
5132
    public static function removeAllBossFromStudent($userId)
5133
    {
5134
        $userId = (int) $userId;
5135
5136
        if (empty($userId)) {
5137
            return false;
5138
        }
5139
5140
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5141
        $sql = "DELETE FROM $userRelUserTable
5142
                WHERE user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
5143
        Database::query($sql);
5144
5145
        return true;
5146
    }
5147
5148
    /**
5149
     * Subscribe boss to students, if $bossList is empty then the boss list will be empty too.
5150
     *
5151
     * @param int   $studentId
5152
     * @param array $bossList
5153
     * @param bool  $sendNotification
5154
     *
5155
     * @return mixed Affected rows or false on failure
5156
     */
5157
    public static function subscribeUserToBossList(
5158
        $studentId,
5159
        $bossList,
5160
        $sendNotification = false
5161
    ) {
5162
        $inserted = 0;
5163
        if (!empty($bossList)) {
5164
            sort($bossList);
5165
            $studentId = (int) $studentId;
5166
            $studentInfo = api_get_user_info($studentId);
5167
5168
            if (empty($studentInfo)) {
5169
                return false;
5170
            }
5171
5172
            $previousBossList = self::getStudentBossList($studentId);
5173
            $previousBossList = !empty($previousBossList) ? array_column($previousBossList, 'boss_id') : [];
5174
            sort($previousBossList);
5175
5176
            // Boss list is the same, nothing changed.
5177
            if ($bossList == $previousBossList) {
5178
                return false;
5179
            }
5180
5181
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5182
            self::removeAllBossFromStudent($studentId);
5183
5184
            foreach ($bossList as $bossId) {
5185
                $bossId = (int) $bossId;
5186
                $bossInfo = api_get_user_info($bossId);
5187
5188
                if (empty($bossInfo)) {
5189
                    continue;
5190
                }
5191
5192
                $bossLanguage = $bossInfo['language'];
5193
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
5194
                        VALUES ($studentId, $bossId, ".USER_RELATION_TYPE_BOSS.")";
5195
                $insertId = Database::query($sql);
5196
5197
                if ($insertId) {
5198
                    if ($sendNotification) {
5199
                        $name = $studentInfo['complete_name'];
5200
                        $url = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?student='.$studentId;
5201
                        $url = Display::url($url, $url);
5202
                        $subject = sprintf(get_lang('You have been assigned the learner %s'), $name);
5203
                        $message = sprintf(get_lang('You have been assigned the learner %sWithUrlX'), $name, $url);
5204
                        MessageManager::send_message_simple(
5205
                            $bossId,
5206
                            $subject,
5207
                            $message
5208
                        );
5209
                    }
5210
                    $inserted++;
5211
                }
5212
            }
5213
        } else {
5214
            self::removeAllBossFromStudent($studentId);
5215
        }
5216
5217
        return $inserted;
5218
    }
5219
5220
    /**
5221
     * Get users followed by student boss.
5222
     *
5223
     * @param int    $userId
5224
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
5225
     * @param bool   $getOnlyUserId
5226
     * @param bool   $getSql
5227
     * @param bool   $getCount
5228
     * @param int    $from
5229
     * @param int    $numberItems
5230
     * @param int    $column
5231
     * @param string $direction
5232
     * @param int    $active
5233
     * @param string $lastConnectionDate
5234
     *
5235
     * @return array users
5236
     */
5237
    public static function getUsersFollowedByStudentBoss(
5238
        $userId,
5239
        $userStatus = 0,
5240
        $getOnlyUserId = false,
5241
        $getSql = false,
5242
        $getCount = false,
5243
        $from = null,
5244
        $numberItems = null,
5245
        $column = null,
5246
        $direction = null,
5247
        $active = null,
5248
        $lastConnectionDate = null
5249
    ) {
5250
        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...
5251
            $userId,
5252
            $userStatus,
5253
            $getOnlyUserId,
5254
            $getSql,
5255
            $getCount,
5256
            $from,
5257
            $numberItems,
5258
            $column,
5259
            $direction,
5260
            $active,
5261
            $lastConnectionDate,
5262
            STUDENT_BOSS
5263
        );
5264
    }
5265
5266
    /**
5267
     * @return array
5268
     */
5269
    public static function getOfficialCodeGrouped()
5270
    {
5271
        $user = Database::get_main_table(TABLE_MAIN_USER);
5272
        $sql = "SELECT DISTINCT official_code
5273
                FROM $user
5274
                GROUP BY official_code";
5275
        $result = Database::query($sql);
5276
        $values = Database::store_result($result, 'ASSOC');
5277
        $result = [];
5278
        foreach ($values as $value) {
5279
            $result[$value['official_code']] = $value['official_code'];
5280
        }
5281
5282
        return $result;
5283
    }
5284
5285
    /**
5286
     * @param string $officialCode
5287
     *
5288
     * @return array
5289
     */
5290
    public static function getUsersByOfficialCode($officialCode)
5291
    {
5292
        $user = Database::get_main_table(TABLE_MAIN_USER);
5293
        $officialCode = Database::escape_string($officialCode);
5294
5295
        $sql = "SELECT DISTINCT id
5296
                FROM $user
5297
                WHERE official_code = '$officialCode'
5298
                ";
5299
        $result = Database::query($sql);
5300
5301
        $users = [];
5302
        while ($row = Database::fetch_array($result)) {
5303
            $users[] = $row['id'];
5304
        }
5305
5306
        return $users;
5307
    }
5308
5309
    /**
5310
     * Calc the expended time (in seconds) by a user in a course.
5311
     *
5312
     * @param int    $userId    The user id
5313
     * @param int    $courseId  The course id
5314
     * @param int    $sessionId Optional. The session id
5315
     * @param string $from      Optional. From date
5316
     * @param string $until     Optional. Until date
5317
     *
5318
     * @return int The time
5319
     */
5320
    public static function getTimeSpentInCourses(
5321
        $userId,
5322
        $courseId,
5323
        $sessionId = 0,
5324
        $from = '',
5325
        $until = ''
5326
    ) {
5327
        $userId = (int) $userId;
5328
        $sessionId = (int) $sessionId;
5329
5330
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5331
        $whereConditions = [
5332
            'user_id = ? ' => $userId,
5333
            'AND c_id = ? ' => $courseId,
5334
            'AND session_id = ? ' => $sessionId,
5335
        ];
5336
5337
        if (!empty($from) && !empty($until)) {
5338
            $whereConditions["AND (login_course_date >= '?' "] = $from;
5339
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
5340
        }
5341
5342
        $trackResult = Database::select(
5343
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
5344
            $trackCourseAccessTable,
5345
            [
5346
                'where' => $whereConditions,
5347
            ],
5348
            'first'
5349
        );
5350
5351
        if (false != $trackResult) {
5352
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
5353
        }
5354
5355
        return 0;
5356
    }
5357
5358
    /**
5359
     * Get the boss user ID from a followed user id.
5360
     *
5361
     * @param $userId
5362
     *
5363
     * @return bool
5364
     */
5365
    public static function getFirstStudentBoss($userId)
5366
    {
5367
        $userId = (int) $userId;
5368
        if ($userId > 0) {
5369
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5370
            $row = Database::select(
5371
                'DISTINCT friend_user_id AS boss_id',
5372
                $userRelTable,
5373
                [
5374
                    'where' => [
5375
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
5376
                            $userId,
5377
                            USER_RELATION_TYPE_BOSS,
5378
                        ],
5379
                    ],
5380
                ]
5381
            );
5382
            if (!empty($row)) {
5383
                return $row[0]['boss_id'];
5384
            }
5385
        }
5386
5387
        return false;
5388
    }
5389
5390
    /**
5391
     * Get the boss user ID from a followed user id.
5392
     *
5393
     * @param int $userId student id
5394
     *
5395
     * @return array
5396
     */
5397
    public static function getStudentBossList($userId)
5398
    {
5399
        $userId = (int) $userId;
5400
5401
        if ($userId > 0) {
5402
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5403
            $result = Database::select(
5404
                'DISTINCT friend_user_id AS boss_id',
5405
                $userRelTable,
5406
                [
5407
                    'where' => [
5408
                        'user_id = ? AND relation_type = ? ' => [
5409
                            $userId,
5410
                            USER_RELATION_TYPE_BOSS,
5411
                        ],
5412
                    ],
5413
                ]
5414
            );
5415
5416
            return $result;
5417
        }
5418
5419
        return [];
5420
    }
5421
5422
    /**
5423
     * @param int $bossId
5424
     * @param int $studentId
5425
     *
5426
     * @return bool
5427
     */
5428
    public static function userIsBossOfStudent($bossId, $studentId)
5429
    {
5430
        $result = false;
5431
        $bossList = self::getStudentBossList($studentId);
5432
        if (!empty($bossList)) {
5433
            $bossList = array_column($bossList, 'boss_id');
5434
            if (in_array($bossId, $bossList)) {
5435
                $result = true;
5436
            }
5437
        }
5438
5439
        return $result;
5440
    }
5441
5442
    /**
5443
     * Displays the name of the user and makes the link to the user profile.
5444
     *
5445
     * @param array $userInfo
5446
     *
5447
     * @return string
5448
     */
5449
    public static function getUserProfileLink($userInfo)
5450
    {
5451
        if (isset($userInfo) && isset($userInfo['user_id'])) {
5452
            return Display::url(
5453
                $userInfo['complete_name_with_username'],
5454
                $userInfo['profile_url']
5455
            );
5456
        }
5457
5458
        return get_lang('Anonymous');
5459
    }
5460
5461
    /**
5462
     * Get users whose name matches $firstname and $lastname.
5463
     *
5464
     * @param string $firstname Firstname to search
5465
     * @param string $lastname  Lastname to search
5466
     *
5467
     * @return array The user list
5468
     */
5469
    public static function getUsersByName($firstname, $lastname)
5470
    {
5471
        $firstname = Database::escape_string($firstname);
5472
        $lastname = Database::escape_string($lastname);
5473
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
5474
5475
        $sql = <<<SQL
5476
            SELECT id, username, lastname, firstname
5477
            FROM $userTable
5478
            WHERE
5479
                firstname LIKE '$firstname%' AND
5480
                lastname LIKE '$lastname%'
5481
SQL;
5482
        $result = Database::query($sql);
5483
        $users = [];
5484
        while ($resultData = Database::fetch_object($result)) {
5485
            $users[] = $resultData;
5486
        }
5487
5488
        return $users;
5489
    }
5490
5491
    /**
5492
     * @param int $optionSelected
5493
     *
5494
     * @return string
5495
     */
5496
    public static function getUserSubscriptionTab($optionSelected = 1)
5497
    {
5498
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
5499
        if (('true' === $allowAdmin && api_is_allowed_to_edit()) ||
5500
            api_is_platform_admin()
5501
        ) {
5502
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
5503
5504
            $headers = [
5505
                [
5506
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
5507
                    'content' => get_lang('Learners'),
5508
                ],
5509
                [
5510
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
5511
                    'content' => get_lang('Trainers'),
5512
                ],
5513
                /*[
5514
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
5515
                    'content' => get_lang('Learners'),
5516
                ],
5517
                [
5518
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
5519
                    'content' => get_lang('Trainers'),
5520
                ],*/
5521
                [
5522
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
5523
                    'content' => get_lang('Groups'),
5524
                ],
5525
                [
5526
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
5527
                    'content' => get_lang('Classes'),
5528
                ],
5529
            ];
5530
5531
            return Display::tabsOnlyLink($headers, $optionSelected);
5532
        }
5533
5534
        return '';
5535
    }
5536
5537
    /**
5538
     * Make sure this function is protected because it does NOT check password!
5539
     *
5540
     * This function defines globals.
5541
     *
5542
     * @param int  $userId
5543
     * @param bool $checkIfUserCanLoginAs
5544
     *
5545
     * @return bool
5546
     *
5547
     * @author Evie Embrechts
5548
     * @author Yannick Warnier <[email protected]>
5549
     */
5550
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
5551
    {
5552
        $userId = (int) $userId;
5553
        $userInfo = api_get_user_info($userId);
5554
5555
        // Check if the user is allowed to 'login_as'
5556
        $canLoginAs = true;
5557
        if ($checkIfUserCanLoginAs) {
5558
            $canLoginAs = api_can_login_as($userId);
5559
        }
5560
5561
        if (!$canLoginAs || empty($userInfo)) {
5562
            return false;
5563
        }
5564
5565
        if ($userId) {
5566
            $logInfo = [
5567
                'tool' => 'logout',
5568
                'tool_id' => 0,
5569
                'tool_id_detail' => 0,
5570
                'action' => '',
5571
                'info' => 'Change user (login as)',
5572
            ];
5573
            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

5573
            Event::/** @scrutinizer ignore-call */ 
5574
                   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...
5574
5575
            // Logout the current user
5576
            self::loginDelete(api_get_user_id());
5577
5578
            return true;
5579
5580
            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...
5581
            Session::erase('is_platformAdmin');
5582
            Session::erase('is_allowedCreateCourse');
5583
            Session::erase('_uid');
5584
5585
            // Cleaning session variables
5586
            $_user['firstName'] = $userInfo['firstname'];
5587
            $_user['lastName'] = $userInfo['lastname'];
5588
            $_user['mail'] = $userInfo['email'];
5589
            $_user['official_code'] = $userInfo['official_code'];
5590
            $_user['picture_uri'] = $userInfo['picture_uri'];
5591
            $_user['user_id'] = $userId;
5592
            $_user['id'] = $userId;
5593
            $_user['status'] = $userInfo['status'];
5594
5595
            // Filling session variables with new data
5596
            Session::write('_uid', $userId);
5597
            Session::write('_user', $userInfo);
5598
            Session::write('is_platformAdmin', (bool) self::is_admin($userId));
5599
            Session::write('is_allowedCreateCourse', 1 == $userInfo['status']);
5600
            // will be useful later to know if the user is actually an admin or not (example reporting)
5601
            Session::write('login_as', true);
5602
            $logInfo = [
5603
                'tool' => 'login',
5604
                'tool_id' => 0,
5605
                'tool_id_detail' => 0,
5606
                'info' => $userId,
5607
            ];
5608
            Event::registerLog($logInfo);
5609
5610
            return true;
5611
        }
5612
5613
        return false;
5614
    }
5615
5616
    /**
5617
     * Remove all login records from the track_e_online stats table,
5618
     * for the given user ID.
5619
     *
5620
     * @param int $userId User ID
5621
     */
5622
    public static function loginDelete($userId)
5623
    {
5624
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5625
        $userId = (int) $userId;
5626
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
5627
        Database::query($query);
5628
    }
5629
5630
    /**
5631
     * Login as first admin user registered in the platform.
5632
     *
5633
     * @return array
5634
     */
5635
    public static function logInAsFirstAdmin()
5636
    {
5637
        $adminList = self::get_all_administrators();
5638
5639
        if (!empty($adminList)) {
5640
            $userInfo = current($adminList);
5641
            if (!empty($userInfo)) {
5642
                $result = self::loginAsUser($userInfo['user_id'], false);
5643
                if ($result && api_is_platform_admin()) {
5644
                    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...
5645
                }
5646
            }
5647
        }
5648
5649
        return [];
5650
    }
5651
5652
    /**
5653
     * Check if user is teacher of a student based in their courses.
5654
     *
5655
     * @param $teacherId
5656
     * @param $studentId
5657
     *
5658
     * @return array
5659
     */
5660
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
5661
    {
5662
        $courses = CourseManager::getCoursesFollowedByUser(
5663
            $teacherId,
5664
            COURSEMANAGER
5665
        );
5666
        if (empty($courses)) {
5667
            return false;
5668
        }
5669
5670
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
5671
        if (empty($coursesFromUser)) {
5672
            return false;
5673
        }
5674
5675
        $coursesCodeList = array_column($courses, 'code');
5676
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
5677
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
5678
        $commonCourses = array_filter($commonCourses);
5679
5680
        if (!empty($commonCourses)) {
5681
            return $commonCourses;
5682
        }
5683
5684
        return [];
5685
    }
5686
5687
    /**
5688
     * @param int $teacherId
5689
     * @param int $studentId
5690
     *
5691
     * @return bool
5692
     */
5693
    public static function isTeacherOfStudent($teacherId, $studentId)
5694
    {
5695
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
5696
            $teacherId,
5697
            $studentId
5698
        );
5699
5700
        if (!empty($courses)) {
5701
            return true;
5702
        }
5703
5704
        return false;
5705
    }
5706
5707
    /**
5708
     * Send user confirmation mail.
5709
     *
5710
     * @throws Exception
5711
     */
5712
    public static function sendUserConfirmationMail(User $user)
5713
    {
5714
        $uniqueId = api_get_unique_id();
5715
        $user->setConfirmationToken($uniqueId);
5716
5717
        Database::getManager()->persist($user);
5718
        Database::getManager()->flush();
5719
5720
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
5721
5722
        // Check if the user was originally set for an automated subscription to a course or session
5723
        $courseCodeToRedirect = Session::read('course_redirect');
5724
        $sessionToRedirect = Session::read('session_redirect');
5725
        if (!empty($courseCodeToRedirect)) {
5726
            $url .= '&c='.$courseCodeToRedirect;
5727
        }
5728
        if (!empty($sessionToRedirect)) {
5729
            $url .= '&s='.$sessionToRedirect;
5730
        }
5731
        $mailSubject = get_lang('Registration confirmation');
5732
        $mailBody = get_lang('Registration confirmationEmailMessage')
5733
            .PHP_EOL
5734
            .Display::url($url, $url);
5735
5736
        api_mail_html(
5737
            self::formatUserFullName($user),
5738
            $user->getEmail(),
5739
            $mailSubject,
5740
            $mailBody
5741
        );
5742
        Display::addFlash(Display::return_message(get_lang('Check your e-mail and follow the instructions.')));
5743
    }
5744
5745
    /**
5746
     * Anonymize a user. Replace personal info by anonymous info.
5747
     *
5748
     * @param int  $userId   User id
5749
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
5750
     *
5751
     * @throws \Exception
5752
     *
5753
     * @return bool
5754
     * @assert (0) === false
5755
     */
5756
    public static function anonymize($userId, $deleteIP = true)
5757
    {
5758
        global $debug;
5759
5760
        $userId = (int) $userId;
5761
5762
        if (empty($userId)) {
5763
            return false;
5764
        }
5765
5766
        $em = Database::getManager();
5767
        $user = api_get_user_entity($userId);
5768
        $uniqueId = uniqid('anon', true);
5769
        $user
5770
            ->setFirstname($uniqueId)
5771
            ->setLastname($uniqueId)
5772
            ->setBiography('')
5773
            ->setAddress('')
5774
            ->setCurriculumItems(null)
5775
            ->setDateOfBirth(null)
5776
            ->setCompetences('')
5777
            ->setDiplomas('')
5778
            ->setOpenarea('')
5779
            ->setTeach('')
5780
            ->setProductions(null)
5781
            ->setOpenid('')
5782
            ->setEmailCanonical($uniqueId.'@example.com')
5783
            ->setEmail($uniqueId.'@example.com')
5784
            ->setUsername($uniqueId)
5785
            ->setUsernameCanonical($uniqueId)
5786
            ->setPhone('')
5787
            ->setOfficialCode('')
5788
        ;
5789
5790
        self::deleteUserPicture($userId);
5791
        self::cleanUserRequestsOfRemoval($userId);
5792
5793
        // The IP address is a border-case personal data, as it does
5794
        // not directly allow for personal identification (it is not
5795
        // a completely safe value in most countries - the IP could
5796
        // be used by neighbours and crackers)
5797
        if ($deleteIP) {
5798
            $substitute = '127.0.0.1';
5799
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
5800
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
5801
            $res = Database::query($sql);
5802
            if (false === $res && $debug > 0) {
5803
                error_log("Could not anonymize IP address for user $userId ($sql)");
5804
            }
5805
5806
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5807
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5808
            $res = Database::query($sql);
5809
            if (false === $res && $debug > 0) {
5810
                error_log("Could not anonymize IP address for user $userId ($sql)");
5811
            }
5812
5813
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
5814
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE exe_user_id = $userId";
5815
            $res = Database::query($sql);
5816
            if (false === $res && $debug > 0) {
5817
                error_log("Could not anonymize IP address for user $userId ($sql)");
5818
            }
5819
5820
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
5821
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
5822
            $res = Database::query($sql);
5823
            if (false === $res && $debug > 0) {
5824
                error_log("Could not anonymize IP address for user $userId ($sql)");
5825
            }
5826
5827
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5828
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
5829
            $res = Database::query($sql);
5830
            if (false === $res && $debug > 0) {
5831
                error_log("Could not anonymize IP address for user $userId ($sql)");
5832
            }
5833
5834
            $table = Database::get_course_table(TABLE_WIKI);
5835
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5836
            $res = Database::query($sql);
5837
            if (false === $res && $debug > 0) {
5838
                error_log("Could not anonymize IP address for user $userId ($sql)");
5839
            }
5840
5841
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
5842
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
5843
            $res = Database::query($sql);
5844
            if (false === $res && $debug > 0) {
5845
                error_log("Could not anonymize IP address for user $userId ($sql)");
5846
            }
5847
5848
            $table = Database::get_course_table(TABLE_WIKI);
5849
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5850
            $res = Database::query($sql);
5851
            if (false === $res && $debug > 0) {
5852
                error_log("Could not anonymize IP address for user $userId ($sql)");
5853
            }
5854
        }
5855
        $em->persist($user);
5856
        $em->flush();
5857
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
5858
5859
        return true;
5860
    }
5861
5862
    /**
5863
     * @param int $userId
5864
     *
5865
     * @throws Exception
5866
     *
5867
     * @return string
5868
     */
5869
    public static function anonymizeUserWithVerification($userId)
5870
    {
5871
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
5872
5873
        $message = '';
5874
        if (api_is_platform_admin() ||
5875
            ($allowDelete && api_is_session_admin())
5876
        ) {
5877
            $userToUpdateInfo = api_get_user_info($userId);
5878
            $currentUserId = api_get_user_id();
5879
5880
            if ($userToUpdateInfo &&
5881
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
5882
            ) {
5883
                if ($userId != $currentUserId &&
5884
                    self::anonymize($userId)
5885
                ) {
5886
                    $message = Display::return_message(
5887
                        sprintf(get_lang('User %s information anonymized.'), $userToUpdateInfo['complete_name_with_username']),
5888
                        'confirmation'
5889
                    );
5890
                } else {
5891
                    $message = Display::return_message(
5892
                        sprintf(get_lang('We could not anonymize user %s information. Please try again or check the logs.'), $userToUpdateInfo['complete_name_with_username']),
5893
                        'error'
5894
                    );
5895
                }
5896
            } else {
5897
                $message = Display::return_message(
5898
                    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']),
5899
                    'error'
5900
                );
5901
            }
5902
        }
5903
5904
        return $message;
5905
    }
5906
5907
    /**
5908
     * @param int $userId
5909
     *
5910
     * @throws Exception
5911
     *
5912
     * @return string
5913
     */
5914
    public static function deleteUserWithVerification($userId)
5915
    {
5916
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
5917
        $message = Display::return_message(get_lang('You cannot delete this user'), 'error');
5918
        $userToUpdateInfo = api_get_user_info($userId);
5919
5920
        // User must exist.
5921
        if (empty($userToUpdateInfo)) {
5922
            return $message;
5923
        }
5924
5925
        $currentUserId = api_get_user_id();
5926
5927
        // Cannot delete myself.
5928
        if ($userId == $currentUserId) {
5929
            return $message;
5930
        }
5931
5932
        if (api_is_platform_admin() ||
5933
            ($allowDelete && api_is_session_admin())
5934
        ) {
5935
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
5936
                if (self::delete_user($userId)) {
5937
                    $message = Display::return_message(
5938
                        get_lang('The user has been deleted').': '.$userToUpdateInfo['complete_name_with_username'],
5939
                        'confirmation'
5940
                    );
5941
                } else {
5942
                    $message = Display::return_message(get_lang('You cannot delete this userBecauseOwnsCourse'), 'error');
5943
                }
5944
            }
5945
        }
5946
5947
        return $message;
5948
    }
5949
5950
    /**
5951
     * @return array
5952
     */
5953
    public static function createDataPrivacyExtraFields()
5954
    {
5955
        self::create_extra_field(
5956
            'request_for_legal_agreement_consent_removal_justification',
5957
            1, //text
5958
            'Request for legal agreement consent removal justification	',
5959
            ''
5960
        );
5961
5962
        self::create_extra_field(
5963
            'request_for_delete_account_justification',
5964
            1, //text
5965
            'Request for delete account justification',
5966
            ''
5967
        );
5968
5969
        $extraFieldId = self::create_extra_field(
5970
            'request_for_legal_agreement_consent_removal',
5971
            1, //text
5972
            'Request for legal agreement consent removal',
5973
            ''
5974
        );
5975
5976
        $extraFieldIdDeleteAccount = self::create_extra_field(
5977
            'request_for_delete_account',
5978
            1, //text
5979
            'Request for delete user account',
5980
            ''
5981
        );
5982
5983
        return [
5984
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
5985
            'delete_legal' => $extraFieldId,
5986
        ];
5987
    }
5988
5989
    /**
5990
     * @param int $userId
5991
     */
5992
    public static function cleanUserRequestsOfRemoval($userId)
5993
    {
5994
        $userId = (int) $userId;
5995
5996
        $extraFieldValue = new ExtraFieldValue('user');
5997
        $extraFieldsToDelete = [
5998
            'legal_accept',
5999
            'request_for_legal_agreement_consent_removal',
6000
            'request_for_legal_agreement_consent_removal_justification',
6001
            'request_for_delete_account_justification', // just in case delete also this
6002
            'request_for_delete_account',
6003
        ];
6004
6005
        foreach ($extraFieldsToDelete as $variable) {
6006
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
6007
                $userId,
6008
                $variable
6009
            );
6010
            if ($value && isset($value['id'])) {
6011
                $extraFieldValue->delete($value['id']);
6012
            }
6013
        }
6014
    }
6015
6016
    /**
6017
     * @param int $searchYear
6018
     *
6019
     * @throws Exception
6020
     *
6021
     * @return array
6022
     */
6023
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
6024
    {
6025
        $timezone = new DateTimeZone(api_get_timezone());
6026
6027
        $sessions = [];
6028
        if (DRH == $userInfo['status']) {
6029
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
6030
        } elseif (api_is_platform_admin(true)) {
6031
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
6032
        } else {
6033
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
6034
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
6035
6036
            foreach ($sessionsByCategory as $sessionsInCategory) {
6037
                $sessions = array_merge($sessions, $sessionsInCategory);
6038
            }
6039
        }
6040
6041
        $sessions = array_map(
6042
            function ($sessionInfo) {
6043
                if (!isset($sessionInfo['session_id'])) {
6044
                    $sessionInfo['session_id'] = $sessionInfo['id'];
6045
                }
6046
                if (!isset($sessionInfo['session_name'])) {
6047
                    $sessionInfo['session_name'] = $sessionInfo['name'];
6048
                }
6049
6050
                return $sessionInfo;
6051
            },
6052
            $sessions
6053
        );
6054
6055
        $calendarSessions = [];
6056
6057
        foreach ($sessions as $sessionInfo) {
6058
            if (!empty($sessionInfo['duration'])) {
6059
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
6060
                    $sessionInfo['session_id'],
6061
                    $userInfo['id']
6062
                );
6063
6064
                if (empty($courseAccess)) {
6065
                    continue;
6066
                }
6067
6068
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
6069
                $lastAccessDate = clone $firstAcessDate;
6070
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
6071
6072
                $firstAccessYear = (int) $firstAcessDate->format('Y');
6073
                $lastAccessYear = (int) $lastAccessDate->format('Y');
6074
6075
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
6076
                    $calendarSessions[$sessionInfo['session_id']] = [
6077
                        'name' => $sessionInfo['session_name'],
6078
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
6079
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
6080
                    ];
6081
                }
6082
6083
                continue;
6084
            }
6085
6086
            $accessStartDate = !empty($sessionInfo['access_start_date'])
6087
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6088
                : null;
6089
            $accessEndDate = !empty($sessionInfo['access_end_date'])
6090
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6091
                : null;
6092
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
6093
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
6094
6095
            $isValid = false;
6096
6097
            if ($accessStartYear && $accessEndYear) {
6098
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
6099
                    $isValid = true;
6100
                }
6101
            }
6102
6103
            if ($accessStartYear && !$accessEndYear) {
6104
                if ($accessStartYear == $searchYear) {
6105
                    $isValid = true;
6106
                }
6107
            }
6108
6109
            if (!$accessStartYear && $accessEndYear) {
6110
                if ($accessEndYear == $searchYear) {
6111
                    $isValid = true;
6112
                }
6113
            }
6114
6115
            if ($isValid) {
6116
                $calendarSessions[$sessionInfo['session_id']] = [
6117
                    'name' => $sessionInfo['session_name'],
6118
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
6119
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
6120
                ];
6121
            }
6122
        }
6123
6124
        return $calendarSessions;
6125
    }
6126
6127
    /**
6128
     * Get sessions info for planification calendar.
6129
     *
6130
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
6131
     * @param int   $searchYear
6132
     *
6133
     * @throws Exception
6134
     *
6135
     * @return array
6136
     */
6137
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
6138
    {
6139
        $timezone = new DateTimeZone(api_get_timezone());
6140
        $calendar = [];
6141
6142
        foreach ($sessionsList as $sessionId => $sessionInfo) {
6143
            $startDate = $sessionInfo['access_start_date']
6144
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6145
                : null;
6146
            $endDate = $sessionInfo['access_end_date']
6147
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6148
                : null;
6149
6150
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
6151
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
6152
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
6153
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
6154
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
6155
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
6156
6157
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
6158
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
6159
6160
            $calendar[] = [
6161
                'id' => $sessionId,
6162
                'name' => $sessionInfo['name'],
6163
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
6164
                'start_in_last_year' => $startYear < $searchYear,
6165
                'end_in_next_year' => $endYear > $searchYear,
6166
                'no_start' => !$startWeek,
6167
                'no_end' => !$endWeek,
6168
                'start' => $start,
6169
                'duration' => $duration > 0 ? $duration : 1,
6170
            ];
6171
        }
6172
6173
        usort(
6174
            $calendar,
6175
            function ($sA, $sB) {
6176
                if ($sA['start'] == $sB['start']) {
6177
                    return 0;
6178
                }
6179
6180
                if ($sA['start'] < $sB['start']) {
6181
                    return -1;
6182
                }
6183
6184
                return 1;
6185
            }
6186
        );
6187
6188
        return $calendar;
6189
    }
6190
6191
    /**
6192
     * Return the user's full name. Optionally with the username.
6193
     *
6194
     * @param bool $includeUsername Optional. By default username is not included.
6195
     */
6196
    public static function formatUserFullName(User $user, $includeUsername = false): string
6197
    {
6198
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
6199
6200
        if ($includeUsername && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
6201
            $username = $user->getUsername();
6202
6203
            return "$fullName ($username)";
6204
        }
6205
6206
        return $fullName;
6207
    }
6208
6209
    /**
6210
     * @param int $userId
6211
     *
6212
     * @return array
6213
     */
6214
    public static function getUserCareers($userId)
6215
    {
6216
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6217
        $tableCareer = Database::get_main_table(TABLE_CAREER);
6218
        $userId = (int) $userId;
6219
6220
        $sql = "SELECT c.id, c.name
6221
                FROM $table uc
6222
                INNER JOIN $tableCareer c
6223
                ON uc.career_id = c.id
6224
                WHERE user_id = $userId
6225
                ORDER BY uc.created_at
6226
                ";
6227
        $result = Database::query($sql);
6228
6229
        return Database::store_result($result, 'ASSOC');
6230
    }
6231
6232
    /**
6233
     * @param int $userId
6234
     * @param int $careerId
6235
     */
6236
    public static function addUserCareer($userId, $careerId)
6237
    {
6238
        if (!api_get_configuration_value('allow_career_users')) {
6239
            return false;
6240
        }
6241
6242
        if (false === self::userHasCareer($userId, $careerId)) {
6243
            $params = ['user_id' => $userId, 'career_id' => $careerId, 'created_at' => api_get_utc_datetime(), 'updated_at' => api_get_utc_datetime()];
6244
            $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6245
            Database::insert($table, $params);
6246
        }
6247
6248
        return true;
6249
    }
6250
6251
    /**
6252
     * @param int   $userCareerId
6253
     * @param array $data
6254
     *
6255
     * @return bool
6256
     */
6257
    public static function updateUserCareer($userCareerId, $data)
6258
    {
6259
        if (!api_get_configuration_value('allow_career_users')) {
6260
            return false;
6261
        }
6262
6263
        $params = ['extra_data' => $data, 'updated_at' => api_get_utc_datetime()];
6264
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6265
        Database::update(
6266
            $table,
6267
            $params,
6268
            ['id = ?' => (int) $userCareerId]
6269
        );
6270
6271
        return true;
6272
    }
6273
6274
    /**
6275
     * @param int $userId
6276
     * @param int $careerId
6277
     *
6278
     * @return array
6279
     */
6280
    public static function getUserCareer($userId, $careerId)
6281
    {
6282
        $userId = (int) $userId;
6283
        $careerId = (int) $careerId;
6284
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6285
6286
        $sql = "SELECT * FROM $table WHERE user_id = $userId AND career_id = $careerId";
6287
        $result = Database::query($sql);
6288
6289
        return Database::fetch_array($result, 'ASSOC');
6290
    }
6291
6292
    /**
6293
     * @param int $userId
6294
     * @param int $careerId
6295
     *
6296
     * @return bool
6297
     */
6298
    public static function userHasCareer($userId, $careerId)
6299
    {
6300
        $userId = (int) $userId;
6301
        $careerId = (int) $careerId;
6302
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6303
6304
        $sql = "SELECT id FROM $table WHERE user_id = $userId AND career_id = $careerId";
6305
        $result = Database::query($sql);
6306
6307
        return Database::num_rows($result) > 0;
6308
    }
6309
6310
    /**
6311
     * @return EncoderFactory
6312
     */
6313
    private static function getEncoderFactory()
6314
    {
6315
        $encryption = self::getPasswordEncryption();
6316
        $encoders = [
6317
            'Chamilo\\CoreBundle\\Entity\\User' => new \Chamilo\CoreBundle\Security\Encoder($encryption),
6318
        ];
6319
6320
        $encoderFactory = new \Symfony\Component\Security\Core\Encoder\EncoderFactory($encoders);
6321
6322
        return $encoderFactory;
6323
    }
6324
6325
    /**
6326
     * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface
6327
     */
6328
    private static function getEncoder(User $user)
6329
    {
6330
        $encoderFactory = self::getEncoderFactory();
6331
6332
        return $encoderFactory->getEncoder($user);
6333
    }
6334
6335
    /**
6336
     * Disables or enables a user.
6337
     *
6338
     * @param int $user_id
6339
     * @param int $active  Enable or disable
6340
     *
6341
     * @return bool True on success, false on failure
6342
     * @assert (-1,0) === false
6343
     * @assert (1,1) === true
6344
     */
6345
    private static function change_active_state($user_id, $active)
6346
    {
6347
        $user_id = (int) $user_id;
6348
        $active = (int) $active;
6349
6350
        if (empty($user_id)) {
6351
            return false;
6352
        }
6353
6354
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6355
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
6356
        $r = Database::query($sql);
6357
        $ev = LOG_USER_DISABLE;
6358
        if (1 == $active) {
6359
            $ev = LOG_USER_ENABLE;
6360
        }
6361
        if (false !== $r) {
6362
            Event::addEvent($ev, LOG_USER_ID, $user_id);
6363
        }
6364
6365
        return $r;
6366
    }
6367
6368
    /**
6369
     * Get either a Gravatar URL or complete image tag for a specified email address.
6370
     *
6371
     * @param string $email The email address
6372
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
6373
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
6374
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
6375
     * @param bool   $img   True to return a complete IMG tag False for just the URL
6376
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
6377
     *
6378
     * @return string containing either just a URL or a complete image tag
6379
     * @source http://gravatar.com/site/implement/images/php/
6380
     */
6381
    private static function getGravatar(
0 ignored issues
show
Unused Code introduced by
The method getGravatar() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
6382
        $email,
6383
        $s = 80,
6384
        $d = 'mm',
6385
        $r = 'g',
6386
        $img = false,
6387
        $atts = []
6388
    ) {
6389
        $url = 'http://www.gravatar.com/avatar/';
6390
        if (!empty($_SERVER['HTTPS'])) {
6391
            $url = 'https://secure.gravatar.com/avatar/';
6392
        }
6393
        $url .= md5(strtolower(trim($email)));
6394
        $url .= "?s=$s&d=$d&r=$r";
6395
        if ($img) {
6396
            $url = '<img src="'.$url.'"';
6397
            foreach ($atts as $key => $val) {
6398
                $url .= ' '.$key.'="'.$val.'"';
6399
            }
6400
            $url .= ' />';
6401
        }
6402
6403
        return $url;
6404
    }
6405
}
6406