Passed
Push — master ( 6c061b...918d95 )
by Yannick
09:44
created

UserManager::getUserSubscriptionTab()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 39
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 15
nc 2
nop 1
dl 0
loc 39
rs 9.7666
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::getUserRepository();
61
    }
62
63
    /**
64
     * @param string $encryptionMethod
65
     */
66
    public static function setPasswordEncryption($encryptionMethod)
67
    {
68
        self::$encryptionMethod = $encryptionMethod;
69
    }
70
71
    /**
72
     * @return bool|mixed
73
     */
74
    public static function getPasswordEncryption()
75
    {
76
        $encryptionMethod = self::$encryptionMethod;
77
        if (empty($encryptionMethod)) {
78
            $encryptionMethod = api_get_configuration_value('password_encryption');
79
        }
80
81
        return $encryptionMethod;
82
    }
83
84
    /**
85
     * Validates the password.
86
     *
87
     * @param $encoded
88
     * @param $raw
89
     * @param $salt
90
     *
91
     * @return bool
92
     */
93
    public static function isPasswordValid($encoded, $raw, $salt)
94
    {
95
        $encoder = new \Chamilo\CoreBundle\Security\Encoder(self::getPasswordEncryption());
96
        $validPassword = $encoder->isPasswordValid($encoded, $raw, $salt);
0 ignored issues
show
Bug introduced by
The method isPasswordValid() does not exist on Chamilo\CoreBundle\Security\Encoder. ( Ignorable by Annotation )

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

96
        /** @scrutinizer ignore-call */ 
97
        $validPassword = $encoder->isPasswordValid($encoded, $raw, $salt);

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...
97
98
        return $validPassword;
99
    }
100
101
    /**
102
     * @param int    $userId
103
     * @param string $password
104
     */
105
    public static function updatePassword($userId, $password)
106
    {
107
        $user = api_get_user_entity($userId);
108
        $userManager = self::getRepository();
109
        $user->setPlainPassword($password);
110
        $userManager->updateUser($user, true);
111
    }
112
113
    /**
114
     * Creates a new user for the platform.
115
     *
116
     * @author Hugues Peeters <[email protected]>,
117
     * @author Roan Embrechts <[email protected]>
118
     *
119
     * @param string        $firstName
120
     * @param string        $lastName
121
     * @param int           $status                  (1 for course tutor, 5 for student, 6 for anonymous)
122
     * @param string        $email
123
     * @param string        $loginName
124
     * @param string        $password
125
     * @param string        $official_code           Any official code (optional)
126
     * @param string        $language                User language    (optional)
127
     * @param string        $phone                   Phone number    (optional)
128
     * @param string        $picture_uri             Picture URI        (optional)
129
     * @param string        $authSource              Authentication source (defaults to 'platform', dependind on constant)
130
     * @param string        $expirationDate          Account expiration date (optional, defaults to null)
131
     * @param int           $active                  Whether the account is enabled or disabled by default
132
     * @param int           $hr_dept_id              The department of HR in which the user is registered (defaults to 0)
133
     * @param array         $extra                   Extra fields (labels must be prefixed by "extra_")
134
     * @param string        $encrypt_method          Used if password is given encrypted. Set to an empty string by default
135
     * @param bool          $send_mail
136
     * @param bool          $isAdmin
137
     * @param string        $address
138
     * @param bool          $sendEmailToAllAdmins
139
     * @param FormValidator $form
140
     * @param int           $creatorId
141
     * @param array         $emailTemplate
142
     * @param string        $redirectToURLAfterLogin
143
     * @param bool          $addUserToNode
144
     *
145
     * @return mixed new user id - if the new user creation succeeds, false otherwise
146
     * @desc The function tries to retrieve user id from the session.
147
     * If it exists, the current user id is the creator id. If a problem arises,
148
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
149
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
150
     */
151
    public static function create_user(
152
        $firstName,
153
        $lastName,
154
        $status,
155
        $email,
156
        $loginName,
157
        $password,
158
        $official_code = '',
159
        $language = '',
160
        $phone = '',
161
        $picture_uri = '',
162
        $authSource = null,
163
        $expirationDate = null,
164
        $active = 1,
165
        $hr_dept_id = 0,
166
        $extra = [],
167
        $encrypt_method = '',
168
        $send_mail = false,
169
        $isAdmin = false,
170
        $address = '',
171
        $sendEmailToAllAdmins = false,
172
        $form = null,
173
        $creatorId = 0,
174
        $emailTemplate = [],
175
        $redirectToURLAfterLogin = '',
176
        $addUserToNode = true,
177
        $addUserToUrl = true
178
    ) {
179
        $authSource = !empty($authSource) ? $authSource : PLATFORM_AUTH_SOURCE;
180
        $creatorId = empty($creatorId) ? api_get_user_id() : 0;
181
        $creatorInfo = api_get_user_info($creatorId);
182
        $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
183
184
        // First check wether the login already exists
185
        if (!self::is_username_available($loginName)) {
186
            Display::addFlash(
187
                Display::return_message(get_lang('This login is already taken !'))
188
            );
189
190
            return false;
191
        }
192
193
        global $_configuration;
194
        $original_password = $password;
195
196
        $access_url_id = 1;
197
        if (api_get_multiple_access_url()) {
198
            $access_url_id = api_get_current_access_url_id();
199
        } else {
200
            // In some cases, the first access_url ID might be different from 1
201
            // for example when using a DB cluster or hacking the DB manually.
202
            // In this case, we want the first row, not necessarily "1".
203
            $accessUrlRepository = Container::getAccessUrlRepository();
204
            $accessUrl = $accessUrlRepository->getFirstId();
205
            if (!empty($accessUrl[0]) && !empty($accessUrl[0][1])) {
206
                $access_url_id = $accessUrl[0][1];
207
            }
208
        }
209
210
        if (isset($_configuration[$access_url_id]) &&
211
            is_array($_configuration[$access_url_id]) &&
212
            isset($_configuration[$access_url_id]['hosting_limit_users']) &&
213
            $_configuration[$access_url_id]['hosting_limit_users'] > 0) {
214
            $num = self::get_number_of_users();
215
            if ($num >= $_configuration[$access_url_id]['hosting_limit_users']) {
216
                api_warn_hosting_contact('hosting_limit_users');
217
                Display::addFlash(
218
                    Display::return_message(
219
                        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.'),
220
                        'warning'
221
                    )
222
                );
223
224
                return false;
225
            }
226
        }
227
228
        if (1 === $status &&
229
            isset($_configuration[$access_url_id]) &&
230
            is_array($_configuration[$access_url_id]) &&
231
            isset($_configuration[$access_url_id]['hosting_limit_teachers']) &&
232
            $_configuration[$access_url_id]['hosting_limit_teachers'] > 0
233
        ) {
234
            $num = self::get_number_of_users(1);
235
            if ($num >= $_configuration[$access_url_id]['hosting_limit_teachers']) {
236
                Display::addFlash(
237
                    Display::return_message(
238
                        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.'),
239
                        'warning'
240
                    )
241
                );
242
                api_warn_hosting_contact('hosting_limit_teachers');
243
244
                return false;
245
            }
246
        }
247
248
        if (empty($password)) {
249
            if (PLATFORM_AUTH_SOURCE === $authSource) {
250
                Display::addFlash(
251
                    Display::return_message(
252
                        get_lang('Required field').': '.get_lang(
253
                            'Password'
254
                        ),
255
                        'warning'
256
                    )
257
                );
258
259
                return false;
260
            }
261
262
            // We use the authSource as password.
263
            // The real validation will be by processed by the auth
264
            // source not Chamilo
265
            $password = $authSource;
266
        }
267
268
        // Checking the user language
269
        $languages = api_get_languages();
270
        $language = strtolower($language);
271
272
        // Default to english
273
        if (!in_array($language, array_keys($languages), true)) {
274
            $language = 'en';
275
        }
276
277
        $currentDate = api_get_utc_datetime();
278
        $now = new DateTime();
279
280
        if (empty($expirationDate) || '0000-00-00 00:00:00' === $expirationDate) {
281
            $expirationDate = null;
282
        // Default expiration date
283
            // if there is a default duration of a valid account then
284
            // we have to change the expiration_date accordingly
285
            // Accept 0000-00-00 00:00:00 as a null value to avoid issues with
286
            // third party code using this method with the previous (pre-1.10)
287
            // value of 0000...
288
            /*if ('' != api_get_setting('account_valid_duration')) {
289
                $expirationDate = new DateTime($currentDate);
290
                $days = (int) api_get_setting('account_valid_duration');
291
                $expirationDate->modify('+'.$days.' day');
292
            }*/
293
        } else {
294
            $expirationDate = api_get_utc_datetime($expirationDate);
295
            $expirationDate = new \DateTime($expirationDate, new DateTimeZone('UTC'));
296
        }
297
298
        $user = new User();
299
        $user
300
            ->setLastname($lastName)
301
            ->setFirstname($firstName)
302
            ->setUsername($loginName)
303
            ->setStatus($status)
304
            ->setPlainPassword($password)
305
            ->setEmail($email)
306
            ->setOfficialCode($official_code)
307
            ->setCreatorId($creatorId)
308
            ->setAuthSource($authSource)
309
            ->setPhone($phone)
310
            ->setAddress($address)
311
            ->setLocale($language)
312
            ->setRegistrationDate($now)
313
            ->setHrDeptId($hr_dept_id)
314
            ->setActive($active)
315
            ->setEnabled($active)
316
            ->setTimezone(api_get_timezone())
317
        ;
318
319
        if (!empty($expirationDate)) {
320
            $user->setExpirationDate($expirationDate);
321
        }
322
323
        $em = Database::getManager();
324
        $repo = Container::getUserRepository();
325
        $repo->updateUser($user, false);
326
327
        // Add user as a node
328
        if ($addUserToNode) {
329
            $resourceNode = new ResourceNode();
330
            $resourceNode
331
                ->setTitle($loginName)
332
                ->setCreator(api_get_user_entity($creatorId))
333
                ->setResourceType($repo->getResourceType())
334
            ;
335
            $em->persist($resourceNode);
336
            $user->setResourceNode($resourceNode);
337
        }
338
339
        $em->persist($user);
340
        $em->flush();
341
342
        // Add user to a group
343
        $statusToGroup = [
344
            COURSEMANAGER => 'TEACHER',
345
            STUDENT => 'STUDENT',
346
            DRH => 'RRHH',
347
            SESSIONADMIN => 'SESSION_ADMIN',
348
            STUDENT_BOSS => 'STUDENT_BOSS',
349
            INVITEE => 'INVITEE',
350
        ];
351
352
        if (isset($statusToGroup[$status])) {
353
            $group = Container::$container->get(GroupRepository::class)->findOneBy(['code' => $statusToGroup[$status]]);
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

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

353
            $group = Container::$container->/** @scrutinizer ignore-call */ get(GroupRepository::class)->findOneBy(['code' => $statusToGroup[$status]]);

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

630
            Event::/** @scrutinizer ignore-call */ 
631
                   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...
631
        } else {
632
            Display::addFlash(
633
                Display::return_message(
634
                    get_lang('There happened an unknown error. Please contact the platform administrator.')
635
                )
636
            );
637
638
            return false;
639
        }
640
641
        return $userId;
642
    }
643
644
    /**
645
     * Can user be deleted? This function checks whether there's a course
646
     * in which the given user is the
647
     * only course administrator. If that is the case, the user can't be
648
     * deleted because the course would remain without a course admin.
649
     *
650
     * @param int $user_id The user id
651
     *
652
     * @return bool true if user can be deleted
653
     *
654
     * @assert (null) === false
655
     * @assert (-1) === false
656
     * @assert ('abc') === false
657
     */
658
    public static function canDeleteUser($user_id)
659
    {
660
        $deny = api_get_configuration_value('deny_delete_users');
661
662
        if ($deny) {
663
            return false;
664
        }
665
666
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
667
        $user_id = (int) $user_id;
668
669
        if (empty($user_id)) {
670
            return false;
671
        }
672
673
        $sql = "SELECT * FROM $table_course_user
674
                WHERE status = 1 AND user_id = ".$user_id;
675
        $res = Database::query($sql);
676
        while ($course = Database::fetch_object($res)) {
677
            $sql = "SELECT id FROM $table_course_user
678
                    WHERE status=1 AND c_id = ".intval($course->c_id);
679
            $res2 = Database::query($sql);
680
            if (1 == Database::num_rows($res2)) {
681
                return false;
682
            }
683
        }
684
685
        return true;
686
    }
687
688
    /**
689
     * Delete a user from the platform, and all its belongings. This is a
690
     * very dangerous function that should only be accessible by
691
     * super-admins. Other roles should only be able to disable a user,
692
     * which removes access to the platform but doesn't delete anything.
693
     *
694
     * @param int The ID of th user to be deleted
695
     *
696
     * @throws Exception
697
     *
698
     * @return bool true if user is successfully deleted, false otherwise
699
     * @assert (null) === false
700
     * @assert ('abc') === false
701
     */
702
    public static function delete_user($user_id)
703
    {
704
        $user_id = (int) $user_id;
705
706
        if (empty($user_id)) {
707
            return false;
708
        }
709
710
        if (!self::canDeleteUser($user_id)) {
711
            return false;
712
        }
713
714
        $usergroup_rel_user = Database::get_main_table(TABLE_USERGROUP_REL_USER);
715
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
716
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
717
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
718
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
719
        $table_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
720
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
721
        $table_group = Database::get_course_table(TABLE_GROUP_USER);
722
        $table_work = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
723
724
        $userInfo = api_get_user_info($user_id);
725
        $repository = Container::getUserRepository();
726
727
        /** @var User $user */
728
        $user = $repository->find($user_id);
729
730
        $repository->deleteUser($user);
731
732
        // Unsubscribe the user from all groups in all his courses
733
        $sql = "SELECT c.id
734
                FROM $table_course c
735
                INNER JOIN $table_course_user cu
736
                ON (c.id = cu.c_id)
737
                WHERE
738
                    cu.user_id = '".$user_id."' AND
739
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
740
                ";
741
742
        $res = Database::query($sql);
743
        while ($course = Database::fetch_object($res)) {
744
            $sql = "DELETE FROM $table_group
745
                    WHERE c_id = {$course->id} AND user_id = $user_id";
746
            Database::query($sql);
747
        }
748
749
        // Unsubscribe user from usergroup_rel_user
750
        $sql = "DELETE FROM $usergroup_rel_user WHERE user_id = '".$user_id."'";
751
        Database::query($sql);
752
753
        // Unsubscribe user from all courses
754
        $sql = "DELETE FROM $table_course_user WHERE user_id = '".$user_id."'";
755
        Database::query($sql);
756
757
        // Unsubscribe user from all courses in sessions
758
        $sql = "DELETE FROM $table_session_course_user WHERE user_id = '".$user_id."'";
759
        Database::query($sql);
760
761
        // If the user was added as a id_coach then set the current admin as coach see BT#
762
        $currentUserId = api_get_user_id();
763
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
764
                WHERE id_coach = '".$user_id."'";
765
        Database::query($sql);
766
767
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
768
                WHERE session_admin_id = '".$user_id."'";
769
        Database::query($sql);
770
771
        // Unsubscribe user from all sessions
772
        $sql = "DELETE FROM $table_session_user
773
                WHERE user_id = '".$user_id."'";
774
        Database::query($sql);
775
776
        if (api_get_configuration_value('plugin_redirection_enabled')) {
777
            RedirectionPlugin::deleteUserRedirection($user_id);
778
        }
779
780
        // Delete the personal course categories
781
        $course_cat_table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
782
        $sql = "DELETE FROM $course_cat_table WHERE user_id = '".$user_id."'";
783
        Database::query($sql);
784
785
        // Delete user from the admin table
786
        $sql = "DELETE FROM $table_admin WHERE user_id = '".$user_id."'";
787
        Database::query($sql);
788
789
        // Delete the personal agenda-items from this user
790
        $agenda_table = Database::get_main_table(TABLE_PERSONAL_AGENDA);
791
        $sql = "DELETE FROM $agenda_table WHERE user = '".$user_id."'";
792
        Database::query($sql);
793
794
        $gradebook_results_table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_RESULT);
795
        $sql = 'DELETE FROM '.$gradebook_results_table.' WHERE user_id = '.$user_id;
796
        Database::query($sql);
797
798
        $extraFieldValue = new ExtraFieldValue('user');
799
        $extraFieldValue->deleteValuesByItem($user_id);
800
801
        UrlManager::deleteUserFromAllUrls($user_id);
802
        //UrlManager::deleteUserFromAllUrls($user_id);
803
804
        if ('true' === api_get_setting('allow_social_tool')) {
805
            $userGroup = new UserGroup();
806
            //Delete user from portal groups
807
            $group_list = $userGroup->get_groups_by_user($user_id);
808
            if (!empty($group_list)) {
809
                foreach ($group_list as $group_id => $data) {
810
                    $userGroup->delete_user_rel_group($user_id, $group_id);
811
                }
812
            }
813
814
            // Delete user from friend lists
815
            SocialManager::remove_user_rel_user($user_id, true);
816
        }
817
818
        // Removing survey invitation
819
        SurveyManager::delete_all_survey_invitations_by_user($user_id);
820
821
        // Delete students works
822
        /*$sql = "DELETE FROM $table_work WHERE user_id = $user_id ";
823
        Database::query($sql);*/
824
825
        /*$sql = "UPDATE c_item_property SET to_user_id = NULL
826
                WHERE to_user_id = '".$user_id."'";
827
        Database::query($sql);
828
829
        $sql = "UPDATE c_item_property SET insert_user_id = NULL
830
                WHERE insert_user_id = '".$user_id."'";
831
        Database::query($sql);
832
833
        $sql = "UPDATE c_item_property SET lastedit_user_id = NULL
834
                WHERE lastedit_user_id = '".$user_id."'";
835
        Database::query($sql);*/
836
837
        // Skills
838
        $em = Database::getManager();
839
840
        $criteria = ['user' => $user_id];
841
        $skills = $em->getRepository(SkillRelUser::class)->findBy($criteria);
842
        if ($skills) {
843
            /** @var SkillRelUser $skill */
844
            foreach ($skills as $skill) {
845
                $comments = $skill->getComments();
846
                if ($comments) {
847
                    /** @var SkillRelUserComment $comment */
848
                    foreach ($comments as $comment) {
849
                        $em->remove($comment);
850
                    }
851
                }
852
                $em->remove($skill);
853
            }
854
        }
855
856
        // ExtraFieldSavedSearch
857
        $criteria = ['user' => $user_id];
858
        $searchList = $em->getRepository(ExtraFieldSavedSearch::class)->findBy($criteria);
859
        if ($searchList) {
860
            foreach ($searchList as $search) {
861
                $em->remove($search);
862
            }
863
        }
864
865
        $connection = Database::getManager()->getConnection();
866
        $tableExists = $connection->getSchemaManager()->tablesExist(['plugin_bbb_room']);
867
        if ($tableExists) {
868
            // Delete user from database
869
            $sql = "DELETE FROM plugin_bbb_room WHERE participant_id = $user_id";
870
            Database::query($sql);
871
        }
872
873
        // Delete user/ticket relationships :(
874
        $tableExists = $connection->getSchemaManager()->tablesExist(['ticket_ticket']);
875
        if ($tableExists) {
876
            TicketManager::deleteUserFromTicketSystem($user_id);
877
        }
878
879
        $tableExists = $connection->getSchemaManager()->tablesExist(['c_lp_category_user']);
880
        if ($tableExists) {
881
            $sql = "DELETE FROM c_lp_category_user WHERE user_id = $user_id";
882
            Database::query($sql);
883
        }
884
885
        $app_plugin = new AppPlugin();
886
        $app_plugin->performActionsWhenDeletingItem('user', $user_id);
887
888
        // Add event to system log
889
        $authorId = api_get_user_id();
890
891
        Event::addEvent(
892
            LOG_USER_DELETE,
893
            LOG_USER_ID,
894
            $user_id,
895
            api_get_utc_datetime(),
896
            $authorId
897
        );
898
899
        Event::addEvent(
900
            LOG_USER_DELETE,
901
            LOG_USER_OBJECT,
902
            $userInfo,
903
            api_get_utc_datetime(),
904
            $authorId
905
        );
906
907
        return true;
908
    }
909
910
    /**
911
     * Deletes users completely. Can be called either as:
912
     * - UserManager::delete_users(1, 2, 3); or
913
     * - UserManager::delete_users(array(1, 2, 3));.
914
     *
915
     * @param array|int $ids
916
     *
917
     * @return bool True if at least one user was successfuly deleted. False otherwise.
918
     *
919
     * @author Laurent Opprecht
920
     *
921
     * @uses \UserManager::delete_user() to actually delete each user
922
     * @assert (null) === false
923
     * @assert (-1) === false
924
     * @assert (array(-1)) === false
925
     */
926
    public static function delete_users($ids = [])
927
    {
928
        $result = false;
929
        $ids = is_array($ids) ? $ids : func_get_args();
930
        if (!is_array($ids) || 0 == count($ids)) {
931
            return false;
932
        }
933
        $ids = array_map('intval', $ids);
934
        foreach ($ids as $id) {
935
            if (empty($id) || $id < 1) {
936
                continue;
937
            }
938
            $deleted = self::delete_user($id);
939
            $result = $deleted || $result;
940
        }
941
942
        return $result;
943
    }
944
945
    /**
946
     * Disable users. Can be called either as:
947
     * - UserManager::deactivate_users(1, 2, 3);
948
     * - UserManager::deactivate_users(array(1, 2, 3));.
949
     *
950
     * @param array|int $ids
951
     *
952
     * @return bool
953
     *
954
     * @author Laurent Opprecht
955
     * @assert (null) === false
956
     * @assert (array(-1)) === false
957
     */
958
    public static function deactivate_users($ids = [])
959
    {
960
        if (empty($ids)) {
961
            return false;
962
        }
963
964
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
965
966
        $ids = is_array($ids) ? $ids : func_get_args();
967
        $ids = array_map('intval', $ids);
968
        $ids = implode(',', $ids);
969
970
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
971
        $r = Database::query($sql);
972
        if (false !== $r) {
973
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
974
975
            return true;
976
        }
977
978
        return false;
979
    }
980
981
    /**
982
     * Enable users. Can be called either as:
983
     * - UserManager::activate_users(1, 2, 3);
984
     * - UserManager::activate_users(array(1, 2, 3));.
985
     *
986
     * @param array|int IDs of the users to enable
987
     *
988
     * @return bool
989
     *
990
     * @author Laurent Opprecht
991
     * @assert (null) === false
992
     * @assert (array(-1)) === false
993
     */
994
    public static function activate_users($ids = [])
995
    {
996
        if (empty($ids)) {
997
            return false;
998
        }
999
1000
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1001
1002
        $ids = is_array($ids) ? $ids : func_get_args();
1003
        $ids = array_map('intval', $ids);
1004
        $ids = implode(',', $ids);
1005
1006
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
1007
        $r = Database::query($sql);
1008
        if (false !== $r) {
1009
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
1010
1011
            return true;
1012
        }
1013
1014
        return false;
1015
    }
1016
1017
    /**
1018
     * Update user information with all the parameters passed to this function.
1019
     *
1020
     * @param int    $user_id         The ID of the user to be updated
1021
     * @param string $firstname       The user's firstname
1022
     * @param string $lastname        The user's lastname
1023
     * @param string $username        The user's username (login)
1024
     * @param string $password        The user's password
1025
     * @param string $auth_source     The authentication source (default: "platform")
1026
     * @param string $email           The user's e-mail address
1027
     * @param int    $status          The user's status
1028
     * @param string $official_code   The user's official code (usually just an internal institutional code)
1029
     * @param string $phone           The user's phone number
1030
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
1031
     * @param string $expiration_date The date at which this user will be automatically disabled
1032
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
1033
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
1034
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
1035
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
1036
     * @param string $language        The language to which the user account will be set
1037
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
1038
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
1039
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
1040
     * @param string $address
1041
     * @param array  $emailTemplate
1042
     *
1043
     * @return bool|int False on error, or the user ID if the user information was updated
1044
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
1045
     */
1046
    public static function update_user(
1047
        $user_id,
1048
        $firstname,
1049
        $lastname,
1050
        $username,
1051
        $password,
1052
        $auth_source,
1053
        $email,
1054
        $status,
1055
        $official_code,
1056
        $phone,
1057
        $picture_uri,
1058
        $expiration_date,
1059
        $active,
1060
        $creator_id = null,
1061
        $hr_dept_id = 0,
1062
        $extra = null,
1063
        $language = 'english',
1064
        $encrypt_method = '',
1065
        $send_email = false,
1066
        $reset_password = 0,
1067
        $address = null,
1068
        $emailTemplate = []
1069
    ) {
1070
        $original_password = $password;
1071
        $user_id = (int) $user_id;
1072
        $creator_id = (int) $creator_id;
1073
1074
        if (empty($user_id)) {
1075
            return false;
1076
        }
1077
1078
        $userManager = self::getRepository();
1079
        $user = api_get_user_entity($user_id);
1080
1081
        if (empty($user)) {
1082
            return false;
1083
        }
1084
1085
        if (0 == $reset_password) {
1086
            $password = null;
1087
            $auth_source = $user->getAuthSource();
1088
        } elseif (1 == $reset_password) {
1089
            $original_password = $password = api_generate_password();
1090
            $auth_source = PLATFORM_AUTH_SOURCE;
1091
        } elseif (2 == $reset_password) {
1092
            $password = $password;
1093
            $auth_source = PLATFORM_AUTH_SOURCE;
1094
        } elseif (3 == $reset_password) {
1095
            $password = $password;
1096
            $auth_source = $auth_source;
1097
        }
1098
1099
        // Checking the user language
1100
        $languages = array_keys(api_get_languages());
1101
        if (!in_array($language, $languages)) {
1102
            $language = api_get_setting('platformLanguage');
1103
        }
1104
1105
        $change_active = 0;
1106
        $isUserActive = $user->getActive();
1107
        if ($isUserActive != $active) {
1108
            $change_active = 1;
1109
        }
1110
1111
        $originalUsername = $user->getUsername();
1112
1113
        // If username is different from original then check if it exists.
1114
        if ($originalUsername !== $username) {
1115
            $available = self::is_username_available($username);
1116
            if (false === $available) {
1117
                return false;
1118
            }
1119
        }
1120
1121
        if (!empty($expiration_date)) {
1122
            $expiration_date = api_get_utc_datetime($expiration_date);
1123
            $expiration_date = new \DateTime(
1124
                $expiration_date,
1125
                new DateTimeZone('UTC')
1126
            );
1127
        }
1128
1129
        $user
1130
            ->setLastname($lastname)
1131
            ->setFirstname($firstname)
1132
            ->setUsername($username)
1133
            ->setStatus($status)
1134
            ->setAuthSource($auth_source)
1135
            ->setLocale($language)
1136
            ->setLocale($language)
1137
            ->setEmail($email)
1138
            ->setOfficialCode($official_code)
1139
            ->setPhone($phone)
1140
            ->setAddress($address)
1141
            //->setPictureUri($picture_uri)
1142
            ->setExpirationDate($expiration_date)
1143
            ->setActive($active)
1144
            ->setEnabled($active)
1145
            ->setHrDeptId((int) $hr_dept_id)
1146
        ;
1147
1148
        if (!is_null($password)) {
1149
            $user->setPlainPassword($password);
1150
        }
1151
1152
        $statusToGroup = [
1153
            COURSEMANAGER => 'TEACHER',
1154
            STUDENT => 'STUDENT',
1155
            DRH => 'RRHH',
1156
            SESSIONADMIN => 'SESSION_ADMIN',
1157
            STUDENT_BOSS => 'STUDENT_BOSS',
1158
            INVITEE => 'INVITEE',
1159
        ];
1160
1161
        $group = Container::$container->get(GroupRepository::class)->findOneBy(['code' => $statusToGroup[$status]]);
1162
        if ($group) {
1163
            $user->addGroup($group);
1164
        }
1165
1166
        $userManager->updateUser($user, true);
1167
        Event::addEvent(LOG_USER_UPDATE, LOG_USER_ID, $user_id);
1168
1169
        if (1 == $change_active) {
1170
            if (1 == $active) {
1171
                $event_title = LOG_USER_ENABLE;
1172
            } else {
1173
                $event_title = LOG_USER_DISABLE;
1174
            }
1175
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
1176
        }
1177
1178
        if (is_array($extra) && count($extra) > 0) {
1179
            $res = true;
1180
            foreach ($extra as $fname => $fvalue) {
1181
                $res = $res && self::update_extra_field_value(
1182
                    $user_id,
1183
                    $fname,
1184
                    $fvalue
1185
                );
1186
            }
1187
        }
1188
1189
        if (!empty($email) && $send_email) {
1190
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
1191
            $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
0 ignored issues
show
Bug introduced by
Are you sure api_get_setting('siteName') of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

1191
            $emailsubject = '['./** @scrutinizer ignore-type */ api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
Loading history...
1192
            $sender_name = api_get_person_name(
1193
                api_get_setting('administratorName'),
1194
                api_get_setting('administratorSurname'),
1195
                null,
1196
                PERSON_NAME_EMAIL_ADDRESS
1197
            );
1198
            $email_admin = api_get_setting('emailAdministrator');
1199
            $url = api_get_path(WEB_PATH);
1200
            if (api_is_multiple_url_enabled()) {
1201
                $access_url_id = api_get_current_access_url_id();
1202
                if (-1 != $access_url_id) {
1203
                    $url = api_get_access_url($access_url_id);
1204
                    $url = $url['url'];
1205
                }
1206
            }
1207
1208
            $tplContent = new Template(
1209
                null,
1210
                false,
1211
                false,
1212
                false,
1213
                false,
1214
                false
1215
            );
1216
            // variables for the default template
1217
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
1218
            $tplContent->assign('login_name', $username);
1219
1220
            $originalPassword = '';
1221
            if ($reset_password > 0) {
1222
                $originalPassword = stripslashes($original_password);
1223
            }
1224
            $tplContent->assign('original_password', $originalPassword);
1225
            $tplContent->assign('portal_url', $url);
1226
1227
            $layoutContent = $tplContent->get_template('mail/user_edit_content.tpl');
1228
            $emailBody = $tplContent->fetch($layoutContent);
1229
1230
            $mailTemplateManager = new MailTemplateManager();
1231
1232
            if (!empty($emailTemplate) &&
1233
                isset($emailTemplate['user_edit_content.tpl']) &&
1234
                !empty($emailTemplate['user_edit_content.tpl'])
1235
            ) {
1236
                $userInfo = api_get_user_info($user_id);
1237
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], $userInfo);
1238
            }
1239
1240
            $creatorInfo = api_get_user_info($creator_id);
1241
            $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
1242
1243
            api_mail_html(
1244
                $recipient_name,
1245
                $email,
1246
                $emailsubject,
1247
                $emailBody,
1248
                $sender_name,
1249
                $email_admin,
1250
                null,
1251
                null,
1252
                null,
1253
                null,
1254
                $creatorEmail
1255
            );
1256
        }
1257
1258
        $cacheAvailable = api_get_configuration_value('apc');
1259
        if (true === $cacheAvailable) {
1260
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1261
            if (apcu_exists($apcVar)) {
1262
                apcu_delete($apcVar);
1263
            }
1264
        }
1265
1266
        return $user->getId();
1267
    }
1268
1269
    /**
1270
     * Disables a user.
1271
     *
1272
     * @param int User id
1273
     *
1274
     * @return bool
1275
     *
1276
     * @uses \UserManager::change_active_state() to actually disable the user
1277
     * @assert (0) === false
1278
     */
1279
    public static function disable($user_id)
1280
    {
1281
        if (empty($user_id)) {
1282
            return false;
1283
        }
1284
        self::change_active_state($user_id, 0);
1285
1286
        return true;
1287
    }
1288
1289
    /**
1290
     * Enable a user.
1291
     *
1292
     * @param int User id
1293
     *
1294
     * @return bool
1295
     *
1296
     * @uses \UserManager::change_active_state() to actually disable the user
1297
     * @assert (0) === false
1298
     */
1299
    public static function enable($user_id)
1300
    {
1301
        if (empty($user_id)) {
1302
            return false;
1303
        }
1304
        self::change_active_state($user_id, 1);
1305
1306
        return true;
1307
    }
1308
1309
    /**
1310
     * Returns the user's id based on the original id and field name in
1311
     * the extra fields. Returns 0 if no user was found. This function is
1312
     * mostly useful in the context of a web services-based sinchronization.
1313
     *
1314
     * @param string Original user id
1315
     * @param string Original field name
1316
     *
1317
     * @return int User id
1318
     * @assert ('0','---') === 0
1319
     */
1320
    public static function get_user_id_from_original_id(
1321
        $original_user_id_value,
1322
        $original_user_id_name
1323
    ) {
1324
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1325
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1326
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1327
1328
        $original_user_id_name = Database::escape_string($original_user_id_name);
1329
        $original_user_id_value = Database::escape_string($original_user_id_value);
1330
1331
        $sql = "SELECT item_id as user_id
1332
                FROM $t_uf uf
1333
                INNER JOIN $t_ufv ufv
1334
                ON ufv.field_id = uf.id
1335
                WHERE
1336
                    variable = '$original_user_id_name' AND
1337
                    value = '$original_user_id_value' AND
1338
                    extra_field_type = $extraFieldType
1339
                ";
1340
        $res = Database::query($sql);
1341
        $row = Database::fetch_object($res);
1342
        if ($row) {
1343
            return $row->user_id;
1344
        }
1345
1346
        return 0;
1347
    }
1348
1349
    /**
1350
     * Check if a username is available.
1351
     *
1352
     * @param string $username the wanted username
1353
     *
1354
     * @return bool true if the wanted username is available
1355
     * @assert ('') === false
1356
     * @assert ('xyzxyzxyz') === true
1357
     */
1358
    public static function is_username_available($username)
1359
    {
1360
        if (empty($username)) {
1361
            return false;
1362
        }
1363
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1364
        $sql = "SELECT username FROM $table_user
1365
                WHERE username = '".Database::escape_string($username)."'";
1366
        $res = Database::query($sql);
1367
1368
        return 0 == Database::num_rows($res);
1369
    }
1370
1371
    /**
1372
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1373
     *
1374
     * @param string $firstname the first name of the user
1375
     * @param string $lastname  the last name of the user
1376
     *
1377
     * @return string suggests a username that contains only ASCII-letters and digits,
1378
     *                without check for uniqueness within the system
1379
     *
1380
     * @author Julio Montoya Armas
1381
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1382
     * @assert ('','') === false
1383
     * @assert ('a','b') === 'ab'
1384
     */
1385
    public static function create_username($firstname, $lastname)
1386
    {
1387
        if (empty($firstname) && empty($lastname)) {
1388
            return false;
1389
        }
1390
1391
        // The first letter only.
1392
        $firstname = api_substr(
1393
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1394
            0,
1395
            1
1396
        );
1397
        //Looking for a space in the lastname
1398
        $pos = api_strpos($lastname, ' ');
1399
        if (false !== $pos) {
1400
            $lastname = api_substr($lastname, 0, $pos);
1401
        }
1402
1403
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1404
        $username = $firstname.$lastname;
1405
        if (empty($username)) {
1406
            $username = 'user';
1407
        }
1408
1409
        $username = URLify::transliterate($username);
1410
1411
        return strtolower(substr($username, 0, USERNAME_MAX_LENGTH - 3));
1412
    }
1413
1414
    /**
1415
     * Creates a unique username, using:
1416
     * 1. the first name and the last name of a user;
1417
     * 2. an already created username but not checked for uniqueness yet.
1418
     *
1419
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1420
     *                          parameter is treated as username which is to be checked f
1421
     *                          or uniqueness and to be modified when it is necessary.
1422
     * @param string $lastname  the last name of the user
1423
     *
1424
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1425
     *                Note: When the method is called several times with same parameters,
1426
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1427
     *
1428
     * @author Ivan Tcholakov, 2009
1429
     */
1430
    public static function create_unique_username($firstname, $lastname = null)
1431
    {
1432
        if (is_null($lastname)) {
1433
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1434
            // For making this method tolerant of mistakes,
1435
            // let us transliterate and purify the suggested input username anyway.
1436
            // So, instead of the sentence $username = $firstname; we place the following:
1437
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1438
        } else {
1439
            $username = self::create_username($firstname, $lastname);
1440
        }
1441
        if (!self::is_username_available($username)) {
1442
            $i = 2;
1443
            $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1444
            while (!self::is_username_available($temp_username)) {
1445
                $i++;
1446
                $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1447
            }
1448
            $username = $temp_username;
1449
        }
1450
1451
        $username = URLify::transliterate($username);
1452
1453
        return $username;
1454
    }
1455
1456
    /**
1457
     * Modifies a given username accordingly to the specification for valid characters and length.
1458
     *
1459
     * @param $username string          The input username
1460
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1461
     *                     otherwise compliance may be partial. The default value is FALSE.
1462
     *
1463
     * @return string the resulting purified username
1464
     */
1465
    public static function purify_username($username, $strict = false)
1466
    {
1467
        if ($strict) {
1468
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1469
            // into ASCII letters in order they not to be totally removed.
1470
            // 2. Applying the strict purifier.
1471
            // 3. Length limitation.
1472
            $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);
1473
            $return = URLify::transliterate($return);
1474
1475
            return $return;
1476
        }
1477
1478
        // 1. Applying the shallow purifier.
1479
        // 2. Length limitation.
1480
        return substr(
1481
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1482
            0,
1483
            USERNAME_MAX_LENGTH
1484
        );
1485
    }
1486
1487
    /**
1488
     * Checks whether the user id exists in the database.
1489
     *
1490
     * @param int $userId User id
1491
     *
1492
     * @return bool True if user id was found, false otherwise
1493
     */
1494
    public static function is_user_id_valid($userId)
1495
    {
1496
        $resultData = Database::select(
1497
            'COUNT(1) AS count',
1498
            Database::get_main_table(TABLE_MAIN_USER),
1499
            [
1500
                'where' => ['id = ?' => (int) $userId],
1501
            ],
1502
            'first'
1503
        );
1504
1505
        if (false === $resultData) {
1506
            return false;
1507
        }
1508
1509
        return $resultData['count'] > 0;
1510
    }
1511
1512
    /**
1513
     * Checks whether a given username matches to the specification strictly.
1514
     * The empty username is assumed here as invalid.
1515
     * Mostly this function is to be used in the user interface built-in validation routines
1516
     * for providing feedback while usernames are enterd manually.
1517
     *
1518
     * @param string $username the input username
1519
     *
1520
     * @return bool returns TRUE if the username is valid, FALSE otherwise
1521
     */
1522
    public static function is_username_valid($username)
1523
    {
1524
        return !empty($username) && $username == self::purify_username($username, true);
1525
    }
1526
1527
    /**
1528
     * Checks whether a username is empty. If the username contains whitespace characters,
1529
     * such as spaces, tabulators, newlines, etc.,
1530
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
1531
     *
1532
     * @param string $username the given username
1533
     *
1534
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1535
     */
1536
    public static function is_username_empty($username)
1537
    {
1538
        return 0 == strlen(self::purify_username($username, false));
1539
    }
1540
1541
    /**
1542
     * Checks whether a username is too long or not.
1543
     *
1544
     * @param string $username the given username, it should contain only ASCII-letters and digits
1545
     *
1546
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1547
     */
1548
    public static function is_username_too_long($username)
1549
    {
1550
        return strlen($username) > USERNAME_MAX_LENGTH;
1551
    }
1552
1553
    /**
1554
     * Get the users by ID.
1555
     *
1556
     * @param array  $ids    student ids
1557
     * @param bool   $active
1558
     * @param string $order
1559
     * @param string $limit
1560
     *
1561
     * @return array $result student information
1562
     */
1563
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
1564
    {
1565
        if (empty($ids)) {
1566
            return [];
1567
        }
1568
1569
        $ids = is_array($ids) ? $ids : [$ids];
1570
        $ids = array_map('intval', $ids);
1571
        $ids = implode(',', $ids);
1572
1573
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
1574
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
1575
        if (!is_null($active)) {
1576
            $sql .= ' AND active='.($active ? '1' : '0');
1577
        }
1578
1579
        if (!is_null($order)) {
1580
            $order = Database::escape_string($order);
1581
            $sql .= ' ORDER BY '.$order;
1582
        }
1583
1584
        if (!is_null($limit)) {
1585
            $limit = Database::escape_string($limit);
1586
            $sql .= ' LIMIT '.$limit;
1587
        }
1588
1589
        $rs = Database::query($sql);
1590
        $result = [];
1591
        while ($row = Database::fetch_array($rs)) {
1592
            $result[] = $row;
1593
        }
1594
1595
        return $result;
1596
    }
1597
1598
    /**
1599
     * Get a list of users of which the given conditions match with an = 'cond'.
1600
     *
1601
     * @param array $conditions a list of condition (example : status=>STUDENT)
1602
     * @param array $order_by   a list of fields on which sort
1603
     *
1604
     * @return array an array with all users of the platform
1605
     *
1606
     * @todo security filter order by
1607
     */
1608
    public static function get_user_list(
1609
        $conditions = [],
1610
        $order_by = [],
1611
        $limit_from = false,
1612
        $limit_to = false,
1613
        $idCampus = null
1614
    ) {
1615
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1616
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1617
        $return_array = [];
1618
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1619
1620
        if (api_is_multiple_url_enabled()) {
1621
            if ($idCampus) {
1622
                $urlId = $idCampus;
1623
            } else {
1624
                $urlId = api_get_current_access_url_id();
1625
            }
1626
            $sql .= " INNER JOIN $userUrlTable url_user
1627
                      ON (user.id = url_user.user_id)
1628
                      WHERE url_user.access_url_id = $urlId";
1629
        } else {
1630
            $sql .= " WHERE 1=1 ";
1631
        }
1632
1633
        if (count($conditions) > 0) {
1634
            foreach ($conditions as $field => $value) {
1635
                $field = Database::escape_string($field);
1636
                $value = Database::escape_string($value);
1637
                $sql .= " AND $field = '$value'";
1638
            }
1639
        }
1640
1641
        if (count($order_by) > 0) {
1642
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1643
        }
1644
1645
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1646
            $limit_from = (int) $limit_from;
1647
            $limit_to = (int) $limit_to;
1648
            $sql .= " LIMIT $limit_from, $limit_to";
1649
        }
1650
        $sql_result = Database::query($sql);
1651
        while ($result = Database::fetch_array($sql_result)) {
1652
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1653
            $return_array[] = $result;
1654
        }
1655
1656
        return $return_array;
1657
    }
1658
1659
    public static function getUserListExtraConditions(
1660
        $conditions = [],
1661
        $order_by = [],
1662
        $limit_from = false,
1663
        $limit_to = false,
1664
        $idCampus = null,
1665
        $extraConditions = '',
1666
        $getCount = false
1667
    ) {
1668
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1669
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1670
        $return_array = [];
1671
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1672
1673
        if ($getCount) {
1674
            $sql = "SELECT count(user.id) count FROM $user_table user ";
1675
        }
1676
1677
        if (api_is_multiple_url_enabled()) {
1678
            if ($idCampus) {
1679
                $urlId = $idCampus;
1680
            } else {
1681
                $urlId = api_get_current_access_url_id();
1682
            }
1683
            $sql .= " INNER JOIN $userUrlTable url_user
1684
                      ON (user.user_id = url_user.user_id)
1685
                      WHERE url_user.access_url_id = $urlId";
1686
        } else {
1687
            $sql .= " WHERE 1=1 ";
1688
        }
1689
1690
        $sql .= " AND status <> ".ANONYMOUS." ";
1691
1692
        if (count($conditions) > 0) {
1693
            foreach ($conditions as $field => $value) {
1694
                $field = Database::escape_string($field);
1695
                $value = Database::escape_string($value);
1696
                $sql .= " AND $field = '$value'";
1697
            }
1698
        }
1699
1700
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
1701
1702
        if (!empty($order_by) && count($order_by) > 0) {
1703
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1704
        }
1705
1706
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1707
            $limit_from = (int) $limit_from;
1708
            $limit_to = (int) $limit_to;
1709
            $sql .= " LIMIT $limit_from, $limit_to";
1710
        }
1711
1712
        $sql_result = Database::query($sql);
1713
1714
        if ($getCount) {
1715
            $result = Database::fetch_array($sql_result);
1716
1717
            return $result['count'];
1718
        }
1719
1720
        while ($result = Database::fetch_array($sql_result)) {
1721
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1722
            $return_array[] = $result;
1723
        }
1724
1725
        return $return_array;
1726
    }
1727
1728
    /**
1729
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
1730
     *
1731
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
1732
     * @param array  $order_by         a list of fields on which sort
1733
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
1734
     * @param string $condition        Whether we want the filters to be combined by AND or OR
1735
     * @param array  $onlyThisUserList
1736
     *
1737
     * @return array an array with all users of the platform
1738
     *
1739
     * @todo optional course code parameter, optional sorting parameters...
1740
     * @todo security filter order_by
1741
     */
1742
    public static function getUserListLike(
1743
        $conditions = [],
1744
        $order_by = [],
1745
        $simple_like = false,
1746
        $condition = 'AND',
1747
        $onlyThisUserList = []
1748
    ) {
1749
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1750
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1751
        $return_array = [];
1752
        $sql_query = "SELECT user.id FROM $user_table user ";
1753
1754
        if (api_is_multiple_url_enabled()) {
1755
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
1756
        }
1757
1758
        $sql_query .= ' WHERE 1 = 1 ';
1759
        if (count($conditions) > 0) {
1760
            $temp_conditions = [];
1761
            foreach ($conditions as $field => $value) {
1762
                $field = Database::escape_string($field);
1763
                $value = Database::escape_string($value);
1764
                if ($simple_like) {
1765
                    $temp_conditions[] = $field." LIKE '$value%'";
1766
                } else {
1767
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
1768
                }
1769
            }
1770
            if (!empty($temp_conditions)) {
1771
                $sql_query .= ' AND '.implode(' '.$condition.' ', $temp_conditions);
1772
            }
1773
1774
            if (api_is_multiple_url_enabled()) {
1775
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1776
            }
1777
        } else {
1778
            if (api_is_multiple_url_enabled()) {
1779
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1780
            }
1781
        }
1782
1783
        if (!empty($onlyThisUserList)) {
1784
            $onlyThisUserListToString = implode("','", $onlyThisUserList);
1785
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
1786
        }
1787
1788
        if (count($order_by) > 0) {
1789
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1790
        }
1791
1792
        $sql_result = Database::query($sql_query);
1793
        while ($result = Database::fetch_array($sql_result)) {
1794
            $userInfo = api_get_user_info($result['id']);
1795
            $return_array[] = $userInfo;
1796
        }
1797
1798
        return $return_array;
1799
    }
1800
1801
    /**
1802
     * Gets the current user image.
1803
     *
1804
     * @param string $userId
1805
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
1806
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
1807
     * @param bool   $addRandomId
1808
     * @param array  $userInfo    to avoid query the DB
1809
     *
1810
     * @todo add gravatar support
1811
     * @todo replace $userId with User entity
1812
     *
1813
     * @return string
1814
     */
1815
    public static function getUserPicture(
1816
        $userId,
1817
        int $size = USER_IMAGE_SIZE_MEDIUM,
1818
        $addRandomId = true,
1819
        $userInfo = []
1820
    ) {
1821
        $user = api_get_user_entity($userId);
1822
        $illustrationRepo = Container::getIllustrationRepository();
1823
1824
        switch ($size) {
1825
            case USER_IMAGE_SIZE_SMALL:
1826
                $width = 32;
1827
                break;
1828
            case USER_IMAGE_SIZE_MEDIUM:
1829
                $width = 64;
1830
                break;
1831
            case USER_IMAGE_SIZE_BIG:
1832
                $width = 128;
1833
                break;
1834
            case USER_IMAGE_SIZE_ORIGINAL:
1835
            default:
1836
                $width = 0;
1837
                break;
1838
        }
1839
1840
        $url = $illustrationRepo->getIllustrationUrl($user);
1841
        $params = [];
1842
        if (!empty($width)) {
1843
            $params['w'] = $width;
1844
        }
1845
1846
        if ($addRandomId) {
1847
            $params['rand'] = uniqid('u_', true);
1848
        }
1849
1850
        $paramsToString = '';
1851
        if (!empty($params)) {
1852
            $paramsToString = '?'.http_build_query($params);
1853
        }
1854
1855
        return $url.$paramsToString;
1856
1857
        /*
1858
        // Make sure userInfo is defined. Otherwise, define it!
1859
        if (empty($userInfo) || !is_array($userInfo) || 0 == count($userInfo)) {
1860
            if (empty($user_id)) {
1861
                return '';
1862
            } else {
1863
                $userInfo = api_get_user_info($user_id);
1864
            }
1865
        }
1866
1867
        $imageWebPath = self::get_user_picture_path_by_id(
1868
            $user_id,
1869
            'web',
1870
            $userInfo
1871
        );
1872
        $pictureWebFile = $imageWebPath['file'];
1873
        $pictureWebDir = $imageWebPath['dir'];
1874
1875
        $pictureAnonymousSize = '128';
1876
        $gravatarSize = 22;
1877
        $realSizeName = 'small_';
1878
1879
        switch ($size) {
1880
            case USER_IMAGE_SIZE_SMALL:
1881
                $pictureAnonymousSize = '32';
1882
                $realSizeName = 'small_';
1883
                $gravatarSize = 32;
1884
                break;
1885
            case USER_IMAGE_SIZE_MEDIUM:
1886
                $pictureAnonymousSize = '64';
1887
                $realSizeName = 'medium_';
1888
                $gravatarSize = 64;
1889
                break;
1890
            case USER_IMAGE_SIZE_ORIGINAL:
1891
                $pictureAnonymousSize = '128';
1892
                $realSizeName = '';
1893
                $gravatarSize = 128;
1894
                break;
1895
            case USER_IMAGE_SIZE_BIG:
1896
                $pictureAnonymousSize = '128';
1897
                $realSizeName = 'big_';
1898
                $gravatarSize = 128;
1899
                break;
1900
        }
1901
1902
        $gravatarEnabled = api_get_setting('gravatar_enabled');
1903
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
1904
        if ('unknown.jpg' == $pictureWebFile || empty($pictureWebFile)) {
1905
            if ('true' === $gravatarEnabled) {
1906
                $file = self::getGravatar(
1907
                    $imageWebPath['email'],
1908
                    $gravatarSize,
1909
                    api_get_setting('gravatar_type')
1910
                );
1911
1912
                if ($addRandomId) {
1913
                    $file .= '&rand='.uniqid();
1914
                }
1915
1916
                return $file;
1917
            }
1918
1919
            return $anonymousPath;
1920
        }
1921
1922
        if ($addRandomId) {
1923
            $picture .= '?rand='.uniqid();
1924
        }
1925
1926
        return $picture;*/
1927
    }
1928
1929
    /**
1930
     * Creates new user photos in various sizes of a user, or deletes user photos.
1931
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
1932
     *
1933
     * @param int    $user_id the user internal identification number
1934
     * @param string $file    The common file name for the newly created photos.
1935
     *                        It will be checked and modified for compatibility with the file system.
1936
     *                        If full name is provided, path component is ignored.
1937
     *                        If an empty name is provided, then old user photos are deleted only,
1938
     *
1939
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
1940
     *
1941
     * @param string $source_file    the full system name of the image from which user photos will be created
1942
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
1943
     *
1944
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
1945
     *              When deletion is requested returns empty string.
1946
     *              In case of internal error or negative validation returns FALSE.
1947
     */
1948
    public static function update_user_picture($userId, UploadedFile $file, $crop = '')
1949
    {
1950
        if (empty($userId) || empty($file)) {
1951
            return false;
1952
        }
1953
1954
        $repo = Container::getUserRepository();
1955
        $user = $repo->find($userId);
1956
        if ($user) {
1957
            $repoIllustration = Container::getIllustrationRepository();
1958
            $repoIllustration->addIllustration($user, $user, $file, $crop);
1959
        }
1960
    }
1961
1962
    /**
1963
     * Deletes user photos.
1964
     *
1965
     * @param int $userId the user internal identification number
1966
     *
1967
     * @return mixed returns empty string on success, FALSE on error
1968
     */
1969
    public static function deleteUserPicture($userId)
1970
    {
1971
        $repo = Container::getUserRepository();
1972
        $user = $repo->find($userId);
1973
        if ($user) {
1974
            $illustrationRepo = Container::getIllustrationRepository();
1975
            $illustrationRepo->deleteIllustration($user);
1976
        }
1977
    }
1978
1979
    /**
1980
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
1981
     * doesn't have any.
1982
     *
1983
     * If there has been a request to remove a production, the function will return
1984
     * without building the list unless forced to do so by the optional second
1985
     * parameter. This increases performance by avoiding to read through the
1986
     * productions on the filesystem before the removal request has been carried
1987
     * out because they'll have to be re-read afterwards anyway.
1988
     *
1989
     * @param int  $user_id    User id
1990
     * @param bool $force      Optional parameter to force building after a removal request
1991
     * @param bool $showDelete
1992
     *
1993
     * @return string A string containing the XHTML code to display the production list, or FALSE
1994
     */
1995
    public static function build_production_list($user_id, $force = false, $showDelete = false)
1996
    {
1997
        if (!$force && !empty($_POST['remove_production'])) {
1998
            return true; // postpone reading from the filesystem
1999
        }
2000
2001
        $productions = self::get_user_productions($user_id);
2002
2003
        if (empty($productions)) {
2004
            return false;
2005
        }
2006
2007
        return false;
2008
2009
        $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...
2010
        $del_image = Display::returnIconPath('delete.png');
2011
        $add_image = Display::returnIconPath('archive.png');
2012
        $del_text = get_lang('Delete');
2013
        $production_list = '';
2014
        if (count($productions) > 0) {
2015
            $production_list = '<div class="files-production"><ul id="productions">';
2016
            foreach ($productions as $file) {
2017
                $production_list .= '<li>
2018
                    <img src="'.$add_image.'" />
2019
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2020
                        '.htmlentities($file).'
2021
                    </a>';
2022
                if ($showDelete) {
2023
                    $production_list .= '&nbsp;&nbsp;
2024
                        <input
2025
                            style="width:16px;"
2026
                            type="image"
2027
                            name="remove_production['.urlencode($file).']"
2028
                            src="'.$del_image.'"
2029
                            alt="'.$del_text.'"
2030
                            title="'.$del_text.' '.htmlentities($file).'"
2031
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2032
                }
2033
            }
2034
            $production_list .= '</ul></div>';
2035
        }
2036
2037
        return $production_list;
2038
    }
2039
2040
    /**
2041
     * Returns an array with the user's productions.
2042
     *
2043
     * @param int $user_id User id
2044
     *
2045
     * @return array An array containing the user's productions
2046
     */
2047
    public static function get_user_productions($user_id)
2048
    {
2049
        return [];
2050
2051
        $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...
2052
        $productions = [];
2053
2054
        if (is_dir($production_repository)) {
2055
            $handle = opendir($production_repository);
2056
            while ($file = readdir($handle)) {
2057
                if ('.' == $file ||
2058
                    '..' == $file ||
2059
                    '.htaccess' == $file ||
2060
                    is_dir($production_repository.$file)
2061
                ) {
2062
                    // skip current/parent directory and .htaccess
2063
                    continue;
2064
                }
2065
2066
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2067
                    // User's photos should not be listed as productions.
2068
                    continue;
2069
                }
2070
                $productions[] = $file;
2071
            }
2072
        }
2073
2074
        return $productions;
2075
    }
2076
2077
    /**
2078
     * Remove a user production.
2079
     *
2080
     * @param int    $user_id    User id
2081
     * @param string $production The production to remove
2082
     *
2083
     * @return bool
2084
     */
2085
    public static function remove_user_production($user_id, $production)
2086
    {
2087
        throw new Exception('remove_user_production');
2088
        /*$production_path = self::get_user_picture_path_by_id($user_id, 'system');
2089
        $production_file = $production_path['dir'].$production;
2090
        if (is_file($production_file)) {
2091
            unlink($production_file);
2092
2093
            return true;
2094
        }
2095
2096
        return false;*/
2097
    }
2098
2099
    /**
2100
     * Update an extra field value for a given user.
2101
     *
2102
     * @param int    $userId   User ID
2103
     * @param string $variable Field variable name
2104
     * @param string $value    Field value
2105
     *
2106
     * @return bool true if field updated, false otherwise
2107
     */
2108
    public static function update_extra_field_value($userId, $variable, $value = '')
2109
    {
2110
        $extraFieldValue = new ExtraFieldValue('user');
2111
        $params = [
2112
            'item_id' => $userId,
2113
            'variable' => $variable,
2114
            'value' => $value,
2115
        ];
2116
2117
        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...
2118
    }
2119
2120
    /**
2121
     * Get an array of extra fields with field details (type, default value and options).
2122
     *
2123
     * @param    int    Offset (from which row)
2124
     * @param    int    Number of items
2125
     * @param    int    Column on which sorting is made
2126
     * @param    string    Sorting direction
2127
     * @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...
2128
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2129
     *
2130
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2131
     */
2132
    public static function get_extra_fields(
2133
        $from = 0,
2134
        $number_of_items = 0,
2135
        $column = 5,
2136
        $direction = 'ASC',
2137
        $all_visibility = true,
2138
        $field_filter = null
2139
    ) {
2140
        $fields = [];
2141
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2142
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2143
        $columns = [
2144
            'id',
2145
            'variable',
2146
            'field_type',
2147
            'display_text',
2148
            'default_value',
2149
            'field_order',
2150
            'filter',
2151
        ];
2152
        $column = (int) $column;
2153
        $sort_direction = '';
2154
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2155
            $sort_direction = strtoupper($direction);
2156
        }
2157
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2158
        $sqlf = "SELECT * FROM $t_uf WHERE extra_field_type = $extraFieldType ";
2159
        if (!$all_visibility) {
2160
            $sqlf .= " AND visible_to_self = 1 ";
2161
        }
2162
        if (!is_null($field_filter)) {
2163
            $field_filter = (int) $field_filter;
2164
            $sqlf .= " AND filter = $field_filter ";
2165
        }
2166
        $sqlf .= " ORDER BY ".$columns[$column]." $sort_direction ";
2167
        if (0 != $number_of_items) {
2168
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2169
        }
2170
        $resf = Database::query($sqlf);
2171
        if (Database::num_rows($resf) > 0) {
2172
            while ($rowf = Database::fetch_array($resf)) {
2173
                $fields[$rowf['id']] = [
2174
                    0 => $rowf['id'],
2175
                    1 => $rowf['variable'],
2176
                    2 => $rowf['field_type'],
2177
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2178
                    4 => $rowf['default_value'],
2179
                    5 => $rowf['field_order'],
2180
                    6 => $rowf['visible_to_self'],
2181
                    7 => $rowf['changeable'],
2182
                    8 => $rowf['filter'],
2183
                    9 => [],
2184
                    10 => '<a name="'.$rowf['id'].'"></a>',
2185
                ];
2186
2187
                $sqlo = "SELECT * FROM $t_ufo
2188
                         WHERE field_id = ".$rowf['id']."
2189
                         ORDER BY option_order ASC";
2190
                $reso = Database::query($sqlo);
2191
                if (Database::num_rows($reso) > 0) {
2192
                    while ($rowo = Database::fetch_array($reso)) {
2193
                        $fields[$rowf['id']][9][$rowo['id']] = [
2194
                            0 => $rowo['id'],
2195
                            1 => $rowo['option_value'],
2196
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2197
                            3 => $rowo['option_order'],
2198
                        ];
2199
                    }
2200
                }
2201
            }
2202
        }
2203
2204
        return $fields;
2205
    }
2206
2207
    /**
2208
     * Creates a new extra field.
2209
     *
2210
     * @param string $variable    Field's internal variable name
2211
     * @param int    $fieldType   Field's type
2212
     * @param string $displayText Field's language var name
2213
     * @param string $default     Field's default value
2214
     *
2215
     * @return int
2216
     */
2217
    public static function create_extra_field(
2218
        $variable,
2219
        $fieldType,
2220
        $displayText,
2221
        $default
2222
    ) {
2223
        $extraField = new ExtraField('user');
2224
        $params = [
2225
            'variable' => $variable,
2226
            'field_type' => $fieldType,
2227
            'display_text' => $displayText,
2228
            'default_value' => $default,
2229
        ];
2230
2231
        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...
2232
    }
2233
2234
    /**
2235
     * Check if a field is available.
2236
     *
2237
     * @param string $variable
2238
     *
2239
     * @return bool
2240
     */
2241
    public static function is_extra_field_available($variable)
2242
    {
2243
        $extraField = new ExtraField('user');
2244
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2245
2246
        return !empty($data) ? true : false;
2247
    }
2248
2249
    /**
2250
     * Gets user extra fields data.
2251
     *
2252
     * @param    int    User ID
2253
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2254
     * @param    bool    Whether to return invisible fields as well
2255
     * @param    bool    Whether to split multiple-selection fields or not
2256
     *
2257
     * @return array Array of fields => value for the given user
2258
     */
2259
    public static function get_extra_user_data(
2260
        $user_id,
2261
        $prefix = false,
2262
        $allVisibility = true,
2263
        $splitMultiple = false,
2264
        $fieldFilter = null
2265
    ) {
2266
        $user_id = (int) $user_id;
2267
2268
        if (empty($user_id)) {
2269
            return [];
2270
        }
2271
2272
        $extra_data = [];
2273
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2274
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2275
        $user_id = (int) $user_id;
2276
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2277
                FROM $t_uf f
2278
                WHERE
2279
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
2280
                ";
2281
        $filter_cond = '';
2282
2283
        if (!$allVisibility) {
2284
            if (isset($fieldFilter)) {
2285
                $fieldFilter = (int) $fieldFilter;
2286
                $filter_cond .= " AND filter = $fieldFilter ";
2287
            }
2288
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2289
        } else {
2290
            if (isset($fieldFilter)) {
2291
                $fieldFilter = (int) $fieldFilter;
2292
                $sql .= " AND filter = $fieldFilter ";
2293
            }
2294
        }
2295
2296
        $sql .= ' ORDER BY f.field_order';
2297
2298
        $res = Database::query($sql);
2299
        if (Database::num_rows($res) > 0) {
2300
            while ($row = Database::fetch_array($res)) {
2301
                if (self::USER_FIELD_TYPE_TAG == $row['type']) {
2302
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2303
                    $extra_data['extra_'.$row['fvar']] = $tags;
2304
                } else {
2305
                    $sqlu = "SELECT value as fval
2306
                            FROM $t_ufv
2307
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2308
                    $resu = Database::query($sqlu);
2309
                    // get default value
2310
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2311
                               WHERE id=".$row['id'];
2312
                    $res_df = Database::query($sql_df);
2313
2314
                    if (Database::num_rows($resu) > 0) {
2315
                        $rowu = Database::fetch_array($resu);
2316
                        $fval = $rowu['fval'];
2317
                        if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2318
                            $fval = explode(';', $rowu['fval']);
2319
                        }
2320
                    } else {
2321
                        $row_df = Database::fetch_array($res_df);
2322
                        $fval = $row_df['fval_df'];
2323
                    }
2324
                    // We get here (and fill the $extra_data array) even if there
2325
                    // is no user with data (we fill it with default values)
2326
                    if ($prefix) {
2327
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2328
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2329
                        } else {
2330
                            $extra_data['extra_'.$row['fvar']] = $fval;
2331
                        }
2332
                    } else {
2333
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2334
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2335
                        } else {
2336
                            $extra_data[$row['fvar']] = $fval;
2337
                        }
2338
                    }
2339
                }
2340
            }
2341
        }
2342
2343
        return $extra_data;
2344
    }
2345
2346
    /** Get extra user data by field.
2347
     * @param int    user ID
2348
     * @param string the internal variable name of the field
2349
     *
2350
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2351
     */
2352
    public static function get_extra_user_data_by_field(
2353
        $user_id,
2354
        $field_variable,
2355
        $prefix = false,
2356
        $all_visibility = true,
2357
        $splitmultiple = false
2358
    ) {
2359
        $user_id = (int) $user_id;
2360
2361
        if (empty($user_id)) {
2362
            return [];
2363
        }
2364
2365
        $extra_data = [];
2366
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2367
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2368
2369
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2370
                FROM $t_uf f
2371
                WHERE f.variable = '$field_variable' ";
2372
2373
        if (!$all_visibility) {
2374
            $sql .= " AND f.visible_to_self = 1 ";
2375
        }
2376
2377
        $sql .= " AND extra_field_type = ".EntityExtraField::USER_FIELD_TYPE;
2378
        $sql .= " ORDER BY f.field_order ";
2379
2380
        $res = Database::query($sql);
2381
        if (Database::num_rows($res) > 0) {
2382
            while ($row = Database::fetch_array($res)) {
2383
                $sqlu = "SELECT value as fval FROM $t_ufv v
2384
                         INNER JOIN $t_uf f
2385
                         ON (v.field_id = f.id)
2386
                         WHERE
2387
                            extra_field_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2388
                            field_id = ".$row['id']." AND
2389
                            item_id = ".$user_id;
2390
                $resu = Database::query($sqlu);
2391
                $fval = '';
2392
                if (Database::num_rows($resu) > 0) {
2393
                    $rowu = Database::fetch_array($resu);
2394
                    $fval = $rowu['fval'];
2395
                    if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2396
                        $fval = explode(';', $rowu['fval']);
2397
                    }
2398
                }
2399
                if ($prefix) {
2400
                    $extra_data['extra_'.$row['fvar']] = $fval;
2401
                } else {
2402
                    $extra_data[$row['fvar']] = $fval;
2403
                }
2404
            }
2405
        }
2406
2407
        return $extra_data;
2408
    }
2409
2410
    /**
2411
     * Get the extra field information for a certain field (the options as well).
2412
     *
2413
     * @param int $variable The name of the field we want to know everything about
2414
     *
2415
     * @return array Array containing all the information about the extra profile field
2416
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2417
     *               as returned by the database)
2418
     *
2419
     * @author Julio Montoya
2420
     *
2421
     * @since v1.8.6
2422
     */
2423
    public static function get_extra_field_information_by_name($variable)
2424
    {
2425
        $extraField = new ExtraField('user');
2426
2427
        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...
2428
    }
2429
2430
    /**
2431
     * Get the extra field information for user tag (the options as well).
2432
     *
2433
     * @param int $variable The name of the field we want to know everything about
2434
     *
2435
     * @return array Array containing all the information about the extra profile field
2436
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2437
     *               as returned by the database)
2438
     *
2439
     * @author José Loguercio
2440
     *
2441
     * @since v1.11.0
2442
     */
2443
    public static function get_extra_field_tags_information_by_name($variable)
2444
    {
2445
        $extraField = new ExtraField('user');
2446
2447
        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...
2448
    }
2449
2450
    /**
2451
     * Get all the extra field information of a certain field (also the options).
2452
     *
2453
     * @param int $fieldId the ID of the field we want to know everything of
2454
     *
2455
     * @return array $return containing all th information about the extra profile field
2456
     *
2457
     * @author Julio Montoya
2458
     *
2459
     * @deprecated
2460
     * @since v1.8.6
2461
     */
2462
    public static function get_extra_field_information($fieldId)
2463
    {
2464
        $extraField = new ExtraField('user');
2465
2466
        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...
2467
    }
2468
2469
    /**
2470
     * Get extra user data by value.
2471
     *
2472
     * @param string $variable       the internal variable name of the field
2473
     * @param string $value          the internal value of the field
2474
     * @param bool   $all_visibility
2475
     *
2476
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2477
     */
2478
    public static function get_extra_user_data_by_value($variable, $value, $all_visibility = true)
2479
    {
2480
        $extraFieldValue = new ExtraFieldValue('user');
2481
        $extraField = new ExtraField('user');
2482
2483
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2484
2485
        if (false === $info) {
2486
            return [];
2487
        }
2488
2489
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2490
            $variable,
2491
            $value,
2492
            false,
2493
            false,
2494
            true
2495
        );
2496
2497
        $result = [];
2498
        if (!empty($data)) {
2499
            foreach ($data as $item) {
2500
                $result[] = $item['item_id'];
2501
            }
2502
        }
2503
2504
        return $result;
2505
    }
2506
2507
    /**
2508
     * Get extra user data by tags value.
2509
     *
2510
     * @param int    $fieldId the ID of the field we want to know everything of
2511
     * @param string $tag     the tag name for search
2512
     *
2513
     * @return array with extra data info of a user
2514
     *
2515
     * @author José Loguercio
2516
     *
2517
     * @since v1.11.0
2518
     */
2519
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2520
    {
2521
        $extraField = new ExtraField('user');
2522
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2523
        $array = [];
2524
        foreach ($result as $index => $user) {
2525
            $array[] = $user['user_id'];
2526
        }
2527
2528
        return $array;
2529
    }
2530
2531
    /**
2532
     * Get extra user data by field variable.
2533
     *
2534
     * @param string $variable field variable
2535
     *
2536
     * @return array data
2537
     */
2538
    public static function get_extra_user_data_by_field_variable($variable)
2539
    {
2540
        $extraInfo = self::get_extra_field_information_by_name($variable);
2541
        $field_id = (int) $extraInfo['id'];
2542
2543
        $extraField = new ExtraFieldValue('user');
2544
        $data = $extraField->getValuesByFieldId($field_id);
2545
2546
        if (!empty($data)) {
2547
            foreach ($data as $row) {
2548
                $user_id = $row['item_id'];
2549
                $data[$user_id] = $row;
2550
            }
2551
        }
2552
2553
        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...
2554
    }
2555
2556
    /**
2557
     * Get extra user data tags by field variable.
2558
     *
2559
     * @param string $variable field variable
2560
     *
2561
     * @return array
2562
     */
2563
    public static function get_extra_user_data_for_tags($variable)
2564
    {
2565
        $data = self::get_extra_field_tags_information_by_name($variable);
2566
2567
        return $data;
2568
    }
2569
2570
    /**
2571
     * Gives a list of [session_category][session_id] for the current user.
2572
     *
2573
     * @param int  $user_id
2574
     * @param bool $is_time_over                 whether to fill the first element or not
2575
     *                                           (to give space for courses out of categories)
2576
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2577
     * @param bool $ignoreTimeLimit              ignore time start/end
2578
     * @param bool $getCount
2579
     *
2580
     * @return array list of statuses [session_category][session_id]
2581
     *
2582
     * @todo ensure multiple access urls are managed correctly
2583
     */
2584
    public static function get_sessions_by_category(
2585
        $user_id,
2586
        $is_time_over = true,
2587
        $ignore_visibility_for_admins = false,
2588
        $ignoreTimeLimit = false,
2589
        $getCount = false
2590
    ) {
2591
        $user_id = (int) $user_id;
2592
2593
        if (empty($user_id)) {
2594
            return [];
2595
        }
2596
2597
        $allowOrder = api_get_configuration_value('session_list_order');
2598
        $position = '';
2599
        if ($allowOrder) {
2600
            $position = ', s.position AS position ';
2601
        }
2602
2603
        // Get the list of sessions per user
2604
        $now = new DateTime('now', new DateTimeZone('UTC'));
2605
2606
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2607
        // join would not catch session-courses where the user is general
2608
        // session coach but which do not have students nor coaches registered
2609
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
2610
2611
        if (!$getCount) {
2612
            $dqlSelect = " DISTINCT
2613
                s.id,
2614
                s.name,
2615
                s.accessStartDate AS access_start_date,
2616
                s.accessEndDate AS access_end_date,
2617
                s.duration,
2618
                sc.id AS session_category_id,
2619
                sc.name AS session_category_name,
2620
                sc.dateStart AS session_category_date_start,
2621
                sc.dateEnd AS session_category_date_end,
2622
                s.coachAccessStartDate AS coach_access_start_date,
2623
                s.coachAccessEndDate AS coach_access_end_date,
2624
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
2625
                $position
2626
            ";
2627
        }
2628
2629
        $dql = "SELECT $dqlSelect
2630
                FROM ChamiloCoreBundle:Session AS s
2631
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2632
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2633
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc ";
2634
2635
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
2636
        // is awfully inefficient for large sets of data (1m25s for 58K
2637
        // sessions, BT#14115) but executing a similar query twice and grouping
2638
        // the results afterwards in PHP takes about 1/1000th of the time
2639
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
2640
        $dqlStudent = $dql." WHERE scu.user = :user AND url.url = :url ";
2641
        $dqlCoach = $dql." WHERE s.generalCoach = :user AND url.url = :url ";
2642
2643
        // Default order
2644
        $order = 'ORDER BY sc.name, s.name';
2645
2646
        // Order by date if showing all sessions
2647
        $showAllSessions = true === api_get_configuration_value('show_all_sessions_on_my_course_page');
2648
        if ($showAllSessions) {
2649
            $order = 'ORDER BY s.accessStartDate';
2650
        }
2651
2652
        // Order by position
2653
        if ($allowOrder) {
2654
            $order = 'ORDER BY s.position';
2655
        }
2656
2657
        // Order by dates according to settings
2658
        $orderBySettings = api_get_configuration_value('my_courses_session_order');
2659
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
2660
            $field = $orderBySettings['field'];
2661
            $orderSetting = $orderBySettings['order'];
2662
            switch ($field) {
2663
                case 'start_date':
2664
                    $order = " ORDER BY s.accessStartDate $orderSetting";
2665
                    break;
2666
                case 'end_date':
2667
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
2668
                    if ('asc' == $orderSetting) {
2669
                        // Put null values at the end
2670
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
2671
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
2672
                    }
2673
                    break;
2674
                case 'name':
2675
                    $order = " ORDER BY s.name $orderSetting ";
2676
                    break;
2677
            }
2678
        }
2679
2680
        $dqlStudent .= $order;
2681
        $dqlCoach .= $order;
2682
2683
        $accessUrlId = api_get_current_access_url_id();
2684
        $dqlStudent = Database::getManager()
2685
            ->createQuery($dqlStudent)
2686
            ->setParameters(
2687
                ['user' => $user_id, 'url' => $accessUrlId]
2688
            )
2689
        ;
2690
        $dqlCoach = Database::getManager()
2691
            ->createQuery($dqlCoach)
2692
            ->setParameters(
2693
                ['user' => $user_id, 'url' => $accessUrlId]
2694
            )
2695
        ;
2696
2697
        if ($getCount) {
2698
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
2699
        }
2700
2701
        $sessionDataStudent = $dqlStudent->getResult();
2702
        $sessionDataCoach = $dqlCoach->getResult();
2703
2704
        $sessionData = [];
2705
        // First fill $sessionData with student sessions
2706
        if (!empty($sessionDataStudent)) {
2707
            foreach ($sessionDataStudent as $row) {
2708
                $sessionData[$row['id']] = $row;
2709
            }
2710
        }
2711
        // Overwrite session data of the user as a student with session data
2712
        // of the user as a coach.
2713
        // There shouldn't be such duplicate rows, but just in case...
2714
        if (!empty($sessionDataCoach)) {
2715
            foreach ($sessionDataCoach as $row) {
2716
                $sessionData[$row['id']] = $row;
2717
            }
2718
        }
2719
2720
        $collapsable = api_get_configuration_value('allow_user_session_collapsable');
2721
        $extraField = new ExtraFieldValue('session');
2722
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
2723
2724
        if (empty($sessionData)) {
2725
            return [];
2726
        }
2727
        $categories = [];
2728
        foreach ($sessionData as $row) {
2729
            $session_id = $row['id'];
2730
            $coachList = SessionManager::getCoachesBySession($session_id);
2731
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
2732
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
2733
            $courseList = self::get_courses_list_by_session($user_id, $session_id);
2734
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
2735
2736
            // User portal filters:
2737
            if (false === $ignoreTimeLimit) {
2738
                if ($is_time_over) {
2739
                    // History
2740
                    if ($row['duration']) {
2741
                        if ($daysLeft >= 0) {
2742
                            continue;
2743
                        }
2744
                    } else {
2745
                        if (empty($row['access_end_date'])) {
2746
                            continue;
2747
                        } else {
2748
                            if ($row['access_end_date'] > $now) {
2749
                                continue;
2750
                            }
2751
                        }
2752
                    }
2753
                } else {
2754
                    // Current user portal
2755
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
2756
                    $isCoachOfCourse = in_array($user_id, $coachList);
2757
2758
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
2759
                        // Teachers can access the session depending in the access_coach date
2760
                    } else {
2761
                        if ($row['duration']) {
2762
                            if ($daysLeft <= 0) {
2763
                                continue;
2764
                            }
2765
                        } else {
2766
                            if (isset($row['access_end_date']) &&
2767
                                !empty($row['access_end_date'])
2768
                            ) {
2769
                                if ($row['access_end_date'] <= $now) {
2770
                                    continue;
2771
                                }
2772
                            }
2773
                        }
2774
                    }
2775
                }
2776
            }
2777
2778
            $categories[$row['session_category_id']]['session_category'] = [
2779
                'id' => $row['session_category_id'],
2780
                'name' => $row['session_category_name'],
2781
                'date_start' => $categoryStart,
2782
                'date_end' => $categoryEnd,
2783
            ];
2784
2785
            $visibility = api_get_session_visibility(
2786
                $session_id,
2787
                null,
2788
                $ignore_visibility_for_admins
2789
            );
2790
2791
            if (SESSION_VISIBLE != $visibility) {
2792
                // Course Coach session visibility.
2793
                $blockedCourseCount = 0;
2794
                $closedVisibilityList = [
2795
                    COURSE_VISIBILITY_CLOSED,
2796
                    COURSE_VISIBILITY_HIDDEN,
2797
                ];
2798
2799
                foreach ($courseList as $course) {
2800
                    // Checking session visibility
2801
                    $sessionCourseVisibility = api_get_session_visibility(
2802
                        $session_id,
2803
                        $course['real_id'],
2804
                        $ignore_visibility_for_admins
2805
                    );
2806
2807
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
2808
                    if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
2809
                        $blockedCourseCount++;
2810
                    }
2811
                }
2812
2813
                // If all courses are blocked then no show in the list.
2814
                if ($blockedCourseCount === count($courseList)) {
2815
                    $visibility = SESSION_INVISIBLE;
2816
                } else {
2817
                    $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...
2818
                }
2819
            }
2820
2821
            switch ($visibility) {
2822
                case SESSION_VISIBLE_READ_ONLY:
2823
                case SESSION_VISIBLE:
2824
                case SESSION_AVAILABLE:
2825
                    break;
2826
                case SESSION_INVISIBLE:
2827
                    if (false === $ignore_visibility_for_admins) {
2828
                        continue 2;
2829
                    }
2830
            }
2831
2832
            $collapsed = '';
2833
            $collapsedAction = '';
2834
            if ($collapsable) {
2835
                $collapsableData = SessionManager::getCollapsableData(
2836
                    $user_id,
2837
                    $session_id,
2838
                    $extraField,
2839
                    $collapsableLink
2840
                );
2841
                $collapsed = $collapsableData['collapsed'];
2842
                $collapsedAction = $collapsableData['collapsable_link'];
2843
            }
2844
2845
            $categories[$row['session_category_id']]['sessions'][] = [
2846
                'session_name' => $row['name'],
2847
                'session_id' => $row['id'],
2848
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
2849
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
2850
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
2851
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
2852
                'courses' => $courseList,
2853
                'collapsed' => $collapsed,
2854
                'collapsable_link' => $collapsedAction,
2855
                'duration' => $row['duration'],
2856
            ];
2857
        }
2858
2859
        return $categories;
2860
    }
2861
2862
    /**
2863
     * Gives a list of [session_id-course_code] => [status] for the current user.
2864
     *
2865
     * @param int $user_id
2866
     * @param int $sessionLimit
2867
     *
2868
     * @return array list of statuses (session_id-course_code => status)
2869
     */
2870
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
2871
    {
2872
        // Database Table Definitions
2873
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2874
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2875
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2876
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
2877
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2878
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2879
        $tblCourseCategory = Database::get_main_table(TABLE_MAIN_CATEGORY);
2880
2881
        $user_id = (int) $user_id;
2882
2883
        if (empty($user_id)) {
2884
            return [];
2885
        }
2886
2887
        // We filter the courses from the URL
2888
        $join_access_url = $where_access_url = '';
2889
        if (api_get_multiple_access_url()) {
2890
            $access_url_id = api_get_current_access_url_id();
2891
            if (-1 != $access_url_id) {
2892
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2893
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
2894
                $where_access_url = " AND access_url_id = $access_url_id ";
2895
            }
2896
        }
2897
2898
        // Courses in which we subscribed out of any session
2899
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
2900
2901
        $sql = "SELECT
2902
                    course.code,
2903
                    course_rel_user.status course_rel_status,
2904
                    course_rel_user.sort sort,
2905
                    course_rel_user.user_course_cat user_course_cat
2906
                 FROM $tbl_course_user course_rel_user
2907
                 LEFT JOIN $tbl_course course
2908
                 ON course.id = course_rel_user.c_id
2909
                 LEFT JOIN $tbl_user_course_category user_course_category
2910
                 ON course_rel_user.user_course_cat = user_course_category.id
2911
                 $join_access_url
2912
                 WHERE
2913
                    course_rel_user.user_id = '".$user_id."' AND
2914
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
2915
                    $where_access_url
2916
                 ORDER BY user_course_category.sort, course_rel_user.sort, course.title ASC";
2917
2918
        $course_list_sql_result = Database::query($sql);
2919
2920
        $personal_course_list = [];
2921
        if (Database::num_rows($course_list_sql_result) > 0) {
2922
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
2923
                $course_info = api_get_course_info($result_row['code']);
2924
                $result_row['course_info'] = $course_info;
2925
                $personal_course_list[] = $result_row;
2926
            }
2927
        }
2928
2929
        $coachCourseConditions = '';
2930
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
2931
        if (api_is_allowed_to_create_course()) {
2932
            $sessionListFromCourseCoach = [];
2933
            $sql = " SELECT DISTINCT session_id
2934
                    FROM $tbl_session_course_user
2935
                    WHERE user_id = $user_id AND status = 2 ";
2936
2937
            $result = Database::query($sql);
2938
            if (Database::num_rows($result)) {
2939
                $result = Database::store_result($result);
2940
                foreach ($result as $session) {
2941
                    $sessionListFromCourseCoach[] = $session['session_id'];
2942
                }
2943
            }
2944
            if (!empty($sessionListFromCourseCoach)) {
2945
                $condition = implode("','", $sessionListFromCourseCoach);
2946
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
2947
            }
2948
        }
2949
2950
        // Get the list of sessions where the user is subscribed
2951
        // This is divided into two different queries
2952
        $sessions = [];
2953
        $sessionLimitRestriction = '';
2954
        if (!empty($sessionLimit)) {
2955
            $sessionLimit = (int) $sessionLimit;
2956
            $sessionLimitRestriction = "LIMIT $sessionLimit";
2957
        }
2958
2959
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
2960
                FROM $tbl_session_user su INNER JOIN $tbl_session s
2961
                ON (s.id = su.session_id)
2962
                WHERE (
2963
                    su.user_id = $user_id AND
2964
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
2965
                )
2966
                $coachCourseConditions
2967
                ORDER BY access_start_date, access_end_date, name
2968
                $sessionLimitRestriction
2969
        ";
2970
2971
        $result = Database::query($sql);
2972
        if (Database::num_rows($result) > 0) {
2973
            while ($row = Database::fetch_assoc($result)) {
2974
                $sessions[$row['id']] = $row;
2975
            }
2976
        }
2977
2978
        $sql = "SELECT DISTINCT
2979
                id, name, access_start_date, access_end_date
2980
                FROM $tbl_session s
2981
                WHERE (
2982
                    id_coach = $user_id
2983
                )
2984
                $coachCourseConditions
2985
                ORDER BY access_start_date, access_end_date, name";
2986
2987
        $result = Database::query($sql);
2988
        if (Database::num_rows($result) > 0) {
2989
            while ($row = Database::fetch_assoc($result)) {
2990
                if (empty($sessions[$row['id']])) {
2991
                    $sessions[$row['id']] = $row;
2992
                }
2993
            }
2994
        }
2995
2996
        if (api_is_allowed_to_create_course()) {
2997
            foreach ($sessions as $enreg) {
2998
                $session_id = $enreg['id'];
2999
                $session_visibility = api_get_session_visibility($session_id);
3000
3001
                if (SESSION_INVISIBLE == $session_visibility) {
3002
                    continue;
3003
                }
3004
3005
                // This query is horribly slow when more than a few thousand
3006
                // users and just a few sessions to which they are subscribed
3007
                $sql = "SELECT DISTINCT
3008
                        course.code code,
3009
                        course.title i,
3010
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
3011
                        email, course.course_language l,
3012
                        1 sort,
3013
                        course_category.code user_course_cat,
3014
                        access_start_date,
3015
                        access_end_date,
3016
                        session.id as session_id,
3017
                        session.name as session_name
3018
                    FROM $tbl_session_course_user as session_course_user
3019
                    INNER JOIN $tbl_course AS course
3020
                        ON course.id = session_course_user.c_id
3021
                    LEFT JOIN $tblCourseCategory course_category ON course.category_id = course_category.id
3022
                    INNER JOIN $tbl_session as session
3023
                        ON session.id = session_course_user.session_id
3024
                    LEFT JOIN $tbl_user as user
3025
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
3026
                    WHERE
3027
                        session_course_user.session_id = $session_id AND (
3028
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
3029
                            OR session.id_coach = $user_id
3030
                        )
3031
                    ORDER BY i";
3032
                $course_list_sql_result = Database::query($sql);
3033
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3034
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3035
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3036
                    $personal_course_list[$key] = $result_row;
3037
                }
3038
            }
3039
        }
3040
3041
        foreach ($sessions as $enreg) {
3042
            $session_id = $enreg['id'];
3043
            $session_visibility = api_get_session_visibility($session_id);
3044
            if (SESSION_INVISIBLE == $session_visibility) {
3045
                continue;
3046
            }
3047
3048
            /* This query is very similar to the above query,
3049
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3050
            $sql = "SELECT DISTINCT
3051
                course.code code,
3052
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
3053
                email,
3054
                course.course_language l,
3055
                1 sort,
3056
                course_category.code user_course_cat,
3057
                access_start_date,
3058
                access_end_date,
3059
                session.id as session_id,
3060
                session.name as session_name,
3061
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
3062
            FROM $tbl_session_course_user as session_course_user
3063
            INNER JOIN $tbl_course AS course
3064
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
3065
            LEFT JOIN $tblCourseCategory course_category ON course.category_id = course_category.id
3066
            INNER JOIN $tbl_session as session
3067
            ON session_course_user.session_id = session.id
3068
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
3069
            WHERE session_course_user.user_id = $user_id
3070
            ORDER BY i";
3071
3072
            $course_list_sql_result = Database::query($sql);
3073
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3074
                $result_row['course_info'] = api_get_course_info($result_row['code']);
3075
                $key = $result_row['session_id'].' - '.$result_row['code'];
3076
                if (!isset($personal_course_list[$key])) {
3077
                    $personal_course_list[$key] = $result_row;
3078
                }
3079
            }
3080
        }
3081
3082
        return $personal_course_list;
3083
    }
3084
3085
    /**
3086
     * Gives a list of courses for the given user in the given session.
3087
     *
3088
     * @param int $user_id
3089
     * @param int $session_id
3090
     *
3091
     * @return array list of statuses (session_id-course_code => status)
3092
     */
3093
    public static function get_courses_list_by_session($user_id, $session_id)
3094
    {
3095
        // Database Table Definitions
3096
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3097
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
3098
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3099
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3100
3101
        $user_id = (int) $user_id;
3102
        $session_id = (int) $session_id;
3103
        // We filter the courses from the URL
3104
        $join_access_url = $where_access_url = '';
3105
        if (api_get_multiple_access_url()) {
3106
            $urlId = api_get_current_access_url_id();
3107
            if (-1 != $urlId) {
3108
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3109
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
3110
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
3111
            }
3112
        }
3113
3114
        /* This query is very similar to the query below, but it will check the
3115
        session_rel_course_user table if there are courses registered
3116
        to our user or not */
3117
        $sql = "SELECT DISTINCT
3118
                    c.title,
3119
                    c.visibility,
3120
                    c.id as real_id,
3121
                    c.code as course_code,
3122
                    sc.position,
3123
                    c.unsubscribe
3124
                FROM $tbl_session_course_user as scu
3125
                INNER JOIN $tbl_session_course sc
3126
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3127
                INNER JOIN $tableCourse as c
3128
                ON (scu.c_id = c.id)
3129
                $join_access_url
3130
                WHERE
3131
                    scu.user_id = $user_id AND
3132
                    scu.session_id = $session_id
3133
                    $where_access_url
3134
                ORDER BY sc.position ASC";
3135
3136
        $myCourseList = [];
3137
        $courses = [];
3138
        $result = Database::query($sql);
3139
        if (Database::num_rows($result) > 0) {
3140
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3141
                $result_row['status'] = 5;
3142
                if (!in_array($result_row['real_id'], $courses)) {
3143
                    $position = $result_row['position'];
3144
                    if (!isset($myCourseList[$position])) {
3145
                        $myCourseList[$position] = $result_row;
3146
                    } else {
3147
                        $myCourseList[] = $result_row;
3148
                    }
3149
                    $courses[] = $result_row['real_id'];
3150
                }
3151
            }
3152
        }
3153
3154
        if (api_is_allowed_to_create_course()) {
3155
            $sql = "SELECT DISTINCT
3156
                        c.title,
3157
                        c.visibility,
3158
                        c.id as real_id,
3159
                        c.code as course_code,
3160
                        sc.position,
3161
                        c.unsubscribe
3162
                    FROM $tbl_session_course_user as scu
3163
                    INNER JOIN $tbl_session as s
3164
                    ON (scu.session_id = s.id)
3165
                    INNER JOIN $tbl_session_course sc
3166
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3167
                    INNER JOIN $tableCourse as c
3168
                    ON (scu.c_id = c.id)
3169
                    $join_access_url
3170
                    WHERE
3171
                      s.id = $session_id AND
3172
                      (
3173
                        (scu.user_id = $user_id AND scu.status = 2) OR
3174
                        s.id_coach = $user_id
3175
                      )
3176
                    $where_access_url
3177
                    ORDER BY sc.position ASC";
3178
            $result = Database::query($sql);
3179
3180
            if (Database::num_rows($result) > 0) {
3181
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3182
                    $result_row['status'] = 2;
3183
                    if (!in_array($result_row['real_id'], $courses)) {
3184
                        $position = $result_row['position'];
3185
                        if (!isset($myCourseList[$position])) {
3186
                            $myCourseList[$position] = $result_row;
3187
                        } else {
3188
                            $myCourseList[] = $result_row;
3189
                        }
3190
                        $courses[] = $result_row['real_id'];
3191
                    }
3192
                }
3193
            }
3194
        }
3195
3196
        if (api_is_drh()) {
3197
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3198
            $sessionList = array_keys($sessionList);
3199
            if (in_array($session_id, $sessionList)) {
3200
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3201
                if (!empty($courseList)) {
3202
                    foreach ($courseList as $course) {
3203
                        if (!in_array($course['id'], $courses)) {
3204
                            $position = $course['position'];
3205
                            if (!isset($myCourseList[$position])) {
3206
                                $myCourseList[$position] = $course;
3207
                            } else {
3208
                                $myCourseList[] = $course;
3209
                            }
3210
                        }
3211
                    }
3212
                }
3213
            }
3214
        } else {
3215
            //check if user is general coach for this session
3216
            $sessionInfo = api_get_session_info($session_id);
3217
            if ($sessionInfo['id_coach'] == $user_id) {
3218
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3219
                if (!empty($courseList)) {
3220
                    foreach ($courseList as $course) {
3221
                        if (!in_array($course['id'], $courses)) {
3222
                            $position = $course['position'];
3223
                            if (!isset($myCourseList[$position])) {
3224
                                $myCourseList[$position] = $course;
3225
                            } else {
3226
                                $myCourseList[] = $course;
3227
                            }
3228
                        }
3229
                    }
3230
                }
3231
            }
3232
        }
3233
3234
        if (!empty($myCourseList)) {
3235
            ksort($myCourseList);
3236
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
3237
            if (empty($checkPosition)) {
3238
                // The session course list doesn't have any position,
3239
                // then order the course list by course code
3240
                $list = array_column($myCourseList, 'course_code');
3241
                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

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

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