Passed
Push — master ( f534c7...28463a )
by Angel Fernando Quiroz
12:52 queued 06:03
created

UserManager::anonymizeUserWithVerification()   B

Complexity

Conditions 8
Paths 4

Size

Total Lines 36
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 22
c 0
b 0
f 0
nc 4
nop 1
dl 0
loc 36
rs 8.4444
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Component\Utils\NameConvention;
5
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
6
use Chamilo\CoreBundle\Entity\ExtraFieldSavedSearch;
7
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
8
use Chamilo\CoreBundle\Entity\SessionRelCourse;
9
use Chamilo\CoreBundle\Entity\SkillRelUser;
10
use Chamilo\CoreBundle\Entity\SkillRelUserComment;
11
use Chamilo\CoreBundle\Entity\User;
12
use Chamilo\CoreBundle\Entity\UserRelUser;
13
use Chamilo\CoreBundle\Framework\Container;
14
use Chamilo\CoreBundle\Repository\GroupRepository;
15
use Chamilo\CoreBundle\Repository\Node\UserRepository;
16
use ChamiloSession as Session;
17
use Symfony\Component\HttpFoundation\File\UploadedFile;
18
use Chamilo\CoreBundle\Entity\ExtraFieldValues as EntityExtraFieldValues;
19
20
/**
21
 * This library provides functions for user management.
22
 * Include/require it in your code to use its functionality.
23
 *
24
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
25
 */
26
class UserManager
27
{
28
    // This constants are deprecated use the constants located in ExtraField
29
    public const USER_FIELD_TYPE_TEXT = 1;
30
    public const USER_FIELD_TYPE_TEXTAREA = 2;
31
    public const USER_FIELD_TYPE_RADIO = 3;
32
    public const USER_FIELD_TYPE_SELECT = 4;
33
    public const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
34
    public const USER_FIELD_TYPE_DATE = 6;
35
    public const USER_FIELD_TYPE_DATETIME = 7;
36
    public const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
37
    public const USER_FIELD_TYPE_DIVIDER = 9;
38
    public const USER_FIELD_TYPE_TAG = 10;
39
    public const USER_FIELD_TYPE_TIMEZONE = 11;
40
    public const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
41
    public const USER_FIELD_TYPE_FILE = 13;
42
    public const USER_FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
43
44
    public function __construct()
45
    {
46
    }
47
48
    /**
49
     * Repository is use to query the DB, selects, etc.
50
     *
51
     * @return UserRepository
52
     */
53
    public static function getRepository()
54
    {
55
        return Container::getUserRepository();
56
    }
57
58
    /**
59
     * Validates the password.
60
     */
61
    public static function isPasswordValid(User $user, string $plainPassword): bool
62
    {
63
        /**
64
         * @psalm-suppress PrivateService
65
         */
66
        $hasher = Container::$container->get('security.user_password_hasher');
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

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

66
        /** @scrutinizer ignore-call */ 
67
        $hasher = Container::$container->get('security.user_password_hasher');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
67
68
        return $hasher->isPasswordValid($user, $plainPassword);
69
    }
70
71
    /**
72
     * @param int    $userId
73
     * @param string $password
74
     */
75
    public static function updatePassword($userId, $password)
76
    {
77
        $user = api_get_user_entity($userId);
78
        $userManager = self::getRepository();
79
        $user->setPlainPassword($password);
80
        $userManager->updateUser($user, true);
81
    }
82
83
    /**
84
     * Creates a new user for the platform.
85
     *
86
     * @author Hugues Peeters <[email protected]>,
87
     * @author Roan Embrechts <[email protected]>
88
     *
89
     * @param string        $firstName
90
     * @param string        $lastName
91
     * @param int           $status                  (1 for course tutor, 5 for student, 6 for anonymous)
92
     * @param string        $email
93
     * @param string        $loginName
94
     * @param string        $password
95
     * @param string        $officialCode           Any official code (optional)
96
     * @param string        $language                User language    (optional)
97
     * @param string        $phone                   Phone number    (optional)
98
     * @param string        $pictureUri             Picture URI        (optional)
99
     * @param string        $authSource              Authentication source (defaults to 'platform', dependind on constant)
100
     * @param string        $expirationDate          Account expiration date (optional, defaults to null)
101
     * @param int           $active                  Whether the account is enabled or disabled by default
102
     * @param int           $hrDeptId              The department of HR in which the user is registered (defaults to 0)
103
     * @param array         $extra                   Extra fields (labels must be prefixed by "extra_")
104
     * @param string        $encryptMethod          Used if password is given encrypted. Set to an empty string by default
105
     * @param bool          $sendMail
106
     * @param bool          $isAdmin
107
     * @param string        $address
108
     * @param bool          $sendEmailToAllAdmins
109
     * @param FormValidator $form
110
     * @param int           $creatorId
111
     * @param array         $emailTemplate
112
     * @param string        $redirectToURLAfterLogin
113
     *
114
     * @return mixed new user id - if the new user creation succeeds, false otherwise
115
     * @desc The function tries to retrieve user id from the session.
116
     * If it exists, the current user id is the creator id. If a problem arises,
117
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
118
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
119
     */
120
    public static function create_user(
121
        $firstName,
122
        $lastName,
123
        $status,
124
        $email,
125
        $loginName,
126
        $password,
127
        $officialCode = '',
128
        $language = '',
129
        $phone = '',
130
        $pictureUri = '',
131
        $authSource = null,
132
        $expirationDate = null,
133
        $active = 1,
134
        $hrDeptId = 0,
135
        $extra = [],
136
        $encryptMethod = '',
137
        $sendMail = false,
138
        $isAdmin = false,
139
        $address = '',
140
        $sendEmailToAllAdmins = false,
141
        $form = null,
142
        $creatorId = 0,
143
        $emailTemplate = [],
144
        $redirectToURLAfterLogin = ''
145
    ) {
146
        $authSource = !empty($authSource) ? $authSource : PLATFORM_AUTH_SOURCE;
147
        $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId;
148
149
        if (0 === $creatorId) {
150
            Display::addFlash(
151
                Display::return_message(get_lang('A user creator is needed'))
152
            );
153
154
            return false;
155
        }
156
157
        $creatorInfo = api_get_user_info($creatorId);
158
        $creatorEmail = $creatorInfo['email'] ?? '';
159
160
        // First check if the login exists.
161
        if (!self::is_username_available($loginName)) {
162
            Display::addFlash(
163
                Display::return_message(get_lang('This login is already taken !'))
164
            );
165
166
            return false;
167
        }
168
169
        global $_configuration;
170
        $original_password = $password;
171
172
        $access_url_id = 1;
173
        if (api_get_multiple_access_url()) {
174
            $access_url_id = api_get_current_access_url_id();
175
        } else {
176
            // In some cases, the first access_url ID might be different from 1
177
            // for example when using a DB cluster or hacking the DB manually.
178
            // In this case, we want the first row, not necessarily "1".
179
            $accessUrlRepository = Container::getAccessUrlRepository();
180
            $accessUrl = $accessUrlRepository->getFirstId();
181
            if (!empty($accessUrl[0]) && !empty($accessUrl[0][1])) {
182
                $access_url_id = $accessUrl[0][1];
183
            }
184
        }
185
186
        if (isset($_configuration[$access_url_id]) &&
187
            is_array($_configuration[$access_url_id]) &&
188
            isset($_configuration[$access_url_id]['hosting_limit_users']) &&
189
            $_configuration[$access_url_id]['hosting_limit_users'] > 0) {
190
            $num = self::get_number_of_users();
191
            if ($num >= $_configuration[$access_url_id]['hosting_limit_users']) {
192
                api_warn_hosting_contact('hosting_limit_users');
193
                Display::addFlash(
194
                    Display::return_message(
195
                        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.'),
196
                        'warning'
197
                    )
198
                );
199
200
                return false;
201
            }
202
        }
203
204
        if (1 === $status &&
205
            isset($_configuration[$access_url_id]) &&
206
            is_array($_configuration[$access_url_id]) &&
207
            isset($_configuration[$access_url_id]['hosting_limit_teachers']) &&
208
            $_configuration[$access_url_id]['hosting_limit_teachers'] > 0
209
        ) {
210
            $num = self::get_number_of_users(1);
211
            if ($num >= $_configuration[$access_url_id]['hosting_limit_teachers']) {
212
                Display::addFlash(
213
                    Display::return_message(
214
                        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.'),
215
                        'warning'
216
                    )
217
                );
218
                api_warn_hosting_contact('hosting_limit_teachers');
219
220
                return false;
221
            }
222
        }
223
224
        if (empty($password)) {
225
            if (PLATFORM_AUTH_SOURCE === $authSource) {
226
                Display::addFlash(
227
                    Display::return_message(
228
                        get_lang('Required field').': '.get_lang(
229
                            'Password'
230
                        ),
231
                        'warning'
232
                    )
233
                );
234
235
                return false;
236
            }
237
238
            // We use the authSource as password.
239
            // The real validation will be by processed by the auth
240
            // source not Chamilo
241
            $password = $authSource;
242
        }
243
244
        // Checking the user language
245
        $languages = api_get_languages();
246
247
        // Default to english
248
        if (!in_array($language, array_keys($languages), true)) {
249
            $language = 'en_US';
250
        }
251
252
        $now = new DateTime();
253
        if (empty($expirationDate) || '0000-00-00 00:00:00' === $expirationDate) {
254
            $expirationDate = null;
255
        // Default expiration date
256
            // if there is a default duration of a valid account then
257
            // we have to change the expiration_date accordingly
258
            // Accept 0000-00-00 00:00:00 as a null value to avoid issues with
259
            // third party code using this method with the previous (pre-1.10)
260
            // value of 0000...
261
            /*if ('' != api_get_setting('account_valid_duration')) {
262
                $expirationDate = new DateTime($currentDate);
263
                $days = (int) api_get_setting('account_valid_duration');
264
                $expirationDate->modify('+'.$days.' day');
265
            }*/
266
        } else {
267
            $expirationDate = api_get_utc_datetime($expirationDate, true, true);
268
        }
269
270
        $repo = Container::getUserRepository();
271
        $user = $repo->createUser()
272
            ->setLastname($lastName)
273
            ->setFirstname($firstName)
274
            ->setUsername($loginName)
275
            ->setStatus($status)
276
            ->setPlainPassword($password)
277
            ->setEmail($email)
278
            ->setOfficialCode($officialCode)
279
            ->setCreatorId($creatorId)
280
            ->setAuthSource($authSource)
281
            ->setPhone($phone)
282
            ->setAddress($address)
283
            ->setLocale($language)
284
            ->setRegistrationDate($now)
285
            ->setHrDeptId($hrDeptId)
286
            ->setActive($active)
287
            ->setTimezone(api_get_timezone())
288
        ;
289
290
        if (null !== $expirationDate) {
291
            $user->setExpirationDate($expirationDate);
292
        }
293
294
        $user->setRoleFromStatus($status);
295
296
        // Add user to a group
297
        /*$statusToGroup = [
298
            COURSEMANAGER => 'TEACHER',
299
            STUDENT => 'STUDENT',
300
            DRH => 'RRHH',
301
            SESSIONADMIN => 'SESSION_ADMIN',
302
            STUDENT_BOSS => 'STUDENT_BOSS',
303
            INVITEE => 'INVITEE',
304
        ];
305
306
        if (isset($statusToGroup[$status])) {
307
            $group = Container::$container->get(GroupRepository::class)->findOneBy(['code' => $statusToGroup[$status]]);
308
            if ($group) {
309
                $user->addGroup($group);
310
            }
311
        }*/
312
313
        $repo->updateUser($user, true);
314
315
        $userId = $user->getId();
316
317
        if (!empty($userId)) {
318
            if ($isAdmin) {
319
                self::addUserAsAdmin($user);
320
            }
321
322
            if (api_get_multiple_access_url()) {
323
                UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
324
            } else {
325
                //we are adding by default the access_url_user table with access_url_id = 1
326
                UrlManager::add_user_to_url($userId, 1);
327
            }
328
329
            if (is_array($extra) && count($extra) > 0) {
330
                $extra['item_id'] = $userId;
331
                $userFieldValue = new ExtraFieldValue('user');
332
                /* Force saving of extra fields (otherwise, if the current
333
                user is not admin, fields not visible to the user - most
334
                of them - are just ignored) */
335
                $userFieldValue->saveFieldValues(
336
                    $extra,
337
                    true,
338
                    false,
339
                    [],
340
                    [],
341
                    true
342
                );
343
            } else {
344
                // Create notify settings by default
345
                self::update_extra_field_value(
346
                    $userId,
347
                    'mail_notify_invitation',
348
                    '1'
349
                );
350
                self::update_extra_field_value(
351
                    $userId,
352
                    'mail_notify_message',
353
                    '1'
354
                );
355
                self::update_extra_field_value(
356
                    $userId,
357
                    'mail_notify_group_message',
358
                    '1'
359
                );
360
            }
361
362
            self::update_extra_field_value(
363
                $userId,
364
                'already_logged_in',
365
                'false'
366
            );
367
368
            if (!empty($redirectToURLAfterLogin) && ('true' === api_get_setting('admin.plugin_redirection_enabled'))) {
369
                RedirectionPlugin::insert($userId, $redirectToURLAfterLogin);
370
            }
371
372
            if (!empty($email) && $sendMail) {
373
                $recipient_name = api_get_person_name(
374
                    $firstName,
375
                    $lastName,
376
                    null,
377
                    PERSON_NAME_EMAIL_ADDRESS
378
                );
379
                $tpl = Container::getTwig();
380
                $emailSubject = $tpl->render('@ChamiloCore/Mailer/Legacy/subject_registration_platform.html.twig');
381
                $sender_name = api_get_person_name(
382
                    api_get_setting('administratorName'),
383
                    api_get_setting('administratorSurname'),
384
                    null,
385
                    PERSON_NAME_EMAIL_ADDRESS
386
                );
387
                $email_admin = api_get_setting('emailAdministrator');
388
389
                $url = api_get_path(WEB_PATH);
390
                $access_url_id = api_get_current_access_url_id();
391
                $urlInfo = api_get_access_url($access_url_id);
392
                if ($urlInfo) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $urlInfo of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
393
                    $url = $urlInfo['url'];
394
                }
395
396
                // variables for the default template
397
                $params = [
398
                    'complete_name' => stripslashes(api_get_person_name($firstName, $lastName)),
399
                    'login_name' => $loginName,
400
                    'original_password' => stripslashes($original_password),
401
                    'mailWebPath' => $url,
402
                    'new_user' => $user,
403
                    'search_link' => $url,
404
                ];
405
406
                // ofaj
407
                if ('true' === api_get_setting('session.allow_search_diagnostic')) {
408
                    $urlSearch = api_get_path(WEB_CODE_PATH).'search/search.php';
409
                    $linkSearch = Display::url($urlSearch, $urlSearch);
410
                    $params['search_link'] = $linkSearch;
411
                }
412
413
                $emailBody = $tpl->render(
414
                    '@ChamiloCore/Mailer/Legacy/content_registration_platform.html.twig',
415
                    $params
416
                );
417
418
                $userInfo = api_get_user_info($userId);
419
                $mailTemplateManager = new MailTemplateManager();
420
                $phoneNumber = $extra['mobile_phone_number'] ?? null;
421
422
                $emailBodyTemplate = '';
423
                if (!empty($emailTemplate)) {
424
                    if (isset($emailTemplate['content_registration_platform.tpl']) &&
425
                        !empty($emailTemplate['content_registration_platform.tpl'])
426
                    ) {
427
                        $emailBodyTemplate = $mailTemplateManager->parseTemplate(
428
                            $emailTemplate['content_registration_platform.tpl'],
429
                            $userInfo
430
                        );
431
                    }
432
                }
433
434
                $twoEmail = ('true' === api_get_setting('mail.send_two_inscription_confirmation_mail'));
435
                if (true === $twoEmail) {
436
                    $emailBody = $tpl->render(
437
                        '@ChamiloCore/Mailer/Legacy/new_user_first_email_confirmation.html.twig'
438
                    );
439
440
                    if (!empty($emailBodyTemplate) &&
441
                        isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
442
                        !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
443
                    ) {
444
                        $emailBody = $mailTemplateManager->parseTemplate(
445
                            $emailTemplate['new_user_first_email_confirmation.tpl'],
446
                            $userInfo
447
                        );
448
                    }
449
450
                    api_mail_html(
451
                        $recipient_name,
452
                        $email,
453
                        $emailSubject,
454
                        $emailBody,
455
                        $sender_name,
456
                        $email_admin,
457
                        [],
458
                        [],
459
                        false,
460
                        [],
461
                        $creatorEmail
462
                    );
463
464
                    $emailBody = $tpl->render('@ChamiloCore/Mailer/Legacy/new_user_second_email_confirmation.html.twig');
465
466
                    if (!empty($emailBodyTemplate) &&
467
                        isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
468
                        !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
469
                    ) {
470
                        $emailBody = $mailTemplateManager->parseTemplate(
471
                            $emailTemplate['new_user_second_email_confirmation.tpl'],
472
                            $userInfo
473
                        );
474
                    }
475
476
                    api_mail_html(
477
                        $recipient_name,
478
                        $email,
479
                        $emailSubject,
480
                        $emailBody,
481
                        $sender_name,
482
                        $email_admin,
483
                        null,
484
                        null,
485
                        null,
486
                        [],
487
                        $creatorEmail
488
                    );
489
                } else {
490
                    if (!empty($emailBodyTemplate)) {
491
                        $emailBody = $emailBodyTemplate;
492
                    }
493
                    $sendToInbox = ('true' === api_get_setting('registration.send_inscription_msg_to_inbox'));
494
                    if ($sendToInbox) {
495
                        $adminList = self::get_all_administrators();
496
                        $senderId = 1;
497
                        if (!empty($adminList)) {
498
                            $adminInfo = current($adminList);
499
                            $senderId = $adminInfo['user_id'];
500
                        }
501
502
                        MessageManager::send_message_simple(
503
                            $userId,
504
                            $emailSubject,
505
                            $emailBody,
506
                            $senderId
507
                        );
508
                    } else {
509
                        api_mail_html(
510
                            $recipient_name,
511
                            $email,
512
                            $emailSubject,
513
                            $emailBody,
514
                            $sender_name,
515
                            $email_admin,
516
                            null,
517
                            null,
518
                            null,
519
                            [],
520
                            $creatorEmail
521
                        );
522
                    }
523
                }
524
525
                $notification = api_get_setting('profile.send_notification_when_user_added', true);
526
                if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
527
                    foreach ($notification['admins'] as $adminId) {
528
                        $emailSubjectToAdmin = get_lang('The user has been added').': '.
529
                            api_get_person_name($firstName, $lastName);
530
                        MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
531
                    }
532
                }
533
534
                if ($sendEmailToAllAdmins) {
535
                    $adminList = self::get_all_administrators();
536
                    // variables for the default template
537
                    $renderer = FormValidator::getDefaultRenderer();
538
                    // Form template
539
                    $elementTemplate = ' {label}: {element} <br />';
540
                    $renderer->setElementTemplate($elementTemplate);
541
                    /** @var FormValidator $form */
542
                    $form->freeze(null, $elementTemplate);
543
                    $form->removeElement('submit');
544
                    $formData = $form->returnForm();
545
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
546
                    $params = [
547
                        'complete_name' => stripslashes(api_get_person_name($firstName, $lastName)),
548
                        'user_added' => $user,
549
                        'link' => Display::url($url, $url),
550
                        'form' => $formData,
551
                        'user_language' => $user->getLocale(),
552
                    ];
553
                    $emailBody = $tpl->render(
554
                        '@ChamiloCore/Mailer/Legacy/content_registration_platform_to_admin.html.twig',
555
                        $params
556
                    );
557
558
                    if (!empty($emailBodyTemplate) &&
559
                        isset($emailTemplate['content_registration_platform_to_admin.tpl']) &&
560
                        !empty($emailTemplate['content_registration_platform_to_admin.tpl'])
561
                    ) {
562
                        $emailBody = $mailTemplateManager->parseTemplate(
563
                            $emailTemplate['content_registration_platform_to_admin.tpl'],
564
                            $userInfo
565
                        );
566
                    }
567
568
                    $subject = get_lang('The user has been added');
569
                    foreach ($adminList as $adminId => $data) {
570
                        MessageManager::send_message_simple(
571
                            $adminId,
572
                            $subject,
573
                            $emailBody,
574
                            $userId
575
                        );
576
                    }
577
                }
578
            }
579
            Event::addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId, null, $creatorId);
0 ignored issues
show
Bug introduced by
The method addEvent() does not exist on Event. ( Ignorable by Annotation )

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

579
            Event::/** @scrutinizer ignore-call */ 
580
                   addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId, null, $creatorId);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
580
        } else {
581
            Display::addFlash(
582
                Display::return_message(
583
                    get_lang('There happened an unknown error. Please contact the platform administrator.')
584
                )
585
            );
586
587
            return false;
588
        }
589
590
        return $userId;
591
    }
592
593
    /**
594
     * Can user be deleted? This function checks whether there's a course
595
     * in which the given user is the
596
     * only course administrator. If that is the case, the user can't be
597
     * deleted because the course would remain without a course admin.
598
     *
599
     * @param int $user_id The user id
600
     *
601
     * @return bool true if user can be deleted
602
     *
603
     * @assert (null) === false
604
     * @assert (-1) === false
605
     * @assert ('abc') === false
606
     */
607
    public static function canDeleteUser($user_id)
608
    {
609
        $deny = api_get_configuration_value('deny_delete_users');
610
611
        if ($deny) {
612
            return false;
613
        }
614
615
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
616
        $user_id = (int) $user_id;
617
618
        if (empty($user_id)) {
619
            return false;
620
        }
621
622
        $sql = "SELECT * FROM $table_course_user
623
                WHERE status = 1 AND user_id = ".$user_id;
624
        $res = Database::query($sql);
625
        while ($course = Database::fetch_object($res)) {
626
            $sql = "SELECT id FROM $table_course_user
627
                    WHERE status=1 AND c_id = ".intval($course->c_id);
628
            $res2 = Database::query($sql);
629
            if (1 == Database::num_rows($res2)) {
630
                return false;
631
            }
632
        }
633
634
        return true;
635
    }
636
637
    /**
638
     * Deletes a user from the system or marks the user as deleted based on the $destroy flag.
639
     * If $destroy is false, the user is only marked as deleted (e.g., active = -1) but not actually removed from the database.
640
     * This allows for the possibility of restoring the user at a later time. If $destroy is true, the user and all their relations
641
     * are permanently removed from the database.
642
     *
643
     * Note: When $destroy is false, the user's relations are not removed, allowing for potential restoration. When $destroy is true,
644
     * the function proceeds to remove all the user's relations, effectively cleaning up all references to the user in the system.
645
     */
646
    public static function delete_user(int $user_id, bool $destroy = false): bool
647
    {
648
        $user_id = (int) $user_id;
649
650
        if (empty($user_id)) {
651
            return false;
652
        }
653
654
        if (!self::canDeleteUser($user_id)) {
655
            return false;
656
        }
657
658
        $repository = Container::getUserRepository();
659
660
        /** @var User $user */
661
        $user = $repository->find($user_id);
662
663
        if (!$user) {
0 ignored issues
show
introduced by
$user is of type Chamilo\CoreBundle\Entity\User, thus it always evaluated to true.
Loading history...
664
            return false;
665
        }
666
667
        $repository->deleteUser($user, $destroy);
668
669
        if ($destroy) {
670
            Event::addEvent(
671
                LOG_USER_DELETE,
672
                LOG_USER_ID,
673
                $user_id,
674
                api_get_utc_datetime(),
675
                api_get_user_id()
676
            );
677
678
            Event::addEvent(
679
                LOG_USER_DELETE,
680
                LOG_USER_OBJECT,
681
                api_get_user_info($user_id),
682
                api_get_utc_datetime(),
683
                api_get_user_id()
684
            );
685
        }
686
687
        return true;
688
    }
689
690
    /**
691
     * Deletes users completely. Can be called either as:
692
     * - UserManager::delete_users(1, 2, 3); or
693
     * - UserManager::delete_users(array(1, 2, 3));.
694
     *
695
     * @param array|int $ids
696
     *
697
     * @return bool True if at least one user was successfuly deleted. False otherwise.
698
     *
699
     * @author Laurent Opprecht
700
     *
701
     * @uses \UserManager::delete_user() to actually delete each user
702
     * @assert (null) === false
703
     * @assert (-1) === false
704
     * @assert (array(-1)) === false
705
     */
706
    public static function delete_users($ids = [])
707
    {
708
        $result = false;
709
        $ids = is_array($ids) ? $ids : func_get_args();
710
        if (!is_array($ids) || 0 == count($ids)) {
711
            return false;
712
        }
713
        $ids = array_map('intval', $ids);
714
        foreach ($ids as $id) {
715
            if (empty($id) || $id < 1) {
716
                continue;
717
            }
718
            $deleted = self::delete_user($id);
719
            $result = $deleted || $result;
720
        }
721
722
        return $result;
723
    }
724
725
    /**
726
     * Disable users. Can be called either as:
727
     * - UserManager::deactivate_users(1, 2, 3);
728
     * - UserManager::deactivate_users(array(1, 2, 3));.
729
     *
730
     * @param array|int $ids
731
     *
732
     * @return bool
733
     *
734
     * @author Laurent Opprecht
735
     * @assert (null) === false
736
     * @assert (array(-1)) === false
737
     */
738
    public static function deactivate_users($ids = [])
739
    {
740
        if (empty($ids)) {
741
            return false;
742
        }
743
744
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
745
746
        $ids = is_array($ids) ? $ids : func_get_args();
747
        $ids = array_map('intval', $ids);
748
        $ids = implode(',', $ids);
749
750
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
751
        $r = Database::query($sql);
752
        if (false !== $r) {
753
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
754
755
            return true;
756
        }
757
758
        return false;
759
    }
760
761
    /**
762
     * Enable users. Can be called either as:
763
     * - UserManager::activate_users(1, 2, 3);
764
     * - UserManager::activate_users(array(1, 2, 3));.
765
     *
766
     * @param array|int IDs of the users to enable
767
     *
768
     * @return bool
769
     *
770
     * @author Laurent Opprecht
771
     * @assert (null) === false
772
     * @assert (array(-1)) === false
773
     */
774
    public static function activate_users($ids = [])
775
    {
776
        if (empty($ids)) {
777
            return false;
778
        }
779
780
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
781
782
        $ids = is_array($ids) ? $ids : func_get_args();
783
        $ids = array_map('intval', $ids);
784
        $ids = implode(',', $ids);
785
786
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
787
        $r = Database::query($sql);
788
        if (false !== $r) {
789
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
790
791
            return true;
792
        }
793
794
        return false;
795
    }
796
797
    /**
798
     * Update user information with all the parameters passed to this function.
799
     *
800
     * @param int    $user_id         The ID of the user to be updated
801
     * @param string $firstname       The user's firstname
802
     * @param string $lastname        The user's lastname
803
     * @param string $username        The user's username (login)
804
     * @param string $password        The user's password
805
     * @param string $auth_source     The authentication source (default: "platform")
806
     * @param string $email           The user's e-mail address
807
     * @param int    $status          The user's status
808
     * @param string $official_code   The user's official code (usually just an internal institutional code)
809
     * @param string $phone           The user's phone number
810
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
811
     * @param string $expiration_date The date at which this user will be automatically disabled
812
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
813
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
814
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
815
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
816
     * @param string $language        The language to which the user account will be set
817
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
818
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
819
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
820
     * @param string $address
821
     * @param array  $emailTemplate
822
     *
823
     * @return bool|int False on error, or the user ID if the user information was updated
824
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
825
     */
826
    public static function update_user(
827
        $user_id,
828
        $firstname,
829
        $lastname,
830
        $username,
831
        $password,
832
        $auth_source,
833
        $email,
834
        $status,
835
        $official_code,
836
        $phone,
837
        $picture_uri,
838
        $expiration_date,
839
        $active,
840
        $creator_id = null,
841
        $hr_dept_id = 0,
842
        $extra = null,
843
        $language = 'en_US',
844
        $encrypt_method = '',
845
        $send_email = false,
846
        $reset_password = 0,
847
        $address = null,
848
        $emailTemplate = []
849
    ) {
850
        $original_password = $password;
851
        $user_id = (int) $user_id;
852
        $creator_id = (int) $creator_id;
853
854
        if (empty($user_id)) {
855
            return false;
856
        }
857
858
        $userManager = self::getRepository();
859
        $user = api_get_user_entity($user_id);
860
861
        if (null === $user) {
862
            return false;
863
        }
864
865
        if (0 == $reset_password) {
866
            $password = null;
867
            $auth_source = $user->getAuthSource();
868
        } elseif (1 == $reset_password) {
869
            $original_password = $password = api_generate_password();
870
            $auth_source = PLATFORM_AUTH_SOURCE;
871
        } elseif (2 == $reset_password) {
872
            $password = $password;
873
            $auth_source = PLATFORM_AUTH_SOURCE;
874
        } elseif (3 == $reset_password) {
875
            $password = $password;
876
            $auth_source = $auth_source;
877
        }
878
879
        // Checking the user language
880
        $languages = array_keys(api_get_languages());
881
        if (!in_array($language, $languages)) {
882
            $language = api_get_setting('platformLanguage');
883
        }
884
885
        $change_active = 0;
886
        $isUserActive = $user->isActive();
887
        if ($active != USER_SOFT_DELETED) {
888
            if ($isUserActive != $active) {
889
                $change_active = 1;
890
            }
891
        }
892
893
        $originalUsername = $user->getUsername();
894
895
        // If username is different from original then check if it exists.
896
        if ($originalUsername !== $username) {
897
            $available = self::is_username_available($username);
898
            if (false === $available) {
899
                return false;
900
            }
901
        }
902
903
        if (!empty($expiration_date)) {
904
            $expiration_date = api_get_utc_datetime($expiration_date);
905
            $expiration_date = new \DateTime($expiration_date, new DateTimeZone('UTC'));
906
        }
907
908
        $previousStatus = $user->getStatus();
909
        $previousRole = $user->getRoleFromStatus($previousStatus);
910
911
        $user
912
            ->removeRole($previousRole)
913
            ->setLastname($lastname)
914
            ->setFirstname($firstname)
915
            ->setUsername($username)
916
            ->setStatus($status)
917
            ->setAuthSource($auth_source)
918
            ->setLocale($language)
919
            ->setEmail($email)
920
            ->setOfficialCode($official_code)
921
            ->setPhone($phone)
922
            ->setAddress($address)
923
            ->setExpirationDate($expiration_date)
924
            ->setActive($active)
925
            ->setHrDeptId((int) $hr_dept_id)
926
        ;
927
928
        if (!is_null($password)) {
929
            $user->setPlainPassword($password);
930
        }
931
932
        $user->setRoleFromStatus($status);
933
        $userManager->updateUser($user, true);
934
        Event::addEvent(LOG_USER_UPDATE, LOG_USER_ID, $user_id);
935
936
        if (1 == $change_active) {
937
            $event_title = LOG_USER_DISABLE;
938
            if (1 == $active) {
939
                $event_title = LOG_USER_ENABLE;
940
            }
941
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
942
        }
943
944
        if (is_array($extra) && count($extra) > 0) {
945
            $res = true;
946
            foreach ($extra as $fname => $fvalue) {
947
                $res = $res && self::update_extra_field_value(
948
                    $user_id,
949
                    $fname,
950
                    $fvalue
951
                );
952
            }
953
        }
954
955
        if (!empty($email) && $send_email) {
956
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
957
            $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
0 ignored issues
show
Bug introduced by
Are you sure api_get_setting('siteName') of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

957
            $emailsubject = '['./** @scrutinizer ignore-type */ api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
Loading history...
958
            $sender_name = api_get_person_name(
959
                api_get_setting('administratorName'),
960
                api_get_setting('administratorSurname'),
961
                null,
962
                PERSON_NAME_EMAIL_ADDRESS
963
            );
964
            $email_admin = api_get_setting('emailAdministrator');
965
            $access_url_id = api_get_current_access_url_id();
966
            $url = api_get_access_url($access_url_id);
967
            $url = $url['url'];
968
969
            $tplContent = new Template(
970
                null,
971
                false,
972
                false,
973
                false,
974
                false,
975
                false
976
            );
977
            // variables for the default template
978
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
979
            $tplContent->assign('login_name', $username);
980
981
            $originalPassword = '';
982
            if ($reset_password > 0) {
983
                $originalPassword = stripslashes($original_password);
984
            }
985
            $tplContent->assign('original_password', $originalPassword);
986
            $tplContent->assign('portal_url', $url);
987
988
            $emailBody = $tplContent->fetch('@ChamiloCore/Mailer/Legacy/user_edit_content.html.twig');
989
990
            $mailTemplateManager = new MailTemplateManager();
991
992
            if (!empty($emailTemplate) &&
993
                isset($emailTemplate['user_edit_content.tpl']) &&
994
                !empty($emailTemplate['user_edit_content.tpl'])
995
            ) {
996
                $userInfo = api_get_user_info($user_id);
997
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], $userInfo);
998
            }
999
1000
            $creatorInfo = api_get_user_info($creator_id);
1001
            $creatorEmail = $creatorInfo['email'] ?? '';
1002
1003
            api_mail_html(
1004
                $recipient_name,
1005
                $email,
1006
                $emailsubject,
1007
                $emailBody,
1008
                $sender_name,
1009
                $email_admin,
1010
                null,
1011
                null,
1012
                null,
1013
                null,
1014
                $creatorEmail
1015
            );
1016
        }
1017
1018
        return $user->getId();
1019
    }
1020
1021
    /**
1022
     * Disables a user.
1023
     *
1024
     * @param int User id
1025
     *
1026
     * @return bool
1027
     *
1028
     * @uses \UserManager::change_active_state() to actually disable the user
1029
     * @assert (0) === false
1030
     */
1031
    public static function disable($user_id)
1032
    {
1033
        if (empty($user_id)) {
1034
            return false;
1035
        }
1036
        self::change_active_state($user_id, 0);
1037
1038
        return true;
1039
    }
1040
1041
    /**
1042
     * Enable a user.
1043
     *
1044
     * @param int User id
1045
     *
1046
     * @return bool
1047
     *
1048
     * @uses \UserManager::change_active_state() to actually disable the user
1049
     * @assert (0) === false
1050
     */
1051
    public static function enable($user_id)
1052
    {
1053
        if (empty($user_id)) {
1054
            return false;
1055
        }
1056
        self::change_active_state($user_id, 1);
1057
1058
        return true;
1059
    }
1060
1061
    /**
1062
     * Returns the user's id based on the original id and field name in
1063
     * the extra fields. Returns 0 if no user was found. This function is
1064
     * mostly useful in the context of a web services-based sinchronization.
1065
     *
1066
     * @param string Original user id
1067
     * @param string Original field name
1068
     *
1069
     * @return int User id
1070
     * @assert ('0','---') === 0
1071
     */
1072
    public static function get_user_id_from_original_id(
1073
        $original_user_id_value,
1074
        $original_user_id_name
1075
    ) {
1076
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1077
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1078
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1079
1080
        $original_user_id_name = Database::escape_string($original_user_id_name);
1081
        $original_user_id_value = Database::escape_string($original_user_id_value);
1082
1083
        $sql = "SELECT item_id as user_id
1084
                FROM $t_uf uf
1085
                INNER JOIN $t_ufv ufv
1086
                ON ufv.field_id = uf.id
1087
                WHERE
1088
                    variable = '$original_user_id_name' AND
1089
                    field_value = '$original_user_id_value' AND
1090
                    item_type = $extraFieldType
1091
                ";
1092
        $res = Database::query($sql);
1093
        $row = Database::fetch_object($res);
1094
        if ($row) {
1095
            return $row->user_id;
1096
        }
1097
1098
        return 0;
1099
    }
1100
1101
    /**
1102
     * Check if a username is available.
1103
     *
1104
     * @param string $username the wanted username
1105
     *
1106
     * @return bool true if the wanted username is available
1107
     * @assert ('') === false
1108
     * @assert ('xyzxyzxyz') === true
1109
     */
1110
    public static function is_username_available($username)
1111
    {
1112
        if (empty($username)) {
1113
            return false;
1114
        }
1115
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1116
        $sql = "SELECT username FROM $table_user
1117
                WHERE username = '".Database::escape_string($username)."'";
1118
        $res = Database::query($sql);
1119
1120
        return 0 == Database::num_rows($res);
1121
    }
1122
1123
    /**
1124
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1125
     *
1126
     * @param string $firstname the first name of the user
1127
     * @param string $lastname  the last name of the user
1128
     *
1129
     * @return string suggests a username that contains only ASCII-letters and digits,
1130
     *                without check for uniqueness within the system
1131
     *
1132
     * @author Julio Montoya Armas
1133
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1134
     * @assert ('','') === false
1135
     * @assert ('a','b') === 'ab'
1136
     */
1137
    public static function create_username($firstname, $lastname)
1138
    {
1139
        if (empty($firstname) && empty($lastname)) {
1140
            return false;
1141
        }
1142
1143
        // The first letter only.
1144
        $firstname = api_substr(
1145
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1146
            0,
1147
            1
1148
        );
1149
        //Looking for a space in the lastname
1150
        $pos = api_strpos($lastname, ' ');
1151
        if (false !== $pos) {
1152
            $lastname = api_substr($lastname, 0, $pos);
1153
        }
1154
1155
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1156
        $username = $firstname.$lastname;
1157
        if (empty($username)) {
1158
            $username = 'user';
1159
        }
1160
1161
        $username = URLify::transliterate($username);
1162
1163
        return strtolower(substr($username, 0, User::USERNAME_MAX_LENGTH - 3));
1164
    }
1165
1166
    /**
1167
     * Creates a unique username, using:
1168
     * 1. the first name and the last name of a user;
1169
     * 2. an already created username but not checked for uniqueness yet.
1170
     *
1171
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1172
     *                          parameter is treated as username which is to be checked f
1173
     *                          or uniqueness and to be modified when it is necessary.
1174
     * @param string $lastname  the last name of the user
1175
     *
1176
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1177
     *                Note: When the method is called several times with same parameters,
1178
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1179
     *
1180
     * @author Ivan Tcholakov, 2009
1181
     */
1182
    public static function create_unique_username($firstname, $lastname = null)
1183
    {
1184
        if (is_null($lastname)) {
1185
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1186
            // For making this method tolerant of mistakes,
1187
            // let us transliterate and purify the suggested input username anyway.
1188
            // So, instead of the sentence $username = $firstname; we place the following:
1189
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1190
        } else {
1191
            $username = self::create_username($firstname, $lastname);
1192
        }
1193
        if (!self::is_username_available($username)) {
1194
            $i = 2;
1195
            $temp_username = substr($username, 0, User::USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1196
            while (!self::is_username_available($temp_username)) {
1197
                $i++;
1198
                $temp_username = substr($username, 0, User::USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1199
            }
1200
            $username = $temp_username;
1201
        }
1202
1203
        $username = URLify::transliterate($username);
1204
1205
        return $username;
1206
    }
1207
1208
    /**
1209
     * Modifies a given username accordingly to the specification for valid characters and length.
1210
     *
1211
     * @param $username string          The input username
1212
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1213
     *                     otherwise compliance may be partial. The default value is FALSE.
1214
     *
1215
     * @return string the resulting purified username
1216
     */
1217
    public static function purify_username($username, $strict = false)
1218
    {
1219
        if ($strict) {
1220
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1221
            // into ASCII letters in order they not to be totally removed.
1222
            // 2. Applying the strict purifier.
1223
            // 3. Length limitation.
1224
            $return = 'true' === api_get_setting('login_is_email') ? substr(preg_replace(USERNAME_PURIFIER_MAIL, '', $username), 0, User::USERNAME_MAX_LENGTH) : substr(preg_replace(USERNAME_PURIFIER, '', $username), 0, User::USERNAME_MAX_LENGTH);
1225
            $return = URLify::transliterate($return);
1226
1227
            // We want everything transliterate() does except converting @ to '(at)'. This is a hack to avoid this.
1228
            $return = str_replace(' (at) ', '@', $return);
1229
1230
            return $return;
1231
        }
1232
1233
        // 1. Applying the shallow purifier.
1234
        // 2. Length limitation.
1235
        return substr(
1236
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1237
            0,
1238
            User::USERNAME_MAX_LENGTH
1239
        );
1240
    }
1241
1242
    /**
1243
     * Checks whether the user id exists in the database.
1244
     *
1245
     * @param int $userId User id
1246
     *
1247
     * @return bool True if user id was found, false otherwise
1248
     */
1249
    public static function is_user_id_valid($userId)
1250
    {
1251
        $resultData = Database::select(
1252
            'COUNT(1) AS count',
1253
            Database::get_main_table(TABLE_MAIN_USER),
1254
            [
1255
                'where' => ['id = ?' => (int) $userId],
1256
            ],
1257
            'first'
1258
        );
1259
1260
        if (false === $resultData) {
1261
            return false;
1262
        }
1263
1264
        return $resultData['count'] > 0;
1265
    }
1266
1267
    /**
1268
     * Checks whether a given username matches to the specification strictly.
1269
     * The empty username is assumed here as invalid.
1270
     * Mostly this function is to be used in the user interface built-in validation routines
1271
     * for providing feedback while usernames are enterd manually.
1272
     *
1273
     * @param string $username the input username
1274
     *
1275
     * @return bool returns TRUE if the username is valid, FALSE otherwise
1276
     */
1277
    public static function is_username_valid($username)
1278
    {
1279
        return !empty($username) && $username == self::purify_username($username, true);
1280
    }
1281
1282
    /**
1283
     * Checks whether a username is empty. If the username contains whitespace characters,
1284
     * such as spaces, tabulators, newlines, etc.,
1285
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
1286
     *
1287
     * @param string $username the given username
1288
     *
1289
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1290
     */
1291
    public static function is_username_empty($username)
1292
    {
1293
        return 0 == strlen(self::purify_username($username, false));
1294
    }
1295
1296
    /**
1297
     * Checks whether a username is too long or not.
1298
     *
1299
     * @param string $username the given username, it should contain only ASCII-letters and digits
1300
     *
1301
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1302
     */
1303
    public static function is_username_too_long($username)
1304
    {
1305
        return strlen($username) > User::USERNAME_MAX_LENGTH;
1306
    }
1307
1308
    /**
1309
     * Get the users by ID.
1310
     *
1311
     * @param array  $ids    student ids
1312
     * @param bool   $active
1313
     * @param string $order
1314
     * @param string $limit
1315
     *
1316
     * @return array $result student information
1317
     */
1318
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
1319
    {
1320
        if (empty($ids)) {
1321
            return [];
1322
        }
1323
1324
        $ids = is_array($ids) ? $ids : [$ids];
1325
        $ids = array_map('intval', $ids);
1326
        $ids = implode(',', $ids);
1327
1328
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
1329
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
1330
        if (!is_null($active)) {
1331
            $sql .= ' AND active='.($active ? '1' : '0');
1332
        }
1333
1334
        if (!is_null($order)) {
1335
            $order = Database::escape_string($order);
1336
            $sql .= ' ORDER BY '.$order;
1337
        }
1338
1339
        if (!is_null($limit)) {
1340
            $limit = Database::escape_string($limit);
1341
            $sql .= ' LIMIT '.$limit;
1342
        }
1343
1344
        $rs = Database::query($sql);
1345
        $result = [];
1346
        while ($row = Database::fetch_array($rs)) {
1347
            $result[] = $row;
1348
        }
1349
1350
        return $result;
1351
    }
1352
1353
    /**
1354
     * Get a list of users of which the given conditions match with an = 'cond'.
1355
     *
1356
     * @param array $conditions a list of condition (example : status=>STUDENT)
1357
     * @param array $order_by   a list of fields on which sort
1358
     *
1359
     * @return array an array with all users of the platform
1360
     *
1361
     * @todo security filter order by
1362
     */
1363
    public static function get_user_list(
1364
        $conditions = [],
1365
        $order_by = [],
1366
        $limit_from = false,
1367
        $limit_to = false,
1368
        $idCampus = null
1369
    ) {
1370
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1371
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1372
        $return_array = [];
1373
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1374
1375
        if ($idCampus) {
1376
            $urlId = $idCampus;
1377
        } else {
1378
            $urlId = api_get_current_access_url_id();
1379
        }
1380
        $sql .= " INNER JOIN $userUrlTable url_user
1381
            ON (user.id = url_user.user_id)
1382
            WHERE url_user.access_url_id = $urlId";
1383
1384
        if (count($conditions) > 0) {
1385
            foreach ($conditions as $field => $value) {
1386
                $field = Database::escape_string($field);
1387
                $value = Database::escape_string($value);
1388
                $sql .= " AND $field = '$value'";
1389
            }
1390
        }
1391
1392
        if (count($order_by) > 0) {
1393
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1394
        }
1395
1396
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1397
            $limit_from = (int) $limit_from;
1398
            $limit_to = (int) $limit_to;
1399
            $sql .= " LIMIT $limit_from, $limit_to";
1400
        }
1401
        $sql_result = Database::query($sql);
1402
        while ($result = Database::fetch_array($sql_result)) {
1403
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1404
            $return_array[] = $result;
1405
        }
1406
1407
        return $return_array;
1408
    }
1409
1410
    public static function getUserListExtraConditions(
1411
        $conditions = [],
1412
        $order_by = [],
1413
        $limit_from = false,
1414
        $limit_to = false,
1415
        $idCampus = null,
1416
        $extraConditions = '',
1417
        $getCount = false
1418
    ) {
1419
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1420
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1421
        $return_array = [];
1422
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1423
1424
        if ($getCount) {
1425
            $sql = "SELECT count(user.id) count FROM $user_table user ";
1426
        }
1427
1428
        if ($idCampus) {
1429
            $urlId = $idCampus;
1430
        } else {
1431
            $urlId = api_get_current_access_url_id();
1432
        }
1433
        $sql .= " INNER JOIN $userUrlTable url_user
1434
            ON (user.user_id = url_user.user_id)
1435
            WHERE url_user.access_url_id = $urlId";
1436
1437
        $sql .= " AND status <> ".ANONYMOUS." ";
1438
1439
        if (count($conditions) > 0) {
1440
            foreach ($conditions as $field => $value) {
1441
                $field = Database::escape_string($field);
1442
                $value = Database::escape_string($value);
1443
                $sql .= " AND $field = '$value'";
1444
            }
1445
        }
1446
1447
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
1448
1449
        if (!empty($order_by) && count($order_by) > 0) {
1450
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1451
        }
1452
1453
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1454
            $limit_from = (int) $limit_from;
1455
            $limit_to = (int) $limit_to;
1456
            $sql .= " LIMIT $limit_from, $limit_to";
1457
        }
1458
1459
        $sql_result = Database::query($sql);
1460
1461
        if ($getCount) {
1462
            $result = Database::fetch_array($sql_result);
1463
1464
            return $result['count'];
1465
        }
1466
1467
        while ($result = Database::fetch_array($sql_result)) {
1468
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1469
            $return_array[] = $result;
1470
        }
1471
1472
        return $return_array;
1473
    }
1474
1475
    /**
1476
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
1477
     *
1478
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
1479
     * @param array  $order_by         a list of fields on which sort
1480
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
1481
     * @param string $condition        Whether we want the filters to be combined by AND or OR
1482
     * @param array  $onlyThisUserList
1483
     *
1484
     * @return array an array with all users of the platform
1485
     *
1486
     * @todo optional course code parameter, optional sorting parameters...
1487
     * @todo security filter order_by
1488
     */
1489
    public static function getUserListLike(
1490
        $conditions = [],
1491
        $order_by = [],
1492
        $simple_like = false,
1493
        $condition = 'AND',
1494
        $onlyThisUserList = []
1495
    ) {
1496
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1497
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1498
        $return_array = [];
1499
        $sql_query = "SELECT user.id FROM $user_table user ";
1500
        $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
1501
        $sql_query .= ' WHERE 1 = 1 ';
1502
        if (count($conditions) > 0) {
1503
            $temp_conditions = [];
1504
            foreach ($conditions as $field => $value) {
1505
                $field = Database::escape_string($field);
1506
                $value = Database::escape_string($value);
1507
                if ($simple_like) {
1508
                    $temp_conditions[] = $field." LIKE '$value%'";
1509
                } else {
1510
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
1511
                }
1512
            }
1513
            if (!empty($temp_conditions)) {
1514
                $sql_query .= ' AND '.implode(' '.$condition.' ', $temp_conditions);
1515
            }
1516
1517
            $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1518
        } else {
1519
            $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1520
        }
1521
1522
        if (!empty($onlyThisUserList)) {
1523
            $onlyThisUserListToString = implode("','", $onlyThisUserList);
1524
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
1525
        }
1526
1527
        if (count($order_by) > 0) {
1528
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1529
        }
1530
1531
        $sql_result = Database::query($sql_query);
1532
        while ($result = Database::fetch_array($sql_result)) {
1533
            $userInfo = api_get_user_info($result['id']);
1534
            $return_array[] = $userInfo;
1535
        }
1536
1537
        return $return_array;
1538
    }
1539
1540
    /**
1541
     * Get user path from user ID (returns an array).
1542
     * The return format is a complete path to a folder ending with "/"
1543
     * In case the first level of subdirectory of users/ does not exist, the
1544
     * function will attempt to create it. Probably not the right place to do it
1545
     * but at least it avoids headaches in many other places.
1546
     *
1547
     * @param int    $id   User ID
1548
     * @param string $type Type of path to return (can be 'system', 'web', 'last')
1549
     *
1550
     * @return string User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
1551
     */
1552
    public static function getUserPathById($id, $type)
1553
    {
1554
        $id = (int) $id;
1555
        if (!$id) {
1556
            return null;
1557
        }
1558
1559
        $userPath = "users/$id/";
1560
        if (api_get_setting('split_users_upload_directory') === 'true') {
1561
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
1562
            // In exceptional cases, on some portals, the intermediate base user
1563
            // directory might not have been created. Make sure it is before
1564
            // going further.
1565
1566
            $rootPath = api_get_path(SYS_PATH).'../app/upload/users/'.substr((string) $id, 0, 1);
1567
            if (!is_dir($rootPath)) {
1568
                $perm = api_get_permissions_for_new_directories();
1569
                try {
1570
                    mkdir($rootPath, $perm);
1571
                } catch (Exception $e) {
1572
                    error_log($e->getMessage());
1573
                }
1574
            }
1575
        }
1576
        switch ($type) {
1577
            case 'system': // Base: absolute system path.
1578
                $userPath = api_get_path(SYS_PATH).'../app/upload/'.$userPath;
1579
                break;
1580
            case 'web': // Base: absolute web path.
1581
                $userPath = api_get_path(WEB_PATH).'../app/upload/'.$userPath;
1582
                break;
1583
            case 'last': // Only the last part starting with users/
1584
                break;
1585
        }
1586
1587
        return $userPath;
1588
    }
1589
1590
    /**
1591
     * Gets the current user image.
1592
     *
1593
     * @param string $userId
1594
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
1595
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
1596
     * @param bool   $addRandomId
1597
     * @param array  $userInfo    to avoid query the DB
1598
     *
1599
     * @todo add gravatar support
1600
     * @todo replace $userId with User entity
1601
     *
1602
     * @return string
1603
     */
1604
    public static function getUserPicture(
1605
        $userId,
1606
        int $size = USER_IMAGE_SIZE_MEDIUM,
1607
        $addRandomId = true,
1608
        $userInfo = []
1609
    ) {
1610
        $user = api_get_user_entity($userId);
1611
        $illustrationRepo = Container::getIllustrationRepository();
1612
1613
        switch ($size) {
1614
            case USER_IMAGE_SIZE_SMALL:
1615
                $width = 32;
1616
                break;
1617
            case USER_IMAGE_SIZE_MEDIUM:
1618
                $width = 64;
1619
                break;
1620
            case USER_IMAGE_SIZE_BIG:
1621
                $width = 128;
1622
                break;
1623
            case USER_IMAGE_SIZE_ORIGINAL:
1624
            default:
1625
                $width = 0;
1626
                break;
1627
        }
1628
1629
        $url = $illustrationRepo->getIllustrationUrl($user);
1630
        $params = [];
1631
        if (!empty($width)) {
1632
            $params['w'] = $width;
1633
        }
1634
1635
        if ($addRandomId) {
1636
            $params['rand'] = uniqid('u_', true);
1637
        }
1638
1639
        $paramsToString = '';
1640
        if (!empty($params)) {
1641
            $paramsToString = '?'.http_build_query($params);
1642
        }
1643
1644
        return $url.$paramsToString;
1645
1646
        /*
1647
        // Make sure userInfo is defined. Otherwise, define it!
1648
        if (empty($userInfo) || !is_array($userInfo) || 0 == count($userInfo)) {
1649
            if (empty($user_id)) {
1650
                return '';
1651
            } else {
1652
                $userInfo = api_get_user_info($user_id);
1653
            }
1654
        }
1655
1656
        $imageWebPath = self::get_user_picture_path_by_id(
1657
            $user_id,
1658
            'web',
1659
            $userInfo
1660
        );
1661
        $pictureWebFile = $imageWebPath['file'];
1662
        $pictureWebDir = $imageWebPath['dir'];
1663
1664
        $pictureAnonymousSize = '128';
1665
        $gravatarSize = 22;
1666
        $realSizeName = 'small_';
1667
1668
        switch ($size) {
1669
            case USER_IMAGE_SIZE_SMALL:
1670
                $pictureAnonymousSize = '32';
1671
                $realSizeName = 'small_';
1672
                $gravatarSize = 32;
1673
                break;
1674
            case USER_IMAGE_SIZE_MEDIUM:
1675
                $pictureAnonymousSize = '64';
1676
                $realSizeName = 'medium_';
1677
                $gravatarSize = 64;
1678
                break;
1679
            case USER_IMAGE_SIZE_ORIGINAL:
1680
                $pictureAnonymousSize = '128';
1681
                $realSizeName = '';
1682
                $gravatarSize = 128;
1683
                break;
1684
            case USER_IMAGE_SIZE_BIG:
1685
                $pictureAnonymousSize = '128';
1686
                $realSizeName = 'big_';
1687
                $gravatarSize = 128;
1688
                break;
1689
        }
1690
1691
        $gravatarEnabled = api_get_setting('gravatar_enabled');
1692
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
1693
        if ('unknown.jpg' == $pictureWebFile || empty($pictureWebFile)) {
1694
            if ('true' === $gravatarEnabled) {
1695
                $file = self::getGravatar(
1696
                    $imageWebPath['email'],
1697
                    $gravatarSize,
1698
                    api_get_setting('gravatar_type')
1699
                );
1700
1701
                if ($addRandomId) {
1702
                    $file .= '&rand='.uniqid();
1703
                }
1704
1705
                return $file;
1706
            }
1707
1708
            return $anonymousPath;
1709
        }
1710
1711
        if ($addRandomId) {
1712
            $picture .= '?rand='.uniqid();
1713
        }
1714
1715
        return $picture;*/
1716
    }
1717
1718
    /**
1719
     * Creates new user photos in various sizes of a user, or deletes user photos.
1720
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
1721
     *
1722
     * @param int    $user_id the user internal identification number
1723
     * @param string $file    The common file name for the newly created photos.
1724
     *                        It will be checked and modified for compatibility with the file system.
1725
     *                        If full name is provided, path component is ignored.
1726
     *                        If an empty name is provided, then old user photos are deleted only,
1727
     *
1728
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
1729
     *
1730
     * @param string $source_file    the full system name of the image from which user photos will be created
1731
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
1732
     *
1733
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
1734
     *              When deletion is requested returns empty string.
1735
     *              In case of internal error or negative validation returns FALSE.
1736
     */
1737
    public static function update_user_picture($userId, UploadedFile $file, string $crop = '')
1738
    {
1739
        if (empty($userId) || empty($file)) {
1740
            return false;
1741
        }
1742
1743
        $repo = Container::getUserRepository();
1744
        $user = $repo->find($userId);
1745
        if ($user) {
1746
            $repoIllustration = Container::getIllustrationRepository();
1747
            $repoIllustration->addIllustration($user, $user, $file, $crop);
1748
        }
1749
    }
1750
1751
    /**
1752
     * Deletes user photos.
1753
     *
1754
     * @param int $userId the user internal identification number
1755
     *
1756
     * @return mixed returns empty string on success, FALSE on error
1757
     */
1758
    public static function deleteUserPicture($userId)
1759
    {
1760
        $repo = Container::getUserRepository();
1761
        $user = $repo->find($userId);
1762
        if ($user) {
1763
            $illustrationRepo = Container::getIllustrationRepository();
1764
            $illustrationRepo->deleteIllustration($user);
1765
        }
1766
    }
1767
1768
    /**
1769
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
1770
     * doesn't have any.
1771
     *
1772
     * If there has been a request to remove a production, the function will return
1773
     * without building the list unless forced to do so by the optional second
1774
     * parameter. This increases performance by avoiding to read through the
1775
     * productions on the filesystem before the removal request has been carried
1776
     * out because they'll have to be re-read afterwards anyway.
1777
     *
1778
     * @deprecated This method is being removed from chamilo 2.0
1779
     * @param int  $user_id    User id
1780
     * @param bool $force      Optional parameter to force building after a removal request
1781
     * @param bool $showDelete
1782
     *
1783
     * @return string A string containing the XHTML code to display the production list, or FALSE
1784
     */
1785
    public static function build_production_list($user_id, $force = false, $showDelete = false)
1786
    {
1787
        if (!$force && !empty($_POST['remove_production'])) {
1788
            return true; // postpone reading from the filesystem
1789
        }
1790
1791
        $productions = self::get_user_productions($user_id);
1792
1793
        if (empty($productions)) {
1794
            return false;
1795
        }
1796
1797
        return false;
1798
1799
        $production_dir = self::getUserPathById($user_id, 'web');
0 ignored issues
show
Unused Code introduced by
$production_dir = self::...thById($user_id, 'web') is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1800
        $del_image = Display::returnIconPath('delete.png');
1801
        $add_image = Display::returnIconPath('archive.png');
1802
        $del_text = get_lang('Delete');
1803
        $production_list = '';
1804
        if (count($productions) > 0) {
1805
            $production_list = '<div class="files-production"><ul id="productions">';
1806
            foreach ($productions as $file) {
1807
                $production_list .= '<li>
1808
                    <img src="'.$add_image.'" />
1809
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
1810
                        '.htmlentities($file).'
1811
                    </a>';
1812
                if ($showDelete) {
1813
                    $production_list .= '&nbsp;&nbsp;
1814
                        <input
1815
                            style="width:16px;"
1816
                            type="image"
1817
                            name="remove_production['.urlencode($file).']"
1818
                            src="'.$del_image.'"
1819
                            alt="'.$del_text.'"
1820
                            title="'.$del_text.' '.htmlentities($file).'"
1821
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
1822
                }
1823
            }
1824
            $production_list .= '</ul></div>';
1825
        }
1826
1827
        return $production_list;
1828
    }
1829
1830
    /**
1831
     * Returns an array with the user's productions.
1832
     *
1833
     * @param int $user_id User id
1834
     *
1835
     * @return array An array containing the user's productions
1836
     */
1837
    public static function get_user_productions($user_id)
1838
    {
1839
        return [];
1840
1841
        $production_repository = self::getUserPathById($user_id, 'system');
0 ignored issues
show
Unused Code introduced by
$production_repository =...yId($user_id, 'system') is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1842
        $productions = [];
1843
1844
        if (is_dir($production_repository)) {
1845
            $handle = opendir($production_repository);
1846
            while ($file = readdir($handle)) {
1847
                if ('.' == $file ||
1848
                    '..' == $file ||
1849
                    '.htaccess' == $file ||
1850
                    is_dir($production_repository.$file)
1851
                ) {
1852
                    // skip current/parent directory and .htaccess
1853
                    continue;
1854
                }
1855
1856
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
1857
                    // User's photos should not be listed as productions.
1858
                    continue;
1859
                }
1860
                $productions[] = $file;
1861
            }
1862
        }
1863
1864
        return $productions;
1865
    }
1866
1867
    /**
1868
     * Remove a user production.
1869
     *
1870
     * @param int    $user_id    User id
1871
     * @param string $production The production to remove
1872
     *
1873
     * @return bool
1874
     */
1875
    public static function remove_user_production($user_id, $production)
1876
    {
1877
        throw new Exception('remove_user_production');
1878
        /*$production_path = self::get_user_picture_path_by_id($user_id, 'system');
1879
        $production_file = $production_path['dir'].$production;
1880
        if (is_file($production_file)) {
1881
            unlink($production_file);
1882
1883
            return true;
1884
        }
1885
1886
        return false;*/
1887
    }
1888
1889
    /**
1890
     * Update an extra field value for a given user.
1891
     *
1892
     * @param int    $userId   User ID
1893
     * @param string $variable Field variable name
1894
     * @param string $value    Field value
1895
     *
1896
     * @return bool true if field updated, false otherwise
1897
     */
1898
    public static function update_extra_field_value($userId, $variable, $value = '')
1899
    {
1900
        $extraFieldValue = new ExtraFieldValue('user');
1901
        $params = [
1902
            'item_id' => $userId,
1903
            'variable' => $variable,
1904
            'field_value' => $value,
1905
        ];
1906
1907
        return $extraFieldValue->save($params);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraFieldValue->save($params) also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
1908
    }
1909
1910
    /**
1911
     * Get an array of extra fields with field details (type, default value and options).
1912
     *
1913
     * @param    int    Offset (from which row)
1914
     * @param    int    Number of items
1915
     * @param    int    Column on which sorting is made
1916
     * @param    string    Sorting direction
1917
     * @param    bool    Optional. Whether we get all the fields or just the visible ones
0 ignored issues
show
Documentation Bug introduced by
The doc comment Optional. at position 0 could not be parsed: Unknown type name 'Optional.' at position 0 in Optional..
Loading history...
1918
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
1919
     *
1920
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
1921
     */
1922
    public static function get_extra_fields(
1923
        $from = 0,
1924
        $number_of_items = 0,
1925
        $column = 5,
1926
        $direction = 'ASC',
1927
        $all_visibility = true,
1928
        $field_filter = null
1929
    ) {
1930
        $fields = [];
1931
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1932
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
1933
        $columns = [
1934
            'id',
1935
            'variable',
1936
            'value_type',
1937
            'display_text',
1938
            'default_value',
1939
            'field_order',
1940
            'filter',
1941
        ];
1942
        $column = (int) $column;
1943
        $sort_direction = '';
1944
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
1945
            $sort_direction = strtoupper($direction);
1946
        }
1947
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1948
        $sqlf = "SELECT * FROM $t_uf WHERE item_type = $extraFieldType ";
1949
        if (!$all_visibility) {
1950
            $sqlf .= " AND visible_to_self = 1 ";
1951
        }
1952
        if (!is_null($field_filter)) {
1953
            $field_filter = (int) $field_filter;
1954
            $sqlf .= " AND filter = $field_filter ";
1955
        }
1956
        $sqlf .= " ORDER BY `".$columns[$column]."` $sort_direction ";
1957
        if (0 != $number_of_items) {
1958
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
1959
        }
1960
        $resf = Database::query($sqlf);
1961
        if (Database::num_rows($resf) > 0) {
1962
            while ($rowf = Database::fetch_array($resf)) {
1963
                $fields[$rowf['id']] = [
1964
                    0 => $rowf['id'],
1965
                    1 => $rowf['variable'],
1966
                    2 => $rowf['value_type'],
1967
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
1968
                    4 => $rowf['default_value'],
1969
                    5 => $rowf['field_order'],
1970
                    6 => $rowf['visible_to_self'],
1971
                    7 => $rowf['changeable'],
1972
                    8 => $rowf['filter'],
1973
                    9 => [],
1974
                    10 => '<a name="'.$rowf['id'].'"></a>',
1975
                ];
1976
1977
                $sqlo = "SELECT * FROM $t_ufo
1978
                         WHERE field_id = ".$rowf['id']."
1979
                         ORDER BY option_order ASC";
1980
                $reso = Database::query($sqlo);
1981
                if (Database::num_rows($reso) > 0) {
1982
                    while ($rowo = Database::fetch_array($reso)) {
1983
                        $fields[$rowf['id']][9][$rowo['id']] = [
1984
                            0 => $rowo['id'],
1985
                            1 => $rowo['option_value'],
1986
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
1987
                            3 => $rowo['option_order'],
1988
                        ];
1989
                    }
1990
                }
1991
            }
1992
        }
1993
1994
        return $fields;
1995
    }
1996
1997
    /**
1998
     * Creates a new extra field.
1999
     *
2000
     * @param string $variable    Field's internal variable name
2001
     * @param int    $fieldType   Field's type
2002
     * @param string $displayText Field's language var name
2003
     * @param string $default     Field's default value
2004
     *
2005
     * @return int
2006
     */
2007
    public static function create_extra_field(
2008
        $variable,
2009
        $valueType,
2010
        $displayText,
2011
        $default
2012
    ) {
2013
        $extraField = new ExtraField('user');
2014
        $params = [
2015
            'variable' => $variable,
2016
            'value_type' => $valueType,
2017
            'display_text' => $displayText,
2018
            'default_value' => $default,
2019
        ];
2020
2021
        return $extraField->save($params);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraField->save($params) also could return the type boolean which is incompatible with the documented return type integer.
Loading history...
2022
    }
2023
2024
    /**
2025
     * Check if a field is available.
2026
     *
2027
     * @param string $variable
2028
     *
2029
     * @return bool
2030
     */
2031
    public static function is_extra_field_available($variable)
2032
    {
2033
        $extraField = new ExtraField('user');
2034
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2035
2036
        return !empty($data) ? true : false;
2037
    }
2038
2039
    /**
2040
     * Gets user extra fields data.
2041
     *
2042
     * @param    int    User ID
2043
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2044
     * @param    bool    Whether to return invisible fields as well
2045
     * @param    bool    Whether to split multiple-selection fields or not
2046
     *
2047
     * @return array Array of fields => value for the given user
2048
     */
2049
    public static function get_extra_user_data(
2050
        $user_id,
2051
        $prefix = false,
2052
        $allVisibility = true,
2053
        $splitMultiple = false,
2054
        $fieldFilter = null
2055
    ) {
2056
        $user_id = (int) $user_id;
2057
2058
        if (empty($user_id)) {
2059
            return [];
2060
        }
2061
2062
        $extra_data = [];
2063
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2064
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2065
        $sql = "SELECT f.id as id, f.variable as fvar, f.value_type as type
2066
                FROM $t_uf f
2067
                WHERE
2068
                    item_type = ".EntityExtraField::USER_FIELD_TYPE."
2069
                ";
2070
        $filter_cond = '';
2071
2072
        if (!$allVisibility) {
2073
            if (isset($fieldFilter)) {
2074
                $fieldFilter = (int) $fieldFilter;
2075
                $filter_cond .= " AND filter = $fieldFilter ";
2076
            }
2077
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2078
        } else {
2079
            if (isset($fieldFilter)) {
2080
                $fieldFilter = (int) $fieldFilter;
2081
                $sql .= " AND filter = $fieldFilter ";
2082
            }
2083
        }
2084
2085
        $sql .= ' ORDER BY f.field_order';
2086
2087
        $res = Database::query($sql);
2088
        if (Database::num_rows($res) > 0) {
2089
            while ($row = Database::fetch_array($res)) {
2090
                if (self::USER_FIELD_TYPE_TAG == $row['type']) {
2091
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2092
                    $extra_data['extra_'.$row['fvar']] = $tags;
2093
                } else {
2094
                    $sqlu = "SELECT field_value as fval
2095
                            FROM $t_ufv
2096
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2097
                    $resu = Database::query($sqlu);
2098
                    // get default value
2099
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2100
                               WHERE id=".$row['id'];
2101
                    $res_df = Database::query($sql_df);
2102
2103
                    if (Database::num_rows($resu) > 0) {
2104
                        $rowu = Database::fetch_array($resu);
2105
                        $fval = $rowu['fval'];
2106
                        if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2107
                            $fval = explode(';', $rowu['fval']);
2108
                        }
2109
                    } else {
2110
                        $row_df = Database::fetch_array($res_df);
2111
                        $fval = $row_df['fval_df'];
2112
                    }
2113
                    // We get here (and fill the $extra_data array) even if there
2114
                    // is no user with data (we fill it with default values)
2115
                    if ($prefix) {
2116
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2117
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2118
                        } else {
2119
                            $extra_data['extra_'.$row['fvar']] = $fval;
2120
                        }
2121
                    } else {
2122
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2123
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2124
                        } else {
2125
                            $extra_data[$row['fvar']] = $fval;
2126
                        }
2127
                    }
2128
                }
2129
            }
2130
        }
2131
2132
        return $extra_data;
2133
    }
2134
2135
    /** Get extra user data by field.
2136
     * @param int    user ID
2137
     * @param string the internal variable name of the field
2138
     *
2139
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2140
     */
2141
    public static function get_extra_user_data_by_field(
2142
        $user_id,
2143
        $field_variable,
2144
        $prefix = false,
2145
        $all_visibility = true,
2146
        $splitmultiple = false
2147
    ) {
2148
        $user_id = (int) $user_id;
2149
2150
        if (empty($user_id)) {
2151
            return [];
2152
        }
2153
2154
        $extra_data = [];
2155
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2156
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2157
2158
        $sql = "SELECT f.id as id, f.variable as fvar, f.value_type as type
2159
                FROM $t_uf f
2160
                WHERE f.variable = '$field_variable' ";
2161
2162
        if (!$all_visibility) {
2163
            $sql .= " AND f.visible_to_self = 1 ";
2164
        }
2165
2166
        $sql .= " AND item_type = ".EntityExtraField::USER_FIELD_TYPE;
2167
        $sql .= " ORDER BY f.field_order ";
2168
2169
        $res = Database::query($sql);
2170
        if (Database::num_rows($res) > 0) {
2171
            while ($row = Database::fetch_array($res)) {
2172
                $sqlu = "SELECT field_value as fval FROM $t_ufv v
2173
                         INNER JOIN $t_uf f
2174
                         ON (v.field_id = f.id)
2175
                         WHERE
2176
                            item_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2177
                            field_id = ".$row['id']." AND
2178
                            item_id = ".$user_id;
2179
                $resu = Database::query($sqlu);
2180
                $fval = '';
2181
                if (Database::num_rows($resu) > 0) {
2182
                    $rowu = Database::fetch_array($resu);
2183
                    $fval = $rowu['fval'];
2184
                    if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2185
                        $fval = explode(';', $rowu['fval']);
2186
                    }
2187
                }
2188
                if ($prefix) {
2189
                    $extra_data['extra_'.$row['fvar']] = $fval;
2190
                } else {
2191
                    $extra_data[$row['fvar']] = $fval;
2192
                }
2193
            }
2194
        }
2195
2196
        return $extra_data;
2197
    }
2198
2199
    /**
2200
     * Get the extra field information for a certain field (the options as well).
2201
     *
2202
     * @param int $variable The name of the field we want to know everything about
2203
     *
2204
     * @return array Array containing all the information about the extra profile field
2205
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2206
     *               as returned by the database)
2207
     *
2208
     * @author Julio Montoya
2209
     *
2210
     * @since v1.8.6
2211
     */
2212
    public static function get_extra_field_information_by_name($variable)
2213
    {
2214
        $extraField = new ExtraField('user');
2215
2216
        return $extraField->get_handler_field_info_by_field_variable($variable);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraField->get_...eld_variable($variable) also could return the type boolean which is incompatible with the documented return type array.
Loading history...
2217
    }
2218
2219
    /**
2220
     * Get the extra field information for user tag (the options as well).
2221
     *
2222
     * @param int $variable The name of the field we want to know everything about
2223
     *
2224
     * @return array Array containing all the information about the extra profile field
2225
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2226
     *               as returned by the database)
2227
     *
2228
     * @author José Loguercio
2229
     *
2230
     * @since v1.11.0
2231
     */
2232
    public static function get_extra_field_tags_information_by_name($variable)
2233
    {
2234
        $extraField = new ExtraField('user');
2235
2236
        return $extraField->get_handler_field_info_by_tags($variable);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraField->get_...info_by_tags($variable) also could return the type boolean which is incompatible with the documented return type array.
Loading history...
2237
    }
2238
2239
    /**
2240
     * Get all the extra field information of a certain field (also the options).
2241
     *
2242
     * @param int $fieldId the ID of the field we want to know everything of
2243
     *
2244
     * @return array $return containing all th information about the extra profile field
2245
     *
2246
     * @author Julio Montoya
2247
     *
2248
     * @deprecated
2249
     * @since v1.8.6
2250
     */
2251
    public static function get_extra_field_information($fieldId)
2252
    {
2253
        $extraField = new ExtraField('user');
2254
2255
        return $extraField->getFieldInfoByFieldId($fieldId);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraField->getF...InfoByFieldId($fieldId) also could return the type boolean which is incompatible with the documented return type array.
Loading history...
2256
    }
2257
2258
    /**
2259
     * Get extra user data by value.
2260
     *
2261
     * @param string $variable       the internal variable name of the field
2262
     * @param string $value          the internal value of the field
2263
     * @param bool   $all_visibility
2264
     *
2265
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2266
     */
2267
    public static function get_extra_user_data_by_value($variable, $value, $all_visibility = true)
2268
    {
2269
        $extraFieldValue = new ExtraFieldValue('user');
2270
        $extraField = new ExtraField('user');
2271
2272
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2273
2274
        if (false === $info) {
2275
            return [];
2276
        }
2277
2278
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2279
            $variable,
2280
            $value,
2281
            false,
2282
            false,
2283
            true
2284
        );
2285
2286
        $result = [];
2287
        if (!empty($data)) {
2288
            foreach ($data as $item) {
2289
                $result[] = $item['item_id'];
2290
            }
2291
        }
2292
2293
        return $result;
2294
    }
2295
2296
    /**
2297
     * Get extra user data by tags value.
2298
     *
2299
     * @param int    $fieldId the ID of the field we want to know everything of
2300
     * @param string $tag     the tag name for search
2301
     *
2302
     * @return array with extra data info of a user
2303
     *
2304
     * @author José Loguercio
2305
     *
2306
     * @since v1.11.0
2307
     */
2308
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2309
    {
2310
        $extraField = new ExtraField('user');
2311
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2312
        $array = [];
2313
        foreach ($result as $index => $user) {
2314
            $array[] = $user['user_id'];
2315
        }
2316
2317
        return $array;
2318
    }
2319
2320
    /**
2321
     * Get extra user data by field variable.
2322
     *
2323
     * @param string $variable field variable
2324
     *
2325
     * @return array data
2326
     */
2327
    public static function get_extra_user_data_by_field_variable($variable)
2328
    {
2329
        $extraInfo = self::get_extra_field_information_by_name($variable);
2330
        $field_id = (int) $extraInfo['id'];
2331
2332
        $extraField = new ExtraFieldValue('user');
2333
        $data = $extraField->getValuesByFieldId($field_id);
2334
2335
        if (!empty($data)) {
2336
            foreach ($data as $row) {
2337
                $user_id = $row['item_id'];
2338
                $data[$user_id] = $row;
2339
            }
2340
        }
2341
2342
        return $data;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $data could also return false which is incompatible with the documented return type array. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
2343
    }
2344
2345
    /**
2346
     * Get extra user data tags by field variable.
2347
     *
2348
     * @param string $variable field variable
2349
     *
2350
     * @return array
2351
     */
2352
    public static function get_extra_user_data_for_tags($variable)
2353
    {
2354
        $data = self::get_extra_field_tags_information_by_name($variable);
2355
2356
        return $data;
2357
    }
2358
2359
    /**
2360
     * Gives a list of [session_category][session_id] for the current user.
2361
     *
2362
     * @param int  $user_id
2363
     * @param bool $is_time_over                 whether to fill the first element or not
2364
     *                                           (to give space for courses out of categories)
2365
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2366
     * @param bool $ignoreTimeLimit              ignore time start/end
2367
     * @param bool $getCount
2368
     *
2369
     * @return array list of statuses [session_category][session_id]
2370
     *
2371
     * @todo ensure multiple access urls are managed correctly
2372
     */
2373
    public static function get_sessions_by_category(
2374
        $user_id,
2375
        $is_time_over = true,
2376
        $ignore_visibility_for_admins = false,
2377
        $ignoreTimeLimit = false,
2378
        $getCount = false
2379
    ) {
2380
        $user_id = (int) $user_id;
2381
2382
        if (empty($user_id)) {
2383
            return [];
2384
        }
2385
2386
        // Get the list of sessions per user
2387
        $now = new DateTime('now', new DateTimeZone('UTC'));
2388
2389
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2390
        // join would not catch session-courses where the user is general
2391
        // session coach but which do not have students nor coaches registered
2392
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
2393
2394
        if (!$getCount) {
2395
            $dqlSelect = " DISTINCT
2396
                s.id,
2397
                s.title,
2398
                s.accessStartDate AS access_start_date,
2399
                s.accessEndDate AS access_end_date,
2400
                s.duration,
2401
                sc.id AS session_category_id,
2402
                sc.title AS session_category_title,
2403
                sc.dateStart AS session_category_date_start,
2404
                sc.dateEnd AS session_category_date_end,
2405
                s.coachAccessStartDate AS coach_access_start_date,
2406
                s.coachAccessEndDate AS coach_access_end_date,
2407
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
2408
                , s.position AS position
2409
            ";
2410
        }
2411
2412
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
2413
        // is awfully inefficient for large sets of data (1m25s for 58K
2414
        // sessions, BT#14115) but executing a similar query twice and grouping
2415
        // the results afterwards in PHP takes about 1/1000th of the time
2416
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
2417
        $dqlStudent = "SELECT $dqlSelect
2418
            FROM ChamiloCoreBundle:Session AS s
2419
            LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2420
            INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2421
            LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2422
            WHERE scu.user = :user AND url.url = :url ";
2423
        $dqlCoach = "SELECT $dqlSelect
2424
            FROM ChamiloCoreBundle:Session AS s
2425
            INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2426
            LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2427
            INNER JOIN ChamiloCoreBundle:SessionRelUser AS su WITH su.session = s
2428
            WHERE (su.user = :user AND su.relationType = ".SessionEntity::GENERAL_COACH.") AND url.url = :url ";
2429
2430
        // Default order
2431
        $order = 'ORDER BY sc.title, s.title';
2432
2433
        // Order by date if showing all sessions
2434
        $showAllSessions = ('true' === api_get_setting('course.show_all_sessions_on_my_course_page'));
2435
        if ($showAllSessions) {
2436
            $order = 'ORDER BY s.accessStartDate';
2437
        }
2438
2439
        // Order by position
2440
        if ('true' === api_get_setting('session.session_list_order')) {
2441
            $order = 'ORDER BY s.position';
2442
        }
2443
2444
        // Order by dates according to settings
2445
        $orderBySettings = api_get_setting('session.my_courses_session_order', true);
2446
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
2447
            $field = $orderBySettings['field'];
2448
            $orderSetting = $orderBySettings['order'];
2449
            switch ($field) {
2450
                case 'start_date':
2451
                    $order = " ORDER BY s.accessStartDate $orderSetting";
2452
                    break;
2453
                case 'end_date':
2454
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
2455
                    if ('asc' == $orderSetting) {
2456
                        // Put null values at the end
2457
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
2458
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
2459
                    }
2460
                    break;
2461
                case 'name':
2462
                case 'title':
2463
                    $order = " ORDER BY s.title $orderSetting ";
2464
                    break;
2465
            }
2466
        }
2467
2468
        $dqlStudent .= $order;
2469
        $dqlCoach .= $order;
2470
2471
        $accessUrlId = api_get_current_access_url_id();
2472
        $dqlStudent = Database::getManager()
2473
            ->createQuery($dqlStudent)
2474
            ->setParameters(
2475
                ['user' => $user_id, 'url' => $accessUrlId]
2476
            )
2477
        ;
2478
        $dqlCoach = Database::getManager()
2479
            ->createQuery($dqlCoach)
2480
            ->setParameters(
2481
                ['user' => $user_id, 'url' => $accessUrlId]
2482
            )
2483
        ;
2484
2485
        if ($getCount) {
2486
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
2487
        }
2488
2489
        $sessionDataStudent = $dqlStudent->getResult();
2490
        $sessionDataCoach = $dqlCoach->getResult();
2491
2492
        $sessionData = [];
2493
        // First fill $sessionData with student sessions
2494
        if (!empty($sessionDataStudent)) {
2495
            foreach ($sessionDataStudent as $row) {
2496
                $sessionData[$row['id']] = $row;
2497
            }
2498
        }
2499
        // Overwrite session data of the user as a student with session data
2500
        // of the user as a coach.
2501
        // There shouldn't be such duplicate rows, but just in case...
2502
        if (!empty($sessionDataCoach)) {
2503
            foreach ($sessionDataCoach as $row) {
2504
                $sessionData[$row['id']] = $row;
2505
            }
2506
        }
2507
2508
        $collapsable = ('true' === api_get_setting('session.allow_user_session_collapsable'));
2509
2510
2511
2512
        $extraField = new ExtraFieldValue('session');
2513
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
2514
2515
        if (empty($sessionData)) {
2516
            return [];
2517
        }
2518
        $categories = [];
2519
        foreach ($sessionData as $row) {
2520
            $session_id = $row['id'];
2521
            $coachList = SessionManager::getCoachesBySession($session_id);
2522
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
2523
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
2524
            $courseList = self::get_courses_list_by_session($user_id, $session_id);
2525
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
2526
2527
            // User portal filters:
2528
            if (false === $ignoreTimeLimit) {
2529
                if ($is_time_over) {
2530
                    // History
2531
                    if ($row['duration']) {
2532
                        if ($daysLeft >= 0) {
2533
                            continue;
2534
                        }
2535
                    } else {
2536
                        if (empty($row['access_end_date'])) {
2537
                            continue;
2538
                        } else {
2539
                            if ($row['access_end_date'] > $now) {
2540
                                continue;
2541
                            }
2542
                        }
2543
                    }
2544
                } else {
2545
                    // Current user portal
2546
                    $isGeneralCoach = api_get_session_entity($row['id'])->hasUserAsGeneralCoach(api_get_user_entity($user_id));
2547
                    $isCoachOfCourse = in_array($user_id, $coachList);
2548
2549
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
2550
                        // Teachers can access the session depending in the access_coach date
2551
                    } else {
2552
                        if ($row['duration']) {
2553
                            if ($daysLeft <= 0) {
2554
                                continue;
2555
                            }
2556
                        } else {
2557
                            if (isset($row['access_end_date']) &&
2558
                                !empty($row['access_end_date'])
2559
                            ) {
2560
                                if ($row['access_end_date'] <= $now) {
2561
                                    continue;
2562
                                }
2563
                            }
2564
                        }
2565
                    }
2566
                }
2567
            }
2568
2569
            $categories[$row['session_category_id']]['session_category'] = [
2570
                'id' => $row['session_category_id'],
2571
                'name' => $row['session_category_title'],
2572
                'date_start' => $categoryStart,
2573
                'date_end' => $categoryEnd,
2574
            ];
2575
2576
            $visibility = api_get_session_visibility(
2577
                $session_id,
2578
                null,
2579
                $ignore_visibility_for_admins
2580
            );
2581
2582
            if (SESSION_VISIBLE != $visibility) {
2583
                // Course Coach session visibility.
2584
                $blockedCourseCount = 0;
2585
                $closedVisibilityList = [
2586
                    COURSE_VISIBILITY_CLOSED,
2587
                    COURSE_VISIBILITY_HIDDEN,
2588
                ];
2589
2590
                foreach ($courseList as $course) {
2591
                    // Checking session visibility
2592
                    $sessionCourseVisibility = api_get_session_visibility(
2593
                        $session_id,
2594
                        $course['real_id'],
2595
                        $ignore_visibility_for_admins
2596
                    );
2597
2598
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
2599
                    if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
2600
                        $blockedCourseCount++;
2601
                    }
2602
                }
2603
2604
                // If all courses are blocked then no show in the list.
2605
                if ($blockedCourseCount === count($courseList)) {
2606
                    $visibility = SESSION_INVISIBLE;
2607
                } else {
2608
                    $visibility = $sessionCourseVisibility;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sessionCourseVisibility does not seem to be defined for all execution paths leading up to this point.
Loading history...
2609
                }
2610
            }
2611
2612
            switch ($visibility) {
2613
                case SESSION_VISIBLE_READ_ONLY:
2614
                case SESSION_VISIBLE:
2615
                case SESSION_AVAILABLE:
2616
                    break;
2617
                case SESSION_INVISIBLE:
2618
                    if (false === $ignore_visibility_for_admins) {
2619
                        continue 2;
2620
                    }
2621
            }
2622
2623
            $collapsed = '';
2624
            $collapsedAction = '';
2625
            if ($collapsable) {
2626
                $collapsableData = SessionManager::getCollapsableData(
2627
                    $user_id,
2628
                    $session_id,
2629
                    $extraField,
2630
                    $collapsableLink
2631
                );
2632
                $collapsed = $collapsableData['collapsed'];
2633
                $collapsedAction = $collapsableData['collapsable_link'];
2634
            }
2635
2636
            $categories[$row['session_category_id']]['sessions'][] = [
2637
                'session_name' => $row['title'],
2638
                'session_id' => $row['id'],
2639
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
2640
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
2641
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
2642
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
2643
                'courses' => $courseList,
2644
                'collapsed' => $collapsed,
2645
                'collapsable_link' => $collapsedAction,
2646
                'duration' => $row['duration'],
2647
            ];
2648
        }
2649
2650
        return $categories;
2651
    }
2652
2653
    /**
2654
     * Gives a list of [session_id-course_code] => [status] for the current user.
2655
     *
2656
     * @param int $user_id
2657
     * @param int $sessionLimit
2658
     *
2659
     * @return array list of statuses (session_id-course_code => status)
2660
     */
2661
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
2662
    {
2663
        // Database Table Definitions
2664
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2665
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2666
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2667
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
2668
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2669
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2670
2671
        $user_id = (int) $user_id;
2672
2673
        if (empty($user_id)) {
2674
            return [];
2675
        }
2676
2677
        $sessionRepo = Container::getSessionRepository();
2678
2679
        $user = api_get_user_entity($user_id);
2680
        $url = null;
2681
        $formattedUserName = Container::$container->get(NameConvention::class)->getPersonName($user);
2682
2683
        // We filter the courses from the URL
2684
        $join_access_url = $where_access_url = '';
2685
        if (api_get_multiple_access_url()) {
2686
            $access_url_id = api_get_current_access_url_id();
2687
            if (-1 != $access_url_id) {
2688
                $url = api_get_url_entity($access_url_id);
2689
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2690
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
2691
                $where_access_url = " AND access_url_id = $access_url_id ";
2692
            }
2693
        }
2694
2695
        // Courses in which we subscribed out of any session
2696
2697
        $sql = "SELECT
2698
                    course.code,
2699
                    course_rel_user.status course_rel_status,
2700
                    course_rel_user.sort sort,
2701
                    course_rel_user.user_course_cat user_course_cat
2702
                 FROM $tbl_course_user course_rel_user
2703
                 LEFT JOIN $tbl_course course
2704
                 ON course.id = course_rel_user.c_id
2705
                 $join_access_url
2706
                 WHERE
2707
                    course_rel_user.user_id = '".$user_id."' AND
2708
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
2709
                    $where_access_url
2710
                 ORDER BY course_rel_user.sort, course.title ASC";
2711
2712
        $course_list_sql_result = Database::query($sql);
2713
        $personal_course_list = [];
2714
        if (Database::num_rows($course_list_sql_result) > 0) {
2715
            while ($result_row = Database::fetch_assoc($course_list_sql_result)) {
2716
                $course_info = api_get_course_info($result_row['code']);
2717
                $result_row['course_info'] = $course_info;
2718
                $personal_course_list[] = $result_row;
2719
            }
2720
        }
2721
2722
        $coachCourseConditions = '';
2723
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
2724
        if (api_is_allowed_to_create_course()) {
2725
            $sessionListFromCourseCoach = [];
2726
            $sql = " SELECT DISTINCT session_id
2727
                    FROM $tbl_session_course_user
2728
                    WHERE user_id = $user_id AND status = ".SessionEntity::COURSE_COACH;
2729
2730
            $result = Database::query($sql);
2731
            if (Database::num_rows($result)) {
2732
                $result = Database::store_result($result);
2733
                foreach ($result as $session) {
2734
                    $sessionListFromCourseCoach[] = $session['session_id'];
2735
                }
2736
            }
2737
            if (!empty($sessionListFromCourseCoach)) {
2738
                $condition = implode("','", $sessionListFromCourseCoach);
2739
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
2740
            }
2741
        }
2742
2743
        // Get the list of sessions where the user is subscribed
2744
        // This is divided into two different queries
2745
        $sessions = [];
2746
        $sessionLimitRestriction = '';
2747
        if (!empty($sessionLimit)) {
2748
            $sessionLimit = (int) $sessionLimit;
2749
            $sessionLimitRestriction = "LIMIT $sessionLimit";
2750
        }
2751
2752
        $sql = "SELECT DISTINCT s.id, s.title, access_start_date, access_end_date
2753
                FROM $tbl_session_user su INNER JOIN $tbl_session s
2754
                ON (s.id = su.session_id)
2755
                WHERE (
2756
                    su.user_id = $user_id AND
2757
                    su.relation_type = ".SessionEntity::STUDENT."
2758
                )
2759
                $coachCourseConditions
2760
                ORDER BY access_start_date, access_end_date, s.title
2761
                $sessionLimitRestriction
2762
        ";
2763
2764
        $result = Database::query($sql);
2765
        if (Database::num_rows($result) > 0) {
2766
            while ($row = Database::fetch_assoc($result)) {
2767
                $sessions[$row['id']] = $row;
2768
            }
2769
        }
2770
2771
        $sql = "SELECT DISTINCT
2772
                s.id, s.title, s.access_start_date, s.access_end_date
2773
                FROM $tbl_session s
2774
                INNER JOIN $tbl_session_user sru ON sru.session_id = s.id
2775
                WHERE (
2776
                    sru.user_id = $user_id AND sru.relation_type = ".SessionEntity::GENERAL_COACH."
2777
                )
2778
                $coachCourseConditions
2779
                ORDER BY s.access_start_date, s.access_end_date, s.title";
2780
2781
        $result = Database::query($sql);
2782
        if (Database::num_rows($result) > 0) {
2783
            while ($row = Database::fetch_assoc($result)) {
2784
                if (empty($sessions[$row['id']])) {
2785
                    $sessions[$row['id']] = $row;
2786
                }
2787
            }
2788
        }
2789
2790
        if (api_is_allowed_to_create_course()) {
2791
            foreach ($sessions as $enreg) {
2792
                $session_id = $enreg['id'];
2793
                $session_visibility = api_get_session_visibility($session_id);
2794
                $session = api_get_session_entity($session_id);
2795
2796
                if (SESSION_INVISIBLE == $session_visibility) {
2797
                    continue;
2798
                }
2799
2800
                $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
2801
                    $user,
2802
                    $session,
2803
                    SessionEntity::GENERAL_COACH,
2804
                    $url
2805
                );
2806
                $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
2807
                    $user,
2808
                    $session,
2809
                    SessionEntity::COURSE_COACH,
2810
                    $url
2811
                );
2812
2813
                // This query is horribly slow when more than a few thousand
2814
                // users and just a few sessions to which they are subscribed
2815
                $coursesInSession = array_map(
2816
                    function (SessionRelCourse $courseInSession) {
2817
                        $course = $courseInSession->getCourse();
2818
2819
                        return [
2820
                            'code' => $course->getCode(),
2821
                            'i' => $course->getTitle(),
2822
                            'l' => $course->getCourseLanguage(),
2823
                            'sort' => 1,
2824
                        ];
2825
                    },
2826
                    array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
2827
                );
2828
2829
                foreach ($coursesInSession as $result_row) {
2830
                    $result_row['t'] = $formattedUserName;
2831
                    $result_row['email'] = $user->getEmail();
2832
                    $result_row['access_start_date'] = $session->getAccessStartDate()?->format('Y-m-d H:i:s');
2833
                    $result_row['access_end_date'] = $session->getAccessEndDate()?->format('Y-m-d H:i:s');
2834
                    $result_row['session_id'] = $session->getId();
2835
                    $result_row['session_name'] = $session->getTitle();
2836
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
2837
                    $key = $result_row['session_id'].' - '.$result_row['code'];
2838
                    $personal_course_list[$key] = $result_row;
2839
                }
2840
            }
2841
        }
2842
2843
        foreach ($sessions as $enreg) {
2844
            $session_id = $enreg['id'];
2845
            $session_visibility = api_get_session_visibility($session_id);
2846
            if (SESSION_INVISIBLE == $session_visibility) {
2847
                continue;
2848
            }
2849
2850
            /* This query is very similar to the above query,
2851
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
2852
            $sql = "SELECT DISTINCT
2853
                course.code code,
2854
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
2855
                email,
2856
                course.course_language l,
2857
                1 sort,
2858
                access_start_date,
2859
                access_end_date,
2860
                session.id as session_id,
2861
                session.title as session_name,
2862
                IF((session_course_user.user_id = 3 AND session_course_user.status = ".SessionEntity::COURSE_COACH."),'2', '5')
2863
            FROM $tbl_session_course_user as session_course_user
2864
            INNER JOIN $tbl_course AS course
2865
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
2866
            INNER JOIN $tbl_session as session
2867
            ON session_course_user.session_id = session.id
2868
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
2869
            WHERE session_course_user.user_id = $user_id
2870
            ORDER BY i";
2871
2872
            $course_list_sql_result = Database::query($sql);
2873
            while ($result_row = Database::fetch_assoc($course_list_sql_result)) {
2874
                $result_row['course_info'] = api_get_course_info($result_row['code']);
2875
                $key = $result_row['session_id'].' - '.$result_row['code'];
2876
                if (!isset($personal_course_list[$key])) {
2877
                    $personal_course_list[$key] = $result_row;
2878
                }
2879
            }
2880
        }
2881
2882
        return $personal_course_list;
2883
    }
2884
2885
    /**
2886
     * Gives a list of courses for the given user in the given session.
2887
     *
2888
     * @param int $user_id
2889
     * @param int $session_id
2890
     *
2891
     * @return array list of statuses (session_id-course_code => status)
2892
     */
2893
    public static function get_courses_list_by_session($user_id, $session_id)
2894
    {
2895
        // Database Table Definitions
2896
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
2897
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2898
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
2899
2900
        $user_id = (int) $user_id;
2901
        $session_id = (int) $session_id;
2902
2903
        $sessionRepo = Container::getSessionRepository();
2904
2905
        $user = api_get_user_entity($user_id);
2906
        $session = api_get_session_entity($session_id);
2907
        $url = null;
2908
2909
        // We filter the courses from the URL
2910
        $join_access_url = $where_access_url = '';
2911
        if (api_get_multiple_access_url()) {
2912
            $urlId = api_get_current_access_url_id();
2913
            if (-1 != $urlId) {
2914
                $url = api_get_url_entity($urlId);
2915
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
2916
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
2917
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
2918
            }
2919
        }
2920
2921
        /* This query is very similar to the query below, but it will check the
2922
        session_rel_course_user table if there are courses registered
2923
        to our user or not */
2924
        $sql = "SELECT DISTINCT
2925
                    c.title,
2926
                    c.visibility,
2927
                    c.id as real_id,
2928
                    c.code as course_code,
2929
                    sc.position,
2930
                    c.unsubscribe
2931
                FROM $tbl_session_course_user as scu
2932
                INNER JOIN $tbl_session_course sc
2933
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
2934
                INNER JOIN $tableCourse as c
2935
                ON (scu.c_id = c.id)
2936
                $join_access_url
2937
                WHERE
2938
                    scu.user_id = $user_id AND
2939
                    scu.session_id = $session_id
2940
                    $where_access_url
2941
                ORDER BY sc.position ASC";
2942
2943
        $myCourseList = [];
2944
        $courses = [];
2945
        $result = Database::query($sql);
2946
        if (Database::num_rows($result) > 0) {
2947
            while ($result_row = Database::fetch_assoc($result)) {
2948
                $result_row['status'] = 5;
2949
                if (!in_array($result_row['real_id'], $courses)) {
2950
                    $position = $result_row['position'];
2951
                    if (!isset($myCourseList[$position])) {
2952
                        $myCourseList[$position] = $result_row;
2953
                    } else {
2954
                        $myCourseList[] = $result_row;
2955
                    }
2956
                    $courses[] = $result_row['real_id'];
2957
                }
2958
            }
2959
        }
2960
2961
        if (api_is_allowed_to_create_course()) {
2962
            $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
2963
                $user,
2964
                $session,
2965
                SessionEntity::GENERAL_COACH,
2966
                $url
2967
            );
2968
            $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
2969
                $user,
2970
                $session,
2971
                SessionEntity::COURSE_COACH,
2972
                $url
2973
            );
2974
2975
            $coursesInSession = array_map(
2976
                function (SessionRelCourse $courseInSession) {
2977
                    $course = $courseInSession->getCourse();
2978
2979
                    return [
2980
                        'title' => $course->getTitle(),
2981
                        'visibility' => $course->getVisibility(),
2982
                        'real_id' => $course->getId(),
2983
                        'course_code' => $course->getCode(),
2984
                        'position' => $courseInSession->getPosition(),
2985
                        'unsubscribe' => $course->getUnsubscribe(),
2986
                    ];
2987
                },
2988
                array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
2989
            );
2990
2991
            foreach ($coursesInSession as $result_row) {
2992
                $result_row['status'] = 2;
2993
                if (!in_array($result_row['real_id'], $courses)) {
2994
                    $position = $result_row['position'];
2995
                    if (!isset($myCourseList[$position])) {
2996
                        $myCourseList[$position] = $result_row;
2997
                    } else {
2998
                        $myCourseList[] = $result_row;
2999
                    }
3000
                    $courses[] = $result_row['real_id'];
3001
                }
3002
            }
3003
        }
3004
3005
        if (api_is_drh()) {
3006
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3007
            $sessionList = array_keys($sessionList);
3008
            if (in_array($session_id, $sessionList)) {
3009
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3010
                if (!empty($courseList)) {
3011
                    foreach ($courseList as $course) {
3012
                        if (!in_array($course['id'], $courses)) {
3013
                            $position = $course['position'];
3014
                            if (!isset($myCourseList[$position])) {
3015
                                $myCourseList[$position] = $course;
3016
                            } else {
3017
                                $myCourseList[] = $course;
3018
                            }
3019
                        }
3020
                    }
3021
                }
3022
            }
3023
        } else {
3024
            //check if user is general coach for this session
3025
            if ($session && $session->hasUserAsGeneralCoach($user)) {
3026
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3027
                if (!empty($courseList)) {
3028
                    foreach ($courseList as $course) {
3029
                        if (!in_array($course['id'], $courses)) {
3030
                            $position = $course['position'];
3031
                            if (!isset($myCourseList[$position])) {
3032
                                $myCourseList[$position] = $course;
3033
                            } else {
3034
                                $myCourseList[] = $course;
3035
                            }
3036
                        }
3037
                    }
3038
                }
3039
            }
3040
        }
3041
3042
        if (!empty($myCourseList)) {
3043
            ksort($myCourseList);
3044
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
3045
            if (empty($checkPosition)) {
3046
                // The session course list doesn't have any position,
3047
                // then order the course list by course code
3048
                $list = array_column($myCourseList, 'course_code');
3049
                array_multisort($myCourseList, SORT_ASC, $list);
0 ignored issues
show
Bug introduced by
SORT_ASC cannot be passed to array_multisort() as the parameter $rest expects a reference. ( Ignorable by Annotation )

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

3049
                array_multisort($myCourseList, /** @scrutinizer ignore-type */ SORT_ASC, $list);
Loading history...
3050
            }
3051
        }
3052
3053
        return $myCourseList;
3054
    }
3055
3056
    /**
3057
     * Get user id from a username.
3058
     *
3059
     * @param string $username
3060
     *
3061
     * @return int User ID (or false if not found)
3062
     */
3063
    public static function get_user_id_from_username($username)
3064
    {
3065
        if (empty($username)) {
3066
            return false;
3067
        }
3068
        $username = trim($username);
3069
        $username = Database::escape_string($username);
3070
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3071
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3072
        $res = Database::query($sql);
3073
3074
        if (false === $res) {
3075
            return false;
3076
        }
3077
        if (1 !== Database::num_rows($res)) {
3078
            return false;
3079
        }
3080
        $row = Database::fetch_array($res);
3081
3082
        return $row['id'];
3083
    }
3084
3085
    /**
3086
     * Get the users files upload from his share_folder.
3087
     *
3088
     * @param string $user_id      User ID
3089
     * @param string $course       course directory
3090
     * @param string $resourceType resource type: images, all
3091
     *
3092
     * @return string
3093
     */
3094
    /*public static function get_user_upload_files_by_course(
3095
        $user_id,
3096
        $course,
3097
        $resourceType = 'all'
3098
    ) {
3099
        $return = '';
3100
        $user_id = (int) $user_id;
3101
3102
        if (!empty($user_id) && !empty($course)) {
3103
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3104
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3105
            $file_list = [];
3106
3107
            if (is_dir($path)) {
3108
                $handle = opendir($path);
3109
                while ($file = readdir($handle)) {
3110
                    if ('.' == $file || '..' == $file || '.htaccess' == $file || is_dir($path.$file)) {
3111
                        continue; // skip current/parent directory and .htaccess
3112
                    }
3113
                    $file_list[] = $file;
3114
                }
3115
                if (count($file_list) > 0) {
3116
                    $return = "<h4>$course</h4>";
3117
                    $return .= '<ul class="thumbnails">';
3118
                }
3119
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
3120
                foreach ($file_list as $file) {
3121
                    if ('all' == $resourceType) {
3122
                        $return .= '<li>
3123
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
3124
                    } elseif ('images' == $resourceType) {
3125
                        //get extension
3126
                        $ext = explode('.', $file);
3127
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
3128
                            $return .= '<li class="span2">
3129
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
3130
                                                <img src="'.$web_path.urlencode($file).'" >
3131
                                            </a>
3132
                                        </li>';
3133
                        }
3134
                    }
3135
                }
3136
                if (count($file_list) > 0) {
3137
                    $return .= '</ul>';
3138
                }
3139
            }
3140
        }
3141
3142
        return $return;
3143
    }*/
3144
3145
    /**
3146
     * Gets the API key (or keys) and return them into an array.
3147
     *
3148
     * @param int     Optional user id (defaults to the result of api_get_user_id())
3149
     * @param string $api_service
3150
     *
3151
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
3152
     */
3153
    public static function get_api_keys($user_id = null, $api_service = 'default')
3154
    {
3155
        if ($user_id != strval(intval($user_id))) {
3156
            return false;
3157
        }
3158
        if (empty($user_id)) {
3159
            $user_id = api_get_user_id();
3160
        }
3161
        if (false === $user_id) {
3162
            return false;
3163
        }
3164
        $service_name = Database::escape_string($api_service);
3165
        if (false === is_string($service_name)) {
3166
            return false;
3167
        }
3168
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3169
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
3170
        $res = Database::query($sql);
3171
        if (false === $res) {
3172
            return false;
3173
        } //error during query
3174
        $num = Database::num_rows($res);
3175
        if (0 == $num) {
3176
            return false;
3177
        }
3178
        $list = [];
3179
        while ($row = Database::fetch_array($res)) {
3180
            $list[$row['id']] = $row['api_key'];
3181
        }
3182
3183
        return $list;
3184
    }
3185
3186
    /**
3187
     * Adds a new API key to the users' account.
3188
     *
3189
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
3190
     * @param string $api_service
3191
     *
3192
     * @return bool True on success, false on failure
3193
     */
3194
    public static function add_api_key($user_id = null, $api_service = 'default')
3195
    {
3196
        if ($user_id != strval(intval($user_id))) {
3197
            return false;
3198
        }
3199
        if (empty($user_id)) {
3200
            $user_id = api_get_user_id();
3201
        }
3202
        if (false === $user_id) {
3203
            return false;
3204
        }
3205
        $service_name = Database::escape_string($api_service);
3206
        if (false === is_string($service_name)) {
3207
            return false;
3208
        }
3209
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3210
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
3211
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
3212
        $res = Database::query($sql);
3213
        if (false === $res) {
3214
            return false;
3215
        } //error during query
3216
        $num = Database::insert_id();
3217
3218
        return 0 == $num ? false : $num;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 0 == $num ? false : $num also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
3219
    }
3220
3221
    /**
3222
     * Deletes an API key from the user's account.
3223
     *
3224
     * @param   int     API key's internal ID
3225
     *
3226
     * @return bool True on success, false on failure
3227
     */
3228
    public static function delete_api_key($key_id)
3229
    {
3230
        if ($key_id != strval(intval($key_id))) {
3231
            return false;
3232
        }
3233
        if (false === $key_id) {
3234
            return false;
3235
        }
3236
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3237
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
3238
        $res = Database::query($sql);
3239
        if (false === $res) {
3240
            return false;
3241
        } //error during query
3242
        $num = Database::num_rows($res);
3243
        if (1 !== $num) {
3244
            return false;
3245
        }
3246
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
3247
        $res = Database::query($sql);
3248
        if (false === $res) {
3249
            return false;
3250
        } //error during query
3251
3252
        return true;
3253
    }
3254
3255
    /**
3256
     * Regenerate an API key from the user's account.
3257
     *
3258
     * @param   int     user ID (defaults to the results of api_get_user_id())
3259
     * @param   string  API key's internal ID
3260
     *
3261
     * @return int num
3262
     */
3263
    public static function update_api_key($user_id, $api_service)
3264
    {
3265
        if ($user_id != strval(intval($user_id))) {
3266
            return false;
3267
        }
3268
        if (false === $user_id) {
3269
            return false;
3270
        }
3271
        $service_name = Database::escape_string($api_service);
3272
        if (false === is_string($service_name)) {
3273
            return false;
3274
        }
3275
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3276
        $sql = "SELECT id FROM $t_api
3277
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3278
        $res = Database::query($sql);
3279
        $num = Database::num_rows($res);
3280
        if (1 == $num) {
3281
            $id_key = Database::fetch_assoc($res);
3282
            self::delete_api_key($id_key['id']);
3283
            $num = self::add_api_key($user_id, $api_service);
3284
        } elseif (0 == $num) {
3285
            $num = self::add_api_key($user_id, $api_service);
3286
        }
3287
3288
        return $num;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $num also could return the type boolean which is incompatible with the documented return type integer.
Loading history...
3289
    }
3290
3291
    /**
3292
     * @param   int     user ID (defaults to the results of api_get_user_id())
3293
     * @param   string    API key's internal ID
3294
     *
3295
     * @return int row ID, or return false if not found
3296
     */
3297
    public static function get_api_key_id($user_id, $api_service)
3298
    {
3299
        if ($user_id != strval(intval($user_id))) {
3300
            return false;
3301
        }
3302
        if (false === $user_id) {
3303
            return false;
3304
        }
3305
        if (empty($api_service)) {
3306
            return false;
3307
        }
3308
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3309
        $api_service = Database::escape_string($api_service);
3310
        $sql = "SELECT id FROM $t_api
3311
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3312
        $res = Database::query($sql);
3313
        if (Database::num_rows($res) < 1) {
3314
            return false;
3315
        }
3316
        $row = Database::fetch_assoc($res);
3317
3318
        return $row['id'];
3319
    }
3320
3321
    /**
3322
     * Checks if a user_id is platform admin.
3323
     *
3324
     * @param   int user ID
3325
     *
3326
     * @return bool True if is admin, false otherwise
3327
     *
3328
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
3329
     */
3330
    public static function is_admin($user_id)
3331
    {
3332
        $user_id = (int) $user_id;
3333
        if (empty($user_id)) {
3334
            return false;
3335
        }
3336
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3337
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3338
        $res = Database::query($sql);
3339
3340
        return 1 === Database::num_rows($res);
3341
    }
3342
3343
    /**
3344
     * Get the total count of users.
3345
     *
3346
     * @param int $status        Status of users to be counted
3347
     * @param int $access_url_id Access URL ID (optional)
3348
     * @param int $active
3349
     *
3350
     * @return mixed Number of users or false on error
3351
     */
3352
    public static function get_number_of_users($status = 0, $access_url_id = 1, $active = null)
3353
    {
3354
        $t_u = Database::get_main_table(TABLE_MAIN_USER);
3355
        $t_a = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3356
3357
        $sql = "SELECT count(u.id)
3358
                FROM $t_u u
3359
                INNER JOIN $t_a url_user
3360
                ON (u.id = url_user.user_id)
3361
                WHERE url_user.access_url_id = $access_url_id
3362
        ";
3363
3364
        $status = (int) $status;
3365
        if (!empty($status) && $status > 0) {
3366
            $sql .= " AND u.status = $status ";
3367
        }
3368
3369
        if (null !== $active) {
3370
            $active = (int) $active;
3371
            $sql .= " AND u.active = $active ";
3372
        }
3373
3374
        $res = Database::query($sql);
3375
        if (1 === Database::num_rows($res)) {
3376
            return (int) Database::result($res, 0, 0);
3377
        }
3378
3379
        return false;
3380
    }
3381
3382
    /**
3383
     * Gets the tags of a specific field_id
3384
     * USER TAGS.
3385
     *
3386
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
3387
     *
3388
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
3389
     *    Called it "books" for example.
3390
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
3391
     * 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
3392
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
3393
     * 5. Test and enjoy.
3394
     *
3395
     * @param string $tag
3396
     * @param int    $field_id      field_id
3397
     * @param string $return_format how we are going to result value in array or in a string (json)
3398
     * @param $limit
3399
     *
3400
     * @return mixed
3401
     */
3402
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
3403
    {
3404
        // database table definition
3405
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3406
        $field_id = (int) $field_id;
3407
        $limit = (int) $limit;
3408
        $tag = trim(Database::escape_string($tag));
3409
3410
        // all the information of the field
3411
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
3412
                WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag LIMIT $limit";
3413
        $result = Database::query($sql);
3414
        $return = [];
3415
        if (Database::num_rows($result) > 0) {
3416
            while ($row = Database::fetch_assoc($result)) {
3417
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
3418
            }
3419
        }
3420
        if ('json' === $return_format) {
3421
            $return = json_encode($return);
3422
        }
3423
3424
        return $return;
3425
    }
3426
3427
    /**
3428
     * @param int $field_id
3429
     * @param int $limit
3430
     *
3431
     * @return array
3432
     */
3433
    public static function get_top_tags($field_id, $limit = 100)
3434
    {
3435
        // database table definition
3436
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3437
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3438
        $field_id = (int) $field_id;
3439
        $limit = (int) $limit;
3440
        // all the information of the field
3441
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
3442
                INNER JOIN $table_user_tag ut
3443
                ON (ut.id = uv.tag_id)
3444
                WHERE field_id = $field_id
3445
                GROUP BY tag_id
3446
                ORDER BY count DESC
3447
                LIMIT $limit";
3448
        $result = Database::query($sql);
3449
        $return = [];
3450
        if (Database::num_rows($result) > 0) {
3451
            while ($row = Database::fetch_assoc($result)) {
3452
                $return[] = $row;
3453
            }
3454
        }
3455
3456
        return $return;
3457
    }
3458
3459
    /**
3460
     * Get user's tags.
3461
     *
3462
     * @param int $user_id
3463
     * @param int $field_id
3464
     *
3465
     * @return array
3466
     */
3467
    public static function get_user_tags($user_id, $field_id)
3468
    {
3469
        // database table definition
3470
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3471
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3472
        $field_id = (int) $field_id;
3473
        $user_id = (int) $user_id;
3474
3475
        // all the information of the field
3476
        $sql = "SELECT ut.id, tag, count
3477
                FROM $table_user_tag ut
3478
                INNER JOIN $table_user_tag_values uv
3479
                ON (uv.tag_id=ut.ID)
3480
                WHERE field_id = $field_id AND user_id = $user_id
3481
                ORDER BY tag";
3482
        $result = Database::query($sql);
3483
        $return = [];
3484
        if (Database::num_rows($result) > 0) {
3485
            while ($row = Database::fetch_assoc($result)) {
3486
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3487
            }
3488
        }
3489
3490
        return $return;
3491
    }
3492
3493
    /**
3494
     * Get user's tags.
3495
     *
3496
     * @param int  $user_id
3497
     * @param int  $field_id
3498
     * @param bool $show_links show links or not
3499
     *
3500
     * @return string
3501
     */
3502
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
3503
    {
3504
        // database table definition
3505
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3506
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3507
        $field_id = (int) $field_id;
3508
        $user_id = (int) $user_id;
3509
3510
        // all the information of the field
3511
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
3512
                INNER JOIN $table_user_tag_values uv
3513
                ON (uv.tag_id = ut.id)
3514
                WHERE field_id = $field_id AND user_id = $user_id
3515
                ORDER BY tag";
3516
3517
        $result = Database::query($sql);
3518
        $return = [];
3519
        if (Database::num_rows($result) > 0) {
3520
            while ($row = Database::fetch_assoc($result)) {
3521
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3522
            }
3523
        }
3524
        $user_tags = $return;
3525
        $tag_tmp = [];
3526
        foreach ($user_tags as $tag) {
3527
            if ($show_links) {
3528
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
3529
                    $tag['tag'].
3530
                '</a>';
3531
            } else {
3532
                $tag_tmp[] = $tag['tag'];
3533
            }
3534
        }
3535
3536
        if (is_array($user_tags) && count($user_tags) > 0) {
3537
            return implode(', ', $tag_tmp);
3538
        } else {
3539
            return '';
3540
        }
3541
    }
3542
3543
    /**
3544
     * Get the tag id.
3545
     *
3546
     * @param int $tag
3547
     * @param int $field_id
3548
     *
3549
     * @return int returns 0 if fails otherwise the tag id
3550
     */
3551
    public static function get_tag_id($tag, $field_id)
3552
    {
3553
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3554
        $tag = Database::escape_string($tag);
3555
        $field_id = (int) $field_id;
3556
        //with COLLATE latin1_bin to select query in a case sensitive mode
3557
        $sql = "SELECT id FROM $table_user_tag
3558
                WHERE tag LIKE '$tag' AND field_id = $field_id";
3559
        $result = Database::query($sql);
3560
        if (Database::num_rows($result) > 0) {
3561
            $row = Database::fetch_assoc($result);
3562
3563
            return $row['id'];
3564
        } else {
3565
            return 0;
3566
        }
3567
    }
3568
3569
    /**
3570
     * Get the tag id.
3571
     *
3572
     * @param int $tag_id
3573
     * @param int $field_id
3574
     *
3575
     * @return int 0 if fails otherwise the tag id
3576
     */
3577
    public static function get_tag_id_from_id($tag_id, $field_id)
3578
    {
3579
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3580
        $tag_id = (int) $tag_id;
3581
        $field_id = (int) $field_id;
3582
        $sql = "SELECT id FROM $table_user_tag
3583
                WHERE id = '$tag_id' AND field_id = $field_id";
3584
        $result = Database::query($sql);
3585
        if (Database::num_rows($result) > 0) {
3586
            $row = Database::fetch_assoc($result);
3587
3588
            return $row['id'];
3589
        } else {
3590
            return false;
3591
        }
3592
    }
3593
3594
    /**
3595
     * Adds a user-tag value.
3596
     *
3597
     * @param mixed $tag
3598
     * @param int   $user_id
3599
     * @param int   $field_id field id of the tag
3600
     *
3601
     * @return bool True if the tag was inserted or updated. False otherwise.
3602
     *              The return value doesn't take into account *values* added to the tag.
3603
     *              Only the creation/update of the tag field itself.
3604
     */
3605
    public static function add_tag($tag, $user_id, $field_id)
3606
    {
3607
        // database table definition
3608
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3609
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3610
        $tag = trim(Database::escape_string($tag));
3611
        $user_id = (int) $user_id;
3612
        $field_id = (int) $field_id;
3613
3614
        $tag_id = self::get_tag_id($tag, $field_id);
3615
3616
        /* IMPORTANT
3617
         *  @todo we don't create tags with numbers
3618
         *
3619
         */
3620
3621
        //this is a new tag
3622
        if (0 == $tag_id) {
3623
            //the tag doesn't exist
3624
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
3625
            Database::query($sql);
3626
            $last_insert_id = Database::insert_id();
3627
        } else {
3628
            //the tag exists we update it
3629
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
3630
            Database::query($sql);
3631
            $last_insert_id = $tag_id;
3632
        }
3633
3634
        if (!empty($last_insert_id) && (0 != $last_insert_id)) {
3635
            //we insert the relationship user-tag
3636
            $sql = "SELECT tag_id FROM $table_user_tag_values
3637
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
3638
            $result = Database::query($sql);
3639
            //if the relationship does not exist we create it
3640
            if (0 == Database::num_rows($result)) {
3641
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
3642
                Database::query($sql);
3643
            }
3644
3645
            return true;
3646
        }
3647
3648
        return false;
3649
    }
3650
3651
    /**
3652
     * Deletes an user tag.
3653
     *
3654
     * @param int $user_id
3655
     * @param int $field_id
3656
     */
3657
    public static function delete_user_tags($user_id, $field_id)
3658
    {
3659
        // database table definition
3660
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3661
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3662
        $user_id = (int) $user_id;
3663
3664
        $tags = self::get_user_tags($user_id, $field_id);
3665
        if (is_array($tags) && count($tags) > 0) {
3666
            foreach ($tags as $key => $tag) {
3667
                if ($tag['count'] > '0') {
3668
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
3669
                    Database::query($sql);
3670
                }
3671
                $sql = "DELETE FROM $table_user_tag_values
3672
                        WHERE user_id = $user_id AND tag_id = $key";
3673
                Database::query($sql);
3674
            }
3675
        }
3676
    }
3677
3678
    /**
3679
     * Process the tag list comes from the UserManager::update_extra_field_value() function.
3680
     *
3681
     * @param array $tags     the tag list that will be added
3682
     * @param int   $user_id
3683
     * @param int   $field_id
3684
     *
3685
     * @return bool
3686
     */
3687
    public static function process_tags($tags, $user_id, $field_id)
3688
    {
3689
        // We loop the tags and add it to the DB
3690
        if (is_array($tags)) {
3691
            foreach ($tags as $tag) {
3692
                self::add_tag($tag, $user_id, $field_id);
3693
            }
3694
        } else {
3695
            self::add_tag($tags, $user_id, $field_id);
3696
        }
3697
3698
        return true;
3699
    }
3700
3701
    /**
3702
     * Returns a list of all administrators.
3703
     *
3704
     * @return array
3705
     */
3706
    public static function get_all_administrators()
3707
    {
3708
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3709
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
3710
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3711
        $access_url_id = api_get_current_access_url_id();
3712
        if (api_get_multiple_access_url()) {
3713
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3714
                    FROM $tbl_url_rel_user as url
3715
                    INNER JOIN $table_admin as admin
3716
                    ON (admin.user_id=url.user_id)
3717
                    INNER JOIN $table_user u
3718
                    ON (u.id=admin.user_id)
3719
                    WHERE access_url_id ='".$access_url_id."'";
3720
        } else {
3721
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3722
                    FROM $table_admin as admin
3723
                    INNER JOIN $table_user u
3724
                    ON (u.id=admin.user_id)";
3725
        }
3726
        $sql .= !str_contains($sql, 'WHERE') ? ' WHERE u.active <> '.USER_SOFT_DELETED : ' AND u.active <> '.USER_SOFT_DELETED;
3727
        $result = Database::query($sql);
3728
        $return = [];
3729
        if (Database::num_rows($result) > 0) {
3730
            while ($row = Database::fetch_assoc($result)) {
3731
                $return[$row['user_id']] = $row;
3732
            }
3733
        }
3734
3735
        return $return;
3736
    }
3737
3738
    /**
3739
     * Search an user (tags, first name, last name and email ).
3740
     *
3741
     * @param string $tag
3742
     * @param int    $field_id        field id of the tag
3743
     * @param int    $from            where to start in the query
3744
     * @param int    $number_of_items
3745
     * @param bool   $getCount        get count or not
3746
     *
3747
     * @return array
3748
     */
3749
    public static function get_all_user_tags(
3750
        $tag,
3751
        $field_id = 0,
3752
        $from = 0,
3753
        $number_of_items = 10,
3754
        $getCount = false
3755
    ) {
3756
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
3757
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3758
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3759
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3760
3761
        $field_id = intval($field_id);
3762
        $from = intval($from);
3763
        $number_of_items = intval($number_of_items);
3764
3765
        $where_field = "";
3766
        $where_extra_fields = self::get_search_form_where_extra_fields();
3767
        if (0 != $field_id) {
3768
            $where_field = " field_id = $field_id AND ";
3769
        }
3770
3771
        // all the information of the field
3772
        if ($getCount) {
3773
            $select = "SELECT count(DISTINCT u.id) count";
3774
        } else {
3775
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, tag, picture_uri";
3776
        }
3777
3778
        $sql = " $select
3779
                FROM $user_table u
3780
                INNER JOIN $access_url_rel_user_table url_rel_user
3781
                ON (u.id = url_rel_user.user_id)
3782
                LEFT JOIN $table_user_tag_values uv
3783
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
3784
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
3785
                WHERE
3786
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
3787
                    (
3788
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
3789
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
3790
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
3791
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
3792
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
3793
                     )
3794
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
3795
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
3796
3797
        $keyword_active = true;
3798
        // only active users
3799
        if ($keyword_active) {
3800
            $sql .= " AND u.active='1'";
3801
        }
3802
        // avoid anonymous
3803
        $sql .= " AND u.status <> 6 ";
3804
        $sql .= " ORDER BY username";
3805
        $sql .= " LIMIT $from , $number_of_items";
3806
3807
        $result = Database::query($sql);
3808
        $return = [];
3809
3810
        if (Database::num_rows($result) > 0) {
3811
            if ($getCount) {
3812
                $row = Database::fetch_assoc($result);
3813
3814
                return $row['count'];
3815
            }
3816
            while ($row = Database::fetch_assoc($result)) {
3817
                $return[$row['id']] = $row;
3818
            }
3819
        }
3820
3821
        return $return;
3822
    }
3823
3824
    /**
3825
     * Get extra filterable user fields (only type select).
3826
     *
3827
     * @return array Array of extra fields as [int => ['name' => ..., 'variable' => ..., 'data' => ...]] (
3828
     *               or empty array if no extra field)
3829
     */
3830
    public static function getExtraFilterableFields()
3831
    {
3832
        $extraFieldList = self::get_extra_fields();
3833
        $fields = [];
3834
        if (is_array($extraFieldList)) {
3835
            foreach ($extraFieldList as $extraField) {
3836
                // If is enabled to filter and is a "<select>" field type
3837
                if (1 == $extraField[8] && 4 == $extraField[2]) {
3838
                    $fields[] = [
3839
                        'name' => $extraField[3],
3840
                        'variable' => $extraField[1],
3841
                        'data' => $extraField[9],
3842
                    ];
3843
                }
3844
            }
3845
        }
3846
3847
        return $fields;
3848
    }
3849
3850
    /**
3851
     * Get extra where clauses for finding users based on extra filterable user fields (type select).
3852
     *
3853
     * @return string With AND clauses based on user's ID which have the values to search in extra user fields
3854
     *                (or empty if no extra field exists)
3855
     */
3856
    public static function get_search_form_where_extra_fields()
3857
    {
3858
        $useExtraFields = false;
3859
        $extraFields = self::getExtraFilterableFields();
3860
        $extraFieldResult = [];
3861
        if (is_array($extraFields) && count($extraFields) > 0) {
3862
            foreach ($extraFields as $extraField) {
3863
                $varName = 'field_'.$extraField['variable'];
3864
                if (self::is_extra_field_available($extraField['variable'])) {
3865
                    if (isset($_GET[$varName]) && '0' != $_GET[$varName]) {
3866
                        $useExtraFields = true;
3867
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
3868
                            $extraField['variable'],
3869
                            $_GET[$varName]
3870
                        );
3871
                    }
3872
                }
3873
            }
3874
        }
3875
3876
        if ($useExtraFields) {
3877
            $finalResult = [];
3878
            if (count($extraFieldResult) > 1) {
3879
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
3880
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
3881
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
3882
                    }
3883
                }
3884
            } else {
3885
                $finalResult = $extraFieldResult[0];
3886
            }
3887
3888
            if (is_array($finalResult) && count($finalResult) > 0) {
3889
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
3890
            } else {
3891
                //no results
3892
                $whereFilter = " AND u.id  = -1 ";
3893
            }
3894
3895
            return $whereFilter;
3896
        }
3897
3898
        return '';
3899
    }
3900
3901
    /**
3902
     * Show the search form.
3903
     *
3904
     * @param string $query the value of the search box
3905
     *
3906
     * @throws Exception
3907
     *
3908
     * @return string HTML form
3909
     */
3910
    public static function get_search_form($query, $defaultParams = [])
3911
    {
3912
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
3913
        $form = new FormValidator(
3914
            'search_user',
3915
            'get',
3916
            api_get_path(WEB_PATH).'main/social/search.php',
3917
            '',
3918
            [],
3919
            FormValidator::LAYOUT_HORIZONTAL
3920
        );
3921
3922
        $query = Security::remove_XSS($query);
3923
3924
        if (!empty($query)) {
3925
            $form->addHeader(get_lang('Results and feedback').' "'.$query.'"');
0 ignored issues
show
Bug introduced by
Are you sure $query of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

3925
            $form->addHeader(get_lang('Results and feedback').' "'./** @scrutinizer ignore-type */ $query.'"');
Loading history...
3926
        }
3927
3928
        $form->addText(
3929
            'q',
3930
            get_lang('Users, Groups'),
3931
            false,
3932
            [
3933
                'id' => 'q',
3934
            ]
3935
        );
3936
        $options = [
3937
            0 => get_lang('Select'),
3938
            1 => get_lang('User'),
3939
            2 => get_lang('Group'),
3940
        ];
3941
        $form->addSelect(
3942
            'search_type',
3943
            get_lang('Type'),
3944
            $options,
3945
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
3946
        );
3947
3948
        // Extra fields
3949
        $extraFields = self::getExtraFilterableFields();
3950
        $defaults = [];
3951
        if (is_array($extraFields) && count($extraFields) > 0) {
3952
            foreach ($extraFields as $extraField) {
3953
                $varName = 'field_'.$extraField['variable'];
3954
                $options = [
3955
                    0 => get_lang('Select'),
3956
                ];
3957
                foreach ($extraField['data'] as $option) {
3958
                    if (isset($_GET[$varName])) {
3959
                        if ($_GET[$varName] == $option[1]) {
3960
                            $defaults[$option[1]] = true;
3961
                        }
3962
                    }
3963
3964
                    $options[$option[1]] = $option[1];
3965
                }
3966
                $form->addSelect($varName, $extraField['name'], $options);
3967
            }
3968
        }
3969
3970
        $defaults['search_type'] = (int) $searchType;
3971
        $defaults['q'] = $query;
3972
3973
        if (!empty($defaultParams)) {
3974
            $defaults = array_merge($defaults, $defaultParams);
3975
        }
3976
        $form->setDefaults($defaults);
3977
        $form->addButtonSearch(get_lang('Search'));
3978
3979
        $js = '<script>
3980
        extra_field_toogle();
3981
        function extra_field_toogle() {
3982
            if (jQuery("select[name=search_type]").val() != "1") {
3983
                jQuery(".extra_field").hide();
3984
            } else {
3985
                jQuery(".extra_field").show();
3986
            }
3987
        }
3988
        </script>';
3989
3990
        return $js.$form->returnForm();
3991
    }
3992
3993
    /**
3994
     * @param int $userId
3995
     *
3996
     * @return array
3997
     */
3998
    public static function getDrhListFromUser($userId)
3999
    {
4000
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4001
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4002
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4003
        $userId = (int) $userId;
4004
4005
        $orderBy = null;
4006
        if (api_is_western_name_order()) {
4007
            $orderBy .= ' ORDER BY firstname, lastname ';
4008
        } else {
4009
            $orderBy .= ' ORDER BY lastname, firstname ';
4010
        }
4011
4012
        $sql = "SELECT u.id, username, u.firstname, u.lastname
4013
                FROM $tblUser u
4014
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
4015
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
4016
                WHERE
4017
                    access_url_id = ".api_get_current_access_url_id()." AND
4018
                    uru.user_id = '$userId' AND
4019
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4020
                    $orderBy
4021
                ";
4022
        $result = Database::query($sql);
4023
4024
        return Database::store_result($result);
4025
    }
4026
4027
    /**
4028
     * get users followed by human resource manager.
4029
     *
4030
     * @param int    $userId
4031
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
4032
     * @param bool   $getOnlyUserId
4033
     * @param bool   $getSql
4034
     * @param bool   $getCount
4035
     * @param int    $from
4036
     * @param int    $numberItems
4037
     * @param int    $column
4038
     * @param string $direction
4039
     * @param int    $active
4040
     * @param string $lastConnectionDate
4041
     *
4042
     * @return array users
4043
     */
4044
    public static function get_users_followed_by_drh(
4045
        $userId,
4046
        $userStatus = 0,
4047
        $getOnlyUserId = false,
4048
        $getSql = false,
4049
        $getCount = false,
4050
        $from = null,
4051
        $numberItems = null,
4052
        $column = null,
4053
        $direction = null,
4054
        $active = null,
4055
        $lastConnectionDate = null
4056
    ) {
4057
        return self::getUsersFollowedByUser(
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::getUsersFol...astConnectionDate, DRH) also could return the type string which is incompatible with the documented return type array.
Loading history...
4058
            $userId,
4059
            $userStatus,
4060
            $getOnlyUserId,
4061
            $getSql,
4062
            $getCount,
4063
            $from,
4064
            $numberItems,
4065
            $column,
4066
            $direction,
4067
            $active,
4068
            $lastConnectionDate,
4069
            DRH
4070
        );
4071
    }
4072
4073
    /**
4074
     * Get users followed by human resource manager.
4075
     *
4076
     * @param int    $userId
4077
     * @param int    $userStatus             Filter users by status (STUDENT, COURSEMANAGER, etc)
4078
     * @param bool   $getOnlyUserId
4079
     * @param bool   $getSql
4080
     * @param bool   $getCount
4081
     * @param int    $from
4082
     * @param int    $numberItems
4083
     * @param int    $column
4084
     * @param string $direction
4085
     * @param int    $active
4086
     * @param string $lastConnectionDate
4087
     * @param int    $status                 the function is called by who? COURSEMANAGER, DRH?
4088
     * @param string $keyword
4089
     * @param bool   $checkSessionVisibility
4090
     *
4091
     * @return mixed Users list (array) or the SQL query if $getSQL was set to true
4092
     */
4093
    public static function getUsersFollowedByUser(
4094
        $userId,
4095
        $userStatus = null,
4096
        $getOnlyUserId = false,
4097
        $getSql = false,
4098
        $getCount = false,
4099
        $from = null,
4100
        $numberItems = null,
4101
        $column = null,
4102
        $direction = null,
4103
        $active = null,
4104
        $lastConnectionDate = null,
4105
        $status = null,
4106
        $keyword = null,
4107
        $checkSessionVisibility = false
4108
    ) {
4109
        // Database Table Definitions
4110
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4111
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4112
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4113
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4114
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4115
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4116
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4117
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4118
4119
        $userId = (int) $userId;
4120
        $limitCondition = '';
4121
4122
        if (isset($from) && isset($numberItems)) {
4123
            $from = (int) $from;
4124
            $numberItems = (int) $numberItems;
4125
            $limitCondition = "LIMIT $from, $numberItems";
4126
        }
4127
4128
        $column = Database::escape_string($column);
4129
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
4130
4131
        $userConditions = '';
4132
        if (!empty($userStatus)) {
4133
            $userConditions .= ' AND u.status = '.intval($userStatus);
4134
        }
4135
4136
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
4137
        if ($getOnlyUserId) {
4138
            $select = " SELECT DISTINCT u.id user_id";
4139
        }
4140
4141
        $masterSelect = "SELECT DISTINCT * FROM ";
4142
4143
        if ($getCount) {
4144
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
4145
            $select = " SELECT DISTINCT(u.id) user_id";
4146
        }
4147
4148
        if (!is_null($active)) {
4149
            $active = intval($active);
4150
            $userConditions .= " AND u.active = $active ";
4151
        }
4152
4153
        if (!empty($keyword)) {
4154
            $keyword = trim(Database::escape_string($keyword));
4155
            $keywordParts = array_filter(explode(' ', $keyword));
4156
            $extraKeyword = '';
4157
            if (!empty($keywordParts)) {
4158
                $keywordPartsFixed = Database::escape_string(implode('%', $keywordParts));
4159
                if (!empty($keywordPartsFixed)) {
4160
                    $extraKeyword .= " OR
4161
                        CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keywordPartsFixed%' OR
4162
                        CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keywordPartsFixed%' ";
4163
                }
4164
            }
4165
            $userConditions .= " AND (
4166
                u.username LIKE '%$keyword%' OR
4167
                u.firstname LIKE '%$keyword%' OR
4168
                u.lastname LIKE '%$keyword%' OR
4169
                u.official_code LIKE '%$keyword%' OR
4170
                u.email LIKE '%$keyword%' OR
4171
                CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keyword%' OR
4172
                CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keyword%'
4173
                $extraKeyword
4174
            )";
4175
        }
4176
4177
        if (!empty($lastConnectionDate)) {
4178
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
4179
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
4180
        }
4181
4182
        $sessionConditionsCoach = null;
4183
        $dateCondition = '';
4184
        $drhConditions = null;
4185
        $teacherSelect = null;
4186
        $urlId = api_get_current_access_url_id();
4187
4188
        switch ($status) {
4189
            case DRH:
4190
                $drhConditions .= " AND
4191
                    friend_user_id = '$userId' AND
4192
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4193
                ";
4194
                break;
4195
            case COURSEMANAGER:
4196
                $drhConditions .= " AND
4197
                    friend_user_id = '$userId' AND
4198
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4199
                ";
4200
4201
                $sessionConditionsTeacher = " AND
4202
                    (scu.status = ".SessionEntity::COURSE_COACH." AND scu.user_id = '$userId')
4203
                ";
4204
4205
                if ($checkSessionVisibility) {
4206
                    $today = api_strtotime('now', 'UTC');
4207
                    $today = date('Y-m-d', $today);
4208
                    $dateCondition = "
4209
                        AND
4210
                        (
4211
                            (s.access_start_date <= '$today' AND '$today' <= s.access_end_date) OR
4212
                            (s.access_start_date IS NULL AND s.access_end_date IS NULL) OR
4213
                            (s.access_start_date <= '$today' AND s.access_end_date IS NULL) OR
4214
                            ('$today' <= s.access_end_date AND s.access_start_date IS NULL)
4215
                        )
4216
					";
4217
                }
4218
4219
                // Use $tbl_session_rel_course_rel_user instead of $tbl_session_rel_user
4220
                /*
4221
                INNER JOIN $tbl_session_rel_user sru
4222
                ON (sru.user_id = u.id)
4223
                INNER JOIN $tbl_session_rel_course_rel_user scu
4224
                ON (scu.user_id = u.id AND scu.c_id IS NOT NULL AND visibility = 1)*/
4225
                $teacherSelect =
4226
                "UNION ALL (
4227
                        $select
4228
                        FROM $tbl_user u
4229
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
4230
                        WHERE
4231
                            (
4232
                                sru.session_id IN (
4233
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
4234
                                    $tbl_session_rel_access_url session_rel_access_rel_user
4235
                                    ON session_rel_access_rel_user.session_id = s.id
4236
                                    INNER JOIN $tbl_session_rel_user sru ON s.id = sru.session_id
4237
                                    WHERE access_url_id = ".$urlId."
4238
                                        AND (sru.relation_type = ".SessionEntity::GENERAL_COACH."
4239
                                        AND sru.user_id = $userId)
4240
                                ) OR sru.session_id IN (
4241
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
4242
                                    INNER JOIN $tbl_session_rel_access_url url
4243
                                    ON (url.session_id = s.id)
4244
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
4245
                                    ON (scu.session_id = s.id)
4246
                                    WHERE access_url_id = ".$urlId."
4247
                                    $sessionConditionsTeacher
4248
                                    $dateCondition
4249
                                )
4250
                            )
4251
                            $userConditions
4252
                    )
4253
                    UNION ALL(
4254
                        $select
4255
                        FROM $tbl_user u
4256
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
4257
                        WHERE cu.c_id IN (
4258
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
4259
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
4260
                        )
4261
                        $userConditions
4262
                    )"
4263
                ;
4264
                break;
4265
            case STUDENT_BOSS:
4266
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".UserRelUser::USER_RELATION_TYPE_BOSS;
4267
                break;
4268
            case HRM_REQUEST:
4269
                $drhConditions .= " AND
4270
                    friend_user_id = '$userId' AND
4271
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_HRM_REQUEST."'
4272
                ";
4273
                break;
4274
        }
4275
4276
        $join = null;
4277
        $sql = " $masterSelect
4278
                (
4279
                    (
4280
                        $select
4281
                        FROM $tbl_user u
4282
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
4283
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
4284
                        $join
4285
                        WHERE
4286
                            access_url_id = ".$urlId."
4287
                            $drhConditions
4288
                            $userConditions
4289
                    )
4290
                    $teacherSelect
4291
4292
                ) as t1";
4293
4294
        if ($getSql) {
4295
            return $sql;
4296
        }
4297
        if ($getCount) {
4298
            $result = Database::query($sql);
4299
            $row = Database::fetch_array($result);
4300
4301
            return $row['count'];
4302
        }
4303
4304
        $orderBy = null;
4305
        if (false == $getOnlyUserId) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
4306
            if (api_is_western_name_order()) {
4307
                $orderBy .= " ORDER BY firstname, lastname ";
4308
            } else {
4309
                $orderBy .= " ORDER BY lastname, firstname ";
4310
            }
4311
4312
            if (!empty($column) && !empty($direction)) {
4313
                // Fixing order due the UNIONs
4314
                $column = str_replace('u.', '', $column);
4315
                $orderBy = " ORDER BY `$column` $direction ";
4316
            }
4317
        }
4318
4319
        $sql .= $orderBy;
4320
        $sql .= $limitCondition;
4321
4322
        $result = Database::query($sql);
4323
        $users = [];
4324
        if (Database::num_rows($result) > 0) {
4325
            while ($row = Database::fetch_array($result)) {
4326
                $users[$row['user_id']] = $row;
4327
            }
4328
        }
4329
4330
        return $users;
4331
    }
4332
4333
    /**
4334
     * Subscribes users to human resource manager (Dashboard feature).
4335
     *
4336
     * @param int   $hr_dept_id
4337
     * @param array $users_id
4338
     * @param bool  $deleteOtherAssignedUsers
4339
     */
4340
    public static function subscribeUsersToHRManager(
4341
        $hr_dept_id,
4342
        $users_id,
4343
        $deleteOtherAssignedUsers = true
4344
    ): void {
4345
        self::subscribeUsersToUser(
4346
            $hr_dept_id,
4347
            $users_id,
4348
            UserRelUser::USER_RELATION_TYPE_RRHH,
4349
            false,
4350
            $deleteOtherAssignedUsers
4351
        );
4352
    }
4353
4354
    /**
4355
     * Register request to assign users to HRM.
4356
     *
4357
     * @param int   $hrmId   The HRM ID
4358
     * @param array $usersId The users IDs
4359
     */
4360
    public static function requestUsersToHRManager($hrmId, $usersId): void
4361
    {
4362
        self::subscribeUsersToUser(
4363
            $hrmId,
4364
            $usersId,
4365
            UserRelUser::USER_RELATION_TYPE_HRM_REQUEST,
4366
            false,
4367
            false
4368
        );
4369
    }
4370
4371
    /**
4372
     * Remove the requests for assign a user to a HRM.
4373
     *
4374
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
4375
     */
4376
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
4377
    {
4378
        $users = implode(', ', $usersId);
4379
        Database::getManager()
4380
            ->createQuery('
4381
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
4382
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
4383
            ')
4384
            ->execute(['hrm_id' => $hrmId, 'relation_type' => UserRelUser::USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
4385
    }
4386
4387
    /**
4388
     * Add subscribed users to a user by relation type.
4389
     *
4390
     * @param int   $userId                   The user id
4391
     * @param array $subscribedUsersId        The id of subscribed users
4392
     * @param int   $relationType             The relation type
4393
     * @param bool  $deleteUsersBeforeInsert
4394
     * @param bool  $deleteOtherAssignedUsers
4395
     */
4396
    public static function subscribeUsersToUser(
4397
        $userId,
4398
        $subscribedUsersId,
4399
        $relationType,
4400
        $deleteUsersBeforeInsert = false,
4401
        $deleteOtherAssignedUsers = true
4402
    ): void {
4403
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4404
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4405
4406
        $userId = (int) $userId;
4407
        $relationType = (int) $relationType;
4408
4409
        if ($deleteOtherAssignedUsers) {
4410
            if (api_get_multiple_access_url()) {
4411
                // Deleting assigned users to hrm_id
4412
                $sql = "SELECT s.user_id
4413
                        FROM $userRelUserTable s
4414
                        INNER JOIN $userRelAccessUrlTable a
4415
                        ON (a.user_id = s.user_id)
4416
                        WHERE
4417
                            friend_user_id = $userId AND
4418
                            relation_type = $relationType AND
4419
                            access_url_id = ".api_get_current_access_url_id();
4420
            } else {
4421
                $sql = "SELECT user_id
4422
                        FROM $userRelUserTable
4423
                        WHERE
4424
                            friend_user_id = $userId AND
4425
                            relation_type = $relationType";
4426
            }
4427
            $result = Database::query($sql);
4428
4429
            if (Database::num_rows($result) > 0) {
4430
                while ($row = Database::fetch_array($result)) {
4431
                    $sql = "DELETE FROM $userRelUserTable
4432
                            WHERE
4433
                                user_id = {$row['user_id']} AND
4434
                                friend_user_id = $userId AND
4435
                                relation_type = $relationType";
4436
                    Database::query($sql);
4437
                }
4438
            }
4439
        }
4440
4441
        if ($deleteUsersBeforeInsert) {
4442
            $sql = "DELETE FROM $userRelUserTable
4443
                    WHERE
4444
                        user_id = $userId AND
4445
                        relation_type = $relationType";
4446
            Database::query($sql);
4447
        }
4448
4449
        // Inserting new user list.
4450
        if (is_array($subscribedUsersId)) {
4451
            foreach ($subscribedUsersId as $subscribedUserId) {
4452
                $subscribedUserId = (int) $subscribedUserId;
4453
                $sql = "SELECT id
4454
                        FROM $userRelUserTable
4455
                        WHERE
4456
                            user_id = $subscribedUserId AND
4457
                            friend_user_id = $userId AND
4458
                            relation_type = $relationType";
4459
4460
                $result = Database::query($sql);
4461
                $num = Database::num_rows($result);
4462
                if (0 === $num) {
4463
                    $userRelUser = (new UserRelUser())
4464
                        ->setUser(api_get_user_entity($subscribedUserId))
4465
                        ->setFriend(api_get_user_entity($userId))
4466
                        ->setRelationType($relationType)
4467
                    ;
4468
                    $em = Database::getManager();
4469
                    $em->persist($userRelUser);
4470
                    $em->flush();
4471
                }
4472
            }
4473
        }
4474
    }
4475
4476
    /**
4477
     * This function checks if a user is followed by provided human resources managers.
4478
     *
4479
     * @param int $user_id
4480
     * @param int $hr_dept_id Human resources manager
4481
     *
4482
     * @return bool
4483
     * @throws Exception
4484
     * @throws \Doctrine\DBAL\Exception
4485
     */
4486
    public static function is_user_followed_by_drh(int $user_id, int $hr_dept_id): bool
4487
    {
4488
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4489
        $result = false;
4490
4491
        $sql = "SELECT user_id FROM $tbl_user_rel_user
4492
                WHERE
4493
                    user_id = $user_id AND
4494
                    friend_user_id = $hr_dept_id AND
4495
                    relation_type = ".UserRelUser::USER_RELATION_TYPE_RRHH;
4496
        $rs = Database::query($sql);
4497
        if (Database::num_rows($rs) > 0) {
4498
            $result = true;
4499
        }
4500
4501
        return $result;
4502
    }
4503
4504
    /**
4505
     * Return the user id of teacher or session administrator.
4506
     *
4507
     * @param array $courseInfo
4508
     *
4509
     * @return int The user id, or 0 if the session ID was negative
4510
     * @throws Exception
4511
     * @throws \Doctrine\DBAL\Exception
4512
     */
4513
    public static function get_user_id_of_course_admin_or_session_admin(array $courseInfo): int
4514
    {
4515
        $session = api_get_session_id();
4516
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4517
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4518
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4519
4520
        if (empty($courseInfo)) {
4521
            return 0;
4522
        }
4523
4524
        $courseId = $courseInfo['real_id'];
4525
4526
        if (0 == $session) {
4527
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
4528
                    INNER JOIN '.$table_course_user.' ru
4529
                    ON ru.user_id = u.id
4530
                    WHERE
4531
                        ru.status = 1 AND
4532
                        ru.c_id = "'.$courseId.'" ';
4533
            $rs = Database::query($sql);
4534
            $num_rows = Database::num_rows($rs);
4535
            if (1 == $num_rows) {
4536
                $row = Database::fetch_array($rs);
4537
4538
                return (int) $row['uid'];
4539
            } else {
4540
                $my_num_rows = $num_rows;
4541
4542
                return (int) Database::result($rs, $my_num_rows - 1, 'uid');
4543
            }
4544
        } elseif ($session > 0) {
4545
            $sql = 'SELECT u.id as uid FROM '.$table_user.' u
4546
                    INNER JOIN '.$table_session_course_user.' sru
4547
                    ON sru.user_id = u.id
4548
                    WHERE
4549
                        sru.c_id = '.$courseId.' AND
4550
                        sru.status = '.SessionEntity::COURSE_COACH;
4551
            $rs = Database::query($sql);
4552
            if (Database::num_rows($rs) > 0) {
4553
                $row = Database::fetch_assoc($rs);
4554
4555
                return (int) $row['uid'];
4556
            }
4557
        }
4558
4559
        return 0;
4560
    }
4561
4562
    /**
4563
     * Determines if a user is a gradebook certified.
4564
     *
4565
     * @param int $cat_id  The category id of gradebook
4566
     * @param int $user_id The user id
4567
     *
4568
     * @return bool
4569
     */
4570
    public static function is_user_certified($cat_id, $user_id)
4571
    {
4572
        $cat_id = (int) $cat_id;
4573
        $user_id = (int) $user_id;
4574
4575
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4576
        $sql = 'SELECT path_certificate
4577
                FROM '.$table.'
4578
                WHERE
4579
                    cat_id = "'.$cat_id.'" AND
4580
                    user_id = "'.$user_id.'"';
4581
        $rs = Database::query($sql);
4582
        $row = Database::fetch_array($rs);
4583
4584
        if (!isset($row['path_certificate']) || '' == $row['path_certificate'] || is_null($row['path_certificate'])) {
4585
            return false;
4586
        }
4587
4588
        return true;
4589
    }
4590
4591
    /**
4592
     * Gets the info about a gradebook certificate for a user by course.
4593
     *
4594
     * @param array $course_info The course code
4595
     * @param int   $session_id
4596
     * @param int   $user_id     The user id
4597
     *
4598
     * @return array if there is not information return false
4599
     */
4600
    public static function get_info_gradebook_certificate($course_info, $session_id, $user_id)
4601
    {
4602
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4603
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
4604
        $session_id = (int) $session_id;
4605
        $user_id = (int) $user_id;
4606
        $courseId = $course_info['real_id'];
4607
4608
        if (empty($session_id)) {
4609
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
4610
        } else {
4611
            $session_condition = " AND session_id = $session_id";
4612
        }
4613
4614
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.'
4615
                WHERE cat_id = (
4616
                    SELECT id FROM '.$tbl_grade_category.'
4617
                    WHERE
4618
                        c_id = "'.$courseId.'" '.$session_condition.'
4619
                    LIMIT 1
4620
                ) AND user_id='.$user_id;
4621
4622
        $rs = Database::query($sql);
4623
        if (Database::num_rows($rs) > 0) {
4624
            $row = Database::fetch_assoc($rs);
4625
            $score = $row['score_certificate'];
4626
            $category_id = $row['cat_id'];
4627
            $cat = Category::load($category_id);
4628
            $displayscore = ScoreDisplay::instance();
4629
            if (isset($cat) && $displayscore->is_custom()) {
4630
                $grade = $displayscore->display_score(
4631
                    [$score, $cat[0]->get_weight()],
4632
                    SCORE_DIV_PERCENT_WITH_CUSTOM
4633
                );
4634
            } else {
4635
                $grade = $displayscore->display_score(
4636
                    [$score, $cat[0]->get_weight()]
4637
                );
4638
            }
4639
            $row['grade'] = $grade;
4640
4641
            return $row;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $row also could return the type boolean which is incompatible with the documented return type array.
Loading history...
4642
        }
4643
4644
        return false;
4645
    }
4646
4647
    /**
4648
     * This function check if the user is a coach inside session course.
4649
     *
4650
     * @param int $user_id    User id
4651
     * @param int $courseId
4652
     * @param int $session_id
4653
     *
4654
     * @return bool True if the user is a coach
4655
     */
4656
    public static function is_session_course_coach($user_id, $courseId, $session_id)
4657
    {
4658
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4659
        // Protect data
4660
        $user_id = intval($user_id);
4661
        $courseId = intval($courseId);
4662
        $session_id = intval($session_id);
4663
        $result = false;
4664
4665
        $sql = "SELECT session_id FROM $table
4666
                WHERE
4667
                  session_id = $session_id AND
4668
                  c_id = $courseId AND
4669
                  user_id = $user_id AND
4670
                  status = ".SessionEntity::COURSE_COACH;
4671
        $res = Database::query($sql);
4672
4673
        if (Database::num_rows($res) > 0) {
4674
            $result = true;
4675
        }
4676
4677
        return $result;
4678
    }
4679
4680
    /**
4681
     * This function returns an icon path that represents the favicon of the website of which the url given.
4682
     * Defaults to the current Chamilo favicon.
4683
     *
4684
     * @param string $url1 URL of website where to look for favicon.ico
4685
     * @param string $url2 Optional second URL of website where to look for favicon.ico
4686
     *
4687
     * @return string Path of icon to load
4688
     */
4689
    public static function get_favicon_from_url($url1, $url2 = null)
4690
    {
4691
        $icon_link = '';
4692
        $url = $url1;
4693
        if (empty($url1)) {
4694
            $url = $url2;
4695
            if (empty($url)) {
4696
                $url = api_get_access_url(api_get_current_access_url_id());
4697
                $url = $url[0];
4698
            }
4699
        }
4700
        if (!empty($url)) {
4701
            $pieces = parse_url($url);
4702
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
4703
        }
4704
4705
        return $icon_link;
4706
    }
4707
4708
    public static function addUserAsAdmin(User $user)
4709
    {
4710
        $userId = $user->getId();
4711
4712
        if (!self::is_admin($userId)) {
4713
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
4714
            $sql = "INSERT INTO $table SET user_id = $userId";
4715
            Database::query($sql);
4716
        }
4717
4718
        $user->addRole('ROLE_ADMIN');
4719
        self::getRepository()->updateUser($user, true);
4720
    }
4721
4722
    public static function removeUserAdmin(User $user)
4723
    {
4724
        $userId = (int) $user->getId();
4725
        if (self::is_admin($userId)) {
4726
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
4727
            $sql = "DELETE FROM $table WHERE user_id = $userId";
4728
            Database::query($sql);
4729
            $user->removeRole('ROLE_ADMIN');
4730
            self::getRepository()->updateUser($user, true);
4731
        }
4732
    }
4733
4734
    /**
4735
     * @param string $from
4736
     * @param string $to
4737
     */
4738
    public static function update_all_user_languages($from, $to)
4739
    {
4740
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4741
        $from = Database::escape_string($from);
4742
        $to = Database::escape_string($to);
4743
4744
        if (!empty($to) && !empty($from)) {
4745
            $sql = "UPDATE $table_user SET language = '$to'
4746
                    WHERE language = '$from'";
4747
            Database::query($sql);
4748
        }
4749
    }
4750
4751
    /**
4752
     * Subscribe boss to students.
4753
     *
4754
     * @param int   $bossId                   The boss id
4755
     * @param array $usersId                  The users array
4756
     * @param bool  $deleteOtherAssignedUsers
4757
     */
4758
    public static function subscribeBossToUsers($bossId, $usersId, $deleteOtherAssignedUsers = true): void
4759
    {
4760
        self::subscribeUsersToUser(
4761
            $bossId,
4762
            $usersId,
4763
            UserRelUser::USER_RELATION_TYPE_BOSS,
4764
            false,
4765
            $deleteOtherAssignedUsers
4766
        );
4767
    }
4768
4769
    /**
4770
     * @param int $userId
4771
     *
4772
     * @return bool
4773
     */
4774
    public static function removeAllBossFromStudent($userId)
4775
    {
4776
        $userId = (int) $userId;
4777
4778
        if (empty($userId)) {
4779
            return false;
4780
        }
4781
4782
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4783
        $sql = "DELETE FROM $userRelUserTable
4784
                WHERE user_id = $userId AND relation_type = ".UserRelUser::USER_RELATION_TYPE_BOSS;
4785
        Database::query($sql);
4786
4787
        return true;
4788
    }
4789
4790
    /**
4791
     * Subscribe boss to students, if $bossList is empty then the boss list will be empty too.
4792
     *
4793
     * @param int   $studentId
4794
     * @param array $bossList
4795
     * @param bool  $sendNotification
4796
     *
4797
     * @return mixed Affected rows or false on failure
4798
     */
4799
    public static function subscribeUserToBossList(
4800
        $studentId,
4801
        $bossList,
4802
        $sendNotification = false
4803
    ) {
4804
        $inserted = 0;
4805
        if (!empty($bossList)) {
4806
            sort($bossList);
4807
            $studentId = (int) $studentId;
4808
            $studentInfo = api_get_user_info($studentId);
4809
4810
            if (empty($studentInfo)) {
4811
                return false;
4812
            }
4813
4814
            $previousBossList = self::getStudentBossList($studentId);
4815
            $previousBossList = !empty($previousBossList) ? array_column($previousBossList, 'boss_id') : [];
4816
            sort($previousBossList);
4817
4818
            // Boss list is the same, nothing changed.
4819
            if ($bossList == $previousBossList) {
4820
                return false;
4821
            }
4822
4823
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4824
            self::removeAllBossFromStudent($studentId);
4825
4826
            foreach ($bossList as $bossId) {
4827
                $bossId = (int) $bossId;
4828
                $bossInfo = api_get_user_info($bossId);
4829
4830
                if (empty($bossInfo)) {
4831
                    continue;
4832
                }
4833
4834
                $bossLanguage = $bossInfo['locale'];
4835
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
4836
                        VALUES ($studentId, $bossId, ".UserRelUser::USER_RELATION_TYPE_BOSS.")";
4837
                $insertId = Database::query($sql);
4838
4839
                if ($insertId) {
4840
                    if ($sendNotification) {
4841
                        $name = $studentInfo['complete_name'];
4842
                        $url = api_get_path(WEB_CODE_PATH).'my_space/myStudents.php?student='.$studentId;
4843
                        $url = Display::url($url, $url);
4844
                        $subject = sprintf(get_lang('You have been assigned the learner %s', $bossLanguage), $name);
4845
                        $message = sprintf(get_lang('You have been assigned the learner %s with url %s', $bossLanguage), $name, $url);
4846
                        MessageManager::send_message_simple(
4847
                            $bossId,
4848
                            $subject,
4849
                            $message
4850
                        );
4851
                    }
4852
                    $inserted++;
4853
                }
4854
            }
4855
        } else {
4856
            self::removeAllBossFromStudent($studentId);
4857
        }
4858
4859
        return $inserted;
4860
    }
4861
4862
    /**
4863
     * Get users followed by student boss.
4864
     *
4865
     * @param int    $userId
4866
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
4867
     * @param bool   $getOnlyUserId
4868
     * @param bool   $getSql
4869
     * @param bool   $getCount
4870
     * @param int    $from
4871
     * @param int    $numberItems
4872
     * @param int    $column
4873
     * @param string $direction
4874
     * @param int    $active
4875
     * @param string $lastConnectionDate
4876
     *
4877
     * @return array users
4878
     */
4879
    public static function getUsersFollowedByStudentBoss(
4880
        $userId,
4881
        $userStatus = 0,
4882
        $getOnlyUserId = false,
4883
        $getSql = false,
4884
        $getCount = false,
4885
        $from = null,
4886
        $numberItems = null,
4887
        $column = null,
4888
        $direction = null,
4889
        $active = null,
4890
        $lastConnectionDate = null
4891
    ) {
4892
        return self::getUsersFollowedByUser(
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::getUsersFol...tionDate, STUDENT_BOSS) also could return the type string which is incompatible with the documented return type array.
Loading history...
4893
            $userId,
4894
            $userStatus,
4895
            $getOnlyUserId,
4896
            $getSql,
4897
            $getCount,
4898
            $from,
4899
            $numberItems,
4900
            $column,
4901
            $direction,
4902
            $active,
4903
            $lastConnectionDate,
4904
            STUDENT_BOSS
4905
        );
4906
    }
4907
4908
    /**
4909
     * @return array
4910
     */
4911
    public static function getOfficialCodeGrouped()
4912
    {
4913
        $user = Database::get_main_table(TABLE_MAIN_USER);
4914
        $sql = "SELECT DISTINCT official_code
4915
                FROM $user
4916
                GROUP BY official_code";
4917
        $result = Database::query($sql);
4918
        $values = Database::store_result($result, 'ASSOC');
4919
        $result = [];
4920
        foreach ($values as $value) {
4921
            $result[$value['official_code']] = $value['official_code'];
4922
        }
4923
4924
        return $result;
4925
    }
4926
4927
    /**
4928
     * @param string $officialCode
4929
     *
4930
     * @return array
4931
     */
4932
    public static function getUsersByOfficialCode($officialCode)
4933
    {
4934
        $user = Database::get_main_table(TABLE_MAIN_USER);
4935
        $officialCode = Database::escape_string($officialCode);
4936
4937
        $sql = "SELECT DISTINCT id
4938
                FROM $user
4939
                WHERE official_code = '$officialCode'
4940
                ";
4941
        $result = Database::query($sql);
4942
4943
        $users = [];
4944
        while ($row = Database::fetch_array($result)) {
4945
            $users[] = $row['id'];
4946
        }
4947
4948
        return $users;
4949
    }
4950
4951
    /**
4952
     * Calc the expended time (in seconds) by a user in a course.
4953
     *
4954
     * @param int    $userId    The user id
4955
     * @param int    $courseId  The course id
4956
     * @param int    $sessionId Optional. The session id
4957
     * @param string $from      Optional. From date
4958
     * @param string $until     Optional. Until date
4959
     *
4960
     * @return int The time
4961
     */
4962
    public static function getTimeSpentInCourses(
4963
        $userId,
4964
        $courseId,
4965
        $sessionId = 0,
4966
        $from = '',
4967
        $until = ''
4968
    ) {
4969
        $userId = (int) $userId;
4970
        $sessionId = (int) $sessionId;
4971
4972
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
4973
        $whereConditions = [
4974
            'user_id = ? ' => $userId,
4975
            'AND c_id = ? ' => $courseId,
4976
            'AND session_id = ? ' => $sessionId,
4977
        ];
4978
4979
        if (!empty($from) && !empty($until)) {
4980
            $whereConditions["AND (login_course_date >= '?' "] = $from;
4981
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
4982
        }
4983
4984
        $trackResult = Database::select(
4985
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
4986
            $trackCourseAccessTable,
4987
            [
4988
                'where' => $whereConditions,
4989
            ],
4990
            'first'
4991
        );
4992
4993
        if (false != $trackResult) {
4994
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
4995
        }
4996
4997
        return 0;
4998
    }
4999
5000
    /**
5001
     * Get the boss user ID from a followed user id.
5002
     *
5003
     * @param $userId
5004
     *
5005
     * @return bool
5006
     */
5007
    public static function getFirstStudentBoss($userId)
5008
    {
5009
        $userId = (int) $userId;
5010
        if ($userId > 0) {
5011
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5012
            $row = Database::select(
5013
                'DISTINCT friend_user_id AS boss_id',
5014
                $userRelTable,
5015
                [
5016
                    'where' => [
5017
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
5018
                            $userId,
5019
                            UserRelUser::USER_RELATION_TYPE_BOSS,
5020
                        ],
5021
                    ],
5022
                ]
5023
            );
5024
            if (!empty($row)) {
5025
                return $row[0]['boss_id'];
5026
            }
5027
        }
5028
5029
        return false;
5030
    }
5031
5032
    /**
5033
     * Get the boss user ID from a followed user id.
5034
     *
5035
     * @param int $userId student id
5036
     *
5037
     * @return array
5038
     */
5039
    public static function getStudentBossList($userId)
5040
    {
5041
        $userId = (int) $userId;
5042
5043
        if ($userId > 0) {
5044
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5045
5046
            return Database::select(
5047
                'DISTINCT friend_user_id AS boss_id',
5048
                $userRelTable,
5049
                [
5050
                    'where' => [
5051
                        'user_id = ? AND relation_type = ? ' => [
5052
                            $userId,
5053
                            UserRelUser::USER_RELATION_TYPE_BOSS,
5054
                        ],
5055
                    ],
5056
                ]
5057
            );
5058
        }
5059
5060
        return [];
5061
    }
5062
5063
    /**
5064
     * @param int $bossId
5065
     * @param int $studentId
5066
     *
5067
     * @return bool
5068
     */
5069
    public static function userIsBossOfStudent($bossId, $studentId)
5070
    {
5071
        $result = false;
5072
        $bossList = self::getStudentBossList($studentId);
5073
        if (!empty($bossList)) {
5074
            $bossList = array_column($bossList, 'boss_id');
5075
            if (in_array($bossId, $bossList)) {
5076
                $result = true;
5077
            }
5078
        }
5079
5080
        return $result;
5081
    }
5082
5083
    /**
5084
     * Displays the name of the user and makes the link to the user profile.
5085
     *
5086
     * @param array $userInfo
5087
     *
5088
     * @return string
5089
     */
5090
    public static function getUserProfileLink($userInfo)
5091
    {
5092
        if (isset($userInfo) && isset($userInfo['user_id'])) {
5093
            return Display::url(
5094
                $userInfo['complete_name_with_username'],
5095
                $userInfo['profile_url']
5096
            );
5097
        }
5098
5099
        return get_lang('Anonymous');
5100
    }
5101
5102
    /**
5103
     * Get users whose name matches $firstname and $lastname.
5104
     *
5105
     * @param string $firstname Firstname to search
5106
     * @param string $lastname  Lastname to search
5107
     *
5108
     * @return array The user list
5109
     */
5110
    public static function getUsersByName($firstname, $lastname)
5111
    {
5112
        $firstname = Database::escape_string($firstname);
5113
        $lastname = Database::escape_string($lastname);
5114
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
5115
5116
        $sql = <<<SQL
5117
            SELECT id, username, lastname, firstname
5118
            FROM $userTable
5119
            WHERE
5120
                firstname LIKE '$firstname%' AND
5121
                lastname LIKE '$lastname%'
5122
SQL;
5123
        $result = Database::query($sql);
5124
        $users = [];
5125
        while ($resultData = Database::fetch_object($result)) {
5126
            $users[] = $resultData;
5127
        }
5128
5129
        return $users;
5130
    }
5131
5132
    /**
5133
     * @param int $optionSelected
5134
     *
5135
     * @return string
5136
     */
5137
    public static function getUserSubscriptionTab($optionSelected = 1)
5138
    {
5139
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
5140
        if (('true' === $allowAdmin && api_is_allowed_to_edit()) ||
5141
            api_is_platform_admin()
5142
        ) {
5143
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
5144
5145
            $headers = [
5146
                [
5147
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
5148
                    'content' => get_lang('Learners'),
5149
                ],
5150
                [
5151
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
5152
                    'content' => get_lang('Trainers'),
5153
                ],
5154
                /*[
5155
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
5156
                    'content' => get_lang('Learners'),
5157
                ],
5158
                [
5159
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
5160
                    'content' => get_lang('Trainers'),
5161
                ],*/
5162
                [
5163
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
5164
                    'content' => get_lang('Groups'),
5165
                ],
5166
                [
5167
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
5168
                    'content' => get_lang('Classes'),
5169
                ],
5170
            ];
5171
5172
            return Display::tabsOnlyLink($headers, $optionSelected);
5173
        }
5174
5175
        return '';
5176
    }
5177
5178
    /**
5179
     * Make sure this function is protected because it does NOT check password!
5180
     *
5181
     * This function defines globals.
5182
     *
5183
     * @param int  $userId
5184
     * @param bool $checkIfUserCanLoginAs
5185
     *
5186
     * @return bool
5187
     *
5188
     * @author Evie Embrechts
5189
     * @author Yannick Warnier <[email protected]>
5190
     */
5191
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
5192
    {
5193
        $userId = (int) $userId;
5194
        $userInfo = api_get_user_info($userId);
5195
5196
        // Check if the user is allowed to 'login_as'
5197
        $canLoginAs = true;
5198
        if ($checkIfUserCanLoginAs) {
5199
            $canLoginAs = api_can_login_as($userId);
5200
        }
5201
5202
        if (!$canLoginAs || empty($userInfo)) {
5203
            return false;
5204
        }
5205
5206
        if ($userId) {
5207
            $logInfo = [
5208
                'tool' => 'logout',
5209
                'tool_id' => 0,
5210
                'tool_id_detail' => 0,
5211
                'action' => '',
5212
                'info' => 'Change user (login as)',
5213
            ];
5214
            Event::registerLog($logInfo);
0 ignored issues
show
Bug introduced by
The method registerLog() does not exist on Event. ( Ignorable by Annotation )

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

5214
            Event::/** @scrutinizer ignore-call */ 
5215
                   registerLog($logInfo);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
5215
5216
            // Logout the current user
5217
            self::loginDelete(api_get_user_id());
5218
5219
            return true;
5220
5221
            Session::erase('_user');
0 ignored issues
show
Unused Code introduced by
ChamiloSession::erase('_user') is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
5222
            Session::erase('is_platformAdmin');
5223
            Session::erase('is_allowedCreateCourse');
5224
            Session::erase('_uid');
5225
5226
            // Cleaning session variables
5227
            $_user['firstName'] = $userInfo['firstname'];
5228
            $_user['lastName'] = $userInfo['lastname'];
5229
            $_user['mail'] = $userInfo['email'];
5230
            $_user['official_code'] = $userInfo['official_code'];
5231
            $_user['picture_uri'] = $userInfo['picture_uri'];
5232
            $_user['user_id'] = $userId;
5233
            $_user['id'] = $userId;
5234
            $_user['status'] = $userInfo['status'];
5235
5236
            // Filling session variables with new data
5237
            Session::write('_uid', $userId);
5238
            Session::write('_user', $userInfo);
5239
            Session::write('is_platformAdmin', (bool) self::is_admin($userId));
5240
            Session::write('is_allowedCreateCourse', 1 == $userInfo['status']);
5241
            // will be useful later to know if the user is actually an admin or not (example reporting)
5242
            Session::write('login_as', true);
5243
            $logInfo = [
5244
                'tool' => 'login',
5245
                'tool_id' => 0,
5246
                'tool_id_detail' => 0,
5247
                'info' => $userId,
5248
            ];
5249
            Event::registerLog($logInfo);
5250
5251
            return true;
5252
        }
5253
5254
        return false;
5255
    }
5256
5257
    /**
5258
     * Remove all login records from the track_e_online stats table,
5259
     * for the given user ID.
5260
     *
5261
     * @param int $userId User ID
5262
     */
5263
    public static function loginDelete($userId)
5264
    {
5265
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5266
        $userId = (int) $userId;
5267
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
5268
        Database::query($query);
5269
    }
5270
5271
    /**
5272
     * Login as first admin user registered in the platform.
5273
     *
5274
     * @return array
5275
     */
5276
    public static function logInAsFirstAdmin()
5277
    {
5278
        $adminList = self::get_all_administrators();
5279
5280
        if (!empty($adminList)) {
5281
            $userInfo = current($adminList);
5282
            if (!empty($userInfo)) {
5283
                $result = self::loginAsUser($userInfo['user_id'], false);
5284
                if ($result && api_is_platform_admin()) {
5285
                    return api_get_user_info();
0 ignored issues
show
Bug Best Practice introduced by
The expression return api_get_user_info() could also return false which is incompatible with the documented return type array. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
5286
                }
5287
            }
5288
        }
5289
5290
        return [];
5291
    }
5292
5293
    /**
5294
     * Check if user is teacher of a student based in their courses.
5295
     *
5296
     * @param $teacherId
5297
     * @param $studentId
5298
     *
5299
     * @return array
5300
     */
5301
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
5302
    {
5303
        $courses = CourseManager::getCoursesFollowedByUser(
5304
            $teacherId,
5305
            COURSEMANAGER
5306
        );
5307
        if (empty($courses)) {
5308
            return false;
5309
        }
5310
5311
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
5312
        if (empty($coursesFromUser)) {
5313
            return false;
5314
        }
5315
5316
        $coursesCodeList = array_column($courses, 'code');
5317
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
5318
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
5319
        $commonCourses = array_filter($commonCourses);
5320
5321
        if (!empty($commonCourses)) {
5322
            return $commonCourses;
5323
        }
5324
5325
        return [];
5326
    }
5327
5328
    /**
5329
     * @param int $teacherId
5330
     * @param int $studentId
5331
     *
5332
     * @return bool
5333
     */
5334
    public static function isTeacherOfStudent($teacherId, $studentId)
5335
    {
5336
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
5337
            $teacherId,
5338
            $studentId
5339
        );
5340
5341
        if (!empty($courses)) {
5342
            return true;
5343
        }
5344
5345
        return false;
5346
    }
5347
5348
    /**
5349
     * Send user confirmation mail.
5350
     *
5351
     * @throws Exception
5352
     */
5353
    public static function sendUserConfirmationMail(User $user)
5354
    {
5355
        $uniqueId = api_get_unique_id();
5356
        $user->setConfirmationToken($uniqueId);
5357
5358
        Database::getManager()->persist($user);
5359
        Database::getManager()->flush();
5360
5361
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
5362
5363
        // Check if the user was originally set for an automated subscription to a course or session
5364
        $courseCodeToRedirect = Session::read('course_redirect');
5365
        $sessionToRedirect = Session::read('session_redirect');
5366
        if (!empty($courseCodeToRedirect)) {
5367
            $url .= '&c='.$courseCodeToRedirect;
5368
        }
5369
        if (!empty($sessionToRedirect)) {
5370
            $url .= '&s='.$sessionToRedirect;
5371
        }
5372
        $mailSubject = get_lang('Registration confirmation');
5373
        $mailBody = get_lang('Registration confirmationEmailMessage')
5374
            .PHP_EOL
5375
            .Display::url($url, $url);
5376
5377
        api_mail_html(
5378
            self::formatUserFullName($user),
5379
            $user->getEmail(),
5380
            $mailSubject,
5381
            $mailBody
5382
        );
5383
        Display::addFlash(Display::return_message(get_lang('Check your e-mail and follow the instructions.')));
5384
    }
5385
5386
    /**
5387
     * Anonymize a user. Replace personal info by anonymous info.
5388
     *
5389
     * @param int  $userId   User id
5390
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
5391
     *
5392
     * @throws \Exception
5393
     *
5394
     * @return bool
5395
     * @assert (0) === false
5396
     */
5397
    public static function anonymize($userId, $deleteIP = true)
5398
    {
5399
        global $debug;
5400
5401
        $userId = (int) $userId;
5402
5403
        if (empty($userId)) {
5404
            return false;
5405
        }
5406
5407
        $em = Database::getManager();
5408
        $user = api_get_user_entity($userId);
5409
        $uniqueId = uniqid('anon', true);
5410
        $user
5411
            ->setFirstname($uniqueId)
5412
            ->setLastname($uniqueId)
5413
            ->setBiography('')
5414
            ->setAddress('')
5415
            //->setCurriculumItems(null)
5416
            ->setDateOfBirth(null)
5417
            ->setCompetences('')
5418
            ->setDiplomas('')
5419
            ->setOpenarea('')
5420
            ->setTeach('')
5421
            ->setProductions(null)
5422
            ->setOpenid('')
5423
            ->setEmailCanonical($uniqueId.'@example.com')
5424
            ->setEmail($uniqueId.'@example.com')
5425
            ->setUsername($uniqueId)
5426
            ->setUsernameCanonical($uniqueId)
5427
            ->setPhone('')
5428
            ->setOfficialCode('')
5429
        ;
5430
5431
        self::deleteUserPicture($userId);
5432
        self::cleanUserRequestsOfRemoval($userId);
5433
5434
        // The IP address is a border-case personal data, as it does
5435
        // not directly allow for personal identification (it is not
5436
        // a completely safe value in most countries - the IP could
5437
        // be used by neighbours and crackers)
5438
        if ($deleteIP) {
5439
            $substitute = '127.0.0.1';
5440
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
5441
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
5442
            $res = Database::query($sql);
5443
            if (false === $res && $debug > 0) {
5444
                error_log("Could not anonymize IP address for user $userId ($sql)");
5445
            }
5446
5447
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5448
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5449
            $res = Database::query($sql);
5450
            if (false === $res && $debug > 0) {
5451
                error_log("Could not anonymize IP address for user $userId ($sql)");
5452
            }
5453
5454
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
5455
            $sql = "UPDATE $table SET user_ip = '$substitute' WHERE exe_user_id = $userId";
5456
            $res = Database::query($sql);
5457
            if (false === $res && $debug > 0) {
5458
                error_log("Could not anonymize IP address for user $userId ($sql)");
5459
            }
5460
5461
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
5462
            $sql = "UPDATE $table SET user_ip = '$substitute' WHERE login_user_id = $userId";
5463
            $res = Database::query($sql);
5464
            if (false === $res && $debug > 0) {
5465
                error_log("Could not anonymize IP address for user $userId ($sql)");
5466
            }
5467
5468
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5469
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
5470
            $res = Database::query($sql);
5471
            if (false === $res && $debug > 0) {
5472
                error_log("Could not anonymize IP address for user $userId ($sql)");
5473
            }
5474
5475
            $table = Database::get_course_table(TABLE_WIKI);
5476
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5477
            $res = Database::query($sql);
5478
            if (false === $res && $debug > 0) {
5479
                error_log("Could not anonymize IP address for user $userId ($sql)");
5480
            }
5481
5482
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
5483
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
5484
            $res = Database::query($sql);
5485
            if (false === $res && $debug > 0) {
5486
                error_log("Could not anonymize IP address for user $userId ($sql)");
5487
            }
5488
5489
            $table = Database::get_course_table(TABLE_WIKI);
5490
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5491
            $res = Database::query($sql);
5492
            if (false === $res && $debug > 0) {
5493
                error_log("Could not anonymize IP address for user $userId ($sql)");
5494
            }
5495
        }
5496
5497
        $extraFieldRepository = $em->getRepository(EntityExtraField::class);
5498
        $autoRemoveFields = $extraFieldRepository->findBy([
5499
            'autoRemove' => 1,
5500
            'itemType' => EntityExtraField::USER_FIELD_TYPE
5501
        ]);
5502
5503
        foreach ($autoRemoveFields as $field) {
5504
            $extraFieldValueRepository = $em->getRepository(EntityExtraFieldValues::class);
5505
            $extraFieldValue = $extraFieldValueRepository->findOneBy([
5506
                'field' => $field,
5507
                'itemId' => $userId
5508
            ]);
5509
5510
            if ($extraFieldValue) {
5511
                $em->remove($extraFieldValue);
5512
            }
5513
        }
5514
5515
        $em->persist($user);
5516
        $em->flush();
5517
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
5518
5519
        return true;
5520
    }
5521
5522
    /**
5523
     * @param int $userId
5524
     *
5525
     * @throws Exception
5526
     *
5527
     * @return string
5528
     */
5529
    public static function anonymizeUserWithVerification($userId)
5530
    {
5531
        $allowDelete = ('true' === api_get_setting('session.allow_delete_user_for_session_admin'));
5532
5533
        $message = '';
5534
        if (api_is_platform_admin() ||
5535
            ($allowDelete && api_is_session_admin())
5536
        ) {
5537
            $userToUpdateInfo = api_get_user_info($userId);
5538
            $currentUserId = api_get_user_id();
5539
5540
            if ($userToUpdateInfo &&
5541
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
5542
            ) {
5543
                if ($userId != $currentUserId &&
5544
                    self::anonymize($userId)
5545
                ) {
5546
                    $message = Display::return_message(
5547
                        sprintf(get_lang('User %s information anonymized.'), $userToUpdateInfo['complete_name_with_username']),
5548
                        'confirmation'
5549
                    );
5550
                } else {
5551
                    $message = Display::return_message(
5552
                        sprintf(get_lang('We could not anonymize user %s information. Please try again or check the logs.'), $userToUpdateInfo['complete_name_with_username']),
5553
                        'error'
5554
                    );
5555
                }
5556
            } else {
5557
                $message = Display::return_message(
5558
                    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']),
5559
                    'error'
5560
                );
5561
            }
5562
        }
5563
5564
        return $message;
5565
    }
5566
5567
    /**
5568
     * @param int $userId
5569
     *
5570
     * @throws Exception
5571
     *
5572
     * @return string
5573
     */
5574
    public static function deleteUserWithVerification($userId, bool $destroy = false)
5575
    {
5576
        $allowDelete = ('true' === api_get_setting('session.allow_delete_user_for_session_admin'));
5577
        $message = Display::return_message(get_lang('You cannot delete this user'), 'error');
5578
        $userToUpdateInfo = api_get_user_info($userId);
5579
5580
        // User must exist.
5581
        if (empty($userToUpdateInfo)) {
5582
            return $message;
5583
        }
5584
5585
        $currentUserId = api_get_user_id();
5586
5587
        // Cannot delete myself.
5588
        if ($userId == $currentUserId) {
5589
            return $message;
5590
        }
5591
5592
        if (api_is_platform_admin() ||
5593
            ($allowDelete && api_is_session_admin())
5594
        ) {
5595
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
5596
                if (self::delete_user($userId, $destroy)) {
5597
                    $message = Display::return_message(
5598
                        get_lang('The user has been deleted').': '.$userToUpdateInfo['complete_name_with_username'],
5599
                        'confirmation'
5600
                    );
5601
                } else {
5602
                    $message = Display::return_message(get_lang('You cannot delete this userBecauseOwnsCourse'), 'error');
5603
                }
5604
            }
5605
        }
5606
5607
        return $message;
5608
    }
5609
5610
    /**
5611
     * @return array
5612
     */
5613
    public static function createDataPrivacyExtraFields()
5614
    {
5615
        self::create_extra_field(
5616
            'request_for_legal_agreement_consent_removal_justification',
5617
            1, //text
5618
            'Request for legal agreement consent removal justification	',
5619
            ''
5620
        );
5621
5622
        self::create_extra_field(
5623
            'request_for_delete_account_justification',
5624
            1, //text
5625
            'Request for delete account justification',
5626
            ''
5627
        );
5628
5629
        $extraFieldId = self::create_extra_field(
5630
            'request_for_legal_agreement_consent_removal',
5631
            1, //text
5632
            'Request for legal agreement consent removal',
5633
            ''
5634
        );
5635
5636
        $extraFieldIdDeleteAccount = self::create_extra_field(
5637
            'request_for_delete_account',
5638
            1, //text
5639
            'Request for delete user account',
5640
            ''
5641
        );
5642
5643
        return [
5644
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
5645
            'delete_legal' => $extraFieldId,
5646
        ];
5647
    }
5648
5649
    /**
5650
     * @param int $userId
5651
     */
5652
    public static function cleanUserRequestsOfRemoval($userId)
5653
    {
5654
        $userId = (int) $userId;
5655
5656
        $extraFieldValue = new ExtraFieldValue('user');
5657
        $extraFieldsToDelete = [
5658
            'legal_accept',
5659
            'request_for_legal_agreement_consent_removal',
5660
            'request_for_legal_agreement_consent_removal_justification',
5661
            'request_for_delete_account_justification', // just in case delete also this
5662
            'request_for_delete_account',
5663
        ];
5664
5665
        foreach ($extraFieldsToDelete as $variable) {
5666
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
5667
                $userId,
5668
                $variable
5669
            );
5670
            if ($value && isset($value['id'])) {
5671
                $extraFieldValue->delete($value['id']);
5672
            }
5673
        }
5674
    }
5675
5676
    /**
5677
     * @param int $searchYear
5678
     *
5679
     * @throws Exception
5680
     *
5681
     * @return array
5682
     */
5683
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
5684
    {
5685
        $timezone = new DateTimeZone(api_get_timezone());
5686
5687
        $sessions = [];
5688
        if (DRH == $userInfo['status']) {
5689
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
5690
        } elseif (api_is_platform_admin(true)) {
5691
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
5692
        } else {
5693
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
5694
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
5695
5696
            foreach ($sessionsByCategory as $sessionsInCategory) {
5697
                $sessions = array_merge($sessions, $sessionsInCategory);
5698
            }
5699
        }
5700
5701
        $sessions = array_map(
5702
            function ($sessionInfo) {
5703
                if (!isset($sessionInfo['session_id'])) {
5704
                    $sessionInfo['session_id'] = $sessionInfo['id'];
5705
                }
5706
                if (!isset($sessionInfo['session_name'])) {
5707
                    $sessionInfo['session_name'] = $sessionInfo['name'];
5708
                }
5709
5710
                return $sessionInfo;
5711
            },
5712
            $sessions
5713
        );
5714
5715
        $calendarSessions = [];
5716
5717
        foreach ($sessions as $sessionInfo) {
5718
            if (!empty($sessionInfo['duration'])) {
5719
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
5720
                    $sessionInfo['session_id'],
5721
                    $userInfo['id']
5722
                );
5723
5724
                if (empty($courseAccess)) {
5725
                    continue;
5726
                }
5727
5728
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
5729
                $lastAccessDate = clone $firstAcessDate;
5730
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
5731
5732
                $firstAccessYear = (int) $firstAcessDate->format('Y');
5733
                $lastAccessYear = (int) $lastAccessDate->format('Y');
5734
5735
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
5736
                    $calendarSessions[$sessionInfo['session_id']] = [
5737
                        'name' => $sessionInfo['session_name'],
5738
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
5739
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
5740
                    ];
5741
                }
5742
5743
                continue;
5744
            }
5745
5746
            $accessStartDate = !empty($sessionInfo['access_start_date'])
5747
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
5748
                : null;
5749
            $accessEndDate = !empty($sessionInfo['access_end_date'])
5750
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
5751
                : null;
5752
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
5753
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
5754
5755
            $isValid = false;
5756
5757
            if ($accessStartYear && $accessEndYear) {
5758
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
5759
                    $isValid = true;
5760
                }
5761
            }
5762
5763
            if ($accessStartYear && !$accessEndYear) {
5764
                if ($accessStartYear == $searchYear) {
5765
                    $isValid = true;
5766
                }
5767
            }
5768
5769
            if (!$accessStartYear && $accessEndYear) {
5770
                if ($accessEndYear == $searchYear) {
5771
                    $isValid = true;
5772
                }
5773
            }
5774
5775
            if ($isValid) {
5776
                $calendarSessions[$sessionInfo['session_id']] = [
5777
                    'name' => $sessionInfo['session_name'],
5778
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
5779
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
5780
                ];
5781
            }
5782
        }
5783
5784
        return $calendarSessions;
5785
    }
5786
5787
    /**
5788
     * Get sessions info for planification calendar.
5789
     *
5790
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
5791
     * @param int   $searchYear
5792
     *
5793
     * @throws Exception
5794
     *
5795
     * @return array
5796
     */
5797
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
5798
    {
5799
        $timezone = new DateTimeZone(api_get_timezone());
5800
        $calendar = [];
5801
5802
        foreach ($sessionsList as $sessionId => $sessionInfo) {
5803
            $startDate = $sessionInfo['access_start_date']
5804
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
5805
                : null;
5806
            $endDate = $sessionInfo['access_end_date']
5807
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
5808
                : null;
5809
5810
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
5811
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
5812
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
5813
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
5814
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
5815
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
5816
5817
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
5818
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
5819
5820
            $calendar[] = [
5821
                'id' => $sessionId,
5822
                'name' => $sessionInfo['name'],
5823
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
5824
                'start_in_last_year' => $startYear < $searchYear,
5825
                'end_in_next_year' => $endYear > $searchYear,
5826
                'no_start' => !$startWeek,
5827
                'no_end' => !$endWeek,
5828
                'start' => $start,
5829
                'duration' => $duration > 0 ? $duration : 1,
5830
            ];
5831
        }
5832
5833
        usort(
5834
            $calendar,
5835
            function ($sA, $sB) {
5836
                if ($sA['start'] == $sB['start']) {
5837
                    return 0;
5838
                }
5839
5840
                if ($sA['start'] < $sB['start']) {
5841
                    return -1;
5842
                }
5843
5844
                return 1;
5845
            }
5846
        );
5847
5848
        return $calendar;
5849
    }
5850
5851
    /**
5852
     * Return the user's full name. Optionally with the username.
5853
     */
5854
    public static function formatUserFullName(User $user, bool $includeUsername = false): string
5855
    {
5856
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
5857
5858
        if ($includeUsername && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
5859
            $username = $user->getUsername();
5860
5861
            return "$fullName ($username)";
5862
        }
5863
5864
        return $fullName;
5865
    }
5866
5867
    /**
5868
     * @param int $userId
5869
     *
5870
     * @return array
5871
     */
5872
    public static function getUserCareers($userId)
5873
    {
5874
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
5875
        $tableCareer = Database::get_main_table(TABLE_CAREER);
5876
        $userId = (int) $userId;
5877
5878
        $sql = "SELECT c.id, c.title
5879
                FROM $table uc
5880
                INNER JOIN $tableCareer c
5881
                ON uc.career_id = c.id
5882
                WHERE user_id = $userId
5883
                ORDER BY uc.created_at
5884
                ";
5885
        $result = Database::query($sql);
5886
5887
        return Database::store_result($result, 'ASSOC');
5888
    }
5889
5890
    /**
5891
     * @param int $userId
5892
     * @param int $careerId
5893
     */
5894
    public static function addUserCareer($userId, $careerId)
5895
    {
5896
        if ('true' !== api_get_setting('profile.allow_career_users')) {
5897
            return false;
5898
        }
5899
5900
        if (false === self::userHasCareer($userId, $careerId)) {
5901
            $params = ['user_id' => $userId, 'career_id' => $careerId, 'created_at' => api_get_utc_datetime(), 'updated_at' => api_get_utc_datetime()];
5902
            $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
5903
            Database::insert($table, $params);
5904
        }
5905
5906
        return true;
5907
    }
5908
5909
    /**
5910
     * @param int   $userCareerId
5911
     * @param array $data
5912
     *
5913
     * @return bool
5914
     */
5915
    public static function updateUserCareer($userCareerId, $data)
5916
    {
5917
        if ('true' !== api_get_setting('profile.allow_career_users')) {
5918
            return false;
5919
        }
5920
5921
        $params = ['extra_data' => $data, 'updated_at' => api_get_utc_datetime()];
5922
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
5923
        Database::update(
5924
            $table,
5925
            $params,
5926
            ['id = ?' => (int) $userCareerId]
5927
        );
5928
5929
        return true;
5930
    }
5931
5932
    /**
5933
     * @param int $userId
5934
     * @param int $careerId
5935
     *
5936
     * @return array
5937
     */
5938
    public static function getUserCareer($userId, $careerId)
5939
    {
5940
        $userId = (int) $userId;
5941
        $careerId = (int) $careerId;
5942
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
5943
5944
        $sql = "SELECT * FROM $table WHERE user_id = $userId AND career_id = $careerId";
5945
        $result = Database::query($sql);
5946
5947
        return Database::fetch_assoc($result);
0 ignored issues
show
Bug Best Practice introduced by
The expression return Database::fetch_assoc($result) also could return the type boolean which is incompatible with the documented return type array.
Loading history...
5948
    }
5949
5950
    /**
5951
     * @param int $userId
5952
     * @param int $careerId
5953
     *
5954
     * @return bool
5955
     */
5956
    public static function userHasCareer($userId, $careerId)
5957
    {
5958
        $userId = (int) $userId;
5959
        $careerId = (int) $careerId;
5960
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
5961
5962
        $sql = "SELECT id FROM $table WHERE user_id = $userId AND career_id = $careerId";
5963
        $result = Database::query($sql);
5964
5965
        return Database::num_rows($result) > 0;
5966
    }
5967
5968
    /**
5969
     * Disables or enables a user.
5970
     *
5971
     * @param int $user_id
5972
     * @param int $active  Enable or disable
5973
     *
5974
     * @return bool True on success, false on failure
5975
     * @assert (-1,0) === false
5976
     * @assert (1,1) === true
5977
     */
5978
    public static function change_active_state($user_id, $active)
5979
    {
5980
        $user_id = (int) $user_id;
5981
        $active = (int) $active;
5982
5983
        if (empty($user_id)) {
5984
            return false;
5985
        }
5986
5987
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5988
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
5989
        $r = Database::query($sql);
5990
        $ev = LOG_USER_DISABLE;
5991
        if (1 == $active) {
5992
            $ev = LOG_USER_ENABLE;
5993
        }
5994
        if (false !== $r) {
5995
            Event::addEvent($ev, LOG_USER_ID, $user_id);
5996
        }
5997
5998
        return $r;
5999
    }
6000
6001
    /**
6002
     * Get either a Gravatar URL or complete image tag for a specified email address.
6003
     *
6004
     * @param string $email The email address
6005
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
6006
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
6007
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
6008
     * @param bool   $img   True to return a complete IMG tag False for just the URL
6009
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
6010
     *
6011
     * @return string containing either just a URL or a complete image tag
6012
     * @source http://gravatar.com/site/implement/images/php/
6013
     */
6014
    private static function getGravatar(
0 ignored issues
show
Unused Code introduced by
The method getGravatar() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
6015
        $email,
6016
        $s = 80,
6017
        $d = 'mm',
6018
        $r = 'g',
6019
        $img = false,
6020
        $atts = []
6021
    ) {
6022
        $url = 'http://www.gravatar.com/avatar/';
6023
        if (!empty($_SERVER['HTTPS'])) {
6024
            $url = 'https://secure.gravatar.com/avatar/';
6025
        }
6026
        $url .= md5(strtolower(trim($email)));
6027
        $url .= "?s=$s&d=$d&r=$r";
6028
        if ($img) {
6029
            $url = '<img src="'.$url.'"';
6030
            foreach ($atts as $key => $val) {
6031
                $url .= ' '.$key.'="'.$val.'"';
6032
            }
6033
            $url .= ' />';
6034
        }
6035
6036
        return $url;
6037
    }
6038
}
6039