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

UserManager::create_user()   F

Complexity

Conditions 63
Paths > 20000

Size

Total Lines 491
Code Lines 305

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 63
eloc 305
nc 159483056
nop 26
dl 0
loc 491
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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