Passed
Push — master ( 0c074a...115052 )
by Julito
10:13 queued 12s
created

UserManager   F

Complexity

Total Complexity 762

Size/Duplication

Total Lines 6364
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 2903
c 0
b 0
f 0
dl 0
loc 6364
rs 0.8
wmc 762

How to fix   Complexity   

Complex Class

Complex classes like UserManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use UserManager, and based on these observations, apply Extract Interface, too.

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;
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);
97
98
        return $validPassword;
99
    }
100
101
    /**
102
     * @param int    $userId
103
     * @param string $password
104
     */
105
    public static function updatePassword($userId, $password)
106
    {
107
        $user = api_get_user_entity($userId);
108
        $userManager = self::getRepository();
109
        $user->setPlainPassword($password);
110
        $userManager->updateUser($user, true);
111
    }
112
113
    /**
114
     * Creates a new user for the platform.
115
     *
116
     * @author Hugues Peeters <[email protected]>,
117
     * @author Roan Embrechts <[email protected]>
118
     *
119
     * @param string        $firstName
120
     * @param string        $lastName
121
     * @param int           $status                  (1 for course tutor, 5 for student, 6 for anonymous)
122
     * @param string        $email
123
     * @param string        $loginName
124
     * @param string        $password
125
     * @param string        $official_code           Any official code (optional)
126
     * @param string        $language                User language    (optional)
127
     * @param string        $phone                   Phone number    (optional)
128
     * @param string        $picture_uri             Picture URI        (optional)
129
     * @param string        $authSource              Authentication source (defaults to 'platform', dependind on constant)
130
     * @param string        $expirationDate          Account expiration date (optional, defaults to null)
131
     * @param int           $active                  Whether the account is enabled or disabled by default
132
     * @param int           $hr_dept_id              The department of HR in which the user is registered (defaults to 0)
133
     * @param array         $extra                   Extra fields (labels must be prefixed by "extra_")
134
     * @param string        $encrypt_method          Used if password is given encrypted. Set to an empty string by default
135
     * @param bool          $send_mail
136
     * @param bool          $isAdmin
137
     * @param string        $address
138
     * @param bool          $sendEmailToAllAdmins
139
     * @param FormValidator $form
140
     * @param int           $creatorId
141
     * @param array         $emailTemplate
142
     * @param string        $redirectToURLAfterLogin
143
     * @param bool          $addUserToNode
144
     *
145
     * @return mixed new user id - if the new user creation succeeds, false otherwise
146
     * @desc The function tries to retrieve user id from the session.
147
     * If it exists, the current user id is the creator id. If a problem arises,
148
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
149
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
150
     */
151
    public static function create_user(
152
        $firstName,
153
        $lastName,
154
        $status,
155
        $email,
156
        $loginName,
157
        $password,
158
        $official_code = '',
159
        $language = '',
160
        $phone = '',
161
        $picture_uri = '',
162
        $authSource = null,
163
        $expirationDate = null,
164
        $active = 1,
165
        $hr_dept_id = 0,
166
        $extra = [],
167
        $encrypt_method = '',
168
        $send_mail = false,
169
        $isAdmin = false,
170
        $address = '',
171
        $sendEmailToAllAdmins = false,
172
        $form = null,
173
        $creatorId = 0,
174
        $emailTemplate = [],
175
        $redirectToURLAfterLogin = '',
176
        $addUserToNode = true,
177
        $addUserToUrl = true
178
    ) {
179
        $authSource = !empty($authSource) ? $authSource : PLATFORM_AUTH_SOURCE;
180
        $creatorId = empty($creatorId) ? api_get_user_id() : 0;
181
        $creatorInfo = api_get_user_info($creatorId);
182
        $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
183
184
        // First check wether the login already exists
185
        if (!self::is_username_available($loginName)) {
186
            Display::addFlash(
187
                Display::return_message(get_lang('This login is already taken !'))
188
            );
189
190
            return false;
191
        }
192
193
        global $_configuration;
194
        $original_password = $password;
195
196
        $access_url_id = 1;
197
        if (api_get_multiple_access_url()) {
198
            $access_url_id = api_get_current_access_url_id();
199
        } else {
200
            // In some cases, the first access_url ID might be different from 1
201
            // for example when using a DB cluster or hacking the DB manually.
202
            // In this case, we want the first row, not necessarily "1".
203
            $accessUrlRepository = Container::getAccessUrlRepository();
204
            $accessUrl = $accessUrlRepository->getFirstId();
205
            if (!empty($accessUrl[0]) && !empty($accessUrl[0][1])) {
206
                $access_url_id = $accessUrl[0][1];
207
            }
208
        }
209
210
        if (isset($_configuration[$access_url_id]) &&
211
            is_array($_configuration[$access_url_id]) &&
212
            isset($_configuration[$access_url_id]['hosting_limit_users']) &&
213
            $_configuration[$access_url_id]['hosting_limit_users'] > 0) {
214
            $num = self::get_number_of_users();
215
            if ($num >= $_configuration[$access_url_id]['hosting_limit_users']) {
216
                api_warn_hosting_contact('hosting_limit_users');
217
                Display::addFlash(
218
                    Display::return_message(
219
                        get_lang('Sorry, this installation has a users limit, which has now been reached. To increase the number of users allowed on this Chamilo installation, please contact your hosting provider or, if available, upgrade to a superior hosting plan.'),
220
                        'warning'
221
                    )
222
                );
223
224
                return false;
225
            }
226
        }
227
228
        if (1 === $status &&
229
            isset($_configuration[$access_url_id]) &&
230
            is_array($_configuration[$access_url_id]) &&
231
            isset($_configuration[$access_url_id]['hosting_limit_teachers']) &&
232
            $_configuration[$access_url_id]['hosting_limit_teachers'] > 0
233
        ) {
234
            $num = self::get_number_of_users(1);
235
            if ($num >= $_configuration[$access_url_id]['hosting_limit_teachers']) {
236
                Display::addFlash(
237
                    Display::return_message(
238
                        get_lang('Sorry, this installation has a teachers limit, which has now been reached. To increase the number of teachers allowed on this Chamilo installation, please contact your hosting provider or, if available, upgrade to a superior hosting plan.'),
239
                        'warning'
240
                    )
241
                );
242
                api_warn_hosting_contact('hosting_limit_teachers');
243
244
                return false;
245
            }
246
        }
247
248
        if (empty($password)) {
249
            if (PLATFORM_AUTH_SOURCE === $authSource) {
250
                Display::addFlash(
251
                    Display::return_message(
252
                        get_lang('Required field').': '.get_lang(
253
                            'Password'
254
                        ),
255
                        'warning'
256
                    )
257
                );
258
259
                return false;
260
            }
261
262
            // We use the authSource as password.
263
            // The real validation will be by processed by the auth
264
            // source not Chamilo
265
            $password = $authSource;
266
        }
267
268
        // Checking the user language
269
        $languages = api_get_languages();
270
        $language = strtolower($language);
271
272
        // Default to english
273
        if (!in_array($language, array_keys($languages), true)) {
274
            $language = 'en';
275
        }
276
277
        $currentDate = api_get_utc_datetime();
278
        $now = new DateTime();
279
280
        if (empty($expirationDate) || '0000-00-00 00:00:00' === $expirationDate) {
281
            $expirationDate = null;
282
        // Default expiration date
283
            // if there is a default duration of a valid account then
284
            // we have to change the expiration_date accordingly
285
            // Accept 0000-00-00 00:00:00 as a null value to avoid issues with
286
            // third party code using this method with the previous (pre-1.10)
287
            // value of 0000...
288
            /*if ('' != api_get_setting('account_valid_duration')) {
289
                $expirationDate = new DateTime($currentDate);
290
                $days = (int) api_get_setting('account_valid_duration');
291
                $expirationDate->modify('+'.$days.' day');
292
            }*/
293
        } else {
294
            $expirationDate = api_get_utc_datetime($expirationDate);
295
            $expirationDate = new \DateTime($expirationDate, new DateTimeZone('UTC'));
296
        }
297
298
        $user = new User();
299
        $user
300
            ->setLastname($lastName)
301
            ->setFirstname($firstName)
302
            ->setUsername($loginName)
303
            ->setStatus($status)
304
            ->setPlainPassword($password)
305
            ->setEmail($email)
306
            ->setOfficialCode($official_code)
307
            ->setCreatorId($creatorId)
308
            ->setAuthSource($authSource)
309
            ->setPhone($phone)
310
            ->setAddress($address)
311
            ->setLocale($language)
312
            ->setRegistrationDate($now)
313
            ->setHrDeptId($hr_dept_id)
314
            ->setActive($active)
315
            ->setEnabled($active)
316
            ->setTimezone(api_get_timezone())
317
        ;
318
319
        if (!empty($expirationDate)) {
320
            $user->setExpirationDate($expirationDate);
321
        }
322
323
        $em = Database::getManager();
324
        $repo = Container::getUserRepository();
325
        $repo->updateUser($user, false);
326
327
        // Add user as a node
328
        if ($addUserToNode) {
329
            $resourceNode = new ResourceNode();
330
            $resourceNode
331
                ->setTitle($loginName)
332
                ->setCreator(api_get_user_entity($creatorId))
333
                ->setResourceType($repo->getResourceType())
334
            ;
335
            $em->persist($resourceNode);
336
            $user->setResourceNode($resourceNode);
337
        }
338
339
        $em->persist($user);
340
        $em->flush();
341
342
        // Add user to a group
343
        $statusToGroup = [
344
            COURSEMANAGER => 'TEACHER',
345
            STUDENT => 'STUDENT',
346
            DRH => 'RRHH',
347
            SESSIONADMIN => 'SESSION_ADMIN',
348
            STUDENT_BOSS => 'STUDENT_BOSS',
349
            INVITEE => 'INVITEE',
350
        ];
351
352
        if (isset($statusToGroup[$status])) {
353
            $group = Container::$container->get(GroupRepository::class)->findOneBy(['code' => $statusToGroup[$status]]);
354
            if ($group) {
355
                $user->addGroup($group);
356
                $repo->updateUser($user);
357
            }
358
        }
359
360
        $em->flush();
361
362
        $userId = $user->getId();
363
364
        if (!empty($userId)) {
365
            if ($isAdmin) {
366
                self::addUserAsAdmin($user);
367
            }
368
369
            if ($addUserToUrl) {
370
                if (api_get_multiple_access_url()) {
371
                    UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
372
                } else {
373
                    //we are adding by default the access_url_user table with access_url_id = 1
374
                    UrlManager::add_user_to_url($userId, 1);
375
                }
376
            }
377
378
            if (is_array($extra) && count($extra) > 0) {
379
                $extra['item_id'] = $userId;
380
                $userFieldValue = new ExtraFieldValue('user');
381
                /* Force saving of extra fields (otherwise, if the current
382
                user is not admin, fields not visible to the user - most
383
                of them - are just ignored) */
384
                $userFieldValue->saveFieldValues(
385
                    $extra,
386
                    true,
387
                    null,
388
                    null,
389
                    null,
390
                    true
391
                );
392
            } else {
393
                // Create notify settings by default
394
                self::update_extra_field_value(
395
                    $userId,
396
                    'mail_notify_invitation',
397
                    '1'
398
                );
399
                self::update_extra_field_value(
400
                    $userId,
401
                    'mail_notify_message',
402
                    '1'
403
                );
404
                self::update_extra_field_value(
405
                    $userId,
406
                    'mail_notify_group_message',
407
                    '1'
408
                );
409
            }
410
411
            self::update_extra_field_value(
412
                $userId,
413
                'already_logged_in',
414
                'false'
415
            );
416
417
            if (!empty($redirectToURLAfterLogin) && api_get_configuration_value('plugin_redirection_enabled')) {
418
                RedirectionPlugin::insert($userId, $redirectToURLAfterLogin);
419
            }
420
421
            if (!empty($email) && $send_mail) {
422
                $recipient_name = api_get_person_name(
423
                    $firstName,
424
                    $lastName,
425
                    null,
426
                    PERSON_NAME_EMAIL_ADDRESS
427
                );
428
                $tpl = Container::getTwig();
429
                $emailSubject = $tpl->render('@ChamiloCore/Mailer/Legacy/subject_registration_platform.html.twig');
430
                $sender_name = api_get_person_name(
431
                    api_get_setting('administratorName'),
432
                    api_get_setting('administratorSurname'),
433
                    null,
434
                    PERSON_NAME_EMAIL_ADDRESS
435
                );
436
                $email_admin = api_get_setting('emailAdministrator');
437
438
                $url = api_get_path(WEB_PATH);
439
                if (api_is_multiple_url_enabled()) {
440
                    $access_url_id = api_get_current_access_url_id();
441
                    if (-1 != $access_url_id) {
442
                        $urlInfo = api_get_access_url($access_url_id);
443
                        if ($urlInfo) {
444
                            $url = $urlInfo['url'];
445
                        }
446
                    }
447
                }
448
449
                // variables for the default template
450
                $params = [
451
                    'complete_name' => stripslashes(api_get_person_name($firstName, $lastName)),
452
                    'login_name' => $loginName,
453
                    'original_password' => stripslashes($original_password),
454
                    'mailWebPath' => $url,
455
                    'new_user' => $user,
456
                ];
457
458
                $emailBody = $tpl->render(
459
                    '@ChamiloCore/Mailer/Legacy/content_registration_platform.html.twig',
460
                    $params
461
                );
462
463
                $userInfo = api_get_user_info($userId);
464
                $mailTemplateManager = new MailTemplateManager();
465
                $phoneNumber = $extra['mobile_phone_number'] ?? null;
466
467
                $additionalParameters = [
468
                    'smsType' => SmsPlugin::WELCOME_LOGIN_PASSWORD,
469
                    'userId' => $userId,
470
                    'mobilePhoneNumber' => $phoneNumber,
471
                    'password' => $original_password,
472
                ];
473
474
                $emailBodyTemplate = '';
475
                if (!empty($emailTemplate)) {
476
                    if (isset($emailTemplate['content_registration_platform.tpl']) &&
477
                        !empty($emailTemplate['content_registration_platform.tpl'])
478
                    ) {
479
                        $emailBodyTemplate = $mailTemplateManager->parseTemplate(
480
                            $emailTemplate['content_registration_platform.tpl'],
481
                            $userInfo
482
                        );
483
                    }
484
                }
485
486
                $twoEmail = api_get_configuration_value('send_two_inscription_confirmation_mail');
487
                if (true === $twoEmail) {
488
                    $emailBody = $tpl->render(
489
                        '@ChamiloCore/Mailer/Legacy/new_user_first_email_confirmation.html.twig'
490
                    );
491
492
                    if (!empty($emailBodyTemplate) &&
493
                        isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
494
                        !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
495
                    ) {
496
                        $emailBody = $mailTemplateManager->parseTemplate(
497
                            $emailTemplate['new_user_first_email_confirmation.tpl'],
498
                            $userInfo
499
                        );
500
                    }
501
502
                    api_mail_html(
503
                        $recipient_name,
504
                        $email,
505
                        $emailSubject,
506
                        $emailBody,
507
                        $sender_name,
508
                        $email_admin,
509
                        null,
510
                        null,
511
                        null,
512
                        $additionalParameters,
513
                        $creatorEmail
514
                    );
515
516
                    $emailBody = $tpl->render('@ChamiloCore/Mailer/Legacy/new_user_second_email_confirmation.html.twig');
517
518
                    if (!empty($emailBodyTemplate) &&
519
                        isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
520
                        !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
521
                    ) {
522
                        $emailBody = $mailTemplateManager->parseTemplate(
523
                            $emailTemplate['new_user_second_email_confirmation.tpl'],
524
                            $userInfo
525
                        );
526
                    }
527
528
                    api_mail_html(
529
                        $recipient_name,
530
                        $email,
531
                        $emailSubject,
532
                        $emailBody,
533
                        $sender_name,
534
                        $email_admin,
535
                        null,
536
                        null,
537
                        null,
538
                        $additionalParameters,
539
                        $creatorEmail
540
                    );
541
                } else {
542
                    if (!empty($emailBodyTemplate)) {
543
                        $emailBody = $emailBodyTemplate;
544
                    }
545
                    $sendToInbox = api_get_configuration_value('send_inscription_msg_to_inbox');
546
                    if ($sendToInbox) {
547
                        $adminList = self::get_all_administrators();
548
                        $senderId = 1;
549
                        if (!empty($adminList)) {
550
                            $adminInfo = current($adminList);
551
                            $senderId = $adminInfo['user_id'];
552
                        }
553
554
                        MessageManager::send_message_simple(
555
                            $userId,
556
                            $emailSubject,
557
                            $emailBody,
558
                            $senderId
559
                        );
560
                    } else {
561
                        api_mail_html(
562
                            $recipient_name,
563
                            $email,
564
                            $emailSubject,
565
                            $emailBody,
566
                            $sender_name,
567
                            $email_admin,
568
                            null,
569
                            null,
570
                            null,
571
                            $additionalParameters,
572
                            $creatorEmail
573
                        );
574
                    }
575
                }
576
577
                $notification = api_get_configuration_value('send_notification_when_user_added');
578
                if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
579
                    foreach ($notification['admins'] as $adminId) {
580
                        $emailSubjectToAdmin = get_lang('The user has been added').': '.
581
                            api_get_person_name($firstName, $lastName);
582
                        MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
583
                    }
584
                }
585
586
                if ($sendEmailToAllAdmins) {
587
                    $adminList = self::get_all_administrators();
588
                    // variables for the default template
589
                    $renderer = FormValidator::getDefaultRenderer();
590
                    // Form template
591
                    $elementTemplate = ' {label}: {element} <br />';
592
                    $renderer->setElementTemplate($elementTemplate);
593
                    /** @var FormValidator $form */
594
                    $form->freeze(null, $elementTemplate);
595
                    $form->removeElement('submit');
596
                    $formData = $form->returnForm();
597
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
598
                    $params = [
599
                        'complete_name' => stripslashes(api_get_person_name($firstName, $lastName)),
600
                        'user_added' => $user,
601
                        'link' => Display::url($url, $url),
602
                        'form' => $formData,
603
                    ];
604
                    $emailBody = $tpl->render(
605
                        '@ChamiloCore/Mailer/Legacy/content_registration_platform_to_admin.html.twig',
606
                        $params
607
                    );
608
609
                    if (!empty($emailBodyTemplate) &&
610
                        isset($emailTemplate['content_registration_platform_to_admin.tpl']) &&
611
                        !empty($emailTemplate['content_registration_platform_to_admin.tpl'])
612
                    ) {
613
                        $emailBody = $mailTemplateManager->parseTemplate(
614
                            $emailTemplate['content_registration_platform_to_admin.tpl'],
615
                            $userInfo
616
                        );
617
                    }
618
619
                    $subject = get_lang('The user has been added');
620
                    foreach ($adminList as $adminId => $data) {
621
                        MessageManager::send_message_simple(
622
                            $adminId,
623
                            $subject,
624
                            $emailBody,
625
                            $userId
626
                        );
627
                    }
628
                }
629
            }
630
            Event::addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId, null, $creatorId);
631
        } else {
632
            Display::addFlash(
633
                Display::return_message(
634
                    get_lang('There happened an unknown error. Please contact the platform administrator.')
635
                )
636
            );
637
638
            return false;
639
        }
640
641
        return $userId;
642
    }
643
644
    /**
645
     * Can user be deleted? This function checks whether there's a course
646
     * in which the given user is the
647
     * only course administrator. If that is the case, the user can't be
648
     * deleted because the course would remain without a course admin.
649
     *
650
     * @param int $user_id The user id
651
     *
652
     * @return bool true if user can be deleted
653
     *
654
     * @assert (null) === false
655
     * @assert (-1) === false
656
     * @assert ('abc') === false
657
     */
658
    public static function canDeleteUser($user_id)
659
    {
660
        $deny = api_get_configuration_value('deny_delete_users');
661
662
        if ($deny) {
663
            return false;
664
        }
665
666
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
667
        $user_id = (int) $user_id;
668
669
        if (empty($user_id)) {
670
            return false;
671
        }
672
673
        $sql = "SELECT * FROM $table_course_user
674
                WHERE status = 1 AND user_id = ".$user_id;
675
        $res = Database::query($sql);
676
        while ($course = Database::fetch_object($res)) {
677
            $sql = "SELECT id FROM $table_course_user
678
                    WHERE status=1 AND c_id = ".intval($course->c_id);
679
            $res2 = Database::query($sql);
680
            if (1 == Database::num_rows($res2)) {
681
                return false;
682
            }
683
        }
684
685
        return true;
686
    }
687
688
    /**
689
     * Delete a user from the platform, and all its belongings. This is a
690
     * very dangerous function that should only be accessible by
691
     * super-admins. Other roles should only be able to disable a user,
692
     * which removes access to the platform but doesn't delete anything.
693
     *
694
     * @param int The ID of th user to be deleted
695
     *
696
     * @throws Exception
697
     *
698
     * @return bool true if user is successfully deleted, false otherwise
699
     * @assert (null) === false
700
     * @assert ('abc') === false
701
     */
702
    public static function delete_user($user_id)
703
    {
704
        $user_id = (int) $user_id;
705
706
        if (empty($user_id)) {
707
            return false;
708
        }
709
710
        if (!self::canDeleteUser($user_id)) {
711
            return false;
712
        }
713
714
        $usergroup_rel_user = Database::get_main_table(TABLE_USERGROUP_REL_USER);
715
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
716
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
717
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
718
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
719
        $table_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
720
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
721
        $table_group = Database::get_course_table(TABLE_GROUP_USER);
722
        $table_work = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
723
724
        $userInfo = api_get_user_info($user_id);
725
        $repository = Container::getUserRepository();
726
727
        /** @var User $user */
728
        $user = $repository->find($user_id);
729
730
        $repository->deleteUser($user);
731
732
        // Unsubscribe the user from all groups in all his courses
733
        $sql = "SELECT c.id
734
                FROM $table_course c
735
                INNER JOIN $table_course_user cu
736
                ON (c.id = cu.c_id)
737
                WHERE
738
                    cu.user_id = '".$user_id."' AND
739
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
740
                ";
741
742
        $res = Database::query($sql);
743
        while ($course = Database::fetch_object($res)) {
744
            $sql = "DELETE FROM $table_group
745
                    WHERE c_id = {$course->id} AND user_id = $user_id";
746
            Database::query($sql);
747
        }
748
749
        // Unsubscribe user from usergroup_rel_user
750
        $sql = "DELETE FROM $usergroup_rel_user WHERE user_id = '".$user_id."'";
751
        Database::query($sql);
752
753
        // Unsubscribe user from all courses
754
        $sql = "DELETE FROM $table_course_user WHERE user_id = '".$user_id."'";
755
        Database::query($sql);
756
757
        // Unsubscribe user from all courses in sessions
758
        $sql = "DELETE FROM $table_session_course_user WHERE user_id = '".$user_id."'";
759
        Database::query($sql);
760
761
        // If the user was added as a id_coach then set the current admin as coach see BT#
762
        $currentUserId = api_get_user_id();
763
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
764
                WHERE id_coach = '".$user_id."'";
765
        Database::query($sql);
766
767
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
768
                WHERE session_admin_id = '".$user_id."'";
769
        Database::query($sql);
770
771
        // Unsubscribe user from all sessions
772
        $sql = "DELETE FROM $table_session_user
773
                WHERE user_id = '".$user_id."'";
774
        Database::query($sql);
775
776
        if (api_get_configuration_value('plugin_redirection_enabled')) {
777
            RedirectionPlugin::deleteUserRedirection($user_id);
778
        }
779
780
        // Delete user from the admin table
781
        $sql = "DELETE FROM $table_admin WHERE user_id = '".$user_id."'";
782
        Database::query($sql);
783
784
        // Delete the personal agenda-items from this user
785
        $agenda_table = Database::get_main_table(TABLE_PERSONAL_AGENDA);
786
        $sql = "DELETE FROM $agenda_table WHERE user = '".$user_id."'";
787
        Database::query($sql);
788
789
        $gradebook_results_table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_RESULT);
790
        $sql = 'DELETE FROM '.$gradebook_results_table.' WHERE user_id = '.$user_id;
791
        Database::query($sql);
792
793
        $extraFieldValue = new ExtraFieldValue('user');
794
        $extraFieldValue->deleteValuesByItem($user_id);
795
796
        UrlManager::deleteUserFromAllUrls($user_id);
797
        //UrlManager::deleteUserFromAllUrls($user_id);
798
799
        if ('true' === api_get_setting('allow_social_tool')) {
800
            $userGroup = new UserGroup();
801
            //Delete user from portal groups
802
            $group_list = $userGroup->get_groups_by_user($user_id);
803
            if (!empty($group_list)) {
804
                foreach ($group_list as $group_id => $data) {
805
                    $userGroup->delete_user_rel_group($user_id, $group_id);
806
                }
807
            }
808
809
            // Delete user from friend lists
810
            SocialManager::remove_user_rel_user($user_id, true);
811
        }
812
813
        // Removing survey invitation
814
        SurveyManager::delete_all_survey_invitations_by_user($user_id);
815
816
        // Delete students works
817
        /*$sql = "DELETE FROM $table_work WHERE user_id = $user_id ";
818
        Database::query($sql);*/
819
820
        /*$sql = "UPDATE c_item_property SET to_user_id = NULL
821
                WHERE to_user_id = '".$user_id."'";
822
        Database::query($sql);
823
824
        $sql = "UPDATE c_item_property SET insert_user_id = NULL
825
                WHERE insert_user_id = '".$user_id."'";
826
        Database::query($sql);
827
828
        $sql = "UPDATE c_item_property SET lastedit_user_id = NULL
829
                WHERE lastedit_user_id = '".$user_id."'";
830
        Database::query($sql);*/
831
832
        // Skills
833
        $em = Database::getManager();
834
835
        $criteria = ['user' => $user_id];
836
        $skills = $em->getRepository(SkillRelUser::class)->findBy($criteria);
837
        if ($skills) {
838
            /** @var SkillRelUser $skill */
839
            foreach ($skills as $skill) {
840
                $comments = $skill->getComments();
841
                if ($comments) {
842
                    /** @var SkillRelUserComment $comment */
843
                    foreach ($comments as $comment) {
844
                        $em->remove($comment);
845
                    }
846
                }
847
                $em->remove($skill);
848
            }
849
        }
850
851
        // ExtraFieldSavedSearch
852
        $criteria = ['user' => $user_id];
853
        $searchList = $em->getRepository(ExtraFieldSavedSearch::class)->findBy($criteria);
854
        if ($searchList) {
855
            foreach ($searchList as $search) {
856
                $em->remove($search);
857
            }
858
        }
859
860
        $connection = Database::getManager()->getConnection();
861
        $tableExists = $connection->getSchemaManager()->tablesExist(['plugin_bbb_room']);
862
        if ($tableExists) {
863
            // Delete user from database
864
            $sql = "DELETE FROM plugin_bbb_room WHERE participant_id = $user_id";
865
            Database::query($sql);
866
        }
867
868
        // Delete user/ticket relationships :(
869
        $tableExists = $connection->getSchemaManager()->tablesExist(['ticket_ticket']);
870
        if ($tableExists) {
871
            TicketManager::deleteUserFromTicketSystem($user_id);
872
        }
873
874
        $tableExists = $connection->getSchemaManager()->tablesExist(['c_lp_category_user']);
875
        if ($tableExists) {
876
            $sql = "DELETE FROM c_lp_category_user WHERE user_id = $user_id";
877
            Database::query($sql);
878
        }
879
880
        $app_plugin = new AppPlugin();
881
        $app_plugin->performActionsWhenDeletingItem('user', $user_id);
882
883
        // Add event to system log
884
        $authorId = api_get_user_id();
885
886
        Event::addEvent(
887
            LOG_USER_DELETE,
888
            LOG_USER_ID,
889
            $user_id,
890
            api_get_utc_datetime(),
891
            $authorId
892
        );
893
894
        Event::addEvent(
895
            LOG_USER_DELETE,
896
            LOG_USER_OBJECT,
897
            $userInfo,
898
            api_get_utc_datetime(),
899
            $authorId
900
        );
901
902
        return true;
903
    }
904
905
    /**
906
     * Deletes users completely. Can be called either as:
907
     * - UserManager::delete_users(1, 2, 3); or
908
     * - UserManager::delete_users(array(1, 2, 3));.
909
     *
910
     * @param array|int $ids
911
     *
912
     * @return bool True if at least one user was successfuly deleted. False otherwise.
913
     *
914
     * @author Laurent Opprecht
915
     *
916
     * @uses \UserManager::delete_user() to actually delete each user
917
     * @assert (null) === false
918
     * @assert (-1) === false
919
     * @assert (array(-1)) === false
920
     */
921
    public static function delete_users($ids = [])
922
    {
923
        $result = false;
924
        $ids = is_array($ids) ? $ids : func_get_args();
925
        if (!is_array($ids) || 0 == count($ids)) {
926
            return false;
927
        }
928
        $ids = array_map('intval', $ids);
929
        foreach ($ids as $id) {
930
            if (empty($id) || $id < 1) {
931
                continue;
932
            }
933
            $deleted = self::delete_user($id);
934
            $result = $deleted || $result;
935
        }
936
937
        return $result;
938
    }
939
940
    /**
941
     * Disable users. Can be called either as:
942
     * - UserManager::deactivate_users(1, 2, 3);
943
     * - UserManager::deactivate_users(array(1, 2, 3));.
944
     *
945
     * @param array|int $ids
946
     *
947
     * @return bool
948
     *
949
     * @author Laurent Opprecht
950
     * @assert (null) === false
951
     * @assert (array(-1)) === false
952
     */
953
    public static function deactivate_users($ids = [])
954
    {
955
        if (empty($ids)) {
956
            return false;
957
        }
958
959
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
960
961
        $ids = is_array($ids) ? $ids : func_get_args();
962
        $ids = array_map('intval', $ids);
963
        $ids = implode(',', $ids);
964
965
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
966
        $r = Database::query($sql);
967
        if (false !== $r) {
968
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
969
970
            return true;
971
        }
972
973
        return false;
974
    }
975
976
    /**
977
     * Enable users. Can be called either as:
978
     * - UserManager::activate_users(1, 2, 3);
979
     * - UserManager::activate_users(array(1, 2, 3));.
980
     *
981
     * @param array|int IDs of the users to enable
982
     *
983
     * @return bool
984
     *
985
     * @author Laurent Opprecht
986
     * @assert (null) === false
987
     * @assert (array(-1)) === false
988
     */
989
    public static function activate_users($ids = [])
990
    {
991
        if (empty($ids)) {
992
            return false;
993
        }
994
995
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
996
997
        $ids = is_array($ids) ? $ids : func_get_args();
998
        $ids = array_map('intval', $ids);
999
        $ids = implode(',', $ids);
1000
1001
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
1002
        $r = Database::query($sql);
1003
        if (false !== $r) {
1004
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
1005
1006
            return true;
1007
        }
1008
1009
        return false;
1010
    }
1011
1012
    /**
1013
     * Update user information with all the parameters passed to this function.
1014
     *
1015
     * @param int    $user_id         The ID of the user to be updated
1016
     * @param string $firstname       The user's firstname
1017
     * @param string $lastname        The user's lastname
1018
     * @param string $username        The user's username (login)
1019
     * @param string $password        The user's password
1020
     * @param string $auth_source     The authentication source (default: "platform")
1021
     * @param string $email           The user's e-mail address
1022
     * @param int    $status          The user's status
1023
     * @param string $official_code   The user's official code (usually just an internal institutional code)
1024
     * @param string $phone           The user's phone number
1025
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
1026
     * @param string $expiration_date The date at which this user will be automatically disabled
1027
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
1028
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
1029
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
1030
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
1031
     * @param string $language        The language to which the user account will be set
1032
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
1033
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
1034
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
1035
     * @param string $address
1036
     * @param array  $emailTemplate
1037
     *
1038
     * @return bool|int False on error, or the user ID if the user information was updated
1039
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
1040
     */
1041
    public static function update_user(
1042
        $user_id,
1043
        $firstname,
1044
        $lastname,
1045
        $username,
1046
        $password,
1047
        $auth_source,
1048
        $email,
1049
        $status,
1050
        $official_code,
1051
        $phone,
1052
        $picture_uri,
1053
        $expiration_date,
1054
        $active,
1055
        $creator_id = null,
1056
        $hr_dept_id = 0,
1057
        $extra = null,
1058
        $language = 'english',
1059
        $encrypt_method = '',
1060
        $send_email = false,
1061
        $reset_password = 0,
1062
        $address = null,
1063
        $emailTemplate = []
1064
    ) {
1065
        $original_password = $password;
1066
        $user_id = (int) $user_id;
1067
        $creator_id = (int) $creator_id;
1068
1069
        if (empty($user_id)) {
1070
            return false;
1071
        }
1072
1073
        $userManager = self::getRepository();
1074
        $user = api_get_user_entity($user_id);
1075
1076
        if (empty($user)) {
1077
            return false;
1078
        }
1079
1080
        if (0 == $reset_password) {
1081
            $password = null;
1082
            $auth_source = $user->getAuthSource();
1083
        } elseif (1 == $reset_password) {
1084
            $original_password = $password = api_generate_password();
1085
            $auth_source = PLATFORM_AUTH_SOURCE;
1086
        } elseif (2 == $reset_password) {
1087
            $password = $password;
1088
            $auth_source = PLATFORM_AUTH_SOURCE;
1089
        } elseif (3 == $reset_password) {
1090
            $password = $password;
1091
            $auth_source = $auth_source;
1092
        }
1093
1094
        // Checking the user language
1095
        $languages = array_keys(api_get_languages());
1096
        if (!in_array($language, $languages)) {
1097
            $language = api_get_setting('platformLanguage');
1098
        }
1099
1100
        $change_active = 0;
1101
        $isUserActive = $user->getActive();
1102
        if ($isUserActive != $active) {
1103
            $change_active = 1;
1104
        }
1105
1106
        $originalUsername = $user->getUsername();
1107
1108
        // If username is different from original then check if it exists.
1109
        if ($originalUsername !== $username) {
1110
            $available = self::is_username_available($username);
1111
            if (false === $available) {
1112
                return false;
1113
            }
1114
        }
1115
1116
        if (!empty($expiration_date)) {
1117
            $expiration_date = api_get_utc_datetime($expiration_date);
1118
            $expiration_date = new \DateTime(
1119
                $expiration_date,
1120
                new DateTimeZone('UTC')
1121
            );
1122
        }
1123
1124
        $user
1125
            ->setLastname($lastname)
1126
            ->setFirstname($firstname)
1127
            ->setUsername($username)
1128
            ->setStatus($status)
1129
            ->setAuthSource($auth_source)
1130
            ->setLocale($language)
1131
            ->setLocale($language)
1132
            ->setEmail($email)
1133
            ->setOfficialCode($official_code)
1134
            ->setPhone($phone)
1135
            ->setAddress($address)
1136
            //->setPictureUri($picture_uri)
1137
            ->setExpirationDate($expiration_date)
1138
            ->setActive($active)
1139
            ->setEnabled($active)
1140
            ->setHrDeptId((int) $hr_dept_id)
1141
        ;
1142
1143
        if (!is_null($password)) {
1144
            $user->setPlainPassword($password);
1145
        }
1146
1147
        $statusToGroup = [
1148
            COURSEMANAGER => 'TEACHER',
1149
            STUDENT => 'STUDENT',
1150
            DRH => 'RRHH',
1151
            SESSIONADMIN => 'SESSION_ADMIN',
1152
            STUDENT_BOSS => 'STUDENT_BOSS',
1153
            INVITEE => 'INVITEE',
1154
        ];
1155
1156
        $group = Container::$container->get(GroupRepository::class)->findOneBy(['code' => $statusToGroup[$status]]);
1157
        if ($group) {
1158
            $user->addGroup($group);
1159
        }
1160
1161
        $userManager->updateUser($user, true);
1162
        Event::addEvent(LOG_USER_UPDATE, LOG_USER_ID, $user_id);
1163
1164
        if (1 == $change_active) {
1165
            if (1 == $active) {
1166
                $event_title = LOG_USER_ENABLE;
1167
            } else {
1168
                $event_title = LOG_USER_DISABLE;
1169
            }
1170
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
1171
        }
1172
1173
        if (is_array($extra) && count($extra) > 0) {
1174
            $res = true;
1175
            foreach ($extra as $fname => $fvalue) {
1176
                $res = $res && self::update_extra_field_value(
1177
                    $user_id,
1178
                    $fname,
1179
                    $fvalue
1180
                );
1181
            }
1182
        }
1183
1184
        if (!empty($email) && $send_email) {
1185
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
1186
            $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
1187
            $sender_name = api_get_person_name(
1188
                api_get_setting('administratorName'),
1189
                api_get_setting('administratorSurname'),
1190
                null,
1191
                PERSON_NAME_EMAIL_ADDRESS
1192
            );
1193
            $email_admin = api_get_setting('emailAdministrator');
1194
            $url = api_get_path(WEB_PATH);
1195
            if (api_is_multiple_url_enabled()) {
1196
                $access_url_id = api_get_current_access_url_id();
1197
                if (-1 != $access_url_id) {
1198
                    $url = api_get_access_url($access_url_id);
1199
                    $url = $url['url'];
1200
                }
1201
            }
1202
1203
            $tplContent = new Template(
1204
                null,
1205
                false,
1206
                false,
1207
                false,
1208
                false,
1209
                false
1210
            );
1211
            // variables for the default template
1212
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
1213
            $tplContent->assign('login_name', $username);
1214
1215
            $originalPassword = '';
1216
            if ($reset_password > 0) {
1217
                $originalPassword = stripslashes($original_password);
1218
            }
1219
            $tplContent->assign('original_password', $originalPassword);
1220
            $tplContent->assign('portal_url', $url);
1221
1222
            $layoutContent = $tplContent->get_template('mail/user_edit_content.tpl');
1223
            $emailBody = $tplContent->fetch($layoutContent);
1224
1225
            $mailTemplateManager = new MailTemplateManager();
1226
1227
            if (!empty($emailTemplate) &&
1228
                isset($emailTemplate['user_edit_content.tpl']) &&
1229
                !empty($emailTemplate['user_edit_content.tpl'])
1230
            ) {
1231
                $userInfo = api_get_user_info($user_id);
1232
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], $userInfo);
1233
            }
1234
1235
            $creatorInfo = api_get_user_info($creator_id);
1236
            $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
1237
1238
            api_mail_html(
1239
                $recipient_name,
1240
                $email,
1241
                $emailsubject,
1242
                $emailBody,
1243
                $sender_name,
1244
                $email_admin,
1245
                null,
1246
                null,
1247
                null,
1248
                null,
1249
                $creatorEmail
1250
            );
1251
        }
1252
1253
        $cacheAvailable = api_get_configuration_value('apc');
1254
        if (true === $cacheAvailable) {
1255
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1256
            if (apcu_exists($apcVar)) {
1257
                apcu_delete($apcVar);
1258
            }
1259
        }
1260
1261
        return $user->getId();
1262
    }
1263
1264
    /**
1265
     * Disables a user.
1266
     *
1267
     * @param int User id
1268
     *
1269
     * @return bool
1270
     *
1271
     * @uses \UserManager::change_active_state() to actually disable the user
1272
     * @assert (0) === false
1273
     */
1274
    public static function disable($user_id)
1275
    {
1276
        if (empty($user_id)) {
1277
            return false;
1278
        }
1279
        self::change_active_state($user_id, 0);
1280
1281
        return true;
1282
    }
1283
1284
    /**
1285
     * Enable a user.
1286
     *
1287
     * @param int User id
1288
     *
1289
     * @return bool
1290
     *
1291
     * @uses \UserManager::change_active_state() to actually disable the user
1292
     * @assert (0) === false
1293
     */
1294
    public static function enable($user_id)
1295
    {
1296
        if (empty($user_id)) {
1297
            return false;
1298
        }
1299
        self::change_active_state($user_id, 1);
1300
1301
        return true;
1302
    }
1303
1304
    /**
1305
     * Returns the user's id based on the original id and field name in
1306
     * the extra fields. Returns 0 if no user was found. This function is
1307
     * mostly useful in the context of a web services-based sinchronization.
1308
     *
1309
     * @param string Original user id
1310
     * @param string Original field name
1311
     *
1312
     * @return int User id
1313
     * @assert ('0','---') === 0
1314
     */
1315
    public static function get_user_id_from_original_id(
1316
        $original_user_id_value,
1317
        $original_user_id_name
1318
    ) {
1319
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1320
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1321
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1322
1323
        $original_user_id_name = Database::escape_string($original_user_id_name);
1324
        $original_user_id_value = Database::escape_string($original_user_id_value);
1325
1326
        $sql = "SELECT item_id as user_id
1327
                FROM $t_uf uf
1328
                INNER JOIN $t_ufv ufv
1329
                ON ufv.field_id = uf.id
1330
                WHERE
1331
                    variable = '$original_user_id_name' AND
1332
                    value = '$original_user_id_value' AND
1333
                    extra_field_type = $extraFieldType
1334
                ";
1335
        $res = Database::query($sql);
1336
        $row = Database::fetch_object($res);
1337
        if ($row) {
1338
            return $row->user_id;
1339
        }
1340
1341
        return 0;
1342
    }
1343
1344
    /**
1345
     * Check if a username is available.
1346
     *
1347
     * @param string $username the wanted username
1348
     *
1349
     * @return bool true if the wanted username is available
1350
     * @assert ('') === false
1351
     * @assert ('xyzxyzxyz') === true
1352
     */
1353
    public static function is_username_available($username)
1354
    {
1355
        if (empty($username)) {
1356
            return false;
1357
        }
1358
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1359
        $sql = "SELECT username FROM $table_user
1360
                WHERE username = '".Database::escape_string($username)."'";
1361
        $res = Database::query($sql);
1362
1363
        return 0 == Database::num_rows($res);
1364
    }
1365
1366
    /**
1367
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1368
     *
1369
     * @param string $firstname the first name of the user
1370
     * @param string $lastname  the last name of the user
1371
     *
1372
     * @return string suggests a username that contains only ASCII-letters and digits,
1373
     *                without check for uniqueness within the system
1374
     *
1375
     * @author Julio Montoya Armas
1376
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1377
     * @assert ('','') === false
1378
     * @assert ('a','b') === 'ab'
1379
     */
1380
    public static function create_username($firstname, $lastname)
1381
    {
1382
        if (empty($firstname) && empty($lastname)) {
1383
            return false;
1384
        }
1385
1386
        // The first letter only.
1387
        $firstname = api_substr(
1388
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1389
            0,
1390
            1
1391
        );
1392
        //Looking for a space in the lastname
1393
        $pos = api_strpos($lastname, ' ');
1394
        if (false !== $pos) {
1395
            $lastname = api_substr($lastname, 0, $pos);
1396
        }
1397
1398
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1399
        $username = $firstname.$lastname;
1400
        if (empty($username)) {
1401
            $username = 'user';
1402
        }
1403
1404
        $username = URLify::transliterate($username);
1405
1406
        return strtolower(substr($username, 0, USERNAME_MAX_LENGTH - 3));
1407
    }
1408
1409
    /**
1410
     * Creates a unique username, using:
1411
     * 1. the first name and the last name of a user;
1412
     * 2. an already created username but not checked for uniqueness yet.
1413
     *
1414
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1415
     *                          parameter is treated as username which is to be checked f
1416
     *                          or uniqueness and to be modified when it is necessary.
1417
     * @param string $lastname  the last name of the user
1418
     *
1419
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1420
     *                Note: When the method is called several times with same parameters,
1421
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1422
     *
1423
     * @author Ivan Tcholakov, 2009
1424
     */
1425
    public static function create_unique_username($firstname, $lastname = null)
1426
    {
1427
        if (is_null($lastname)) {
1428
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1429
            // For making this method tolerant of mistakes,
1430
            // let us transliterate and purify the suggested input username anyway.
1431
            // So, instead of the sentence $username = $firstname; we place the following:
1432
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1433
        } else {
1434
            $username = self::create_username($firstname, $lastname);
1435
        }
1436
        if (!self::is_username_available($username)) {
1437
            $i = 2;
1438
            $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1439
            while (!self::is_username_available($temp_username)) {
1440
                $i++;
1441
                $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1442
            }
1443
            $username = $temp_username;
1444
        }
1445
1446
        $username = URLify::transliterate($username);
1447
1448
        return $username;
1449
    }
1450
1451
    /**
1452
     * Modifies a given username accordingly to the specification for valid characters and length.
1453
     *
1454
     * @param $username string          The input username
1455
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1456
     *                     otherwise compliance may be partial. The default value is FALSE.
1457
     *
1458
     * @return string the resulting purified username
1459
     */
1460
    public static function purify_username($username, $strict = false)
1461
    {
1462
        if ($strict) {
1463
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1464
            // into ASCII letters in order they not to be totally removed.
1465
            // 2. Applying the strict purifier.
1466
            // 3. Length limitation.
1467
            $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);
1468
            $return = URLify::transliterate($return);
1469
1470
            return $return;
1471
        }
1472
1473
        // 1. Applying the shallow purifier.
1474
        // 2. Length limitation.
1475
        return substr(
1476
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1477
            0,
1478
            USERNAME_MAX_LENGTH
1479
        );
1480
    }
1481
1482
    /**
1483
     * Checks whether the user id exists in the database.
1484
     *
1485
     * @param int $userId User id
1486
     *
1487
     * @return bool True if user id was found, false otherwise
1488
     */
1489
    public static function is_user_id_valid($userId)
1490
    {
1491
        $resultData = Database::select(
1492
            'COUNT(1) AS count',
1493
            Database::get_main_table(TABLE_MAIN_USER),
1494
            [
1495
                'where' => ['id = ?' => (int) $userId],
1496
            ],
1497
            'first'
1498
        );
1499
1500
        if (false === $resultData) {
1501
            return false;
1502
        }
1503
1504
        return $resultData['count'] > 0;
1505
    }
1506
1507
    /**
1508
     * Checks whether a given username matches to the specification strictly.
1509
     * The empty username is assumed here as invalid.
1510
     * Mostly this function is to be used in the user interface built-in validation routines
1511
     * for providing feedback while usernames are enterd manually.
1512
     *
1513
     * @param string $username the input username
1514
     *
1515
     * @return bool returns TRUE if the username is valid, FALSE otherwise
1516
     */
1517
    public static function is_username_valid($username)
1518
    {
1519
        return !empty($username) && $username == self::purify_username($username, true);
1520
    }
1521
1522
    /**
1523
     * Checks whether a username is empty. If the username contains whitespace characters,
1524
     * such as spaces, tabulators, newlines, etc.,
1525
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
1526
     *
1527
     * @param string $username the given username
1528
     *
1529
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1530
     */
1531
    public static function is_username_empty($username)
1532
    {
1533
        return 0 == strlen(self::purify_username($username, false));
1534
    }
1535
1536
    /**
1537
     * Checks whether a username is too long or not.
1538
     *
1539
     * @param string $username the given username, it should contain only ASCII-letters and digits
1540
     *
1541
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1542
     */
1543
    public static function is_username_too_long($username)
1544
    {
1545
        return strlen($username) > USERNAME_MAX_LENGTH;
1546
    }
1547
1548
    /**
1549
     * Get the users by ID.
1550
     *
1551
     * @param array  $ids    student ids
1552
     * @param bool   $active
1553
     * @param string $order
1554
     * @param string $limit
1555
     *
1556
     * @return array $result student information
1557
     */
1558
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
1559
    {
1560
        if (empty($ids)) {
1561
            return [];
1562
        }
1563
1564
        $ids = is_array($ids) ? $ids : [$ids];
1565
        $ids = array_map('intval', $ids);
1566
        $ids = implode(',', $ids);
1567
1568
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
1569
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
1570
        if (!is_null($active)) {
1571
            $sql .= ' AND active='.($active ? '1' : '0');
1572
        }
1573
1574
        if (!is_null($order)) {
1575
            $order = Database::escape_string($order);
1576
            $sql .= ' ORDER BY '.$order;
1577
        }
1578
1579
        if (!is_null($limit)) {
1580
            $limit = Database::escape_string($limit);
1581
            $sql .= ' LIMIT '.$limit;
1582
        }
1583
1584
        $rs = Database::query($sql);
1585
        $result = [];
1586
        while ($row = Database::fetch_array($rs)) {
1587
            $result[] = $row;
1588
        }
1589
1590
        return $result;
1591
    }
1592
1593
    /**
1594
     * Get a list of users of which the given conditions match with an = 'cond'.
1595
     *
1596
     * @param array $conditions a list of condition (example : status=>STUDENT)
1597
     * @param array $order_by   a list of fields on which sort
1598
     *
1599
     * @return array an array with all users of the platform
1600
     *
1601
     * @todo security filter order by
1602
     */
1603
    public static function get_user_list(
1604
        $conditions = [],
1605
        $order_by = [],
1606
        $limit_from = false,
1607
        $limit_to = false,
1608
        $idCampus = null
1609
    ) {
1610
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1611
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1612
        $return_array = [];
1613
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1614
1615
        if (api_is_multiple_url_enabled()) {
1616
            if ($idCampus) {
1617
                $urlId = $idCampus;
1618
            } else {
1619
                $urlId = api_get_current_access_url_id();
1620
            }
1621
            $sql .= " INNER JOIN $userUrlTable url_user
1622
                      ON (user.id = url_user.user_id)
1623
                      WHERE url_user.access_url_id = $urlId";
1624
        } else {
1625
            $sql .= " WHERE 1=1 ";
1626
        }
1627
1628
        if (count($conditions) > 0) {
1629
            foreach ($conditions as $field => $value) {
1630
                $field = Database::escape_string($field);
1631
                $value = Database::escape_string($value);
1632
                $sql .= " AND $field = '$value'";
1633
            }
1634
        }
1635
1636
        if (count($order_by) > 0) {
1637
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1638
        }
1639
1640
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1641
            $limit_from = (int) $limit_from;
1642
            $limit_to = (int) $limit_to;
1643
            $sql .= " LIMIT $limit_from, $limit_to";
1644
        }
1645
        $sql_result = Database::query($sql);
1646
        while ($result = Database::fetch_array($sql_result)) {
1647
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1648
            $return_array[] = $result;
1649
        }
1650
1651
        return $return_array;
1652
    }
1653
1654
    public static function getUserListExtraConditions(
1655
        $conditions = [],
1656
        $order_by = [],
1657
        $limit_from = false,
1658
        $limit_to = false,
1659
        $idCampus = null,
1660
        $extraConditions = '',
1661
        $getCount = false
1662
    ) {
1663
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1664
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1665
        $return_array = [];
1666
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1667
1668
        if ($getCount) {
1669
            $sql = "SELECT count(user.id) count FROM $user_table user ";
1670
        }
1671
1672
        if (api_is_multiple_url_enabled()) {
1673
            if ($idCampus) {
1674
                $urlId = $idCampus;
1675
            } else {
1676
                $urlId = api_get_current_access_url_id();
1677
            }
1678
            $sql .= " INNER JOIN $userUrlTable url_user
1679
                      ON (user.user_id = url_user.user_id)
1680
                      WHERE url_user.access_url_id = $urlId";
1681
        } else {
1682
            $sql .= " WHERE 1=1 ";
1683
        }
1684
1685
        $sql .= " AND status <> ".ANONYMOUS." ";
1686
1687
        if (count($conditions) > 0) {
1688
            foreach ($conditions as $field => $value) {
1689
                $field = Database::escape_string($field);
1690
                $value = Database::escape_string($value);
1691
                $sql .= " AND $field = '$value'";
1692
            }
1693
        }
1694
1695
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
1696
1697
        if (!empty($order_by) && count($order_by) > 0) {
1698
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1699
        }
1700
1701
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1702
            $limit_from = (int) $limit_from;
1703
            $limit_to = (int) $limit_to;
1704
            $sql .= " LIMIT $limit_from, $limit_to";
1705
        }
1706
1707
        $sql_result = Database::query($sql);
1708
1709
        if ($getCount) {
1710
            $result = Database::fetch_array($sql_result);
1711
1712
            return $result['count'];
1713
        }
1714
1715
        while ($result = Database::fetch_array($sql_result)) {
1716
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1717
            $return_array[] = $result;
1718
        }
1719
1720
        return $return_array;
1721
    }
1722
1723
    /**
1724
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
1725
     *
1726
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
1727
     * @param array  $order_by         a list of fields on which sort
1728
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
1729
     * @param string $condition        Whether we want the filters to be combined by AND or OR
1730
     * @param array  $onlyThisUserList
1731
     *
1732
     * @return array an array with all users of the platform
1733
     *
1734
     * @todo optional course code parameter, optional sorting parameters...
1735
     * @todo security filter order_by
1736
     */
1737
    public static function getUserListLike(
1738
        $conditions = [],
1739
        $order_by = [],
1740
        $simple_like = false,
1741
        $condition = 'AND',
1742
        $onlyThisUserList = []
1743
    ) {
1744
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1745
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1746
        $return_array = [];
1747
        $sql_query = "SELECT user.id FROM $user_table user ";
1748
1749
        if (api_is_multiple_url_enabled()) {
1750
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
1751
        }
1752
1753
        $sql_query .= ' WHERE 1 = 1 ';
1754
        if (count($conditions) > 0) {
1755
            $temp_conditions = [];
1756
            foreach ($conditions as $field => $value) {
1757
                $field = Database::escape_string($field);
1758
                $value = Database::escape_string($value);
1759
                if ($simple_like) {
1760
                    $temp_conditions[] = $field." LIKE '$value%'";
1761
                } else {
1762
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
1763
                }
1764
            }
1765
            if (!empty($temp_conditions)) {
1766
                $sql_query .= ' AND '.implode(' '.$condition.' ', $temp_conditions);
1767
            }
1768
1769
            if (api_is_multiple_url_enabled()) {
1770
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1771
            }
1772
        } else {
1773
            if (api_is_multiple_url_enabled()) {
1774
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1775
            }
1776
        }
1777
1778
        if (!empty($onlyThisUserList)) {
1779
            $onlyThisUserListToString = implode("','", $onlyThisUserList);
1780
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
1781
        }
1782
1783
        if (count($order_by) > 0) {
1784
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1785
        }
1786
1787
        $sql_result = Database::query($sql_query);
1788
        while ($result = Database::fetch_array($sql_result)) {
1789
            $userInfo = api_get_user_info($result['id']);
1790
            $return_array[] = $userInfo;
1791
        }
1792
1793
        return $return_array;
1794
    }
1795
1796
    /**
1797
     * Gets the current user image.
1798
     *
1799
     * @param string $userId
1800
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
1801
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
1802
     * @param bool   $addRandomId
1803
     * @param array  $userInfo    to avoid query the DB
1804
     *
1805
     * @todo add gravatar support
1806
     * @todo replace $userId with User entity
1807
     *
1808
     * @return string
1809
     */
1810
    public static function getUserPicture(
1811
        $userId,
1812
        int $size = USER_IMAGE_SIZE_MEDIUM,
1813
        $addRandomId = true,
1814
        $userInfo = []
1815
    ) {
1816
        $user = api_get_user_entity($userId);
1817
        $illustrationRepo = Container::getIllustrationRepository();
1818
1819
        switch ($size) {
1820
            case USER_IMAGE_SIZE_SMALL:
1821
                $width = 32;
1822
                break;
1823
            case USER_IMAGE_SIZE_MEDIUM:
1824
                $width = 64;
1825
                break;
1826
            case USER_IMAGE_SIZE_BIG:
1827
                $width = 128;
1828
                break;
1829
            case USER_IMAGE_SIZE_ORIGINAL:
1830
            default:
1831
                $width = 0;
1832
                break;
1833
        }
1834
1835
        $url = $illustrationRepo->getIllustrationUrl($user);
1836
        $params = [];
1837
        if (!empty($width)) {
1838
            $params['w'] = $width;
1839
        }
1840
1841
        if ($addRandomId) {
1842
            $params['rand'] = uniqid('u_', true);
1843
        }
1844
1845
        $paramsToString = '';
1846
        if (!empty($params)) {
1847
            $paramsToString = '?'.http_build_query($params);
1848
        }
1849
1850
        return $url.$paramsToString;
1851
1852
        /*
1853
        // Make sure userInfo is defined. Otherwise, define it!
1854
        if (empty($userInfo) || !is_array($userInfo) || 0 == count($userInfo)) {
1855
            if (empty($user_id)) {
1856
                return '';
1857
            } else {
1858
                $userInfo = api_get_user_info($user_id);
1859
            }
1860
        }
1861
1862
        $imageWebPath = self::get_user_picture_path_by_id(
1863
            $user_id,
1864
            'web',
1865
            $userInfo
1866
        );
1867
        $pictureWebFile = $imageWebPath['file'];
1868
        $pictureWebDir = $imageWebPath['dir'];
1869
1870
        $pictureAnonymousSize = '128';
1871
        $gravatarSize = 22;
1872
        $realSizeName = 'small_';
1873
1874
        switch ($size) {
1875
            case USER_IMAGE_SIZE_SMALL:
1876
                $pictureAnonymousSize = '32';
1877
                $realSizeName = 'small_';
1878
                $gravatarSize = 32;
1879
                break;
1880
            case USER_IMAGE_SIZE_MEDIUM:
1881
                $pictureAnonymousSize = '64';
1882
                $realSizeName = 'medium_';
1883
                $gravatarSize = 64;
1884
                break;
1885
            case USER_IMAGE_SIZE_ORIGINAL:
1886
                $pictureAnonymousSize = '128';
1887
                $realSizeName = '';
1888
                $gravatarSize = 128;
1889
                break;
1890
            case USER_IMAGE_SIZE_BIG:
1891
                $pictureAnonymousSize = '128';
1892
                $realSizeName = 'big_';
1893
                $gravatarSize = 128;
1894
                break;
1895
        }
1896
1897
        $gravatarEnabled = api_get_setting('gravatar_enabled');
1898
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
1899
        if ('unknown.jpg' == $pictureWebFile || empty($pictureWebFile)) {
1900
            if ('true' === $gravatarEnabled) {
1901
                $file = self::getGravatar(
1902
                    $imageWebPath['email'],
1903
                    $gravatarSize,
1904
                    api_get_setting('gravatar_type')
1905
                );
1906
1907
                if ($addRandomId) {
1908
                    $file .= '&rand='.uniqid();
1909
                }
1910
1911
                return $file;
1912
            }
1913
1914
            return $anonymousPath;
1915
        }
1916
1917
        if ($addRandomId) {
1918
            $picture .= '?rand='.uniqid();
1919
        }
1920
1921
        return $picture;*/
1922
    }
1923
1924
    /**
1925
     * Creates new user photos in various sizes of a user, or deletes user photos.
1926
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
1927
     *
1928
     * @param int    $user_id the user internal identification number
1929
     * @param string $file    The common file name for the newly created photos.
1930
     *                        It will be checked and modified for compatibility with the file system.
1931
     *                        If full name is provided, path component is ignored.
1932
     *                        If an empty name is provided, then old user photos are deleted only,
1933
     *
1934
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
1935
     *
1936
     * @param string $source_file    the full system name of the image from which user photos will be created
1937
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
1938
     *
1939
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
1940
     *              When deletion is requested returns empty string.
1941
     *              In case of internal error or negative validation returns FALSE.
1942
     */
1943
    public static function update_user_picture($userId, UploadedFile $file, $crop = '')
1944
    {
1945
        if (empty($userId) || empty($file)) {
1946
            return false;
1947
        }
1948
1949
        $repo = Container::getUserRepository();
1950
        $user = $repo->find($userId);
1951
        if ($user) {
1952
            $repoIllustration = Container::getIllustrationRepository();
1953
            $repoIllustration->addIllustration($user, $user, $file, $crop);
1954
        }
1955
    }
1956
1957
    /**
1958
     * Deletes user photos.
1959
     *
1960
     * @param int $userId the user internal identification number
1961
     *
1962
     * @return mixed returns empty string on success, FALSE on error
1963
     */
1964
    public static function deleteUserPicture($userId)
1965
    {
1966
        $repo = Container::getUserRepository();
1967
        $user = $repo->find($userId);
1968
        if ($user) {
1969
            $illustrationRepo = Container::getIllustrationRepository();
1970
            $illustrationRepo->deleteIllustration($user);
1971
        }
1972
    }
1973
1974
    /**
1975
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
1976
     * doesn't have any.
1977
     *
1978
     * If there has been a request to remove a production, the function will return
1979
     * without building the list unless forced to do so by the optional second
1980
     * parameter. This increases performance by avoiding to read through the
1981
     * productions on the filesystem before the removal request has been carried
1982
     * out because they'll have to be re-read afterwards anyway.
1983
     *
1984
     * @param int  $user_id    User id
1985
     * @param bool $force      Optional parameter to force building after a removal request
1986
     * @param bool $showDelete
1987
     *
1988
     * @return string A string containing the XHTML code to display the production list, or FALSE
1989
     */
1990
    public static function build_production_list($user_id, $force = false, $showDelete = false)
1991
    {
1992
        if (!$force && !empty($_POST['remove_production'])) {
1993
            return true; // postpone reading from the filesystem
1994
        }
1995
1996
        $productions = self::get_user_productions($user_id);
1997
1998
        if (empty($productions)) {
1999
            return false;
2000
        }
2001
2002
        return false;
2003
2004
        $production_dir = self::getUserPathById($user_id, 'web');
2005
        $del_image = Display::returnIconPath('delete.png');
2006
        $add_image = Display::returnIconPath('archive.png');
2007
        $del_text = get_lang('Delete');
2008
        $production_list = '';
2009
        if (count($productions) > 0) {
2010
            $production_list = '<div class="files-production"><ul id="productions">';
2011
            foreach ($productions as $file) {
2012
                $production_list .= '<li>
2013
                    <img src="'.$add_image.'" />
2014
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2015
                        '.htmlentities($file).'
2016
                    </a>';
2017
                if ($showDelete) {
2018
                    $production_list .= '&nbsp;&nbsp;
2019
                        <input
2020
                            style="width:16px;"
2021
                            type="image"
2022
                            name="remove_production['.urlencode($file).']"
2023
                            src="'.$del_image.'"
2024
                            alt="'.$del_text.'"
2025
                            title="'.$del_text.' '.htmlentities($file).'"
2026
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2027
                }
2028
            }
2029
            $production_list .= '</ul></div>';
2030
        }
2031
2032
        return $production_list;
2033
    }
2034
2035
    /**
2036
     * Returns an array with the user's productions.
2037
     *
2038
     * @param int $user_id User id
2039
     *
2040
     * @return array An array containing the user's productions
2041
     */
2042
    public static function get_user_productions($user_id)
2043
    {
2044
        return [];
2045
2046
        $production_repository = self::getUserPathById($user_id, 'system');
2047
        $productions = [];
2048
2049
        if (is_dir($production_repository)) {
2050
            $handle = opendir($production_repository);
2051
            while ($file = readdir($handle)) {
2052
                if ('.' == $file ||
2053
                    '..' == $file ||
2054
                    '.htaccess' == $file ||
2055
                    is_dir($production_repository.$file)
2056
                ) {
2057
                    // skip current/parent directory and .htaccess
2058
                    continue;
2059
                }
2060
2061
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2062
                    // User's photos should not be listed as productions.
2063
                    continue;
2064
                }
2065
                $productions[] = $file;
2066
            }
2067
        }
2068
2069
        return $productions;
2070
    }
2071
2072
    /**
2073
     * Remove a user production.
2074
     *
2075
     * @param int    $user_id    User id
2076
     * @param string $production The production to remove
2077
     *
2078
     * @return bool
2079
     */
2080
    public static function remove_user_production($user_id, $production)
2081
    {
2082
        throw new Exception('remove_user_production');
2083
        /*$production_path = self::get_user_picture_path_by_id($user_id, 'system');
2084
        $production_file = $production_path['dir'].$production;
2085
        if (is_file($production_file)) {
2086
            unlink($production_file);
2087
2088
            return true;
2089
        }
2090
2091
        return false;*/
2092
    }
2093
2094
    /**
2095
     * Update an extra field value for a given user.
2096
     *
2097
     * @param int    $userId   User ID
2098
     * @param string $variable Field variable name
2099
     * @param string $value    Field value
2100
     *
2101
     * @return bool true if field updated, false otherwise
2102
     */
2103
    public static function update_extra_field_value($userId, $variable, $value = '')
2104
    {
2105
        $extraFieldValue = new ExtraFieldValue('user');
2106
        $params = [
2107
            'item_id' => $userId,
2108
            'variable' => $variable,
2109
            'value' => $value,
2110
        ];
2111
2112
        return $extraFieldValue->save($params);
2113
    }
2114
2115
    /**
2116
     * Get an array of extra fields with field details (type, default value and options).
2117
     *
2118
     * @param    int    Offset (from which row)
2119
     * @param    int    Number of items
2120
     * @param    int    Column on which sorting is made
2121
     * @param    string    Sorting direction
2122
     * @param    bool    Optional. Whether we get all the fields or just the visible ones
2123
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2124
     *
2125
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2126
     */
2127
    public static function get_extra_fields(
2128
        $from = 0,
2129
        $number_of_items = 0,
2130
        $column = 5,
2131
        $direction = 'ASC',
2132
        $all_visibility = true,
2133
        $field_filter = null
2134
    ) {
2135
        $fields = [];
2136
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2137
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2138
        $columns = [
2139
            'id',
2140
            'variable',
2141
            'field_type',
2142
            'display_text',
2143
            'default_value',
2144
            'field_order',
2145
            'filter',
2146
        ];
2147
        $column = (int) $column;
2148
        $sort_direction = '';
2149
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2150
            $sort_direction = strtoupper($direction);
2151
        }
2152
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2153
        $sqlf = "SELECT * FROM $t_uf WHERE extra_field_type = $extraFieldType ";
2154
        if (!$all_visibility) {
2155
            $sqlf .= " AND visible_to_self = 1 ";
2156
        }
2157
        if (!is_null($field_filter)) {
2158
            $field_filter = (int) $field_filter;
2159
            $sqlf .= " AND filter = $field_filter ";
2160
        }
2161
        $sqlf .= " ORDER BY `".$columns[$column]."` $sort_direction ";
2162
        if ($number_of_items != 0) {
2163
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2164
        }
2165
        $resf = Database::query($sqlf);
2166
        if (Database::num_rows($resf) > 0) {
2167
            while ($rowf = Database::fetch_array($resf)) {
2168
                $fields[$rowf['id']] = [
2169
                    0 => $rowf['id'],
2170
                    1 => $rowf['variable'],
2171
                    2 => $rowf['field_type'],
2172
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2173
                    4 => $rowf['default_value'],
2174
                    5 => $rowf['field_order'],
2175
                    6 => $rowf['visible_to_self'],
2176
                    7 => $rowf['changeable'],
2177
                    8 => $rowf['filter'],
2178
                    9 => [],
2179
                    10 => '<a name="'.$rowf['id'].'"></a>',
2180
                ];
2181
2182
                $sqlo = "SELECT * FROM $t_ufo
2183
                         WHERE field_id = ".$rowf['id']."
2184
                         ORDER BY option_order ASC";
2185
                $reso = Database::query($sqlo);
2186
                if (Database::num_rows($reso) > 0) {
2187
                    while ($rowo = Database::fetch_array($reso)) {
2188
                        $fields[$rowf['id']][9][$rowo['id']] = [
2189
                            0 => $rowo['id'],
2190
                            1 => $rowo['option_value'],
2191
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2192
                            3 => $rowo['option_order'],
2193
                        ];
2194
                    }
2195
                }
2196
            }
2197
        }
2198
2199
        return $fields;
2200
    }
2201
2202
    /**
2203
     * Creates a new extra field.
2204
     *
2205
     * @param string $variable    Field's internal variable name
2206
     * @param int    $fieldType   Field's type
2207
     * @param string $displayText Field's language var name
2208
     * @param string $default     Field's default value
2209
     *
2210
     * @return int
2211
     */
2212
    public static function create_extra_field(
2213
        $variable,
2214
        $fieldType,
2215
        $displayText,
2216
        $default
2217
    ) {
2218
        $extraField = new ExtraField('user');
2219
        $params = [
2220
            'variable' => $variable,
2221
            'field_type' => $fieldType,
2222
            'display_text' => $displayText,
2223
            'default_value' => $default,
2224
        ];
2225
2226
        return $extraField->save($params);
2227
    }
2228
2229
    /**
2230
     * Check if a field is available.
2231
     *
2232
     * @param string $variable
2233
     *
2234
     * @return bool
2235
     */
2236
    public static function is_extra_field_available($variable)
2237
    {
2238
        $extraField = new ExtraField('user');
2239
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2240
2241
        return !empty($data) ? true : false;
2242
    }
2243
2244
    /**
2245
     * Gets user extra fields data.
2246
     *
2247
     * @param    int    User ID
2248
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2249
     * @param    bool    Whether to return invisible fields as well
2250
     * @param    bool    Whether to split multiple-selection fields or not
2251
     *
2252
     * @return array Array of fields => value for the given user
2253
     */
2254
    public static function get_extra_user_data(
2255
        $user_id,
2256
        $prefix = false,
2257
        $allVisibility = true,
2258
        $splitMultiple = false,
2259
        $fieldFilter = null
2260
    ) {
2261
        $user_id = (int) $user_id;
2262
2263
        if (empty($user_id)) {
2264
            return [];
2265
        }
2266
2267
        $extra_data = [];
2268
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2269
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2270
        $user_id = (int) $user_id;
2271
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2272
                FROM $t_uf f
2273
                WHERE
2274
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
2275
                ";
2276
        $filter_cond = '';
2277
2278
        if (!$allVisibility) {
2279
            if (isset($fieldFilter)) {
2280
                $fieldFilter = (int) $fieldFilter;
2281
                $filter_cond .= " AND filter = $fieldFilter ";
2282
            }
2283
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2284
        } else {
2285
            if (isset($fieldFilter)) {
2286
                $fieldFilter = (int) $fieldFilter;
2287
                $sql .= " AND filter = $fieldFilter ";
2288
            }
2289
        }
2290
2291
        $sql .= ' ORDER BY f.field_order';
2292
2293
        $res = Database::query($sql);
2294
        if (Database::num_rows($res) > 0) {
2295
            while ($row = Database::fetch_array($res)) {
2296
                if (self::USER_FIELD_TYPE_TAG == $row['type']) {
2297
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2298
                    $extra_data['extra_'.$row['fvar']] = $tags;
2299
                } else {
2300
                    $sqlu = "SELECT value as fval
2301
                            FROM $t_ufv
2302
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2303
                    $resu = Database::query($sqlu);
2304
                    // get default value
2305
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2306
                               WHERE id=".$row['id'];
2307
                    $res_df = Database::query($sql_df);
2308
2309
                    if (Database::num_rows($resu) > 0) {
2310
                        $rowu = Database::fetch_array($resu);
2311
                        $fval = $rowu['fval'];
2312
                        if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2313
                            $fval = explode(';', $rowu['fval']);
2314
                        }
2315
                    } else {
2316
                        $row_df = Database::fetch_array($res_df);
2317
                        $fval = $row_df['fval_df'];
2318
                    }
2319
                    // We get here (and fill the $extra_data array) even if there
2320
                    // is no user with data (we fill it with default values)
2321
                    if ($prefix) {
2322
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2323
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2324
                        } else {
2325
                            $extra_data['extra_'.$row['fvar']] = $fval;
2326
                        }
2327
                    } else {
2328
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2329
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2330
                        } else {
2331
                            $extra_data[$row['fvar']] = $fval;
2332
                        }
2333
                    }
2334
                }
2335
            }
2336
        }
2337
2338
        return $extra_data;
2339
    }
2340
2341
    /** Get extra user data by field.
2342
     * @param int    user ID
2343
     * @param string the internal variable name of the field
2344
     *
2345
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2346
     */
2347
    public static function get_extra_user_data_by_field(
2348
        $user_id,
2349
        $field_variable,
2350
        $prefix = false,
2351
        $all_visibility = true,
2352
        $splitmultiple = false
2353
    ) {
2354
        $user_id = (int) $user_id;
2355
2356
        if (empty($user_id)) {
2357
            return [];
2358
        }
2359
2360
        $extra_data = [];
2361
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2362
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2363
2364
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2365
                FROM $t_uf f
2366
                WHERE f.variable = '$field_variable' ";
2367
2368
        if (!$all_visibility) {
2369
            $sql .= " AND f.visible_to_self = 1 ";
2370
        }
2371
2372
        $sql .= " AND extra_field_type = ".EntityExtraField::USER_FIELD_TYPE;
2373
        $sql .= " ORDER BY f.field_order ";
2374
2375
        $res = Database::query($sql);
2376
        if (Database::num_rows($res) > 0) {
2377
            while ($row = Database::fetch_array($res)) {
2378
                $sqlu = "SELECT value as fval FROM $t_ufv v
2379
                         INNER JOIN $t_uf f
2380
                         ON (v.field_id = f.id)
2381
                         WHERE
2382
                            extra_field_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2383
                            field_id = ".$row['id']." AND
2384
                            item_id = ".$user_id;
2385
                $resu = Database::query($sqlu);
2386
                $fval = '';
2387
                if (Database::num_rows($resu) > 0) {
2388
                    $rowu = Database::fetch_array($resu);
2389
                    $fval = $rowu['fval'];
2390
                    if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2391
                        $fval = explode(';', $rowu['fval']);
2392
                    }
2393
                }
2394
                if ($prefix) {
2395
                    $extra_data['extra_'.$row['fvar']] = $fval;
2396
                } else {
2397
                    $extra_data[$row['fvar']] = $fval;
2398
                }
2399
            }
2400
        }
2401
2402
        return $extra_data;
2403
    }
2404
2405
    /**
2406
     * Get the extra field information for a certain field (the options as well).
2407
     *
2408
     * @param int $variable The name of the field we want to know everything about
2409
     *
2410
     * @return array Array containing all the information about the extra profile field
2411
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2412
     *               as returned by the database)
2413
     *
2414
     * @author Julio Montoya
2415
     *
2416
     * @since v1.8.6
2417
     */
2418
    public static function get_extra_field_information_by_name($variable)
2419
    {
2420
        $extraField = new ExtraField('user');
2421
2422
        return $extraField->get_handler_field_info_by_field_variable($variable);
2423
    }
2424
2425
    /**
2426
     * Get the extra field information for user tag (the options as well).
2427
     *
2428
     * @param int $variable The name of the field we want to know everything about
2429
     *
2430
     * @return array Array containing all the information about the extra profile field
2431
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2432
     *               as returned by the database)
2433
     *
2434
     * @author José Loguercio
2435
     *
2436
     * @since v1.11.0
2437
     */
2438
    public static function get_extra_field_tags_information_by_name($variable)
2439
    {
2440
        $extraField = new ExtraField('user');
2441
2442
        return $extraField->get_handler_field_info_by_tags($variable);
2443
    }
2444
2445
    /**
2446
     * Get all the extra field information of a certain field (also the options).
2447
     *
2448
     * @param int $fieldId the ID of the field we want to know everything of
2449
     *
2450
     * @return array $return containing all th information about the extra profile field
2451
     *
2452
     * @author Julio Montoya
2453
     *
2454
     * @deprecated
2455
     * @since v1.8.6
2456
     */
2457
    public static function get_extra_field_information($fieldId)
2458
    {
2459
        $extraField = new ExtraField('user');
2460
2461
        return $extraField->getFieldInfoByFieldId($fieldId);
2462
    }
2463
2464
    /**
2465
     * Get extra user data by value.
2466
     *
2467
     * @param string $variable       the internal variable name of the field
2468
     * @param string $value          the internal value of the field
2469
     * @param bool   $all_visibility
2470
     *
2471
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2472
     */
2473
    public static function get_extra_user_data_by_value($variable, $value, $all_visibility = true)
2474
    {
2475
        $extraFieldValue = new ExtraFieldValue('user');
2476
        $extraField = new ExtraField('user');
2477
2478
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2479
2480
        if (false === $info) {
2481
            return [];
2482
        }
2483
2484
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2485
            $variable,
2486
            $value,
2487
            false,
2488
            false,
2489
            true
2490
        );
2491
2492
        $result = [];
2493
        if (!empty($data)) {
2494
            foreach ($data as $item) {
2495
                $result[] = $item['item_id'];
2496
            }
2497
        }
2498
2499
        return $result;
2500
    }
2501
2502
    /**
2503
     * Get extra user data by tags value.
2504
     *
2505
     * @param int    $fieldId the ID of the field we want to know everything of
2506
     * @param string $tag     the tag name for search
2507
     *
2508
     * @return array with extra data info of a user
2509
     *
2510
     * @author José Loguercio
2511
     *
2512
     * @since v1.11.0
2513
     */
2514
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2515
    {
2516
        $extraField = new ExtraField('user');
2517
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2518
        $array = [];
2519
        foreach ($result as $index => $user) {
2520
            $array[] = $user['user_id'];
2521
        }
2522
2523
        return $array;
2524
    }
2525
2526
    /**
2527
     * Get extra user data by field variable.
2528
     *
2529
     * @param string $variable field variable
2530
     *
2531
     * @return array data
2532
     */
2533
    public static function get_extra_user_data_by_field_variable($variable)
2534
    {
2535
        $extraInfo = self::get_extra_field_information_by_name($variable);
2536
        $field_id = (int) $extraInfo['id'];
2537
2538
        $extraField = new ExtraFieldValue('user');
2539
        $data = $extraField->getValuesByFieldId($field_id);
2540
2541
        if (!empty($data)) {
2542
            foreach ($data as $row) {
2543
                $user_id = $row['item_id'];
2544
                $data[$user_id] = $row;
2545
            }
2546
        }
2547
2548
        return $data;
2549
    }
2550
2551
    /**
2552
     * Get extra user data tags by field variable.
2553
     *
2554
     * @param string $variable field variable
2555
     *
2556
     * @return array
2557
     */
2558
    public static function get_extra_user_data_for_tags($variable)
2559
    {
2560
        $data = self::get_extra_field_tags_information_by_name($variable);
2561
2562
        return $data;
2563
    }
2564
2565
    /**
2566
     * Gives a list of [session_category][session_id] for the current user.
2567
     *
2568
     * @param int  $user_id
2569
     * @param bool $is_time_over                 whether to fill the first element or not
2570
     *                                           (to give space for courses out of categories)
2571
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2572
     * @param bool $ignoreTimeLimit              ignore time start/end
2573
     * @param bool $getCount
2574
     *
2575
     * @return array list of statuses [session_category][session_id]
2576
     *
2577
     * @todo ensure multiple access urls are managed correctly
2578
     */
2579
    public static function get_sessions_by_category(
2580
        $user_id,
2581
        $is_time_over = true,
2582
        $ignore_visibility_for_admins = false,
2583
        $ignoreTimeLimit = false,
2584
        $getCount = false
2585
    ) {
2586
        $user_id = (int) $user_id;
2587
2588
        if (empty($user_id)) {
2589
            return [];
2590
        }
2591
2592
        $allowOrder = api_get_configuration_value('session_list_order');
2593
        $position = '';
2594
        if ($allowOrder) {
2595
            $position = ', s.position AS position ';
2596
        }
2597
2598
        // Get the list of sessions per user
2599
        $now = new DateTime('now', new DateTimeZone('UTC'));
2600
2601
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2602
        // join would not catch session-courses where the user is general
2603
        // session coach but which do not have students nor coaches registered
2604
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
2605
2606
        if (!$getCount) {
2607
            $dqlSelect = " DISTINCT
2608
                s.id,
2609
                s.name,
2610
                s.accessStartDate AS access_start_date,
2611
                s.accessEndDate AS access_end_date,
2612
                s.duration,
2613
                sc.id AS session_category_id,
2614
                sc.name AS session_category_name,
2615
                sc.dateStart AS session_category_date_start,
2616
                sc.dateEnd AS session_category_date_end,
2617
                s.coachAccessStartDate AS coach_access_start_date,
2618
                s.coachAccessEndDate AS coach_access_end_date,
2619
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
2620
                $position
2621
            ";
2622
        }
2623
2624
        $dql = "SELECT $dqlSelect
2625
                FROM ChamiloCoreBundle:Session AS s
2626
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2627
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2628
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc ";
2629
2630
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
2631
        // is awfully inefficient for large sets of data (1m25s for 58K
2632
        // sessions, BT#14115) but executing a similar query twice and grouping
2633
        // the results afterwards in PHP takes about 1/1000th of the time
2634
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
2635
        $dqlStudent = $dql." WHERE scu.user = :user AND url.url = :url ";
2636
        $dqlCoach = $dql." WHERE s.generalCoach = :user AND url.url = :url ";
2637
2638
        // Default order
2639
        $order = 'ORDER BY sc.name, s.name';
2640
2641
        // Order by date if showing all sessions
2642
        $showAllSessions = true === api_get_configuration_value('show_all_sessions_on_my_course_page');
2643
        if ($showAllSessions) {
2644
            $order = 'ORDER BY s.accessStartDate';
2645
        }
2646
2647
        // Order by position
2648
        if ($allowOrder) {
2649
            $order = 'ORDER BY s.position';
2650
        }
2651
2652
        // Order by dates according to settings
2653
        $orderBySettings = api_get_configuration_value('my_courses_session_order');
2654
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
2655
            $field = $orderBySettings['field'];
2656
            $orderSetting = $orderBySettings['order'];
2657
            switch ($field) {
2658
                case 'start_date':
2659
                    $order = " ORDER BY s.accessStartDate $orderSetting";
2660
                    break;
2661
                case 'end_date':
2662
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
2663
                    if ('asc' == $orderSetting) {
2664
                        // Put null values at the end
2665
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
2666
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
2667
                    }
2668
                    break;
2669
                case 'name':
2670
                    $order = " ORDER BY s.name $orderSetting ";
2671
                    break;
2672
            }
2673
        }
2674
2675
        $dqlStudent .= $order;
2676
        $dqlCoach .= $order;
2677
2678
        $accessUrlId = api_get_current_access_url_id();
2679
        $dqlStudent = Database::getManager()
2680
            ->createQuery($dqlStudent)
2681
            ->setParameters(
2682
                ['user' => $user_id, 'url' => $accessUrlId]
2683
            )
2684
        ;
2685
        $dqlCoach = Database::getManager()
2686
            ->createQuery($dqlCoach)
2687
            ->setParameters(
2688
                ['user' => $user_id, 'url' => $accessUrlId]
2689
            )
2690
        ;
2691
2692
        if ($getCount) {
2693
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
2694
        }
2695
2696
        $sessionDataStudent = $dqlStudent->getResult();
2697
        $sessionDataCoach = $dqlCoach->getResult();
2698
2699
        $sessionData = [];
2700
        // First fill $sessionData with student sessions
2701
        if (!empty($sessionDataStudent)) {
2702
            foreach ($sessionDataStudent as $row) {
2703
                $sessionData[$row['id']] = $row;
2704
            }
2705
        }
2706
        // Overwrite session data of the user as a student with session data
2707
        // of the user as a coach.
2708
        // There shouldn't be such duplicate rows, but just in case...
2709
        if (!empty($sessionDataCoach)) {
2710
            foreach ($sessionDataCoach as $row) {
2711
                $sessionData[$row['id']] = $row;
2712
            }
2713
        }
2714
2715
        $collapsable = api_get_configuration_value('allow_user_session_collapsable');
2716
        $extraField = new ExtraFieldValue('session');
2717
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
2718
2719
        if (empty($sessionData)) {
2720
            return [];
2721
        }
2722
        $categories = [];
2723
        foreach ($sessionData as $row) {
2724
            $session_id = $row['id'];
2725
            $coachList = SessionManager::getCoachesBySession($session_id);
2726
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
2727
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
2728
            $courseList = self::get_courses_list_by_session($user_id, $session_id);
2729
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
2730
2731
            // User portal filters:
2732
            if (false === $ignoreTimeLimit) {
2733
                if ($is_time_over) {
2734
                    // History
2735
                    if ($row['duration']) {
2736
                        if ($daysLeft >= 0) {
2737
                            continue;
2738
                        }
2739
                    } else {
2740
                        if (empty($row['access_end_date'])) {
2741
                            continue;
2742
                        } else {
2743
                            if ($row['access_end_date'] > $now) {
2744
                                continue;
2745
                            }
2746
                        }
2747
                    }
2748
                } else {
2749
                    // Current user portal
2750
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
2751
                    $isCoachOfCourse = in_array($user_id, $coachList);
2752
2753
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
2754
                        // Teachers can access the session depending in the access_coach date
2755
                    } else {
2756
                        if ($row['duration']) {
2757
                            if ($daysLeft <= 0) {
2758
                                continue;
2759
                            }
2760
                        } else {
2761
                            if (isset($row['access_end_date']) &&
2762
                                !empty($row['access_end_date'])
2763
                            ) {
2764
                                if ($row['access_end_date'] <= $now) {
2765
                                    continue;
2766
                                }
2767
                            }
2768
                        }
2769
                    }
2770
                }
2771
            }
2772
2773
            $categories[$row['session_category_id']]['session_category'] = [
2774
                'id' => $row['session_category_id'],
2775
                'name' => $row['session_category_name'],
2776
                'date_start' => $categoryStart,
2777
                'date_end' => $categoryEnd,
2778
            ];
2779
2780
            $visibility = api_get_session_visibility(
2781
                $session_id,
2782
                null,
2783
                $ignore_visibility_for_admins
2784
            );
2785
2786
            if (SESSION_VISIBLE != $visibility) {
2787
                // Course Coach session visibility.
2788
                $blockedCourseCount = 0;
2789
                $closedVisibilityList = [
2790
                    COURSE_VISIBILITY_CLOSED,
2791
                    COURSE_VISIBILITY_HIDDEN,
2792
                ];
2793
2794
                foreach ($courseList as $course) {
2795
                    // Checking session visibility
2796
                    $sessionCourseVisibility = api_get_session_visibility(
2797
                        $session_id,
2798
                        $course['real_id'],
2799
                        $ignore_visibility_for_admins
2800
                    );
2801
2802
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
2803
                    if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
2804
                        $blockedCourseCount++;
2805
                    }
2806
                }
2807
2808
                // If all courses are blocked then no show in the list.
2809
                if ($blockedCourseCount === count($courseList)) {
2810
                    $visibility = SESSION_INVISIBLE;
2811
                } else {
2812
                    $visibility = $sessionCourseVisibility;
2813
                }
2814
            }
2815
2816
            switch ($visibility) {
2817
                case SESSION_VISIBLE_READ_ONLY:
2818
                case SESSION_VISIBLE:
2819
                case SESSION_AVAILABLE:
2820
                    break;
2821
                case SESSION_INVISIBLE:
2822
                    if (false === $ignore_visibility_for_admins) {
2823
                        continue 2;
2824
                    }
2825
            }
2826
2827
            $collapsed = '';
2828
            $collapsedAction = '';
2829
            if ($collapsable) {
2830
                $collapsableData = SessionManager::getCollapsableData(
2831
                    $user_id,
2832
                    $session_id,
2833
                    $extraField,
2834
                    $collapsableLink
2835
                );
2836
                $collapsed = $collapsableData['collapsed'];
2837
                $collapsedAction = $collapsableData['collapsable_link'];
2838
            }
2839
2840
            $categories[$row['session_category_id']]['sessions'][] = [
2841
                'session_name' => $row['name'],
2842
                'session_id' => $row['id'],
2843
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
2844
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
2845
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
2846
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
2847
                'courses' => $courseList,
2848
                'collapsed' => $collapsed,
2849
                'collapsable_link' => $collapsedAction,
2850
                'duration' => $row['duration'],
2851
            ];
2852
        }
2853
2854
        return $categories;
2855
    }
2856
2857
    /**
2858
     * Gives a list of [session_id-course_code] => [status] for the current user.
2859
     *
2860
     * @param int $user_id
2861
     * @param int $sessionLimit
2862
     *
2863
     * @return array list of statuses (session_id-course_code => status)
2864
     */
2865
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
2866
    {
2867
        // Database Table Definitions
2868
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2869
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2870
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2871
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
2872
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2873
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2874
        $tblCourseCategory = Database::get_main_table(TABLE_MAIN_CATEGORY);
2875
2876
        $user_id = (int) $user_id;
2877
2878
        if (empty($user_id)) {
2879
            return [];
2880
        }
2881
2882
        // We filter the courses from the URL
2883
        $join_access_url = $where_access_url = '';
2884
        if (api_get_multiple_access_url()) {
2885
            $access_url_id = api_get_current_access_url_id();
2886
            if (-1 != $access_url_id) {
2887
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2888
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
2889
                $where_access_url = " AND access_url_id = $access_url_id ";
2890
            }
2891
        }
2892
2893
        // Courses in which we subscribed out of any session
2894
2895
        $sql = "SELECT
2896
                    course.code,
2897
                    course_rel_user.status course_rel_status,
2898
                    course_rel_user.sort sort,
2899
                    course_rel_user.user_course_cat user_course_cat
2900
                 FROM $tbl_course_user course_rel_user
2901
                 LEFT JOIN $tbl_course course
2902
                 ON course.id = course_rel_user.c_id
2903
                 $join_access_url
2904
                 WHERE
2905
                    course_rel_user.user_id = '".$user_id."' AND
2906
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
2907
                    $where_access_url
2908
                 ORDER BY course_rel_user.sort, course.title ASC";
2909
2910
        $course_list_sql_result = Database::query($sql);
2911
        $personal_course_list = [];
2912
        if (Database::num_rows($course_list_sql_result) > 0) {
2913
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
2914
                $course_info = api_get_course_info($result_row['code']);
2915
                $result_row['course_info'] = $course_info;
2916
                $personal_course_list[] = $result_row;
2917
            }
2918
        }
2919
2920
        $coachCourseConditions = '';
2921
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
2922
        if (api_is_allowed_to_create_course()) {
2923
            $sessionListFromCourseCoach = [];
2924
            $sql = " SELECT DISTINCT session_id
2925
                    FROM $tbl_session_course_user
2926
                    WHERE user_id = $user_id AND status = 2 ";
2927
2928
            $result = Database::query($sql);
2929
            if (Database::num_rows($result)) {
2930
                $result = Database::store_result($result);
2931
                foreach ($result as $session) {
2932
                    $sessionListFromCourseCoach[] = $session['session_id'];
2933
                }
2934
            }
2935
            if (!empty($sessionListFromCourseCoach)) {
2936
                $condition = implode("','", $sessionListFromCourseCoach);
2937
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
2938
            }
2939
        }
2940
2941
        // Get the list of sessions where the user is subscribed
2942
        // This is divided into two different queries
2943
        $sessions = [];
2944
        $sessionLimitRestriction = '';
2945
        if (!empty($sessionLimit)) {
2946
            $sessionLimit = (int) $sessionLimit;
2947
            $sessionLimitRestriction = "LIMIT $sessionLimit";
2948
        }
2949
2950
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
2951
                FROM $tbl_session_user su INNER JOIN $tbl_session s
2952
                ON (s.id = su.session_id)
2953
                WHERE (
2954
                    su.user_id = $user_id AND
2955
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
2956
                )
2957
                $coachCourseConditions
2958
                ORDER BY access_start_date, access_end_date, name
2959
                $sessionLimitRestriction
2960
        ";
2961
2962
        $result = Database::query($sql);
2963
        if (Database::num_rows($result) > 0) {
2964
            while ($row = Database::fetch_assoc($result)) {
2965
                $sessions[$row['id']] = $row;
2966
            }
2967
        }
2968
2969
        $sql = "SELECT DISTINCT
2970
                id, name, access_start_date, access_end_date
2971
                FROM $tbl_session s
2972
                WHERE (
2973
                    id_coach = $user_id
2974
                )
2975
                $coachCourseConditions
2976
                ORDER BY access_start_date, access_end_date, name";
2977
2978
        $result = Database::query($sql);
2979
        if (Database::num_rows($result) > 0) {
2980
            while ($row = Database::fetch_assoc($result)) {
2981
                if (empty($sessions[$row['id']])) {
2982
                    $sessions[$row['id']] = $row;
2983
                }
2984
            }
2985
        }
2986
2987
        if (api_is_allowed_to_create_course()) {
2988
            foreach ($sessions as $enreg) {
2989
                $session_id = $enreg['id'];
2990
                $session_visibility = api_get_session_visibility($session_id);
2991
2992
                if (SESSION_INVISIBLE == $session_visibility) {
2993
                    continue;
2994
                }
2995
2996
                // This query is horribly slow when more than a few thousand
2997
                // users and just a few sessions to which they are subscribed
2998
                $sql = "SELECT DISTINCT
2999
                        course.code code,
3000
                        course.title i,
3001
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
3002
                        email, course.course_language l,
3003
                        1 sort,
3004
                        course_category.code user_course_cat,
3005
                        access_start_date,
3006
                        access_end_date,
3007
                        session.id as session_id,
3008
                        session.name as session_name
3009
                    FROM $tbl_session_course_user as session_course_user
3010
                    INNER JOIN $tbl_course AS course
3011
                        ON course.id = session_course_user.c_id
3012
                    LEFT JOIN $tblCourseCategory course_category ON course.category_id = course_category.id
3013
                    INNER JOIN $tbl_session as session
3014
                        ON session.id = session_course_user.session_id
3015
                    LEFT JOIN $tbl_user as user
3016
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
3017
                    WHERE
3018
                        session_course_user.session_id = $session_id AND (
3019
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
3020
                            OR session.id_coach = $user_id
3021
                        )
3022
                    ORDER BY i";
3023
                $course_list_sql_result = Database::query($sql);
3024
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3025
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3026
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3027
                    $personal_course_list[$key] = $result_row;
3028
                }
3029
            }
3030
        }
3031
3032
        foreach ($sessions as $enreg) {
3033
            $session_id = $enreg['id'];
3034
            $session_visibility = api_get_session_visibility($session_id);
3035
            if (SESSION_INVISIBLE == $session_visibility) {
3036
                continue;
3037
            }
3038
3039
            /* This query is very similar to the above query,
3040
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3041
            $sql = "SELECT DISTINCT
3042
                course.code code,
3043
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
3044
                email,
3045
                course.course_language l,
3046
                1 sort,
3047
                course_category.code user_course_cat,
3048
                access_start_date,
3049
                access_end_date,
3050
                session.id as session_id,
3051
                session.name as session_name,
3052
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
3053
            FROM $tbl_session_course_user as session_course_user
3054
            INNER JOIN $tbl_course AS course
3055
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
3056
            LEFT JOIN $tblCourseCategory course_category ON course.category_id = course_category.id
3057
            INNER JOIN $tbl_session as session
3058
            ON session_course_user.session_id = session.id
3059
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
3060
            WHERE session_course_user.user_id = $user_id
3061
            ORDER BY i";
3062
3063
            $course_list_sql_result = Database::query($sql);
3064
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3065
                $result_row['course_info'] = api_get_course_info($result_row['code']);
3066
                $key = $result_row['session_id'].' - '.$result_row['code'];
3067
                if (!isset($personal_course_list[$key])) {
3068
                    $personal_course_list[$key] = $result_row;
3069
                }
3070
            }
3071
        }
3072
3073
        return $personal_course_list;
3074
    }
3075
3076
    /**
3077
     * Gives a list of courses for the given user in the given session.
3078
     *
3079
     * @param int $user_id
3080
     * @param int $session_id
3081
     *
3082
     * @return array list of statuses (session_id-course_code => status)
3083
     */
3084
    public static function get_courses_list_by_session($user_id, $session_id)
3085
    {
3086
        // Database Table Definitions
3087
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3088
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
3089
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3090
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3091
3092
        $user_id = (int) $user_id;
3093
        $session_id = (int) $session_id;
3094
        // We filter the courses from the URL
3095
        $join_access_url = $where_access_url = '';
3096
        if (api_get_multiple_access_url()) {
3097
            $urlId = api_get_current_access_url_id();
3098
            if (-1 != $urlId) {
3099
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3100
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
3101
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
3102
            }
3103
        }
3104
3105
        /* This query is very similar to the query below, but it will check the
3106
        session_rel_course_user table if there are courses registered
3107
        to our user or not */
3108
        $sql = "SELECT DISTINCT
3109
                    c.title,
3110
                    c.visibility,
3111
                    c.id as real_id,
3112
                    c.code as course_code,
3113
                    sc.position,
3114
                    c.unsubscribe
3115
                FROM $tbl_session_course_user as scu
3116
                INNER JOIN $tbl_session_course sc
3117
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3118
                INNER JOIN $tableCourse as c
3119
                ON (scu.c_id = c.id)
3120
                $join_access_url
3121
                WHERE
3122
                    scu.user_id = $user_id AND
3123
                    scu.session_id = $session_id
3124
                    $where_access_url
3125
                ORDER BY sc.position ASC";
3126
3127
        $myCourseList = [];
3128
        $courses = [];
3129
        $result = Database::query($sql);
3130
        if (Database::num_rows($result) > 0) {
3131
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3132
                $result_row['status'] = 5;
3133
                if (!in_array($result_row['real_id'], $courses)) {
3134
                    $position = $result_row['position'];
3135
                    if (!isset($myCourseList[$position])) {
3136
                        $myCourseList[$position] = $result_row;
3137
                    } else {
3138
                        $myCourseList[] = $result_row;
3139
                    }
3140
                    $courses[] = $result_row['real_id'];
3141
                }
3142
            }
3143
        }
3144
3145
        if (api_is_allowed_to_create_course()) {
3146
            $sql = "SELECT DISTINCT
3147
                        c.title,
3148
                        c.visibility,
3149
                        c.id as real_id,
3150
                        c.code as course_code,
3151
                        sc.position,
3152
                        c.unsubscribe
3153
                    FROM $tbl_session_course_user as scu
3154
                    INNER JOIN $tbl_session as s
3155
                    ON (scu.session_id = s.id)
3156
                    INNER JOIN $tbl_session_course sc
3157
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3158
                    INNER JOIN $tableCourse as c
3159
                    ON (scu.c_id = c.id)
3160
                    $join_access_url
3161
                    WHERE
3162
                      s.id = $session_id AND
3163
                      (
3164
                        (scu.user_id = $user_id AND scu.status = 2) OR
3165
                        s.id_coach = $user_id
3166
                      )
3167
                    $where_access_url
3168
                    ORDER BY sc.position ASC";
3169
            $result = Database::query($sql);
3170
3171
            if (Database::num_rows($result) > 0) {
3172
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3173
                    $result_row['status'] = 2;
3174
                    if (!in_array($result_row['real_id'], $courses)) {
3175
                        $position = $result_row['position'];
3176
                        if (!isset($myCourseList[$position])) {
3177
                            $myCourseList[$position] = $result_row;
3178
                        } else {
3179
                            $myCourseList[] = $result_row;
3180
                        }
3181
                        $courses[] = $result_row['real_id'];
3182
                    }
3183
                }
3184
            }
3185
        }
3186
3187
        if (api_is_drh()) {
3188
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3189
            $sessionList = array_keys($sessionList);
3190
            if (in_array($session_id, $sessionList)) {
3191
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3192
                if (!empty($courseList)) {
3193
                    foreach ($courseList as $course) {
3194
                        if (!in_array($course['id'], $courses)) {
3195
                            $position = $course['position'];
3196
                            if (!isset($myCourseList[$position])) {
3197
                                $myCourseList[$position] = $course;
3198
                            } else {
3199
                                $myCourseList[] = $course;
3200
                            }
3201
                        }
3202
                    }
3203
                }
3204
            }
3205
        } else {
3206
            //check if user is general coach for this session
3207
            $sessionInfo = api_get_session_info($session_id);
3208
            if ($sessionInfo['id_coach'] == $user_id) {
3209
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3210
                if (!empty($courseList)) {
3211
                    foreach ($courseList as $course) {
3212
                        if (!in_array($course['id'], $courses)) {
3213
                            $position = $course['position'];
3214
                            if (!isset($myCourseList[$position])) {
3215
                                $myCourseList[$position] = $course;
3216
                            } else {
3217
                                $myCourseList[] = $course;
3218
                            }
3219
                        }
3220
                    }
3221
                }
3222
            }
3223
        }
3224
3225
        if (!empty($myCourseList)) {
3226
            ksort($myCourseList);
3227
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
3228
            if (empty($checkPosition)) {
3229
                // The session course list doesn't have any position,
3230
                // then order the course list by course code
3231
                $list = array_column($myCourseList, 'course_code');
3232
                array_multisort($myCourseList, SORT_ASC, $list);
3233
            }
3234
        }
3235
3236
        return $myCourseList;
3237
    }
3238
3239
    /**
3240
     * Get user id from a username.
3241
     *
3242
     * @param string $username
3243
     *
3244
     * @return int User ID (or false if not found)
3245
     */
3246
    public static function get_user_id_from_username($username)
3247
    {
3248
        if (empty($username)) {
3249
            return false;
3250
        }
3251
        $username = trim($username);
3252
        $username = Database::escape_string($username);
3253
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3254
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3255
        $res = Database::query($sql);
3256
3257
        if (false === $res) {
3258
            return false;
3259
        }
3260
        if (1 !== Database::num_rows($res)) {
3261
            return false;
3262
        }
3263
        $row = Database::fetch_array($res);
3264
3265
        return $row['id'];
3266
    }
3267
3268
    /**
3269
     * Get the users files upload from his share_folder.
3270
     *
3271
     * @param string $user_id      User ID
3272
     * @param string $course       course directory
3273
     * @param string $resourceType resource type: images, all
3274
     *
3275
     * @return string
3276
     */
3277
    public static function get_user_upload_files_by_course(
3278
        $user_id,
3279
        $course,
3280
        $resourceType = 'all'
3281
    ) {
3282
        $return = '';
3283
        $user_id = (int) $user_id;
3284
3285
        if (!empty($user_id) && !empty($course)) {
3286
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3287
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3288
            $file_list = [];
3289
3290
            if (is_dir($path)) {
3291
                $handle = opendir($path);
3292
                while ($file = readdir($handle)) {
3293
                    if ('.' == $file || '..' == $file || '.htaccess' == $file || is_dir($path.$file)) {
3294
                        continue; // skip current/parent directory and .htaccess
3295
                    }
3296
                    $file_list[] = $file;
3297
                }
3298
                if (count($file_list) > 0) {
3299
                    $return = "<h4>$course</h4>";
3300
                    $return .= '<ul class="thumbnails">';
3301
                }
3302
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
3303
                foreach ($file_list as $file) {
3304
                    if ('all' == $resourceType) {
3305
                        $return .= '<li>
3306
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
3307
                    } elseif ('images' == $resourceType) {
3308
                        //get extension
3309
                        $ext = explode('.', $file);
3310
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
3311
                            $return .= '<li class="span2">
3312
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
3313
                                                <img src="'.$web_path.urlencode($file).'" >
3314
                                            </a>
3315
                                        </li>';
3316
                        }
3317
                    }
3318
                }
3319
                if (count($file_list) > 0) {
3320
                    $return .= '</ul>';
3321
                }
3322
            }
3323
        }
3324
3325
        return $return;
3326
    }
3327
3328
    /**
3329
     * Gets the API key (or keys) and return them into an array.
3330
     *
3331
     * @param int     Optional user id (defaults to the result of api_get_user_id())
3332
     * @param string $api_service
3333
     *
3334
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
3335
     */
3336
    public static function get_api_keys($user_id = null, $api_service = 'dokeos')
3337
    {
3338
        if ($user_id != strval(intval($user_id))) {
3339
            return false;
3340
        }
3341
        if (empty($user_id)) {
3342
            $user_id = api_get_user_id();
3343
        }
3344
        if (false === $user_id) {
3345
            return false;
3346
        }
3347
        $service_name = Database::escape_string($api_service);
3348
        if (false === is_string($service_name)) {
3349
            return false;
3350
        }
3351
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3352
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
3353
        $res = Database::query($sql);
3354
        if (false === $res) {
3355
            return false;
3356
        } //error during query
3357
        $num = Database::num_rows($res);
3358
        if (0 == $num) {
3359
            return false;
3360
        }
3361
        $list = [];
3362
        while ($row = Database::fetch_array($res)) {
3363
            $list[$row['id']] = $row['api_key'];
3364
        }
3365
3366
        return $list;
3367
    }
3368
3369
    /**
3370
     * Adds a new API key to the users' account.
3371
     *
3372
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
3373
     * @param string $api_service
3374
     *
3375
     * @return bool True on success, false on failure
3376
     */
3377
    public static function add_api_key($user_id = null, $api_service = 'dokeos')
3378
    {
3379
        if ($user_id != strval(intval($user_id))) {
3380
            return false;
3381
        }
3382
        if (empty($user_id)) {
3383
            $user_id = api_get_user_id();
3384
        }
3385
        if (false === $user_id) {
3386
            return false;
3387
        }
3388
        $service_name = Database::escape_string($api_service);
3389
        if (false === is_string($service_name)) {
3390
            return false;
3391
        }
3392
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3393
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
3394
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
3395
        $res = Database::query($sql);
3396
        if (false === $res) {
3397
            return false;
3398
        } //error during query
3399
        $num = Database::insert_id();
3400
3401
        return 0 == $num ? false : $num;
3402
    }
3403
3404
    /**
3405
     * Deletes an API key from the user's account.
3406
     *
3407
     * @param   int     API key's internal ID
3408
     *
3409
     * @return bool True on success, false on failure
3410
     */
3411
    public static function delete_api_key($key_id)
3412
    {
3413
        if ($key_id != strval(intval($key_id))) {
3414
            return false;
3415
        }
3416
        if (false === $key_id) {
3417
            return false;
3418
        }
3419
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3420
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
3421
        $res = Database::query($sql);
3422
        if (false === $res) {
3423
            return false;
3424
        } //error during query
3425
        $num = Database::num_rows($res);
3426
        if (1 !== $num) {
3427
            return false;
3428
        }
3429
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
3430
        $res = Database::query($sql);
3431
        if (false === $res) {
3432
            return false;
3433
        } //error during query
3434
3435
        return true;
3436
    }
3437
3438
    /**
3439
     * Regenerate an API key from the user's account.
3440
     *
3441
     * @param   int     user ID (defaults to the results of api_get_user_id())
3442
     * @param   string  API key's internal ID
3443
     *
3444
     * @return int num
3445
     */
3446
    public static function update_api_key($user_id, $api_service)
3447
    {
3448
        if ($user_id != strval(intval($user_id))) {
3449
            return false;
3450
        }
3451
        if (false === $user_id) {
3452
            return false;
3453
        }
3454
        $service_name = Database::escape_string($api_service);
3455
        if (false === is_string($service_name)) {
3456
            return false;
3457
        }
3458
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3459
        $sql = "SELECT id FROM $t_api
3460
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3461
        $res = Database::query($sql);
3462
        $num = Database::num_rows($res);
3463
        if (1 == $num) {
3464
            $id_key = Database::fetch_array($res, 'ASSOC');
3465
            self::delete_api_key($id_key['id']);
3466
            $num = self::add_api_key($user_id, $api_service);
3467
        } elseif (0 == $num) {
3468
            $num = self::add_api_key($user_id, $api_service);
3469
        }
3470
3471
        return $num;
3472
    }
3473
3474
    /**
3475
     * @param   int     user ID (defaults to the results of api_get_user_id())
3476
     * @param   string    API key's internal ID
3477
     *
3478
     * @return int row ID, or return false if not found
3479
     */
3480
    public static function get_api_key_id($user_id, $api_service)
3481
    {
3482
        if ($user_id != strval(intval($user_id))) {
3483
            return false;
3484
        }
3485
        if (false === $user_id) {
3486
            return false;
3487
        }
3488
        if (empty($api_service)) {
3489
            return false;
3490
        }
3491
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3492
        $api_service = Database::escape_string($api_service);
3493
        $sql = "SELECT id FROM $t_api
3494
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3495
        $res = Database::query($sql);
3496
        if (Database::num_rows($res) < 1) {
3497
            return false;
3498
        }
3499
        $row = Database::fetch_array($res, 'ASSOC');
3500
3501
        return $row['id'];
3502
    }
3503
3504
    /**
3505
     * Checks if a user_id is platform admin.
3506
     *
3507
     * @param   int user ID
3508
     *
3509
     * @return bool True if is admin, false otherwise
3510
     *
3511
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
3512
     */
3513
    public static function is_admin($user_id)
3514
    {
3515
        $user_id = (int) $user_id;
3516
        if (empty($user_id)) {
3517
            return false;
3518
        }
3519
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3520
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3521
        $res = Database::query($sql);
3522
3523
        return 1 === Database::num_rows($res);
3524
    }
3525
3526
    /**
3527
     * Get the total count of users.
3528
     *
3529
     * @param int $status        Status of users to be counted
3530
     * @param int $access_url_id Access URL ID (optional)
3531
     * @param int $active
3532
     *
3533
     * @return mixed Number of users or false on error
3534
     */
3535
    public static function get_number_of_users($status = 0, $access_url_id = 1, $active = null)
3536
    {
3537
        $t_u = Database::get_main_table(TABLE_MAIN_USER);
3538
        $t_a = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3539
3540
        if (api_is_multiple_url_enabled()) {
3541
            $sql = "SELECT count(u.id)
3542
                    FROM $t_u u
3543
                    INNER JOIN $t_a url_user
3544
                    ON (u.id = url_user.user_id)
3545
                    WHERE url_user.access_url_id = $access_url_id
3546
            ";
3547
        } else {
3548
            $sql = "SELECT count(u.id)
3549
                    FROM $t_u u
3550
                    WHERE 1 = 1 ";
3551
        }
3552
3553
        if (is_int($status) && $status > 0) {
3554
            $status = (int) $status;
3555
            $sql .= " AND u.status = $status ";
3556
        }
3557
3558
        if (null !== $active) {
3559
            $active = (int) $active;
3560
            $sql .= " AND u.active = $active ";
3561
        }
3562
3563
        $res = Database::query($sql);
3564
        if (1 === Database::num_rows($res)) {
3565
            return (int) Database::result($res, 0, 0);
3566
        }
3567
3568
        return false;
3569
    }
3570
3571
    /**
3572
     * Gets the tags of a specific field_id
3573
     * USER TAGS.
3574
     *
3575
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
3576
     *
3577
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
3578
     *    Called it "books" for example.
3579
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
3580
     * 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
3581
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
3582
     * 5. Test and enjoy.
3583
     *
3584
     * @param string $tag
3585
     * @param int    $field_id      field_id
3586
     * @param string $return_format how we are going to result value in array or in a string (json)
3587
     * @param $limit
3588
     *
3589
     * @return mixed
3590
     */
3591
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
3592
    {
3593
        // database table definition
3594
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3595
        $field_id = (int) $field_id;
3596
        $limit = (int) $limit;
3597
        $tag = trim(Database::escape_string($tag));
3598
3599
        // all the information of the field
3600
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
3601
                WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag LIMIT $limit";
3602
        $result = Database::query($sql);
3603
        $return = [];
3604
        if (Database::num_rows($result) > 0) {
3605
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3606
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
3607
            }
3608
        }
3609
        if ('json' === $return_format) {
3610
            $return = json_encode($return);
3611
        }
3612
3613
        return $return;
3614
    }
3615
3616
    /**
3617
     * @param int $field_id
3618
     * @param int $limit
3619
     *
3620
     * @return array
3621
     */
3622
    public static function get_top_tags($field_id, $limit = 100)
3623
    {
3624
        // database table definition
3625
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3626
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3627
        $field_id = (int) $field_id;
3628
        $limit = (int) $limit;
3629
        // all the information of the field
3630
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
3631
                INNER JOIN $table_user_tag ut
3632
                ON (ut.id = uv.tag_id)
3633
                WHERE field_id = $field_id
3634
                GROUP BY tag_id
3635
                ORDER BY count DESC
3636
                LIMIT $limit";
3637
        $result = Database::query($sql);
3638
        $return = [];
3639
        if (Database::num_rows($result) > 0) {
3640
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3641
                $return[] = $row;
3642
            }
3643
        }
3644
3645
        return $return;
3646
    }
3647
3648
    /**
3649
     * Get user's tags.
3650
     *
3651
     * @param int $user_id
3652
     * @param int $field_id
3653
     *
3654
     * @return array
3655
     */
3656
    public static function get_user_tags($user_id, $field_id)
3657
    {
3658
        // database table definition
3659
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3660
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3661
        $field_id = (int) $field_id;
3662
        $user_id = (int) $user_id;
3663
3664
        // all the information of the field
3665
        $sql = "SELECT ut.id, tag, count
3666
                FROM $table_user_tag ut
3667
                INNER JOIN $table_user_tag_values uv
3668
                ON (uv.tag_id=ut.ID)
3669
                WHERE field_id = $field_id AND user_id = $user_id
3670
                ORDER BY tag";
3671
        $result = Database::query($sql);
3672
        $return = [];
3673
        if (Database::num_rows($result) > 0) {
3674
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3675
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3676
            }
3677
        }
3678
3679
        return $return;
3680
    }
3681
3682
    /**
3683
     * Get user's tags.
3684
     *
3685
     * @param int  $user_id
3686
     * @param int  $field_id
3687
     * @param bool $show_links show links or not
3688
     *
3689
     * @return string
3690
     */
3691
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
3692
    {
3693
        // database table definition
3694
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3695
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3696
        $field_id = (int) $field_id;
3697
        $user_id = (int) $user_id;
3698
3699
        // all the information of the field
3700
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
3701
                INNER JOIN $table_user_tag_values uv
3702
                ON (uv.tag_id = ut.id)
3703
                WHERE field_id = $field_id AND user_id = $user_id
3704
                ORDER BY tag";
3705
3706
        $result = Database::query($sql);
3707
        $return = [];
3708
        if (Database::num_rows($result) > 0) {
3709
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3710
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3711
            }
3712
        }
3713
        $user_tags = $return;
3714
        $tag_tmp = [];
3715
        foreach ($user_tags as $tag) {
3716
            if ($show_links) {
3717
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
3718
                    $tag['tag'].
3719
                '</a>';
3720
            } else {
3721
                $tag_tmp[] = $tag['tag'];
3722
            }
3723
        }
3724
3725
        if (is_array($user_tags) && count($user_tags) > 0) {
3726
            return implode(', ', $tag_tmp);
3727
        } else {
3728
            return '';
3729
        }
3730
    }
3731
3732
    /**
3733
     * Get the tag id.
3734
     *
3735
     * @param int $tag
3736
     * @param int $field_id
3737
     *
3738
     * @return int returns 0 if fails otherwise the tag id
3739
     */
3740
    public static function get_tag_id($tag, $field_id)
3741
    {
3742
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3743
        $tag = Database::escape_string($tag);
3744
        $field_id = (int) $field_id;
3745
        //with COLLATE latin1_bin to select query in a case sensitive mode
3746
        $sql = "SELECT id FROM $table_user_tag
3747
                WHERE tag LIKE '$tag' AND field_id = $field_id";
3748
        $result = Database::query($sql);
3749
        if (Database::num_rows($result) > 0) {
3750
            $row = Database::fetch_array($result, 'ASSOC');
3751
3752
            return $row['id'];
3753
        } else {
3754
            return 0;
3755
        }
3756
    }
3757
3758
    /**
3759
     * Get the tag id.
3760
     *
3761
     * @param int $tag_id
3762
     * @param int $field_id
3763
     *
3764
     * @return int 0 if fails otherwise the tag id
3765
     */
3766
    public static function get_tag_id_from_id($tag_id, $field_id)
3767
    {
3768
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3769
        $tag_id = (int) $tag_id;
3770
        $field_id = (int) $field_id;
3771
        $sql = "SELECT id FROM $table_user_tag
3772
                WHERE id = '$tag_id' AND field_id = $field_id";
3773
        $result = Database::query($sql);
3774
        if (Database::num_rows($result) > 0) {
3775
            $row = Database::fetch_array($result, 'ASSOC');
3776
3777
            return $row['id'];
3778
        } else {
3779
            return false;
3780
        }
3781
    }
3782
3783
    /**
3784
     * Adds a user-tag value.
3785
     *
3786
     * @param mixed $tag
3787
     * @param int   $user_id
3788
     * @param int   $field_id field id of the tag
3789
     *
3790
     * @return bool True if the tag was inserted or updated. False otherwise.
3791
     *              The return value doesn't take into account *values* added to the tag.
3792
     *              Only the creation/update of the tag field itself.
3793
     */
3794
    public static function add_tag($tag, $user_id, $field_id)
3795
    {
3796
        // database table definition
3797
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3798
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3799
        $tag = trim(Database::escape_string($tag));
3800
        $user_id = (int) $user_id;
3801
        $field_id = (int) $field_id;
3802
3803
        $tag_id = self::get_tag_id($tag, $field_id);
3804
3805
        /* IMPORTANT
3806
         *  @todo we don't create tags with numbers
3807
         *
3808
         */
3809
        if (is_numeric($tag)) {
3810
            //the form is sending an id this means that the user select it from the list so it MUST exists
3811
            /* $new_tag_id = self::get_tag_id_from_id($tag,$field_id);
3812
              if ($new_tag_id !== false) {
3813
              $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $new_tag_id";
3814
              $result = Database::query($sql);
3815
              $last_insert_id = $new_tag_id;
3816
              } else {
3817
              $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
3818
              $result = Database::query($sql);
3819
              $last_insert_id = Database::insert_id();
3820
              } */
3821
        }
3822
3823
        //this is a new tag
3824
        if (0 == $tag_id) {
3825
            //the tag doesn't exist
3826
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
3827
            Database::query($sql);
3828
            $last_insert_id = Database::insert_id();
3829
        } else {
3830
            //the tag exists we update it
3831
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
3832
            Database::query($sql);
3833
            $last_insert_id = $tag_id;
3834
        }
3835
3836
        if (!empty($last_insert_id) && (0 != $last_insert_id)) {
3837
            //we insert the relationship user-tag
3838
            $sql = "SELECT tag_id FROM $table_user_tag_values
3839
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
3840
            $result = Database::query($sql);
3841
            //if the relationship does not exist we create it
3842
            if (0 == Database::num_rows($result)) {
3843
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
3844
                Database::query($sql);
3845
            }
3846
3847
            return true;
3848
        }
3849
3850
        return false;
3851
    }
3852
3853
    /**
3854
     * Deletes an user tag.
3855
     *
3856
     * @param int $user_id
3857
     * @param int $field_id
3858
     */
3859
    public static function delete_user_tags($user_id, $field_id)
3860
    {
3861
        // database table definition
3862
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3863
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3864
        $user_id = (int) $user_id;
3865
3866
        $tags = self::get_user_tags($user_id, $field_id);
3867
        if (is_array($tags) && count($tags) > 0) {
3868
            foreach ($tags as $key => $tag) {
3869
                if ($tag['count'] > '0') {
3870
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
3871
                    Database::query($sql);
3872
                }
3873
                $sql = "DELETE FROM $table_user_tag_values
3874
                        WHERE user_id = $user_id AND tag_id = $key";
3875
                Database::query($sql);
3876
            }
3877
        }
3878
    }
3879
3880
    /**
3881
     * Process the tag list comes from the UserManager::update_extra_field_value() function.
3882
     *
3883
     * @param array $tags     the tag list that will be added
3884
     * @param int   $user_id
3885
     * @param int   $field_id
3886
     *
3887
     * @return bool
3888
     */
3889
    public static function process_tags($tags, $user_id, $field_id)
3890
    {
3891
        // We loop the tags and add it to the DB
3892
        if (is_array($tags)) {
3893
            foreach ($tags as $tag) {
3894
                self::add_tag($tag, $user_id, $field_id);
3895
            }
3896
        } else {
3897
            self::add_tag($tags, $user_id, $field_id);
3898
        }
3899
3900
        return true;
3901
    }
3902
3903
    /**
3904
     * Returns a list of all administrators.
3905
     *
3906
     * @return array
3907
     */
3908
    public static function get_all_administrators()
3909
    {
3910
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3911
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
3912
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3913
        $access_url_id = api_get_current_access_url_id();
3914
        if (api_get_multiple_access_url()) {
3915
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3916
                    FROM $tbl_url_rel_user as url
3917
                    INNER JOIN $table_admin as admin
3918
                    ON (admin.user_id=url.user_id)
3919
                    INNER JOIN $table_user u
3920
                    ON (u.id=admin.user_id)
3921
                    WHERE access_url_id ='".$access_url_id."'";
3922
        } else {
3923
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3924
                    FROM $table_admin as admin
3925
                    INNER JOIN $table_user u
3926
                    ON (u.id=admin.user_id)";
3927
        }
3928
        $result = Database::query($sql);
3929
        $return = [];
3930
        if (Database::num_rows($result) > 0) {
3931
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3932
                $return[$row['user_id']] = $row;
3933
            }
3934
        }
3935
3936
        return $return;
3937
    }
3938
3939
    /**
3940
     * Search an user (tags, first name, last name and email ).
3941
     *
3942
     * @param string $tag
3943
     * @param int    $field_id        field id of the tag
3944
     * @param int    $from            where to start in the query
3945
     * @param int    $number_of_items
3946
     * @param bool   $getCount        get count or not
3947
     *
3948
     * @return array
3949
     */
3950
    public static function get_all_user_tags(
3951
        $tag,
3952
        $field_id = 0,
3953
        $from = 0,
3954
        $number_of_items = 10,
3955
        $getCount = false
3956
    ) {
3957
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
3958
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3959
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3960
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3961
3962
        $field_id = intval($field_id);
3963
        $from = intval($from);
3964
        $number_of_items = intval($number_of_items);
3965
3966
        $where_field = "";
3967
        $where_extra_fields = self::get_search_form_where_extra_fields();
3968
        if (0 != $field_id) {
3969
            $where_field = " field_id = $field_id AND ";
3970
        }
3971
3972
        // all the information of the field
3973
        if ($getCount) {
3974
            $select = "SELECT count(DISTINCT u.id) count";
3975
        } else {
3976
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, tag, picture_uri";
3977
        }
3978
3979
        $sql = " $select
3980
                FROM $user_table u
3981
                INNER JOIN $access_url_rel_user_table url_rel_user
3982
                ON (u.id = url_rel_user.user_id)
3983
                LEFT JOIN $table_user_tag_values uv
3984
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
3985
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
3986
                WHERE
3987
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
3988
                    (
3989
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
3990
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
3991
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
3992
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
3993
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
3994
                     )
3995
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
3996
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
3997
3998
        $keyword_active = true;
3999
        // only active users
4000
        if ($keyword_active) {
4001
            $sql .= " AND u.active='1'";
4002
        }
4003
        // avoid anonymous
4004
        $sql .= " AND u.status <> 6 ";
4005
        $sql .= " ORDER BY username";
4006
        $sql .= " LIMIT $from , $number_of_items";
4007
4008
        $result = Database::query($sql);
4009
        $return = [];
4010
4011
        if (Database::num_rows($result) > 0) {
4012
            if ($getCount) {
4013
                $row = Database::fetch_array($result, 'ASSOC');
4014
4015
                return $row['count'];
4016
            }
4017
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4018
                $return[$row['id']] = $row;
4019
            }
4020
        }
4021
4022
        return $return;
4023
    }
4024
4025
    /**
4026
     * Get extra filterable user fields (only type select).
4027
     *
4028
     * @return array Array of extra fields as [int => ['name' => ..., 'variable' => ..., 'data' => ...]] (
4029
     *               or empty array if no extra field)
4030
     */
4031
    public static function getExtraFilterableFields()
4032
    {
4033
        $extraFieldList = self::get_extra_fields();
4034
        $fields = [];
4035
        if (is_array($extraFieldList)) {
4036
            foreach ($extraFieldList as $extraField) {
4037
                // If is enabled to filter and is a "<select>" field type
4038
                if (1 == $extraField[8] && 4 == $extraField[2]) {
4039
                    $fields[] = [
4040
                        'name' => $extraField[3],
4041
                        'variable' => $extraField[1],
4042
                        'data' => $extraField[9],
4043
                    ];
4044
                }
4045
            }
4046
        }
4047
4048
        return $fields;
4049
    }
4050
4051
    /**
4052
     * Get extra where clauses for finding users based on extra filterable user fields (type select).
4053
     *
4054
     * @return string With AND clauses based on user's ID which have the values to search in extra user fields
4055
     *                (or empty if no extra field exists)
4056
     */
4057
    public static function get_search_form_where_extra_fields()
4058
    {
4059
        $useExtraFields = false;
4060
        $extraFields = self::getExtraFilterableFields();
4061
        $extraFieldResult = [];
4062
        if (is_array($extraFields) && count($extraFields) > 0) {
4063
            foreach ($extraFields as $extraField) {
4064
                $varName = 'field_'.$extraField['variable'];
4065
                if (self::is_extra_field_available($extraField['variable'])) {
4066
                    if (isset($_GET[$varName]) && '0' != $_GET[$varName]) {
4067
                        $useExtraFields = true;
4068
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
4069
                            $extraField['variable'],
4070
                            $_GET[$varName]
4071
                        );
4072
                    }
4073
                }
4074
            }
4075
        }
4076
4077
        if ($useExtraFields) {
4078
            $finalResult = [];
4079
            if (count($extraFieldResult) > 1) {
4080
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
4081
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
4082
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
4083
                    }
4084
                }
4085
            } else {
4086
                $finalResult = $extraFieldResult[0];
4087
            }
4088
4089
            if (is_array($finalResult) && count($finalResult) > 0) {
4090
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
4091
            } else {
4092
                //no results
4093
                $whereFilter = " AND u.id  = -1 ";
4094
            }
4095
4096
            return $whereFilter;
4097
        }
4098
4099
        return '';
4100
    }
4101
4102
    /**
4103
     * Show the search form.
4104
     *
4105
     * @param string $query the value of the search box
4106
     *
4107
     * @throws Exception
4108
     *
4109
     * @return string HTML form
4110
     */
4111
    public static function get_search_form($query, $defaultParams = [])
4112
    {
4113
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
4114
        $form = new FormValidator(
4115
            'search_user',
4116
            'get',
4117
            api_get_path(WEB_PATH).'main/social/search.php',
4118
            '',
4119
            [],
4120
            FormValidator::LAYOUT_HORIZONTAL
4121
        );
4122
4123
        $query = Security::remove_XSS($query);
4124
4125
        if (!empty($query)) {
4126
            $form->addHeader(get_lang('Results and feedback').' "'.$query.'"');
4127
        }
4128
4129
        $form->addText(
4130
            'q',
4131
            get_lang('Users, Groups'),
4132
            false,
4133
            [
4134
                'id' => 'q',
4135
            ]
4136
        );
4137
        $options = [
4138
            0 => get_lang('Select'),
4139
            1 => get_lang('User'),
4140
            2 => get_lang('Group'),
4141
        ];
4142
        $form->addSelect(
4143
            'search_type',
4144
            get_lang('Type'),
4145
            $options,
4146
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
4147
        );
4148
4149
        // Extra fields
4150
        $extraFields = self::getExtraFilterableFields();
4151
        $defaults = [];
4152
        if (is_array($extraFields) && count($extraFields) > 0) {
4153
            foreach ($extraFields as $extraField) {
4154
                $varName = 'field_'.$extraField['variable'];
4155
                $options = [
4156
                    0 => get_lang('Select'),
4157
                ];
4158
                foreach ($extraField['data'] as $option) {
4159
                    if (isset($_GET[$varName])) {
4160
                        if ($_GET[$varName] == $option[1]) {
4161
                            $defaults[$option[1]] = true;
4162
                        }
4163
                    }
4164
4165
                    $options[$option[1]] = $option[1];
4166
                }
4167
                $form->addSelect($varName, $extraField['name'], $options);
4168
            }
4169
        }
4170
4171
        $defaults['search_type'] = (int) $searchType;
4172
        $defaults['q'] = $query;
4173
4174
        if (!empty($defaultParams)) {
4175
            $defaults = array_merge($defaults, $defaultParams);
4176
        }
4177
        $form->setDefaults($defaults);
4178
        $form->addButtonSearch(get_lang('Search'));
4179
4180
        $js = '<script>
4181
        extra_field_toogle();
4182
        function extra_field_toogle() {
4183
            if (jQuery("select[name=search_type]").val() != "1") {
4184
                jQuery(".extra_field").hide();
4185
            } else {
4186
                jQuery(".extra_field").show();
4187
            }
4188
        }
4189
        </script>';
4190
4191
        return $js.$form->returnForm();
4192
    }
4193
4194
    /**
4195
     * Shows the user menu.
4196
     */
4197
    public static function show_menu()
4198
    {
4199
        echo '<div class="actions">';
4200
        echo '<a href="/main/auth/profile.php">'.
4201
            Display::return_icon('profile.png').' '.get_lang('Profile').'</a>';
4202
        echo '<a href="/main/messages/inbox.php">'.
4203
            Display::return_icon('inbox.png').' '.get_lang('Inbox').'</a>';
4204
        echo '<a href="/main/messages/outbox.php">'.
4205
            Display::return_icon('outbox.png').' '.get_lang('Outbox').'</a>';
4206
        echo '<span style="float:right; padding-top:7px;">'.
4207
        '<a href="/main/auth/profile.php?show=1">'.
4208
            Display::return_icon('edit.gif').' '.get_lang('Configuration').'</a>';
4209
        echo '</span>';
4210
        echo '</div>';
4211
    }
4212
4213
    /**
4214
     * Allow to register contact to social network.
4215
     *
4216
     * @param int $friend_id     user friend id
4217
     * @param int $my_user_id    user id
4218
     * @param int $relation_type relation between users see constants definition
4219
     *
4220
     * @return bool
4221
     */
4222
    public static function relate_users($friend_id, $my_user_id, $relation_type)
4223
    {
4224
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4225
4226
        $friend_id = (int) $friend_id;
4227
        $my_user_id = (int) $my_user_id;
4228
        $relation_type = (int) $relation_type;
4229
4230
        $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
4231
                WHERE
4232
                    friend_user_id='.$friend_id.' AND
4233
                    user_id='.$my_user_id.' AND
4234
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
4235
        $result = Database::query($sql);
4236
        $row = Database::fetch_array($result, 'ASSOC');
4237
        $current_date = api_get_utc_datetime();
4238
4239
        if (0 == $row['count']) {
4240
            $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
4241
                    VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
4242
            Database::query($sql);
4243
4244
            return true;
4245
        }
4246
4247
        $sql = 'SELECT COUNT(*) as count, relation_type  FROM '.$tbl_my_friend.'
4248
                WHERE
4249
                    friend_user_id='.$friend_id.' AND
4250
                    user_id='.$my_user_id.' AND
4251
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
4252
        $result = Database::query($sql);
4253
        $row = Database::fetch_array($result, 'ASSOC');
4254
4255
        if (1 == $row['count']) {
4256
            //only for the case of a RRHH or a Student BOSS
4257
            if ($row['relation_type'] != $relation_type &&
4258
                (USER_RELATION_TYPE_RRHH == $relation_type || USER_RELATION_TYPE_BOSS == $relation_type)
4259
            ) {
4260
                $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
4261
                        VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
4262
            } else {
4263
                $sql = 'UPDATE '.$tbl_my_friend.' SET relation_type='.$relation_type.'
4264
                        WHERE friend_user_id='.$friend_id.' AND user_id='.$my_user_id;
4265
            }
4266
            Database::query($sql);
4267
4268
            return true;
4269
        }
4270
4271
        return false;
4272
    }
4273
4274
    /**
4275
     * Deletes a contact.
4276
     *
4277
     * @param bool   $friend_id
4278
     * @param bool   $real_removed          true will delete ALL friends relationship
4279
     * @param string $with_status_condition
4280
     *
4281
     * @author isaac flores paz <[email protected]>
4282
     * @author Julio Montoya <[email protected]> Cleaning code
4283
     */
4284
    public static function remove_user_rel_user(
4285
        $friend_id,
4286
        $real_removed = false,
4287
        $with_status_condition = ''
4288
    ) {
4289
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4290
        $tbl_my_message = Database::get_main_table(TABLE_MESSAGE);
4291
        $friend_id = (int) $friend_id;
4292
        $user_id = api_get_user_id();
4293
4294
        if ($real_removed) {
4295
            $extra_condition = '';
4296
            if ('' != $with_status_condition) {
4297
                $extra_condition = ' AND relation_type = '.intval($with_status_condition);
4298
            }
4299
            $sql = 'DELETE FROM '.$tbl_my_friend.'
4300
                    WHERE
4301
                        relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
4302
                        friend_user_id='.$friend_id.' '.$extra_condition;
4303
            Database::query($sql);
4304
            $sql = 'DELETE FROM '.$tbl_my_friend.'
4305
                   WHERE
4306
                    relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
4307
                    user_id='.$friend_id.' '.$extra_condition;
4308
            Database::query($sql);
4309
        } else {
4310
            $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
4311
                    WHERE
4312
                        user_id='.$user_id.' AND
4313
                        relation_type NOT IN('.USER_RELATION_TYPE_DELETED.', '.USER_RELATION_TYPE_RRHH.') AND
4314
                        friend_user_id='.$friend_id;
4315
            $result = Database::query($sql);
4316
            $row = Database::fetch_array($result, 'ASSOC');
4317
            if (1 == $row['count']) {
4318
                //Delete user rel user
4319
                $sql_i = 'UPDATE '.$tbl_my_friend.' SET relation_type='.USER_RELATION_TYPE_DELETED.'
4320
                          WHERE user_id='.$user_id.' AND friend_user_id='.$friend_id;
4321
4322
                $sql_j = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
4323
                          WHERE
4324
                                user_receiver_id='.$user_id.' AND
4325
                                user_sender_id='.$friend_id.' AND update_date="0000-00-00 00:00:00" ';
4326
                // Delete user
4327
                $sql_ij = 'UPDATE '.$tbl_my_friend.'  SET relation_type='.USER_RELATION_TYPE_DELETED.'
4328
                           WHERE user_id='.$friend_id.' AND friend_user_id='.$user_id;
4329
                $sql_ji = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
4330
                           WHERE
4331
                                user_receiver_id='.$friend_id.' AND
4332
                                user_sender_id='.$user_id.' AND
4333
                                update_date="0000-00-00 00:00:00" ';
4334
                Database::query($sql_i);
4335
                Database::query($sql_j);
4336
                Database::query($sql_ij);
4337
                Database::query($sql_ji);
4338
            }
4339
        }
4340
4341
        // Delete accepted invitations
4342
        $sql = "DELETE FROM $tbl_my_message
4343
                WHERE
4344
                    msg_status = ".MESSAGE_STATUS_INVITATION_ACCEPTED." AND
4345
                    (
4346
                        user_receiver_id = $user_id AND
4347
                        user_sender_id = $friend_id
4348
                    ) OR
4349
                    (
4350
                        user_sender_id = $user_id AND
4351
                        user_receiver_id = $friend_id
4352
                    )
4353
        ";
4354
        Database::query($sql);
4355
    }
4356
4357
    /**
4358
     * @param int $userId
4359
     *
4360
     * @return array
4361
     */
4362
    public static function getDrhListFromUser($userId)
4363
    {
4364
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4365
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4366
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4367
        $userId = (int) $userId;
4368
4369
        $orderBy = null;
4370
        if (api_is_western_name_order()) {
4371
            $orderBy .= ' ORDER BY firstname, lastname ';
4372
        } else {
4373
            $orderBy .= ' ORDER BY lastname, firstname ';
4374
        }
4375
4376
        $sql = "SELECT u.id, username, u.firstname, u.lastname
4377
                FROM $tblUser u
4378
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
4379
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
4380
                WHERE
4381
                    access_url_id = ".api_get_current_access_url_id()." AND
4382
                    uru.user_id = '$userId' AND
4383
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4384
                $orderBy
4385
                ";
4386
        $result = Database::query($sql);
4387
4388
        return Database::store_result($result);
4389
    }
4390
4391
    /**
4392
     * get users followed by human resource manager.
4393
     *
4394
     * @param int    $userId
4395
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
4396
     * @param bool   $getOnlyUserId
4397
     * @param bool   $getSql
4398
     * @param bool   $getCount
4399
     * @param int    $from
4400
     * @param int    $numberItems
4401
     * @param int    $column
4402
     * @param string $direction
4403
     * @param int    $active
4404
     * @param string $lastConnectionDate
4405
     *
4406
     * @return array users
4407
     */
4408
    public static function get_users_followed_by_drh(
4409
        $userId,
4410
        $userStatus = 0,
4411
        $getOnlyUserId = false,
4412
        $getSql = false,
4413
        $getCount = false,
4414
        $from = null,
4415
        $numberItems = null,
4416
        $column = null,
4417
        $direction = null,
4418
        $active = null,
4419
        $lastConnectionDate = null
4420
    ) {
4421
        return self::getUsersFollowedByUser(
4422
            $userId,
4423
            $userStatus,
4424
            $getOnlyUserId,
4425
            $getSql,
4426
            $getCount,
4427
            $from,
4428
            $numberItems,
4429
            $column,
4430
            $direction,
4431
            $active,
4432
            $lastConnectionDate,
4433
            DRH
4434
        );
4435
    }
4436
4437
    /**
4438
     * Get users followed by human resource manager.
4439
     *
4440
     * @param int    $userId
4441
     * @param int    $userStatus         Filter users by status (STUDENT, COURSEMANAGER, etc)
4442
     * @param bool   $getOnlyUserId
4443
     * @param bool   $getSql
4444
     * @param bool   $getCount
4445
     * @param int    $from
4446
     * @param int    $numberItems
4447
     * @param int    $column
4448
     * @param string $direction
4449
     * @param int    $active
4450
     * @param string $lastConnectionDate
4451
     * @param int    $status             the function is called by who? COURSEMANAGER, DRH?
4452
     * @param string $keyword
4453
     *
4454
     * @return mixed Users list (array) or the SQL query if $getSQL was set to true
4455
     */
4456
    public static function getUsersFollowedByUser(
4457
        $userId,
4458
        $userStatus = null,
4459
        $getOnlyUserId = false,
4460
        $getSql = false,
4461
        $getCount = false,
4462
        $from = null,
4463
        $numberItems = null,
4464
        $column = null,
4465
        $direction = null,
4466
        $active = null,
4467
        $lastConnectionDate = null,
4468
        $status = null,
4469
        $keyword = null
4470
    ) {
4471
        // Database Table Definitions
4472
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4473
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4474
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4475
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4476
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4477
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4478
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4479
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4480
4481
        $userId = (int) $userId;
4482
        $limitCondition = '';
4483
4484
        if (isset($from) && isset($numberItems)) {
4485
            $from = (int) $from;
4486
            $numberItems = (int) $numberItems;
4487
            $limitCondition = "LIMIT $from, $numberItems";
4488
        }
4489
4490
        $column = Database::escape_string($column);
4491
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
4492
4493
        $userConditions = '';
4494
        if (!empty($userStatus)) {
4495
            $userConditions .= ' AND u.status = '.intval($userStatus);
4496
        }
4497
4498
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
4499
        if ($getOnlyUserId) {
4500
            $select = " SELECT DISTINCT u.id user_id";
4501
        }
4502
4503
        $masterSelect = "SELECT DISTINCT * FROM ";
4504
4505
        if ($getCount) {
4506
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
4507
            $select = " SELECT DISTINCT(u.id) user_id";
4508
        }
4509
4510
        if (!is_null($active)) {
4511
            $active = intval($active);
4512
            $userConditions .= " AND u.active = $active ";
4513
        }
4514
4515
        if (!empty($keyword)) {
4516
            $keyword = Database::escape_string($keyword);
4517
            $userConditions .= " AND (
4518
                u.username LIKE '%$keyword%' OR
4519
                u.firstname LIKE '%$keyword%' OR
4520
                u.lastname LIKE '%$keyword%' OR
4521
                u.official_code LIKE '%$keyword%' OR
4522
                u.email LIKE '%$keyword%'
4523
            )";
4524
        }
4525
4526
        if (!empty($lastConnectionDate)) {
4527
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
4528
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
4529
        }
4530
4531
        $sessionConditionsCoach = null;
4532
        $sessionConditionsTeacher = null;
4533
        $drhConditions = null;
4534
        $teacherSelect = null;
4535
4536
        switch ($status) {
4537
            case DRH:
4538
                $drhConditions .= " AND
4539
                    friend_user_id = '$userId' AND
4540
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4541
                ";
4542
                break;
4543
            case COURSEMANAGER:
4544
                $drhConditions .= " AND
4545
                    friend_user_id = '$userId' AND
4546
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4547
                ";
4548
4549
                $sessionConditionsCoach .= " AND
4550
                    (s.id_coach = '$userId')
4551
                ";
4552
4553
                $sessionConditionsTeacher .= " AND
4554
                    (scu.status = 2 AND scu.user_id = '$userId')
4555
                ";
4556
4557
                $teacherSelect =
4558
                "UNION ALL (
4559
                        $select
4560
                        FROM $tbl_user u
4561
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
4562
                        WHERE
4563
                            (
4564
                                sru.session_id IN (
4565
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
4566
                                    $tbl_session_rel_access_url session_rel_access_rel_user
4567
                                    ON session_rel_access_rel_user.session_id = s.id
4568
                                    WHERE access_url_id = ".api_get_current_access_url_id()."
4569
                                    $sessionConditionsCoach
4570
                                ) OR sru.session_id IN (
4571
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
4572
                                    INNER JOIN $tbl_session_rel_access_url url
4573
                                    ON (url.session_id = s.id)
4574
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
4575
                                    ON (scu.session_id = s.id)
4576
                                    WHERE access_url_id = ".api_get_current_access_url_id()."
4577
                                    $sessionConditionsTeacher
4578
                                )
4579
                            )
4580
                            $userConditions
4581
                    )
4582
                    UNION ALL(
4583
                        $select
4584
                        FROM $tbl_user u
4585
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
4586
                        WHERE cu.c_id IN (
4587
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
4588
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
4589
                        )
4590
                        $userConditions
4591
                    )"
4592
                ;
4593
                break;
4594
            case STUDENT_BOSS:
4595
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
4596
                break;
4597
            case HRM_REQUEST:
4598
                $drhConditions .= " AND
4599
                    friend_user_id = '$userId' AND
4600
                    relation_type = '".USER_RELATION_TYPE_HRM_REQUEST."'
4601
                ";
4602
                break;
4603
        }
4604
4605
        $join = null;
4606
        $sql = " $masterSelect
4607
                (
4608
                    (
4609
                        $select
4610
                        FROM $tbl_user u
4611
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
4612
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
4613
                        $join
4614
                        WHERE
4615
                            access_url_id = ".api_get_current_access_url_id()."
4616
                            $drhConditions
4617
                            $userConditions
4618
                    )
4619
                    $teacherSelect
4620
4621
                ) as t1";
4622
4623
        if ($getSql) {
4624
            return $sql;
4625
        }
4626
        if ($getCount) {
4627
            $result = Database::query($sql);
4628
            $row = Database::fetch_array($result);
4629
4630
            return $row['count'];
4631
        }
4632
4633
        $orderBy = null;
4634
        if (false == $getOnlyUserId) {
4635
            if (api_is_western_name_order()) {
4636
                $orderBy .= " ORDER BY firstname, lastname ";
4637
            } else {
4638
                $orderBy .= " ORDER BY lastname, firstname ";
4639
            }
4640
4641
            if (!empty($column) && !empty($direction)) {
4642
                // Fixing order due the UNIONs
4643
                $column = str_replace('u.', '', $column);
4644
                $orderBy = " ORDER BY `$column` $direction ";
4645
            }
4646
        }
4647
4648
        $sql .= $orderBy;
4649
        $sql .= $limitCondition;
4650
4651
        $result = Database::query($sql);
4652
        $users = [];
4653
        if (Database::num_rows($result) > 0) {
4654
            while ($row = Database::fetch_array($result)) {
4655
                $users[$row['user_id']] = $row;
4656
            }
4657
        }
4658
4659
        return $users;
4660
    }
4661
4662
    /**
4663
     * Subscribes users to human resource manager (Dashboard feature).
4664
     *
4665
     * @param int   $hr_dept_id
4666
     * @param array $users_id
4667
     * @param bool  $deleteOtherAssignedUsers
4668
     *
4669
     * @return int
4670
     */
4671
    public static function subscribeUsersToHRManager(
4672
        $hr_dept_id,
4673
        $users_id,
4674
        $deleteOtherAssignedUsers = true
4675
    ) {
4676
        return self::subscribeUsersToUser(
4677
            $hr_dept_id,
4678
            $users_id,
4679
            USER_RELATION_TYPE_RRHH,
4680
            false,
4681
            $deleteOtherAssignedUsers
4682
        );
4683
    }
4684
4685
    /**
4686
     * Register request to assign users to HRM.
4687
     *
4688
     * @param int   $hrmId   The HRM ID
4689
     * @param array $usersId The users IDs
4690
     *
4691
     * @return int
4692
     */
4693
    public static function requestUsersToHRManager($hrmId, $usersId)
4694
    {
4695
        return self::subscribeUsersToUser(
4696
            $hrmId,
4697
            $usersId,
4698
            USER_RELATION_TYPE_HRM_REQUEST,
4699
            false,
4700
            false
4701
        );
4702
    }
4703
4704
    /**
4705
     * Remove the requests for assign a user to a HRM.
4706
     *
4707
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
4708
     */
4709
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
4710
    {
4711
        $users = implode(', ', $usersId);
4712
        Database::getManager()
4713
            ->createQuery('
4714
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
4715
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
4716
            ')
4717
            ->execute(['hrm_id' => $hrmId, 'relation_type' => USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
4718
    }
4719
4720
    /**
4721
     * Add subscribed users to a user by relation type.
4722
     *
4723
     * @param int    $userId                   The user id
4724
     * @param array  $subscribedUsersId        The id of subscribed users
4725
     * @param string $relationType             The relation type
4726
     * @param bool   $deleteUsersBeforeInsert
4727
     * @param bool   $deleteOtherAssignedUsers
4728
     *
4729
     * @return int
4730
     */
4731
    public static function subscribeUsersToUser(
4732
        $userId,
4733
        $subscribedUsersId,
4734
        $relationType,
4735
        $deleteUsersBeforeInsert = false,
4736
        $deleteOtherAssignedUsers = true
4737
    ) {
4738
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4739
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4740
4741
        $userId = (int) $userId;
4742
        $relationType = (int) $relationType;
4743
        $affectedRows = 0;
4744
4745
        if ($deleteOtherAssignedUsers) {
4746
            if (api_get_multiple_access_url()) {
4747
                // Deleting assigned users to hrm_id
4748
                $sql = "SELECT s.user_id
4749
                        FROM $userRelUserTable s
4750
                        INNER JOIN $userRelAccessUrlTable a
4751
                        ON (a.user_id = s.user_id)
4752
                        WHERE
4753
                            friend_user_id = $userId AND
4754
                            relation_type = $relationType AND
4755
                            access_url_id = ".api_get_current_access_url_id();
4756
            } else {
4757
                $sql = "SELECT user_id
4758
                        FROM $userRelUserTable
4759
                        WHERE
4760
                            friend_user_id = $userId AND
4761
                            relation_type = $relationType";
4762
            }
4763
            $result = Database::query($sql);
4764
4765
            if (Database::num_rows($result) > 0) {
4766
                while ($row = Database::fetch_array($result)) {
4767
                    $sql = "DELETE FROM $userRelUserTable
4768
                            WHERE
4769
                                user_id = {$row['user_id']} AND
4770
                                friend_user_id = $userId AND
4771
                                relation_type = $relationType";
4772
                    Database::query($sql);
4773
                }
4774
            }
4775
        }
4776
4777
        if ($deleteUsersBeforeInsert) {
4778
            $sql = "DELETE FROM $userRelUserTable
4779
                    WHERE
4780
                        user_id = $userId AND
4781
                        relation_type = $relationType";
4782
            Database::query($sql);
4783
        }
4784
4785
        // Inserting new user list
4786
        if (is_array($subscribedUsersId)) {
4787
            foreach ($subscribedUsersId as $subscribedUserId) {
4788
                $subscribedUserId = (int) $subscribedUserId;
4789
                $sql = "SELECT id
4790
                        FROM $userRelUserTable
4791
                        WHERE
4792
                            user_id = $subscribedUserId AND
4793
                            friend_user_id = $userId AND
4794
                            relation_type = $relationType";
4795
4796
                $result = Database::query($sql);
4797
                $num = Database::num_rows($result);
4798
                if (0 === $num) {
4799
                    $date = api_get_utc_datetime();
4800
                    $sql = "INSERT INTO $userRelUserTable (user_id, friend_user_id, relation_type, last_edit)
4801
                            VALUES ($subscribedUserId, $userId, $relationType, '$date')";
4802
                    $result = Database::query($sql);
4803
                    $affectedRows += Database::affected_rows($result);
4804
                }
4805
            }
4806
        }
4807
4808
        return $affectedRows;
4809
    }
4810
4811
    /**
4812
     * This function check if an user is followed by human resources manager.
4813
     *
4814
     * @param int $user_id
4815
     * @param int $hr_dept_id Human resources manager
4816
     *
4817
     * @return bool
4818
     */
4819
    public static function is_user_followed_by_drh($user_id, $hr_dept_id)
4820
    {
4821
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4822
        $user_id = (int) $user_id;
4823
        $hr_dept_id = (int) $hr_dept_id;
4824
        $result = false;
4825
4826
        $sql = "SELECT user_id FROM $tbl_user_rel_user
4827
                WHERE
4828
                    user_id = $user_id AND
4829
                    friend_user_id = $hr_dept_id AND
4830
                    relation_type = ".USER_RELATION_TYPE_RRHH;
4831
        $rs = Database::query($sql);
4832
        if (Database::num_rows($rs) > 0) {
4833
            $result = true;
4834
        }
4835
4836
        return $result;
4837
    }
4838
4839
    /**
4840
     * Return the user id of teacher or session administrator.
4841
     *
4842
     * @param array $courseInfo
4843
     *
4844
     * @return mixed The user id, or false if the session ID was negative
4845
     */
4846
    public static function get_user_id_of_course_admin_or_session_admin($courseInfo)
4847
    {
4848
        $session = api_get_session_id();
4849
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4850
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4851
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4852
4853
        if (empty($courseInfo)) {
4854
            return false;
4855
        }
4856
4857
        $courseId = $courseInfo['real_id'];
4858
4859
        if (0 == $session || is_null($session)) {
4860
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
4861
                    INNER JOIN '.$table_course_user.' ru
4862
                    ON ru.user_id = u.id
4863
                    WHERE
4864
                        ru.status = 1 AND
4865
                        ru.c_id = "'.$courseId.'" ';
4866
            $rs = Database::query($sql);
4867
            $num_rows = Database::num_rows($rs);
4868
            if (1 == $num_rows) {
4869
                $row = Database::fetch_array($rs);
4870
4871
                return $row['uid'];
4872
            } else {
4873
                $my_num_rows = $num_rows;
4874
                $my_user_id = Database::result($rs, $my_num_rows - 1, 'uid');
4875
4876
                return $my_user_id;
4877
            }
4878
        } elseif ($session > 0) {
4879
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
4880
                    INNER JOIN '.$table_session_course_user.' sru
4881
                    ON sru.user_id=u.id
4882
                    WHERE
4883
                        sru.c_id="'.$courseId.'" AND
4884
                        sru.status=2';
4885
            $rs = Database::query($sql);
4886
            $row = Database::fetch_array($rs);
4887
4888
            return $row['uid'];
4889
        }
4890
4891
        return false;
4892
    }
4893
4894
    /**
4895
     * Determines if a user is a gradebook certified.
4896
     *
4897
     * @param int $cat_id  The category id of gradebook
4898
     * @param int $user_id The user id
4899
     *
4900
     * @return bool
4901
     */
4902
    public static function is_user_certified($cat_id, $user_id)
4903
    {
4904
        $cat_id = (int) $cat_id;
4905
        $user_id = (int) $user_id;
4906
4907
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4908
        $sql = 'SELECT path_certificate
4909
                FROM '.$table.'
4910
                WHERE
4911
                    cat_id = "'.$cat_id.'" AND
4912
                    user_id = "'.$user_id.'"';
4913
        $rs = Database::query($sql);
4914
        $row = Database::fetch_array($rs);
4915
4916
        if ('' == $row['path_certificate'] || is_null($row['path_certificate'])) {
4917
            return false;
4918
        }
4919
4920
        return true;
4921
    }
4922
4923
    /**
4924
     * Gets the info about a gradebook certificate for a user by course.
4925
     *
4926
     * @param array $course_info The course code
4927
     * @param int   $session_id
4928
     * @param int   $user_id     The user id
4929
     *
4930
     * @return array if there is not information return false
4931
     */
4932
    public static function get_info_gradebook_certificate($course_info, $session_id, $user_id)
4933
    {
4934
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4935
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
4936
        $session_id = (int) $session_id;
4937
        $user_id = (int) $user_id;
4938
        $courseId = $course_info['real_id'];
4939
4940
        if (empty($session_id)) {
4941
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
4942
        } else {
4943
            $session_condition = " AND session_id = $session_id";
4944
        }
4945
4946
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.'
4947
                WHERE cat_id = (
4948
                    SELECT id FROM '.$tbl_grade_category.'
4949
                    WHERE
4950
                        c_id = "'.$courseId.'" '.$session_condition.'
4951
                    LIMIT 1
4952
                ) AND user_id='.$user_id;
4953
4954
        $rs = Database::query($sql);
4955
        if (Database::num_rows($rs) > 0) {
4956
            $row = Database::fetch_array($rs, 'ASSOC');
4957
            $score = $row['score_certificate'];
4958
            $category_id = $row['cat_id'];
4959
            $cat = Category::load($category_id);
4960
            $displayscore = ScoreDisplay::instance();
4961
            if (isset($cat) && $displayscore->is_custom()) {
4962
                $grade = $displayscore->display_score(
4963
                    [$score, $cat[0]->get_weight()],
4964
                    SCORE_DIV_PERCENT_WITH_CUSTOM
4965
                );
4966
            } else {
4967
                $grade = $displayscore->display_score(
4968
                    [$score, $cat[0]->get_weight()]
4969
                );
4970
            }
4971
            $row['grade'] = $grade;
4972
4973
            return $row;
4974
        }
4975
4976
        return false;
4977
    }
4978
4979
    /**
4980
     * This function check if the user is a coach inside session course.
4981
     *
4982
     * @param int $user_id    User id
4983
     * @param int $courseId
4984
     * @param int $session_id
4985
     *
4986
     * @return bool True if the user is a coach
4987
     */
4988
    public static function is_session_course_coach($user_id, $courseId, $session_id)
4989
    {
4990
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4991
        // Protect data
4992
        $user_id = intval($user_id);
4993
        $courseId = intval($courseId);
4994
        $session_id = intval($session_id);
4995
        $result = false;
4996
4997
        $sql = "SELECT session_id FROM $table
4998
                WHERE
4999
                  session_id = $session_id AND
5000
                  c_id = $courseId AND
5001
                  user_id = $user_id AND
5002
                  status = 2 ";
5003
        $res = Database::query($sql);
5004
5005
        if (Database::num_rows($res) > 0) {
5006
            $result = true;
5007
        }
5008
5009
        return $result;
5010
    }
5011
5012
    /**
5013
     * This function returns an icon path that represents the favicon of the website of which the url given.
5014
     * Defaults to the current Chamilo favicon.
5015
     *
5016
     * @param string $url1 URL of website where to look for favicon.ico
5017
     * @param string $url2 Optional second URL of website where to look for favicon.ico
5018
     *
5019
     * @return string Path of icon to load
5020
     */
5021
    public static function get_favicon_from_url($url1, $url2 = null)
5022
    {
5023
        $icon_link = '';
5024
        $url = $url1;
5025
        if (empty($url1)) {
5026
            $url = $url2;
5027
            if (empty($url)) {
5028
                $url = api_get_access_url(api_get_current_access_url_id());
5029
                $url = $url[0];
5030
            }
5031
        }
5032
        if (!empty($url)) {
5033
            $pieces = parse_url($url);
5034
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
5035
        }
5036
5037
        return $icon_link;
5038
    }
5039
5040
    public static function addUserAsAdmin(User $user)
5041
    {
5042
        if ($user) {
5043
            $userId = $user->getId();
5044
5045
            if (!self::is_admin($userId)) {
5046
                $table = Database::get_main_table(TABLE_MAIN_ADMIN);
5047
                $sql = "INSERT INTO $table SET user_id = $userId";
5048
                Database::query($sql);
5049
            }
5050
5051
            $user->addRole('ROLE_SUPER_ADMIN');
5052
            self::getRepository()->updateUser($user, true);
5053
        }
5054
    }
5055
5056
    public static function removeUserAdmin(User $user)
5057
    {
5058
        $userId = (int) $user->getId();
5059
        if (self::is_admin($userId)) {
5060
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
5061
            $sql = "DELETE FROM $table WHERE user_id = $userId";
5062
            Database::query($sql);
5063
            $user->removeRole('ROLE_SUPER_ADMIN');
5064
            self::getRepository()->updateUser($user, true);
5065
        }
5066
    }
5067
5068
    /**
5069
     * @param string $from
5070
     * @param string $to
5071
     */
5072
    public static function update_all_user_languages($from, $to)
5073
    {
5074
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5075
        $from = Database::escape_string($from);
5076
        $to = Database::escape_string($to);
5077
5078
        if (!empty($to) && !empty($from)) {
5079
            $sql = "UPDATE $table_user SET language = '$to'
5080
                    WHERE language = '$from'";
5081
            Database::query($sql);
5082
        }
5083
    }
5084
5085
    /**
5086
     * Subscribe boss to students.
5087
     *
5088
     * @param int   $bossId  The boss id
5089
     * @param array $usersId The users array
5090
     *
5091
     * @return int Affected rows
5092
     */
5093
    public static function subscribeBossToUsers($bossId, $usersId)
5094
    {
5095
        return self::subscribeUsersToUser(
5096
            $bossId,
5097
            $usersId,
5098
            USER_RELATION_TYPE_BOSS
5099
        );
5100
    }
5101
5102
    /**
5103
     * @param int $userId
5104
     *
5105
     * @return bool
5106
     */
5107
    public static function removeAllBossFromStudent($userId)
5108
    {
5109
        $userId = (int) $userId;
5110
5111
        if (empty($userId)) {
5112
            return false;
5113
        }
5114
5115
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5116
        $sql = "DELETE FROM $userRelUserTable
5117
                WHERE user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
5118
        Database::query($sql);
5119
5120
        return true;
5121
    }
5122
5123
    /**
5124
     * Subscribe boss to students, if $bossList is empty then the boss list will be empty too.
5125
     *
5126
     * @param int   $studentId
5127
     * @param array $bossList
5128
     * @param bool  $sendNotification
5129
     *
5130
     * @return mixed Affected rows or false on failure
5131
     */
5132
    public static function subscribeUserToBossList(
5133
        $studentId,
5134
        $bossList,
5135
        $sendNotification = false
5136
    ) {
5137
        $inserted = 0;
5138
        if (!empty($bossList)) {
5139
            sort($bossList);
5140
            $studentId = (int) $studentId;
5141
            $studentInfo = api_get_user_info($studentId);
5142
5143
            if (empty($studentInfo)) {
5144
                return false;
5145
            }
5146
5147
            $previousBossList = self::getStudentBossList($studentId);
5148
            $previousBossList = !empty($previousBossList) ? array_column($previousBossList, 'boss_id') : [];
5149
            sort($previousBossList);
5150
5151
            // Boss list is the same, nothing changed.
5152
            if ($bossList == $previousBossList) {
5153
                return false;
5154
            }
5155
5156
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5157
            self::removeAllBossFromStudent($studentId);
5158
5159
            foreach ($bossList as $bossId) {
5160
                $bossId = (int) $bossId;
5161
                $bossInfo = api_get_user_info($bossId);
5162
5163
                if (empty($bossInfo)) {
5164
                    continue;
5165
                }
5166
5167
                $bossLanguage = $bossInfo['language'];
5168
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
5169
                        VALUES ($studentId, $bossId, ".USER_RELATION_TYPE_BOSS.")";
5170
                $insertId = Database::query($sql);
5171
5172
                if ($insertId) {
5173
                    if ($sendNotification) {
5174
                        $name = $studentInfo['complete_name'];
5175
                        $url = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?student='.$studentId;
5176
                        $url = Display::url($url, $url);
5177
                        $subject = sprintf(get_lang('You have been assigned the learner %s'), $name);
5178
                        $message = sprintf(get_lang('You have been assigned the learner %sWithUrlX'), $name, $url);
5179
                        MessageManager::send_message_simple(
5180
                            $bossId,
5181
                            $subject,
5182
                            $message
5183
                        );
5184
                    }
5185
                    $inserted++;
5186
                }
5187
            }
5188
        } else {
5189
            self::removeAllBossFromStudent($studentId);
5190
        }
5191
5192
        return $inserted;
5193
    }
5194
5195
    /**
5196
     * Get users followed by student boss.
5197
     *
5198
     * @param int    $userId
5199
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
5200
     * @param bool   $getOnlyUserId
5201
     * @param bool   $getSql
5202
     * @param bool   $getCount
5203
     * @param int    $from
5204
     * @param int    $numberItems
5205
     * @param int    $column
5206
     * @param string $direction
5207
     * @param int    $active
5208
     * @param string $lastConnectionDate
5209
     *
5210
     * @return array users
5211
     */
5212
    public static function getUsersFollowedByStudentBoss(
5213
        $userId,
5214
        $userStatus = 0,
5215
        $getOnlyUserId = false,
5216
        $getSql = false,
5217
        $getCount = false,
5218
        $from = null,
5219
        $numberItems = null,
5220
        $column = null,
5221
        $direction = null,
5222
        $active = null,
5223
        $lastConnectionDate = null
5224
    ) {
5225
        return self::getUsersFollowedByUser(
5226
            $userId,
5227
            $userStatus,
5228
            $getOnlyUserId,
5229
            $getSql,
5230
            $getCount,
5231
            $from,
5232
            $numberItems,
5233
            $column,
5234
            $direction,
5235
            $active,
5236
            $lastConnectionDate,
5237
            STUDENT_BOSS
5238
        );
5239
    }
5240
5241
    /**
5242
     * @return array
5243
     */
5244
    public static function getOfficialCodeGrouped()
5245
    {
5246
        $user = Database::get_main_table(TABLE_MAIN_USER);
5247
        $sql = "SELECT DISTINCT official_code
5248
                FROM $user
5249
                GROUP BY official_code";
5250
        $result = Database::query($sql);
5251
        $values = Database::store_result($result, 'ASSOC');
5252
        $result = [];
5253
        foreach ($values as $value) {
5254
            $result[$value['official_code']] = $value['official_code'];
5255
        }
5256
5257
        return $result;
5258
    }
5259
5260
    /**
5261
     * @param string $officialCode
5262
     *
5263
     * @return array
5264
     */
5265
    public static function getUsersByOfficialCode($officialCode)
5266
    {
5267
        $user = Database::get_main_table(TABLE_MAIN_USER);
5268
        $officialCode = Database::escape_string($officialCode);
5269
5270
        $sql = "SELECT DISTINCT id
5271
                FROM $user
5272
                WHERE official_code = '$officialCode'
5273
                ";
5274
        $result = Database::query($sql);
5275
5276
        $users = [];
5277
        while ($row = Database::fetch_array($result)) {
5278
            $users[] = $row['id'];
5279
        }
5280
5281
        return $users;
5282
    }
5283
5284
    /**
5285
     * Calc the expended time (in seconds) by a user in a course.
5286
     *
5287
     * @param int    $userId    The user id
5288
     * @param int    $courseId  The course id
5289
     * @param int    $sessionId Optional. The session id
5290
     * @param string $from      Optional. From date
5291
     * @param string $until     Optional. Until date
5292
     *
5293
     * @return int The time
5294
     */
5295
    public static function getTimeSpentInCourses(
5296
        $userId,
5297
        $courseId,
5298
        $sessionId = 0,
5299
        $from = '',
5300
        $until = ''
5301
    ) {
5302
        $userId = (int) $userId;
5303
        $sessionId = (int) $sessionId;
5304
5305
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5306
        $whereConditions = [
5307
            'user_id = ? ' => $userId,
5308
            'AND c_id = ? ' => $courseId,
5309
            'AND session_id = ? ' => $sessionId,
5310
        ];
5311
5312
        if (!empty($from) && !empty($until)) {
5313
            $whereConditions["AND (login_course_date >= '?' "] = $from;
5314
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
5315
        }
5316
5317
        $trackResult = Database::select(
5318
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
5319
            $trackCourseAccessTable,
5320
            [
5321
                'where' => $whereConditions,
5322
            ],
5323
            'first'
5324
        );
5325
5326
        if (false != $trackResult) {
5327
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
5328
        }
5329
5330
        return 0;
5331
    }
5332
5333
    /**
5334
     * Get the boss user ID from a followed user id.
5335
     *
5336
     * @param $userId
5337
     *
5338
     * @return bool
5339
     */
5340
    public static function getFirstStudentBoss($userId)
5341
    {
5342
        $userId = (int) $userId;
5343
        if ($userId > 0) {
5344
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5345
            $row = Database::select(
5346
                'DISTINCT friend_user_id AS boss_id',
5347
                $userRelTable,
5348
                [
5349
                    'where' => [
5350
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
5351
                            $userId,
5352
                            USER_RELATION_TYPE_BOSS,
5353
                        ],
5354
                    ],
5355
                ]
5356
            );
5357
            if (!empty($row)) {
5358
                return $row[0]['boss_id'];
5359
            }
5360
        }
5361
5362
        return false;
5363
    }
5364
5365
    /**
5366
     * Get the boss user ID from a followed user id.
5367
     *
5368
     * @param int $userId student id
5369
     *
5370
     * @return array
5371
     */
5372
    public static function getStudentBossList($userId)
5373
    {
5374
        $userId = (int) $userId;
5375
5376
        if ($userId > 0) {
5377
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5378
            $result = Database::select(
5379
                'DISTINCT friend_user_id AS boss_id',
5380
                $userRelTable,
5381
                [
5382
                    'where' => [
5383
                        'user_id = ? AND relation_type = ? ' => [
5384
                            $userId,
5385
                            USER_RELATION_TYPE_BOSS,
5386
                        ],
5387
                    ],
5388
                ]
5389
            );
5390
5391
            return $result;
5392
        }
5393
5394
        return [];
5395
    }
5396
5397
    /**
5398
     * @param int $bossId
5399
     * @param int $studentId
5400
     *
5401
     * @return bool
5402
     */
5403
    public static function userIsBossOfStudent($bossId, $studentId)
5404
    {
5405
        $result = false;
5406
        $bossList = self::getStudentBossList($studentId);
5407
        if (!empty($bossList)) {
5408
            $bossList = array_column($bossList, 'boss_id');
5409
            if (in_array($bossId, $bossList)) {
5410
                $result = true;
5411
            }
5412
        }
5413
5414
        return $result;
5415
    }
5416
5417
    /**
5418
     * Displays the name of the user and makes the link to the user profile.
5419
     *
5420
     * @param array $userInfo
5421
     *
5422
     * @return string
5423
     */
5424
    public static function getUserProfileLink($userInfo)
5425
    {
5426
        if (isset($userInfo) && isset($userInfo['user_id'])) {
5427
            return Display::url(
5428
                $userInfo['complete_name_with_username'],
5429
                $userInfo['profile_url']
5430
            );
5431
        }
5432
5433
        return get_lang('Anonymous');
5434
    }
5435
5436
    /**
5437
     * Get users whose name matches $firstname and $lastname.
5438
     *
5439
     * @param string $firstname Firstname to search
5440
     * @param string $lastname  Lastname to search
5441
     *
5442
     * @return array The user list
5443
     */
5444
    public static function getUsersByName($firstname, $lastname)
5445
    {
5446
        $firstname = Database::escape_string($firstname);
5447
        $lastname = Database::escape_string($lastname);
5448
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
5449
5450
        $sql = <<<SQL
5451
            SELECT id, username, lastname, firstname
5452
            FROM $userTable
5453
            WHERE
5454
                firstname LIKE '$firstname%' AND
5455
                lastname LIKE '$lastname%'
5456
SQL;
5457
        $result = Database::query($sql);
5458
        $users = [];
5459
        while ($resultData = Database::fetch_object($result)) {
5460
            $users[] = $resultData;
5461
        }
5462
5463
        return $users;
5464
    }
5465
5466
    /**
5467
     * @param int $optionSelected
5468
     *
5469
     * @return string
5470
     */
5471
    public static function getUserSubscriptionTab($optionSelected = 1)
5472
    {
5473
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
5474
        if (('true' === $allowAdmin && api_is_allowed_to_edit()) ||
5475
            api_is_platform_admin()
5476
        ) {
5477
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
5478
5479
            $headers = [
5480
                [
5481
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
5482
                    'content' => get_lang('Learners'),
5483
                ],
5484
                [
5485
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
5486
                    'content' => get_lang('Trainers'),
5487
                ],
5488
                /*[
5489
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
5490
                    'content' => get_lang('Learners'),
5491
                ],
5492
                [
5493
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
5494
                    'content' => get_lang('Trainers'),
5495
                ],*/
5496
                [
5497
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
5498
                    'content' => get_lang('Groups'),
5499
                ],
5500
                [
5501
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
5502
                    'content' => get_lang('Classes'),
5503
                ],
5504
            ];
5505
5506
            return Display::tabsOnlyLink($headers, $optionSelected);
5507
        }
5508
5509
        return '';
5510
    }
5511
5512
    /**
5513
     * Make sure this function is protected because it does NOT check password!
5514
     *
5515
     * This function defines globals.
5516
     *
5517
     * @param int  $userId
5518
     * @param bool $checkIfUserCanLoginAs
5519
     *
5520
     * @return bool
5521
     *
5522
     * @author Evie Embrechts
5523
     * @author Yannick Warnier <[email protected]>
5524
     */
5525
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
5526
    {
5527
        $userId = (int) $userId;
5528
        $userInfo = api_get_user_info($userId);
5529
5530
        // Check if the user is allowed to 'login_as'
5531
        $canLoginAs = true;
5532
        if ($checkIfUserCanLoginAs) {
5533
            $canLoginAs = api_can_login_as($userId);
5534
        }
5535
5536
        if (!$canLoginAs || empty($userInfo)) {
5537
            return false;
5538
        }
5539
5540
        if ($userId) {
5541
            $logInfo = [
5542
                'tool' => 'logout',
5543
                'tool_id' => 0,
5544
                'tool_id_detail' => 0,
5545
                'action' => '',
5546
                'info' => 'Change user (login as)',
5547
            ];
5548
            Event::registerLog($logInfo);
5549
5550
            // Logout the current user
5551
            self::loginDelete(api_get_user_id());
5552
5553
            return true;
5554
5555
            Session::erase('_user');
5556
            Session::erase('is_platformAdmin');
5557
            Session::erase('is_allowedCreateCourse');
5558
            Session::erase('_uid');
5559
5560
            // Cleaning session variables
5561
            $_user['firstName'] = $userInfo['firstname'];
5562
            $_user['lastName'] = $userInfo['lastname'];
5563
            $_user['mail'] = $userInfo['email'];
5564
            $_user['official_code'] = $userInfo['official_code'];
5565
            $_user['picture_uri'] = $userInfo['picture_uri'];
5566
            $_user['user_id'] = $userId;
5567
            $_user['id'] = $userId;
5568
            $_user['status'] = $userInfo['status'];
5569
5570
            // Filling session variables with new data
5571
            Session::write('_uid', $userId);
5572
            Session::write('_user', $userInfo);
5573
            Session::write('is_platformAdmin', (bool) self::is_admin($userId));
5574
            Session::write('is_allowedCreateCourse', 1 == $userInfo['status']);
5575
            // will be useful later to know if the user is actually an admin or not (example reporting)
5576
            Session::write('login_as', true);
5577
            $logInfo = [
5578
                'tool' => 'login',
5579
                'tool_id' => 0,
5580
                'tool_id_detail' => 0,
5581
                'info' => $userId,
5582
            ];
5583
            Event::registerLog($logInfo);
5584
5585
            return true;
5586
        }
5587
5588
        return false;
5589
    }
5590
5591
    /**
5592
     * Remove all login records from the track_e_online stats table,
5593
     * for the given user ID.
5594
     *
5595
     * @param int $userId User ID
5596
     */
5597
    public static function loginDelete($userId)
5598
    {
5599
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5600
        $userId = (int) $userId;
5601
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
5602
        Database::query($query);
5603
    }
5604
5605
    /**
5606
     * Login as first admin user registered in the platform.
5607
     *
5608
     * @return array
5609
     */
5610
    public static function logInAsFirstAdmin()
5611
    {
5612
        $adminList = self::get_all_administrators();
5613
5614
        if (!empty($adminList)) {
5615
            $userInfo = current($adminList);
5616
            if (!empty($userInfo)) {
5617
                $result = self::loginAsUser($userInfo['user_id'], false);
5618
                if ($result && api_is_platform_admin()) {
5619
                    return api_get_user_info();
5620
                }
5621
            }
5622
        }
5623
5624
        return [];
5625
    }
5626
5627
    /**
5628
     * Check if user is teacher of a student based in their courses.
5629
     *
5630
     * @param $teacherId
5631
     * @param $studentId
5632
     *
5633
     * @return array
5634
     */
5635
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
5636
    {
5637
        $courses = CourseManager::getCoursesFollowedByUser(
5638
            $teacherId,
5639
            COURSEMANAGER
5640
        );
5641
        if (empty($courses)) {
5642
            return false;
5643
        }
5644
5645
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
5646
        if (empty($coursesFromUser)) {
5647
            return false;
5648
        }
5649
5650
        $coursesCodeList = array_column($courses, 'code');
5651
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
5652
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
5653
        $commonCourses = array_filter($commonCourses);
5654
5655
        if (!empty($commonCourses)) {
5656
            return $commonCourses;
5657
        }
5658
5659
        return [];
5660
    }
5661
5662
    /**
5663
     * @param int $teacherId
5664
     * @param int $studentId
5665
     *
5666
     * @return bool
5667
     */
5668
    public static function isTeacherOfStudent($teacherId, $studentId)
5669
    {
5670
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
5671
            $teacherId,
5672
            $studentId
5673
        );
5674
5675
        if (!empty($courses)) {
5676
            return true;
5677
        }
5678
5679
        return false;
5680
    }
5681
5682
    /**
5683
     * Send user confirmation mail.
5684
     *
5685
     * @throws Exception
5686
     */
5687
    public static function sendUserConfirmationMail(User $user)
5688
    {
5689
        $uniqueId = api_get_unique_id();
5690
        $user->setConfirmationToken($uniqueId);
5691
5692
        Database::getManager()->persist($user);
5693
        Database::getManager()->flush();
5694
5695
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
5696
5697
        // Check if the user was originally set for an automated subscription to a course or session
5698
        $courseCodeToRedirect = Session::read('course_redirect');
5699
        $sessionToRedirect = Session::read('session_redirect');
5700
        if (!empty($courseCodeToRedirect)) {
5701
            $url .= '&c='.$courseCodeToRedirect;
5702
        }
5703
        if (!empty($sessionToRedirect)) {
5704
            $url .= '&s='.$sessionToRedirect;
5705
        }
5706
        $mailSubject = get_lang('Registration confirmation');
5707
        $mailBody = get_lang('Registration confirmationEmailMessage')
5708
            .PHP_EOL
5709
            .Display::url($url, $url);
5710
5711
        api_mail_html(
5712
            self::formatUserFullName($user),
5713
            $user->getEmail(),
5714
            $mailSubject,
5715
            $mailBody
5716
        );
5717
        Display::addFlash(Display::return_message(get_lang('Check your e-mail and follow the instructions.')));
5718
    }
5719
5720
    /**
5721
     * Anonymize a user. Replace personal info by anonymous info.
5722
     *
5723
     * @param int  $userId   User id
5724
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
5725
     *
5726
     * @throws \Exception
5727
     *
5728
     * @return bool
5729
     * @assert (0) === false
5730
     */
5731
    public static function anonymize($userId, $deleteIP = true)
5732
    {
5733
        global $debug;
5734
5735
        $userId = (int) $userId;
5736
5737
        if (empty($userId)) {
5738
            return false;
5739
        }
5740
5741
        $em = Database::getManager();
5742
        $user = api_get_user_entity($userId);
5743
        $uniqueId = uniqid('anon', true);
5744
        $user
5745
            ->setFirstname($uniqueId)
5746
            ->setLastname($uniqueId)
5747
            ->setBiography('')
5748
            ->setAddress('')
5749
            ->setCurriculumItems(null)
5750
            ->setDateOfBirth(null)
5751
            ->setCompetences('')
5752
            ->setDiplomas('')
5753
            ->setOpenarea('')
5754
            ->setTeach('')
5755
            ->setProductions(null)
5756
            ->setOpenid('')
5757
            ->setEmailCanonical($uniqueId.'@example.com')
5758
            ->setEmail($uniqueId.'@example.com')
5759
            ->setUsername($uniqueId)
5760
            ->setUsernameCanonical($uniqueId)
5761
            ->setPhone('')
5762
            ->setOfficialCode('')
5763
        ;
5764
5765
        self::deleteUserPicture($userId);
5766
        self::cleanUserRequestsOfRemoval($userId);
5767
5768
        // The IP address is a border-case personal data, as it does
5769
        // not directly allow for personal identification (it is not
5770
        // a completely safe value in most countries - the IP could
5771
        // be used by neighbours and crackers)
5772
        if ($deleteIP) {
5773
            $substitute = '127.0.0.1';
5774
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
5775
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
5776
            $res = Database::query($sql);
5777
            if (false === $res && $debug > 0) {
5778
                error_log("Could not anonymize IP address for user $userId ($sql)");
5779
            }
5780
5781
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5782
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5783
            $res = Database::query($sql);
5784
            if (false === $res && $debug > 0) {
5785
                error_log("Could not anonymize IP address for user $userId ($sql)");
5786
            }
5787
5788
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
5789
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE exe_user_id = $userId";
5790
            $res = Database::query($sql);
5791
            if (false === $res && $debug > 0) {
5792
                error_log("Could not anonymize IP address for user $userId ($sql)");
5793
            }
5794
5795
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
5796
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
5797
            $res = Database::query($sql);
5798
            if (false === $res && $debug > 0) {
5799
                error_log("Could not anonymize IP address for user $userId ($sql)");
5800
            }
5801
5802
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5803
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
5804
            $res = Database::query($sql);
5805
            if (false === $res && $debug > 0) {
5806
                error_log("Could not anonymize IP address for user $userId ($sql)");
5807
            }
5808
5809
            $table = Database::get_course_table(TABLE_WIKI);
5810
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5811
            $res = Database::query($sql);
5812
            if (false === $res && $debug > 0) {
5813
                error_log("Could not anonymize IP address for user $userId ($sql)");
5814
            }
5815
5816
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
5817
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
5818
            $res = Database::query($sql);
5819
            if (false === $res && $debug > 0) {
5820
                error_log("Could not anonymize IP address for user $userId ($sql)");
5821
            }
5822
5823
            $table = Database::get_course_table(TABLE_WIKI);
5824
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5825
            $res = Database::query($sql);
5826
            if (false === $res && $debug > 0) {
5827
                error_log("Could not anonymize IP address for user $userId ($sql)");
5828
            }
5829
        }
5830
        $em->persist($user);
5831
        $em->flush();
5832
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
5833
5834
        return true;
5835
    }
5836
5837
    /**
5838
     * @param int $userId
5839
     *
5840
     * @throws Exception
5841
     *
5842
     * @return string
5843
     */
5844
    public static function anonymizeUserWithVerification($userId)
5845
    {
5846
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
5847
5848
        $message = '';
5849
        if (api_is_platform_admin() ||
5850
            ($allowDelete && api_is_session_admin())
5851
        ) {
5852
            $userToUpdateInfo = api_get_user_info($userId);
5853
            $currentUserId = api_get_user_id();
5854
5855
            if ($userToUpdateInfo &&
5856
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
5857
            ) {
5858
                if ($userId != $currentUserId &&
5859
                    self::anonymize($userId)
5860
                ) {
5861
                    $message = Display::return_message(
5862
                        sprintf(get_lang('User %s information anonymized.'), $userToUpdateInfo['complete_name_with_username']),
5863
                        'confirmation'
5864
                    );
5865
                } else {
5866
                    $message = Display::return_message(
5867
                        sprintf(get_lang('We could not anonymize user %s information. Please try again or check the logs.'), $userToUpdateInfo['complete_name_with_username']),
5868
                        'error'
5869
                    );
5870
                }
5871
            } else {
5872
                $message = Display::return_message(
5873
                    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']),
5874
                    'error'
5875
                );
5876
            }
5877
        }
5878
5879
        return $message;
5880
    }
5881
5882
    /**
5883
     * @param int $userId
5884
     *
5885
     * @throws Exception
5886
     *
5887
     * @return string
5888
     */
5889
    public static function deleteUserWithVerification($userId)
5890
    {
5891
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
5892
        $message = Display::return_message(get_lang('You cannot delete this user'), 'error');
5893
        $userToUpdateInfo = api_get_user_info($userId);
5894
5895
        // User must exist.
5896
        if (empty($userToUpdateInfo)) {
5897
            return $message;
5898
        }
5899
5900
        $currentUserId = api_get_user_id();
5901
5902
        // Cannot delete myself.
5903
        if ($userId == $currentUserId) {
5904
            return $message;
5905
        }
5906
5907
        if (api_is_platform_admin() ||
5908
            ($allowDelete && api_is_session_admin())
5909
        ) {
5910
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
5911
                if (self::delete_user($userId)) {
5912
                    $message = Display::return_message(
5913
                        get_lang('The user has been deleted').': '.$userToUpdateInfo['complete_name_with_username'],
5914
                        'confirmation'
5915
                    );
5916
                } else {
5917
                    $message = Display::return_message(get_lang('You cannot delete this userBecauseOwnsCourse'), 'error');
5918
                }
5919
            }
5920
        }
5921
5922
        return $message;
5923
    }
5924
5925
    /**
5926
     * @return array
5927
     */
5928
    public static function createDataPrivacyExtraFields()
5929
    {
5930
        self::create_extra_field(
5931
            'request_for_legal_agreement_consent_removal_justification',
5932
            1, //text
5933
            'Request for legal agreement consent removal justification	',
5934
            ''
5935
        );
5936
5937
        self::create_extra_field(
5938
            'request_for_delete_account_justification',
5939
            1, //text
5940
            'Request for delete account justification',
5941
            ''
5942
        );
5943
5944
        $extraFieldId = self::create_extra_field(
5945
            'request_for_legal_agreement_consent_removal',
5946
            1, //text
5947
            'Request for legal agreement consent removal',
5948
            ''
5949
        );
5950
5951
        $extraFieldIdDeleteAccount = self::create_extra_field(
5952
            'request_for_delete_account',
5953
            1, //text
5954
            'Request for delete user account',
5955
            ''
5956
        );
5957
5958
        return [
5959
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
5960
            'delete_legal' => $extraFieldId,
5961
        ];
5962
    }
5963
5964
    /**
5965
     * @param int $userId
5966
     */
5967
    public static function cleanUserRequestsOfRemoval($userId)
5968
    {
5969
        $userId = (int) $userId;
5970
5971
        $extraFieldValue = new ExtraFieldValue('user');
5972
        $extraFieldsToDelete = [
5973
            'legal_accept',
5974
            'request_for_legal_agreement_consent_removal',
5975
            'request_for_legal_agreement_consent_removal_justification',
5976
            'request_for_delete_account_justification', // just in case delete also this
5977
            'request_for_delete_account',
5978
        ];
5979
5980
        foreach ($extraFieldsToDelete as $variable) {
5981
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
5982
                $userId,
5983
                $variable
5984
            );
5985
            if ($value && isset($value['id'])) {
5986
                $extraFieldValue->delete($value['id']);
5987
            }
5988
        }
5989
    }
5990
5991
    /**
5992
     * @param int $searchYear
5993
     *
5994
     * @throws Exception
5995
     *
5996
     * @return array
5997
     */
5998
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
5999
    {
6000
        $timezone = new DateTimeZone(api_get_timezone());
6001
6002
        $sessions = [];
6003
        if (DRH == $userInfo['status']) {
6004
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
6005
        } elseif (api_is_platform_admin(true)) {
6006
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
6007
        } else {
6008
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
6009
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
6010
6011
            foreach ($sessionsByCategory as $sessionsInCategory) {
6012
                $sessions = array_merge($sessions, $sessionsInCategory);
6013
            }
6014
        }
6015
6016
        $sessions = array_map(
6017
            function ($sessionInfo) {
6018
                if (!isset($sessionInfo['session_id'])) {
6019
                    $sessionInfo['session_id'] = $sessionInfo['id'];
6020
                }
6021
                if (!isset($sessionInfo['session_name'])) {
6022
                    $sessionInfo['session_name'] = $sessionInfo['name'];
6023
                }
6024
6025
                return $sessionInfo;
6026
            },
6027
            $sessions
6028
        );
6029
6030
        $calendarSessions = [];
6031
6032
        foreach ($sessions as $sessionInfo) {
6033
            if (!empty($sessionInfo['duration'])) {
6034
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
6035
                    $sessionInfo['session_id'],
6036
                    $userInfo['id']
6037
                );
6038
6039
                if (empty($courseAccess)) {
6040
                    continue;
6041
                }
6042
6043
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
6044
                $lastAccessDate = clone $firstAcessDate;
6045
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
6046
6047
                $firstAccessYear = (int) $firstAcessDate->format('Y');
6048
                $lastAccessYear = (int) $lastAccessDate->format('Y');
6049
6050
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
6051
                    $calendarSessions[$sessionInfo['session_id']] = [
6052
                        'name' => $sessionInfo['session_name'],
6053
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
6054
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
6055
                    ];
6056
                }
6057
6058
                continue;
6059
            }
6060
6061
            $accessStartDate = !empty($sessionInfo['access_start_date'])
6062
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6063
                : null;
6064
            $accessEndDate = !empty($sessionInfo['access_end_date'])
6065
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6066
                : null;
6067
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
6068
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
6069
6070
            $isValid = false;
6071
6072
            if ($accessStartYear && $accessEndYear) {
6073
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
6074
                    $isValid = true;
6075
                }
6076
            }
6077
6078
            if ($accessStartYear && !$accessEndYear) {
6079
                if ($accessStartYear == $searchYear) {
6080
                    $isValid = true;
6081
                }
6082
            }
6083
6084
            if (!$accessStartYear && $accessEndYear) {
6085
                if ($accessEndYear == $searchYear) {
6086
                    $isValid = true;
6087
                }
6088
            }
6089
6090
            if ($isValid) {
6091
                $calendarSessions[$sessionInfo['session_id']] = [
6092
                    'name' => $sessionInfo['session_name'],
6093
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
6094
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
6095
                ];
6096
            }
6097
        }
6098
6099
        return $calendarSessions;
6100
    }
6101
6102
    /**
6103
     * Get sessions info for planification calendar.
6104
     *
6105
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
6106
     * @param int   $searchYear
6107
     *
6108
     * @throws Exception
6109
     *
6110
     * @return array
6111
     */
6112
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
6113
    {
6114
        $timezone = new DateTimeZone(api_get_timezone());
6115
        $calendar = [];
6116
6117
        foreach ($sessionsList as $sessionId => $sessionInfo) {
6118
            $startDate = $sessionInfo['access_start_date']
6119
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6120
                : null;
6121
            $endDate = $sessionInfo['access_end_date']
6122
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6123
                : null;
6124
6125
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
6126
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
6127
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
6128
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
6129
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
6130
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
6131
6132
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
6133
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
6134
6135
            $calendar[] = [
6136
                'id' => $sessionId,
6137
                'name' => $sessionInfo['name'],
6138
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
6139
                'start_in_last_year' => $startYear < $searchYear,
6140
                'end_in_next_year' => $endYear > $searchYear,
6141
                'no_start' => !$startWeek,
6142
                'no_end' => !$endWeek,
6143
                'start' => $start,
6144
                'duration' => $duration > 0 ? $duration : 1,
6145
            ];
6146
        }
6147
6148
        usort(
6149
            $calendar,
6150
            function ($sA, $sB) {
6151
                if ($sA['start'] == $sB['start']) {
6152
                    return 0;
6153
                }
6154
6155
                if ($sA['start'] < $sB['start']) {
6156
                    return -1;
6157
                }
6158
6159
                return 1;
6160
            }
6161
        );
6162
6163
        return $calendar;
6164
    }
6165
6166
    /**
6167
     * Return the user's full name. Optionally with the username.
6168
     *
6169
     * @param bool $includeUsername Optional. By default username is not included.
6170
     */
6171
    public static function formatUserFullName(User $user, $includeUsername = false): string
6172
    {
6173
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
6174
6175
        if ($includeUsername && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
6176
            $username = $user->getUsername();
6177
6178
            return "$fullName ($username)";
6179
        }
6180
6181
        return $fullName;
6182
    }
6183
6184
    /**
6185
     * @param int $userId
6186
     *
6187
     * @return array
6188
     */
6189
    public static function getUserCareers($userId)
6190
    {
6191
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6192
        $tableCareer = Database::get_main_table(TABLE_CAREER);
6193
        $userId = (int) $userId;
6194
6195
        $sql = "SELECT c.id, c.name
6196
                FROM $table uc
6197
                INNER JOIN $tableCareer c
6198
                ON uc.career_id = c.id
6199
                WHERE user_id = $userId
6200
                ORDER BY uc.created_at
6201
                ";
6202
        $result = Database::query($sql);
6203
6204
        return Database::store_result($result, 'ASSOC');
6205
    }
6206
6207
    /**
6208
     * @param int $userId
6209
     * @param int $careerId
6210
     */
6211
    public static function addUserCareer($userId, $careerId)
6212
    {
6213
        if (!api_get_configuration_value('allow_career_users')) {
6214
            return false;
6215
        }
6216
6217
        if (false === self::userHasCareer($userId, $careerId)) {
6218
            $params = ['user_id' => $userId, 'career_id' => $careerId, 'created_at' => api_get_utc_datetime(), 'updated_at' => api_get_utc_datetime()];
6219
            $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6220
            Database::insert($table, $params);
6221
        }
6222
6223
        return true;
6224
    }
6225
6226
    /**
6227
     * @param int   $userCareerId
6228
     * @param array $data
6229
     *
6230
     * @return bool
6231
     */
6232
    public static function updateUserCareer($userCareerId, $data)
6233
    {
6234
        if (!api_get_configuration_value('allow_career_users')) {
6235
            return false;
6236
        }
6237
6238
        $params = ['extra_data' => $data, 'updated_at' => api_get_utc_datetime()];
6239
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6240
        Database::update(
6241
            $table,
6242
            $params,
6243
            ['id = ?' => (int) $userCareerId]
6244
        );
6245
6246
        return true;
6247
    }
6248
6249
    /**
6250
     * @param int $userId
6251
     * @param int $careerId
6252
     *
6253
     * @return array
6254
     */
6255
    public static function getUserCareer($userId, $careerId)
6256
    {
6257
        $userId = (int) $userId;
6258
        $careerId = (int) $careerId;
6259
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6260
6261
        $sql = "SELECT * FROM $table WHERE user_id = $userId AND career_id = $careerId";
6262
        $result = Database::query($sql);
6263
6264
        return Database::fetch_array($result, 'ASSOC');
6265
    }
6266
6267
    /**
6268
     * @param int $userId
6269
     * @param int $careerId
6270
     *
6271
     * @return bool
6272
     */
6273
    public static function userHasCareer($userId, $careerId)
6274
    {
6275
        $userId = (int) $userId;
6276
        $careerId = (int) $careerId;
6277
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6278
6279
        $sql = "SELECT id FROM $table WHERE user_id = $userId AND career_id = $careerId";
6280
        $result = Database::query($sql);
6281
6282
        return Database::num_rows($result) > 0;
6283
    }
6284
6285
    /**
6286
     * @return EncoderFactory
6287
     */
6288
    private static function getEncoderFactory()
6289
    {
6290
        $encryption = self::getPasswordEncryption();
6291
        $encoders = [
6292
            'Chamilo\\CoreBundle\\Entity\\User' => new \Chamilo\CoreBundle\Security\Encoder($encryption),
6293
        ];
6294
6295
        $encoderFactory = new \Symfony\Component\Security\Core\Encoder\EncoderFactory($encoders);
6296
6297
        return $encoderFactory;
6298
    }
6299
6300
    /**
6301
     * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface
6302
     */
6303
    private static function getEncoder(User $user)
6304
    {
6305
        $encoderFactory = self::getEncoderFactory();
6306
6307
        return $encoderFactory->getEncoder($user);
6308
    }
6309
6310
    /**
6311
     * Disables or enables a user.
6312
     *
6313
     * @param int $user_id
6314
     * @param int $active  Enable or disable
6315
     *
6316
     * @return bool True on success, false on failure
6317
     * @assert (-1,0) === false
6318
     * @assert (1,1) === true
6319
     */
6320
    private static function change_active_state($user_id, $active)
6321
    {
6322
        $user_id = (int) $user_id;
6323
        $active = (int) $active;
6324
6325
        if (empty($user_id)) {
6326
            return false;
6327
        }
6328
6329
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6330
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
6331
        $r = Database::query($sql);
6332
        $ev = LOG_USER_DISABLE;
6333
        if (1 == $active) {
6334
            $ev = LOG_USER_ENABLE;
6335
        }
6336
        if (false !== $r) {
6337
            Event::addEvent($ev, LOG_USER_ID, $user_id);
6338
        }
6339
6340
        return $r;
6341
    }
6342
6343
    /**
6344
     * Get either a Gravatar URL or complete image tag for a specified email address.
6345
     *
6346
     * @param string $email The email address
6347
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
6348
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
6349
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
6350
     * @param bool   $img   True to return a complete IMG tag False for just the URL
6351
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
6352
     *
6353
     * @return string containing either just a URL or a complete image tag
6354
     * @source http://gravatar.com/site/implement/images/php/
6355
     */
6356
    private static function getGravatar(
6357
        $email,
6358
        $s = 80,
6359
        $d = 'mm',
6360
        $r = 'g',
6361
        $img = false,
6362
        $atts = []
6363
    ) {
6364
        $url = 'http://www.gravatar.com/avatar/';
6365
        if (!empty($_SERVER['HTTPS'])) {
6366
            $url = 'https://secure.gravatar.com/avatar/';
6367
        }
6368
        $url .= md5(strtolower(trim($email)));
6369
        $url .= "?s=$s&d=$d&r=$r";
6370
        if ($img) {
6371
            $url = '<img src="'.$url.'"';
6372
            foreach ($atts as $key => $val) {
6373
                $url .= ' '.$key.'="'.$val.'"';
6374
            }
6375
            $url .= ' />';
6376
        }
6377
6378
        return $url;
6379
    }
6380
}
6381