Passed
Push — master ( a81919...251179 )
by Julito
15:43 queued 07:28
created

UserManager::getUsersFollowedByUser()   F

Complexity

Conditions 22
Paths 15360

Size

Total Lines 204
Code Lines 122

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
eloc 122
nop 13
dl 0
loc 204
rs 0
c 0
b 0
f 0
nc 15360

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