Passed
Push — master ( 4f5cd5...8c8eae )
by Julito
10:19
created

UserManager::deleteUserPicture()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
8
use Chamilo\CoreBundle\Entity\SkillRelUserComment;
9
use Chamilo\CoreBundle\Entity\User;
10
use Chamilo\CoreBundle\Framework\Container;
11
use Chamilo\CoreBundle\Repository\GroupRepository;
12
use Chamilo\CoreBundle\Repository\Node\UserRepository;
13
use ChamiloSession as Session;
14
use Symfony\Component\HttpFoundation\File\UploadedFile;
15
16
/**
17
 * Class UserManager.
18
 *
19
 * This library provides functions for user management.
20
 * Include/require it in your code to use its functionality.
21
 *
22
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
23
 */
24
class UserManager
25
{
26
    // This constants are deprecated use the constants located in ExtraField
27
    public const USER_FIELD_TYPE_TEXT = 1;
28
    public const USER_FIELD_TYPE_TEXTAREA = 2;
29
    public const USER_FIELD_TYPE_RADIO = 3;
30
    public const USER_FIELD_TYPE_SELECT = 4;
31
    public const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
32
    public const USER_FIELD_TYPE_DATE = 6;
33
    public const USER_FIELD_TYPE_DATETIME = 7;
34
    public const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
35
    public const USER_FIELD_TYPE_DIVIDER = 9;
36
    public const USER_FIELD_TYPE_TAG = 10;
37
    public const USER_FIELD_TYPE_TIMEZONE = 11;
38
    public const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
39
    public const USER_FIELD_TYPE_FILE = 13;
40
    public const USER_FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
41
42
    private static $encryptionMethod;
43
44
    /**
45
     * Constructor.
46
     *
47
     * @assert () === null
48
     */
49
    public function __construct()
50
    {
51
    }
52
53
    /**
54
     * Repository is use to query the DB, selects, etc.
55
     *
56
     * @return UserRepository
57
     */
58
    public static function getRepository()
59
    {
60
        return Container::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               Only needed during installation.
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
182
        if ($addUserToNode && 0 === $creatorId) {
183
            Display::addFlash(
184
                Display::return_message(get_lang('A user creator is needed'))
185
            );
186
            return false;
187
        }
188
189
        $creatorInfo = api_get_user_info($creatorId);
190
        $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
191
192
        // First check if the login exists.
193
        if (!self::is_username_available($loginName)) {
194
            Display::addFlash(
195
                Display::return_message(get_lang('This login is already taken !'))
196
            );
197
198
            return false;
199
        }
200
201
        global $_configuration;
202
        $original_password = $password;
203
204
        $access_url_id = 1;
205
        if (api_get_multiple_access_url()) {
206
            $access_url_id = api_get_current_access_url_id();
207
        } else {
208
            // In some cases, the first access_url ID might be different from 1
209
            // for example when using a DB cluster or hacking the DB manually.
210
            // In this case, we want the first row, not necessarily "1".
211
            $accessUrlRepository = Container::getAccessUrlRepository();
212
            $accessUrl = $accessUrlRepository->getFirstId();
213
            if (!empty($accessUrl[0]) && !empty($accessUrl[0][1])) {
214
                $access_url_id = $accessUrl[0][1];
215
            }
216
        }
217
218
        if (isset($_configuration[$access_url_id]) &&
219
            is_array($_configuration[$access_url_id]) &&
220
            isset($_configuration[$access_url_id]['hosting_limit_users']) &&
221
            $_configuration[$access_url_id]['hosting_limit_users'] > 0) {
222
            $num = self::get_number_of_users();
223
            if ($num >= $_configuration[$access_url_id]['hosting_limit_users']) {
224
                api_warn_hosting_contact('hosting_limit_users');
225
                Display::addFlash(
226
                    Display::return_message(
227
                        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.'),
228
                        'warning'
229
                    )
230
                );
231
232
                return false;
233
            }
234
        }
235
236
        if (1 === $status &&
237
            isset($_configuration[$access_url_id]) &&
238
            is_array($_configuration[$access_url_id]) &&
239
            isset($_configuration[$access_url_id]['hosting_limit_teachers']) &&
240
            $_configuration[$access_url_id]['hosting_limit_teachers'] > 0
241
        ) {
242
            $num = self::get_number_of_users(1);
243
            if ($num >= $_configuration[$access_url_id]['hosting_limit_teachers']) {
244
                Display::addFlash(
245
                    Display::return_message(
246
                        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.'),
247
                        'warning'
248
                    )
249
                );
250
                api_warn_hosting_contact('hosting_limit_teachers');
251
252
                return false;
253
            }
254
        }
255
256
        if (empty($password)) {
257
            if (PLATFORM_AUTH_SOURCE === $authSource) {
258
                Display::addFlash(
259
                    Display::return_message(
260
                        get_lang('Required field').': '.get_lang(
261
                            'Password'
262
                        ),
263
                        'warning'
264
                    )
265
                );
266
267
                return false;
268
            }
269
270
            // We use the authSource as password.
271
            // The real validation will be by processed by the auth
272
            // source not Chamilo
273
            $password = $authSource;
274
        }
275
276
        // Checking the user language
277
        $languages = api_get_languages();
278
        $language = strtolower($language);
279
280
        // Default to english
281
        if (!in_array($language, array_keys($languages), true)) {
282
            $language = 'en';
283
        }
284
285
        $currentDate = api_get_utc_datetime();
286
        $now = new DateTime();
287
288
        if (empty($expirationDate) || '0000-00-00 00:00:00' === $expirationDate) {
289
            $expirationDate = null;
290
            // Default expiration date
291
            // if there is a default duration of a valid account then
292
            // we have to change the expiration_date accordingly
293
            // Accept 0000-00-00 00:00:00 as a null value to avoid issues with
294
            // third party code using this method with the previous (pre-1.10)
295
            // value of 0000...
296
            /*if ('' != api_get_setting('account_valid_duration')) {
297
                $expirationDate = new DateTime($currentDate);
298
                $days = (int) api_get_setting('account_valid_duration');
299
                $expirationDate->modify('+'.$days.' day');
300
            }*/
301
        } else {
302
            $expirationDate = api_get_utc_datetime($expirationDate);
303
            $expirationDate = new \DateTime($expirationDate, new DateTimeZone('UTC'));
304
        }
305
306
        $user = new User();
307
        $user
308
            ->setLastname($lastName)
309
            ->setFirstname($firstName)
310
            ->setUsername($loginName)
311
            ->setStatus($status)
312
            ->setPlainPassword($password)
313
            ->setEmail($email)
314
            ->setOfficialCode($official_code)
315
            ->setCreatorId($creatorId)
316
            ->setAuthSource($authSource)
317
            ->setPhone($phone)
318
            ->setAddress($address)
319
            ->setLocale($language)
320
            ->setRegistrationDate($now)
321
            ->setHrDeptId($hr_dept_id)
322
            ->setActive($active)
323
            ->setEnabled($active)
324
            ->setTimezone(api_get_timezone())
325
        ;
326
327
        if (!empty($expirationDate)) {
328
            $user->setExpirationDate($expirationDate);
329
        }
330
331
        $em = Database::getManager();
332
        $repo = Container::getUserRepository();
333
        $repo->updateUser($user, false);
334
335
        // Add user as a node
336
        if ($addUserToNode) {
337
            $resourceNode = new ResourceNode();
338
            $resourceNode
339
                ->setTitle($loginName)
340
                ->setCreator(api_get_user_entity($creatorId))
341
                ->setResourceType($repo->getResourceType())
342
            ;
343
            $em->persist($resourceNode);
344
            $user->setResourceNode($resourceNode);
345
        }
346
347
        $em->persist($user);
348
        $em->flush();
349
350
        // Add user to a group
351
        $statusToGroup = [
352
            COURSEMANAGER => 'TEACHER',
353
            STUDENT => 'STUDENT',
354
            DRH => 'RRHH',
355
            SESSIONADMIN => 'SESSION_ADMIN',
356
            STUDENT_BOSS => 'STUDENT_BOSS',
357
            INVITEE => 'INVITEE',
358
        ];
359
360
        if (isset($statusToGroup[$status])) {
361
            $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

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

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

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

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

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