Passed
Pull Request — master (#6071)
by
unknown
21:45 queued 10:39
created

UserManager::get_sessions_list_by_course()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 17
c 1
b 0
f 0
dl 0
loc 30
rs 9.7
cc 4
nc 3
nop 2
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
        $hostingLimitUsers = get_hosting_limit($access_url_id, 'hosting_limit_users');
187
188
        if ($hostingLimitUsers !== null && $hostingLimitUsers > 0) {
189
            $num = self::get_number_of_users();
190
            if ($num >= $hostingLimitUsers) {
191
                api_warn_hosting_contact('hosting_limit_users');
192
                Display::addFlash(
193
                    Display::return_message(
194
                        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.'),
195
                        'warning'
196
                    )
197
                );
198
199
                return false;
200
            }
201
        }
202
203
        if (1 === $status) {
204
            $hostingLimitTeachers = get_hosting_limit($access_url_id, 'hosting_limit_teachers');
205
206
            if ($hostingLimitTeachers !== null && $hostingLimitTeachers > 0) {
207
                $num = self::get_number_of_users(1);
208
                if ($num >= $hostingLimitTeachers) {
209
                    Display::addFlash(
210
                        Display::return_message(
211
                            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.'),
212
                            'warning'
213
                        )
214
                    );
215
                    api_warn_hosting_contact('hosting_limit_teachers');
216
217
                    return false;
218
                }
219
            }
220
        }
221
222
        if (empty($password)) {
223
            if (PLATFORM_AUTH_SOURCE === $authSource) {
224
                Display::addFlash(
225
                    Display::return_message(
226
                        get_lang('Required field').': '.get_lang(
227
                            'Password'
228
                        ),
229
                        'warning'
230
                    )
231
                );
232
233
                return false;
234
            }
235
236
            // We use the authSource as password.
237
            // The real validation will be by processed by the auth
238
            // source not Chamilo
239
            $password = $authSource;
240
        }
241
242
        // Checking the user language
243
        $languages = api_get_languages();
244
245
        // Default to english
246
        if (!in_array($language, array_keys($languages), true)) {
247
            $language = 'en_US';
248
        }
249
250
        $now = new DateTime();
251
        if (empty($expirationDate) || '0000-00-00 00:00:00' === $expirationDate) {
252
            $expirationDate = null;
253
        // Default expiration date
254
            // if there is a default duration of a valid account then
255
            // we have to change the expiration_date accordingly
256
            // Accept 0000-00-00 00:00:00 as a null value to avoid issues with
257
            // third party code using this method with the previous (pre-1.10)
258
            // value of 0000...
259
            /*if ('' != api_get_setting('account_valid_duration')) {
260
                $expirationDate = new DateTime($currentDate);
261
                $days = (int) api_get_setting('account_valid_duration');
262
                $expirationDate->modify('+'.$days.' day');
263
            }*/
264
        } else {
265
            $expirationDate = api_get_utc_datetime($expirationDate, true, true);
266
        }
267
268
        $repo = Container::getUserRepository();
269
        $user = $repo->createUser()
270
            ->setLastname($lastName)
271
            ->setFirstname($firstName)
272
            ->setUsername($loginName)
273
            ->setStatus($status)
274
            ->setPlainPassword($password)
275
            ->setEmail($email)
276
            ->setOfficialCode($officialCode)
277
            ->setCreatorId($creatorId)
278
            ->setAuthSource($authSource)
279
            ->setPhone($phone)
280
            ->setAddress($address)
281
            ->setLocale($language)
282
            ->setRegistrationDate($now)
283
            ->setHrDeptId($hrDeptId)
284
            ->setActive($active)
285
            ->setTimezone(api_get_timezone())
286
        ;
287
288
        if (null !== $expirationDate) {
289
            $user->setExpirationDate($expirationDate);
290
        }
291
292
        $user->setRoleFromStatus($status);
293
294
        // Add user to a group
295
        /*$statusToGroup = [
296
            COURSEMANAGER => 'TEACHER',
297
            STUDENT => 'STUDENT',
298
            DRH => 'RRHH',
299
            SESSIONADMIN => 'SESSION_ADMIN',
300
            STUDENT_BOSS => 'STUDENT_BOSS',
301
            INVITEE => 'INVITEE',
302
        ];
303
304
        if (isset($statusToGroup[$status])) {
305
            $group = Container::$container->get(GroupRepository::class)->findOneBy(['code' => $statusToGroup[$status]]);
306
            if ($group) {
307
                $user->addGroup($group);
308
            }
309
        }*/
310
311
        $repo->updateUser($user, true);
312
313
        $userId = $user->getId();
314
315
        if (!empty($userId)) {
316
            $userLocale = $user->getLocale();
317
            if ($isAdmin) {
318
                self::addUserAsAdmin($user);
319
            }
320
321
            if (api_get_multiple_access_url()) {
322
                UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
323
            } else {
324
                //we are adding by default the access_url_user table with access_url_id = 1
325
                UrlManager::add_user_to_url($userId, 1);
326
            }
327
328
            if (is_array($extra) && count($extra) > 0) {
329
                $extra['item_id'] = $userId;
330
                $userFieldValue = new ExtraFieldValue('user');
331
                /* Force saving of extra fields (otherwise, if the current
332
                user is not admin, fields not visible to the user - most
333
                of them - are just ignored) */
334
                $userFieldValue->saveFieldValues(
335
                    $extra,
336
                    true,
337
                    false,
338
                    [],
339
                    [],
340
                    true
341
                );
342
            } else {
343
                // Create notify settings by default
344
                self::update_extra_field_value(
345
                    $userId,
346
                    'mail_notify_invitation',
347
                    '1'
348
                );
349
                self::update_extra_field_value(
350
                    $userId,
351
                    'mail_notify_message',
352
                    '1'
353
                );
354
                self::update_extra_field_value(
355
                    $userId,
356
                    'mail_notify_group_message',
357
                    '1'
358
                );
359
            }
360
361
            self::update_extra_field_value(
362
                $userId,
363
                'already_logged_in',
364
                'false'
365
            );
366
367
            if (!empty($redirectToURLAfterLogin) && ('true' === api_get_setting('admin.plugin_redirection_enabled'))) {
368
                RedirectionPlugin::insert($userId, $redirectToURLAfterLogin);
369
            }
370
371
            if (!empty($email) && $sendMail) {
372
                $recipient_name = api_get_person_name(
373
                    $firstName,
374
                    $lastName,
375
                    null,
376
                    PERSON_NAME_EMAIL_ADDRESS
377
                );
378
                $tpl = Container::getTwig();
379
                $emailSubject = $tpl->render('@ChamiloCore/Mailer/Legacy/subject_registration_platform.html.twig', ['locale' => $userLocale]);
380
                $sender_name = api_get_person_name(
381
                    api_get_setting('administratorName'),
382
                    api_get_setting('administratorSurname'),
383
                    null,
384
                    PERSON_NAME_EMAIL_ADDRESS
385
                );
386
                $email_admin = api_get_setting('emailAdministrator');
387
388
                $url = api_get_path(WEB_PATH);
389
                if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

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

389
                if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

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

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

961
            $emailsubject = '['./** @scrutinizer ignore-type */ api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
Loading history...
962
            $sender_name = api_get_person_name(
963
                api_get_setting('administratorName'),
964
                api_get_setting('administratorSurname'),
965
                null,
966
                PERSON_NAME_EMAIL_ADDRESS
967
            );
968
            $email_admin = api_get_setting('emailAdministrator');
969
            $url = api_get_path(WEB_PATH);
970
            if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

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

970
            if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
971
                $access_url_id = api_get_current_access_url_id();
972
                if (-1 != $access_url_id) {
973
                    $url = api_get_access_url($access_url_id);
974
                    $url = $url['url'];
975
                }
976
            }
977
978
            $tplContent = new Template(
979
                null,
980
                false,
981
                false,
982
                false,
983
                false,
984
                false
985
            );
986
            // variables for the default template
987
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
988
            $tplContent->assign('login_name', $username);
989
990
            $originalPassword = '';
991
            if ($reset_password > 0) {
992
                $originalPassword = stripslashes($original_password);
993
            }
994
            $tplContent->assign('original_password', $originalPassword);
995
            $tplContent->assign('portal_url', $url);
996
997
            $emailBody = $tplContent->fetch('@ChamiloCore/Mailer/Legacy/user_edit_content.html.twig');
998
999
            $mailTemplateManager = new MailTemplateManager();
1000
1001
            if (!empty($emailTemplate) &&
1002
                isset($emailTemplate['user_edit_content.tpl']) &&
1003
                !empty($emailTemplate['user_edit_content.tpl'])
1004
            ) {
1005
                $userInfo = api_get_user_info($user_id);
1006
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], $userInfo);
1007
            }
1008
1009
            $creatorInfo = api_get_user_info($creator_id);
1010
            $creatorEmail = $creatorInfo['email'] ?? '';
1011
1012
            api_mail_html(
1013
                $recipient_name,
1014
                $email,
1015
                $emailsubject,
1016
                $emailBody,
1017
                $sender_name,
1018
                $email_admin,
1019
                null,
1020
                null,
1021
                null,
1022
                null,
1023
                $creatorEmail
1024
            );
1025
        }
1026
1027
        return $user->getId();
1028
    }
1029
1030
    /**
1031
     * Disables a user.
1032
     *
1033
     * @param int User id
1034
     *
1035
     * @return bool
1036
     *
1037
     * @uses \UserManager::change_active_state() to actually disable the user
1038
     * @assert (0) === false
1039
     */
1040
    public static function disable($user_id)
1041
    {
1042
        if (empty($user_id)) {
1043
            return false;
1044
        }
1045
        self::change_active_state($user_id, 0);
1046
1047
        return true;
1048
    }
1049
1050
    /**
1051
     * Enable a user.
1052
     *
1053
     * @param int User id
1054
     *
1055
     * @return bool
1056
     *
1057
     * @uses \UserManager::change_active_state() to actually disable the user
1058
     * @assert (0) === false
1059
     */
1060
    public static function enable($user_id)
1061
    {
1062
        if (empty($user_id)) {
1063
            return false;
1064
        }
1065
        self::change_active_state($user_id, 1);
1066
1067
        return true;
1068
    }
1069
1070
    /**
1071
     * Returns the user's id based on the original id and field name in
1072
     * the extra fields. Returns 0 if no user was found. This function is
1073
     * mostly useful in the context of a web services-based sinchronization.
1074
     *
1075
     * @param string Original user id
1076
     * @param string Original field name
1077
     *
1078
     * @return int User id
1079
     * @assert ('0','---') === 0
1080
     */
1081
    public static function get_user_id_from_original_id(
1082
        $original_user_id_value,
1083
        $original_user_id_name
1084
    ) {
1085
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1086
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1087
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1088
1089
        $original_user_id_name = Database::escape_string($original_user_id_name);
1090
        $original_user_id_value = Database::escape_string($original_user_id_value);
1091
1092
        $sql = "SELECT item_id as user_id
1093
                FROM $t_uf uf
1094
                INNER JOIN $t_ufv ufv
1095
                ON ufv.field_id = uf.id
1096
                WHERE
1097
                    variable = '$original_user_id_name' AND
1098
                    field_value = '$original_user_id_value' AND
1099
                    item_type = $extraFieldType
1100
                ";
1101
        $res = Database::query($sql);
1102
        $row = Database::fetch_object($res);
1103
        if ($row) {
1104
            return $row->user_id;
1105
        }
1106
1107
        return 0;
1108
    }
1109
1110
    /**
1111
     * Check if a username is available.
1112
     *
1113
     * @param string $username the wanted username
1114
     *
1115
     * @return bool true if the wanted username is available
1116
     * @assert ('') === false
1117
     * @assert ('xyzxyzxyz') === true
1118
     */
1119
    public static function is_username_available($username)
1120
    {
1121
        if (empty($username)) {
1122
            return false;
1123
        }
1124
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1125
        $sql = "SELECT username FROM $table_user
1126
                WHERE username = '".Database::escape_string($username)."'";
1127
        $res = Database::query($sql);
1128
1129
        return 0 == Database::num_rows($res);
1130
    }
1131
1132
    /**
1133
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1134
     *
1135
     * @param string $firstname the first name of the user
1136
     * @param string $lastname  the last name of the user
1137
     *
1138
     * @return string suggests a username that contains only ASCII-letters and digits,
1139
     *                without check for uniqueness within the system
1140
     *
1141
     * @author Julio Montoya Armas
1142
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1143
     * @assert ('','') === false
1144
     * @assert ('a','b') === 'ab'
1145
     */
1146
    public static function create_username($firstname, $lastname)
1147
    {
1148
        if (empty($firstname) && empty($lastname)) {
1149
            return false;
1150
        }
1151
1152
        // The first letter only.
1153
        $firstname = api_substr(
1154
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1155
            0,
1156
            1
1157
        );
1158
        //Looking for a space in the lastname
1159
        $pos = api_strpos($lastname, ' ');
1160
        if (false !== $pos) {
1161
            $lastname = api_substr($lastname, 0, $pos);
1162
        }
1163
1164
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1165
        $username = $firstname.$lastname;
1166
        if (empty($username)) {
1167
            $username = 'user';
1168
        }
1169
1170
        $username = URLify::transliterate($username);
1171
1172
        return strtolower(substr($username, 0, User::USERNAME_MAX_LENGTH - 3));
1173
    }
1174
1175
    /**
1176
     * Creates a unique username, using:
1177
     * 1. the first name and the last name of a user;
1178
     * 2. an already created username but not checked for uniqueness yet.
1179
     *
1180
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1181
     *                          parameter is treated as username which is to be checked f
1182
     *                          or uniqueness and to be modified when it is necessary.
1183
     * @param string $lastname  the last name of the user
1184
     *
1185
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1186
     *                Note: When the method is called several times with same parameters,
1187
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1188
     *
1189
     * @author Ivan Tcholakov, 2009
1190
     */
1191
    public static function create_unique_username($firstname, $lastname = null)
1192
    {
1193
        if (is_null($lastname)) {
1194
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1195
            // For making this method tolerant of mistakes,
1196
            // let us transliterate and purify the suggested input username anyway.
1197
            // So, instead of the sentence $username = $firstname; we place the following:
1198
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1199
        } else {
1200
            $username = self::create_username($firstname, $lastname);
1201
        }
1202
        if (!self::is_username_available($username)) {
1203
            $i = 2;
1204
            $temp_username = substr($username, 0, User::USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1205
            while (!self::is_username_available($temp_username)) {
1206
                $i++;
1207
                $temp_username = substr($username, 0, User::USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1208
            }
1209
            $username = $temp_username;
1210
        }
1211
1212
        $username = URLify::transliterate($username);
1213
1214
        return $username;
1215
    }
1216
1217
    /**
1218
     * Modifies a given username accordingly to the specification for valid characters and length.
1219
     *
1220
     * @param $username string          The input username
1221
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1222
     *                     otherwise compliance may be partial. The default value is FALSE.
1223
     *
1224
     * @return string the resulting purified username
1225
     */
1226
    public static function purify_username($username, $strict = false)
1227
    {
1228
        if ($strict) {
1229
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1230
            // into ASCII letters in order they not to be totally removed.
1231
            // 2. Applying the strict purifier.
1232
            // 3. Length limitation.
1233
            $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);
1234
            $return = URLify::transliterate($return);
1235
1236
            // We want everything transliterate() does except converting @ to '(at)'. This is a hack to avoid this.
1237
            $return = str_replace(' (at) ', '@', $return);
1238
1239
            return $return;
1240
        }
1241
1242
        // 1. Applying the shallow purifier.
1243
        // 2. Length limitation.
1244
        return substr(
1245
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1246
            0,
1247
            User::USERNAME_MAX_LENGTH
1248
        );
1249
    }
1250
1251
    /**
1252
     * Checks whether the user id exists in the database.
1253
     *
1254
     * @param int $userId User id
1255
     *
1256
     * @return bool True if user id was found, false otherwise
1257
     */
1258
    public static function is_user_id_valid($userId)
1259
    {
1260
        $resultData = Database::select(
1261
            'COUNT(1) AS count',
1262
            Database::get_main_table(TABLE_MAIN_USER),
1263
            [
1264
                'where' => ['id = ?' => (int) $userId],
1265
            ],
1266
            'first'
1267
        );
1268
1269
        if (false === $resultData) {
1270
            return false;
1271
        }
1272
1273
        return $resultData['count'] > 0;
1274
    }
1275
1276
    /**
1277
     * Checks whether a given username matches to the specification strictly.
1278
     * The empty username is assumed here as invalid.
1279
     * Mostly this function is to be used in the user interface built-in validation routines
1280
     * for providing feedback while usernames are enterd manually.
1281
     *
1282
     * @param string $username the input username
1283
     *
1284
     * @return bool returns TRUE if the username is valid, FALSE otherwise
1285
     */
1286
    public static function is_username_valid($username)
1287
    {
1288
        return !empty($username) && $username == self::purify_username($username, true);
1289
    }
1290
1291
    /**
1292
     * Checks whether a username is empty. If the username contains whitespace characters,
1293
     * such as spaces, tabulators, newlines, etc.,
1294
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
1295
     *
1296
     * @param string $username the given username
1297
     *
1298
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1299
     */
1300
    public static function is_username_empty($username)
1301
    {
1302
        return 0 == strlen(self::purify_username($username, false));
1303
    }
1304
1305
    /**
1306
     * Checks whether a username is too long or not.
1307
     *
1308
     * @param string $username the given username, it should contain only ASCII-letters and digits
1309
     *
1310
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1311
     */
1312
    public static function is_username_too_long($username)
1313
    {
1314
        return strlen($username) > User::USERNAME_MAX_LENGTH;
1315
    }
1316
1317
    /**
1318
     * Get the users by ID.
1319
     *
1320
     * @param array  $ids    student ids
1321
     * @param bool   $active
1322
     * @param string $order
1323
     * @param string $limit
1324
     *
1325
     * @return array $result student information
1326
     */
1327
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
1328
    {
1329
        if (empty($ids)) {
1330
            return [];
1331
        }
1332
1333
        $ids = is_array($ids) ? $ids : [$ids];
1334
        $ids = array_map('intval', $ids);
1335
        $ids = implode(',', $ids);
1336
1337
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
1338
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
1339
        if (!is_null($active)) {
1340
            $sql .= ' AND active='.($active ? '1' : '0');
1341
        }
1342
1343
        if (!is_null($order)) {
1344
            $order = Database::escape_string($order);
1345
            $sql .= ' ORDER BY '.$order;
1346
        }
1347
1348
        if (!is_null($limit)) {
1349
            $limit = Database::escape_string($limit);
1350
            $sql .= ' LIMIT '.$limit;
1351
        }
1352
1353
        $rs = Database::query($sql);
1354
        $result = [];
1355
        while ($row = Database::fetch_array($rs)) {
1356
            $result[] = $row;
1357
        }
1358
1359
        return $result;
1360
    }
1361
1362
    /**
1363
     * Get a list of users of which the given conditions match with an = 'cond'.
1364
     *
1365
     * @param array $conditions a list of condition (example : status=>STUDENT)
1366
     * @param array $order_by   a list of fields on which sort
1367
     *
1368
     * @return array an array with all users of the platform
1369
     *
1370
     * @todo security filter order by
1371
     */
1372
    public static function get_user_list(
1373
        $conditions = [],
1374
        $order_by = [],
1375
        $limit_from = false,
1376
        $limit_to = false,
1377
        $idCampus = null
1378
    ) {
1379
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1380
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1381
        $return_array = [];
1382
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1383
1384
        if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

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

1384
        if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1385
            if ($idCampus) {
1386
                $urlId = $idCampus;
1387
            } else {
1388
                $urlId = api_get_current_access_url_id();
1389
            }
1390
            $sql .= " INNER JOIN $userUrlTable url_user
1391
                      ON (user.id = url_user.user_id)
1392
                      WHERE url_user.access_url_id = $urlId";
1393
        } else {
1394
            $sql .= " WHERE 1=1 ";
1395
        }
1396
1397
        if (count($conditions) > 0) {
1398
            foreach ($conditions as $field => $value) {
1399
                $field = Database::escape_string($field);
1400
                $value = Database::escape_string($value);
1401
                $sql .= " AND $field = '$value'";
1402
            }
1403
        }
1404
1405
        if (count($order_by) > 0) {
1406
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1407
        }
1408
1409
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1410
            $limit_from = (int) $limit_from;
1411
            $limit_to = (int) $limit_to;
1412
            $sql .= " LIMIT $limit_from, $limit_to";
1413
        }
1414
        $sql_result = Database::query($sql);
1415
        while ($result = Database::fetch_array($sql_result)) {
1416
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1417
            $return_array[] = $result;
1418
        }
1419
1420
        return $return_array;
1421
    }
1422
1423
    public static function getUserListExtraConditions(
1424
        $conditions = [],
1425
        $order_by = [],
1426
        $limit_from = false,
1427
        $limit_to = false,
1428
        $idCampus = null,
1429
        $extraConditions = '',
1430
        $getCount = false
1431
    ) {
1432
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1433
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1434
        $return_array = [];
1435
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1436
1437
        if ($getCount) {
1438
            $sql = "SELECT count(user.id) count FROM $user_table user ";
1439
        }
1440
1441
        if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

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

1441
        if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1442
            if ($idCampus) {
1443
                $urlId = $idCampus;
1444
            } else {
1445
                $urlId = api_get_current_access_url_id();
1446
            }
1447
            $sql .= " INNER JOIN $userUrlTable url_user
1448
                      ON (user.id = url_user.user_id)
1449
                      WHERE url_user.access_url_id = $urlId";
1450
        } else {
1451
            $sql .= " WHERE 1=1 ";
1452
        }
1453
1454
        $sql .= " AND status <> ".ANONYMOUS." ";
1455
1456
        if (count($conditions) > 0) {
1457
            foreach ($conditions as $field => $value) {
1458
                $field = Database::escape_string($field);
1459
                $value = Database::escape_string($value);
1460
                $sql .= " AND $field = '$value'";
1461
            }
1462
        }
1463
1464
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
1465
1466
        if (!empty($order_by) && count($order_by) > 0) {
1467
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1468
        }
1469
1470
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1471
            $limit_from = (int) $limit_from;
1472
            $limit_to = (int) $limit_to;
1473
            $sql .= " LIMIT $limit_from, $limit_to";
1474
        }
1475
1476
        $sql_result = Database::query($sql);
1477
1478
        if ($getCount) {
1479
            $result = Database::fetch_array($sql_result);
1480
1481
            return $result['count'];
1482
        }
1483
1484
        while ($result = Database::fetch_array($sql_result)) {
1485
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1486
            $return_array[] = $result;
1487
        }
1488
1489
        return $return_array;
1490
    }
1491
1492
    /**
1493
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
1494
     *
1495
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
1496
     * @param array  $order_by         a list of fields on which sort
1497
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
1498
     * @param string $condition        Whether we want the filters to be combined by AND or OR
1499
     * @param array  $onlyThisUserList
1500
     *
1501
     * @return array an array with all users of the platform
1502
     *
1503
     * @todo optional course code parameter, optional sorting parameters...
1504
     * @todo security filter order_by
1505
     */
1506
    public static function getUserListLike(
1507
        $conditions = [],
1508
        $order_by = [],
1509
        $simple_like = false,
1510
        $condition = 'AND',
1511
        $onlyThisUserList = []
1512
    ) {
1513
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1514
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1515
        $return_array = [];
1516
        $sql_query = "SELECT user.id FROM $user_table user ";
1517
1518
        if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

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

1518
        if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1519
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
1520
        }
1521
1522
        $sql_query .= ' WHERE 1 = 1 ';
1523
        if (count($conditions) > 0) {
1524
            $temp_conditions = [];
1525
            foreach ($conditions as $field => $value) {
1526
                $field = Database::escape_string($field);
1527
                $value = Database::escape_string($value);
1528
                if ($simple_like) {
1529
                    $temp_conditions[] = $field." LIKE '$value%'";
1530
                } else {
1531
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
1532
                }
1533
            }
1534
            if (!empty($temp_conditions)) {
1535
                $sql_query .= ' AND '.implode(' '.$condition.' ', $temp_conditions);
1536
            }
1537
1538
            if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

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

1538
            if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1539
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1540
            }
1541
        } else {
1542
            if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

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

1542
            if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1543
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1544
            }
1545
        }
1546
1547
        if (!empty($onlyThisUserList)) {
1548
            $onlyThisUserListToString = implode("','", $onlyThisUserList);
1549
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
1550
        }
1551
1552
        if (count($order_by) > 0) {
1553
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1554
        }
1555
1556
        $sql_result = Database::query($sql_query);
1557
        while ($result = Database::fetch_array($sql_result)) {
1558
            $userInfo = api_get_user_info($result['id']);
1559
            $return_array[] = $userInfo;
1560
        }
1561
1562
        return $return_array;
1563
    }
1564
1565
    /**
1566
     * Get user path from user ID (returns an array).
1567
     * The return format is a complete path to a folder ending with "/"
1568
     * In case the first level of subdirectory of users/ does not exist, the
1569
     * function will attempt to create it. Probably not the right place to do it
1570
     * but at least it avoids headaches in many other places.
1571
     *
1572
     * @param int    $id   User ID
1573
     * @param string $type Type of path to return (can be 'system', 'web', 'last')
1574
     *
1575
     * @return string User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
1576
     */
1577
    public static function getUserPathById($id, $type)
1578
    {
1579
        $id = (int) $id;
1580
        if (!$id) {
1581
            return null;
1582
        }
1583
1584
        $userPath = "users/$id/";
1585
        if (api_get_setting('split_users_upload_directory') === 'true') {
1586
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
1587
            // In exceptional cases, on some portals, the intermediate base user
1588
            // directory might not have been created. Make sure it is before
1589
            // going further.
1590
1591
            $rootPath = api_get_path(SYS_PATH).'../app/upload/users/'.substr((string) $id, 0, 1);
1592
            if (!is_dir($rootPath)) {
1593
                $perm = api_get_permissions_for_new_directories();
1594
                try {
1595
                    mkdir($rootPath, $perm);
1596
                } catch (Exception $e) {
1597
                    error_log($e->getMessage());
1598
                }
1599
            }
1600
        }
1601
        switch ($type) {
1602
            case 'system': // Base: absolute system path.
1603
                $userPath = api_get_path(SYS_PATH).'../app/upload/'.$userPath;
1604
                break;
1605
            case 'web': // Base: absolute web path.
1606
                $userPath = api_get_path(WEB_PATH).'../app/upload/'.$userPath;
1607
                break;
1608
            case 'last': // Only the last part starting with users/
1609
                break;
1610
        }
1611
1612
        return $userPath;
1613
    }
1614
1615
    /**
1616
     * Gets the current user image.
1617
     *
1618
     * @param string $userId
1619
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
1620
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
1621
     * @param bool   $addRandomId
1622
     * @param array  $userInfo    to avoid query the DB
1623
     *
1624
     * @todo add gravatar support
1625
     * @todo replace $userId with User entity
1626
     *
1627
     * @return string
1628
     */
1629
    public static function getUserPicture(
1630
        $userId,
1631
        int $size = USER_IMAGE_SIZE_MEDIUM,
1632
        $addRandomId = true,
1633
        $userInfo = []
1634
    ) {
1635
        $user = api_get_user_entity($userId);
1636
        $illustrationRepo = Container::getIllustrationRepository();
1637
1638
        switch ($size) {
1639
            case USER_IMAGE_SIZE_SMALL:
1640
                $width = 32;
1641
                break;
1642
            case USER_IMAGE_SIZE_MEDIUM:
1643
                $width = 64;
1644
                break;
1645
            case USER_IMAGE_SIZE_BIG:
1646
                $width = 128;
1647
                break;
1648
            case USER_IMAGE_SIZE_ORIGINAL:
1649
            default:
1650
                $width = 0;
1651
                break;
1652
        }
1653
1654
        $url = $illustrationRepo->getIllustrationUrl($user);
1655
        $params = [];
1656
        if (!empty($width)) {
1657
            $params['w'] = $width;
1658
        }
1659
1660
        if ($addRandomId) {
1661
            $params['rand'] = uniqid('u_', true);
1662
        }
1663
1664
        $paramsToString = '';
1665
        if (!empty($params)) {
1666
            $paramsToString = '?'.http_build_query($params);
1667
        }
1668
1669
        return $url.$paramsToString;
1670
1671
        /*
1672
        // Make sure userInfo is defined. Otherwise, define it!
1673
        if (empty($userInfo) || !is_array($userInfo) || 0 == count($userInfo)) {
1674
            if (empty($user_id)) {
1675
                return '';
1676
            } else {
1677
                $userInfo = api_get_user_info($user_id);
1678
            }
1679
        }
1680
1681
        $imageWebPath = self::get_user_picture_path_by_id(
1682
            $user_id,
1683
            'web',
1684
            $userInfo
1685
        );
1686
        $pictureWebFile = $imageWebPath['file'];
1687
        $pictureWebDir = $imageWebPath['dir'];
1688
1689
        $pictureAnonymousSize = '128';
1690
        $gravatarSize = 22;
1691
        $realSizeName = 'small_';
1692
1693
        switch ($size) {
1694
            case USER_IMAGE_SIZE_SMALL:
1695
                $pictureAnonymousSize = '32';
1696
                $realSizeName = 'small_';
1697
                $gravatarSize = 32;
1698
                break;
1699
            case USER_IMAGE_SIZE_MEDIUM:
1700
                $pictureAnonymousSize = '64';
1701
                $realSizeName = 'medium_';
1702
                $gravatarSize = 64;
1703
                break;
1704
            case USER_IMAGE_SIZE_ORIGINAL:
1705
                $pictureAnonymousSize = '128';
1706
                $realSizeName = '';
1707
                $gravatarSize = 128;
1708
                break;
1709
            case USER_IMAGE_SIZE_BIG:
1710
                $pictureAnonymousSize = '128';
1711
                $realSizeName = 'big_';
1712
                $gravatarSize = 128;
1713
                break;
1714
        }
1715
1716
        $gravatarEnabled = api_get_setting('gravatar_enabled');
1717
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
1718
        if ('unknown.jpg' == $pictureWebFile || empty($pictureWebFile)) {
1719
            if ('true' === $gravatarEnabled) {
1720
                $file = self::getGravatar(
1721
                    $imageWebPath['email'],
1722
                    $gravatarSize,
1723
                    api_get_setting('gravatar_type')
1724
                );
1725
1726
                if ($addRandomId) {
1727
                    $file .= '&rand='.uniqid();
1728
                }
1729
1730
                return $file;
1731
            }
1732
1733
            return $anonymousPath;
1734
        }
1735
1736
        if ($addRandomId) {
1737
            $picture .= '?rand='.uniqid();
1738
        }
1739
1740
        return $picture;*/
1741
    }
1742
1743
    /**
1744
     * Creates new user photos in various sizes of a user, or deletes user photos.
1745
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
1746
     *
1747
     * @param int    $user_id the user internal identification number
1748
     * @param string $file    The common file name for the newly created photos.
1749
     *                        It will be checked and modified for compatibility with the file system.
1750
     *                        If full name is provided, path component is ignored.
1751
     *                        If an empty name is provided, then old user photos are deleted only,
1752
     *
1753
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
1754
     *
1755
     * @param string $source_file    the full system name of the image from which user photos will be created
1756
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
1757
     *
1758
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
1759
     *              When deletion is requested returns empty string.
1760
     *              In case of internal error or negative validation returns FALSE.
1761
     */
1762
    public static function update_user_picture($userId, UploadedFile $file, string $crop = '')
1763
    {
1764
        if (empty($userId) || empty($file)) {
1765
            return false;
1766
        }
1767
1768
        $repo = Container::getUserRepository();
1769
        $user = $repo->find($userId);
1770
        if ($user) {
1771
            $repoIllustration = Container::getIllustrationRepository();
1772
            $repoIllustration->addIllustration($user, $user, $file, $crop);
1773
        }
1774
    }
1775
1776
    /**
1777
     * Deletes user photos.
1778
     *
1779
     * @param int $userId the user internal identification number
1780
     *
1781
     * @return mixed returns empty string on success, FALSE on error
1782
     */
1783
    public static function deleteUserPicture($userId)
1784
    {
1785
        $repo = Container::getUserRepository();
1786
        $user = $repo->find($userId);
1787
        if ($user) {
1788
            $illustrationRepo = Container::getIllustrationRepository();
1789
            $illustrationRepo->deleteIllustration($user);
1790
        }
1791
    }
1792
1793
    /**
1794
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
1795
     * doesn't have any.
1796
     *
1797
     * If there has been a request to remove a production, the function will return
1798
     * without building the list unless forced to do so by the optional second
1799
     * parameter. This increases performance by avoiding to read through the
1800
     * productions on the filesystem before the removal request has been carried
1801
     * out because they'll have to be re-read afterwards anyway.
1802
     *
1803
     * @deprecated This method is being removed from chamilo 2.0
1804
     * @param int  $user_id    User id
1805
     * @param bool $force      Optional parameter to force building after a removal request
1806
     * @param bool $showDelete
1807
     *
1808
     * @return string A string containing the XHTML code to display the production list, or FALSE
1809
     */
1810
    public static function build_production_list($user_id, $force = false, $showDelete = false)
1811
    {
1812
        if (!$force && !empty($_POST['remove_production'])) {
1813
            return true; // postpone reading from the filesystem
1814
        }
1815
1816
        $productions = self::get_user_productions($user_id);
1817
1818
        if (empty($productions)) {
1819
            return false;
1820
        }
1821
1822
        return false;
1823
1824
        $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...
1825
        $del_image = Display::returnIconPath('delete.png');
1826
        $add_image = Display::returnIconPath('archive.png');
1827
        $del_text = get_lang('Delete');
1828
        $production_list = '';
1829
        if (count($productions) > 0) {
1830
            $production_list = '<div class="files-production"><ul id="productions">';
1831
            foreach ($productions as $file) {
1832
                $production_list .= '<li>
1833
                    <img src="'.$add_image.'" />
1834
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
1835
                        '.htmlentities($file).'
1836
                    </a>';
1837
                if ($showDelete) {
1838
                    $production_list .= '&nbsp;&nbsp;
1839
                        <input
1840
                            style="width:16px;"
1841
                            type="image"
1842
                            name="remove_production['.urlencode($file).']"
1843
                            src="'.$del_image.'"
1844
                            alt="'.$del_text.'"
1845
                            title="'.$del_text.' '.htmlentities($file).'"
1846
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
1847
                }
1848
            }
1849
            $production_list .= '</ul></div>';
1850
        }
1851
1852
        return $production_list;
1853
    }
1854
1855
    /**
1856
     * Returns an array with the user's productions.
1857
     *
1858
     * @param int $user_id User id
1859
     *
1860
     * @return array An array containing the user's productions
1861
     */
1862
    public static function get_user_productions($user_id)
1863
    {
1864
        return [];
1865
1866
        $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...
1867
        $productions = [];
1868
1869
        if (is_dir($production_repository)) {
1870
            $handle = opendir($production_repository);
1871
            while ($file = readdir($handle)) {
1872
                if ('.' == $file ||
1873
                    '..' == $file ||
1874
                    '.htaccess' == $file ||
1875
                    is_dir($production_repository.$file)
1876
                ) {
1877
                    // skip current/parent directory and .htaccess
1878
                    continue;
1879
                }
1880
1881
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
1882
                    // User's photos should not be listed as productions.
1883
                    continue;
1884
                }
1885
                $productions[] = $file;
1886
            }
1887
        }
1888
1889
        return $productions;
1890
    }
1891
1892
    /**
1893
     * Remove a user production.
1894
     *
1895
     * @param int    $user_id    User id
1896
     * @param string $production The production to remove
1897
     *
1898
     * @return bool
1899
     */
1900
    public static function remove_user_production($user_id, $production)
1901
    {
1902
        throw new Exception('remove_user_production');
1903
        /*$production_path = self::get_user_picture_path_by_id($user_id, 'system');
1904
        $production_file = $production_path['dir'].$production;
1905
        if (is_file($production_file)) {
1906
            unlink($production_file);
1907
1908
            return true;
1909
        }
1910
1911
        return false;*/
1912
    }
1913
1914
    /**
1915
     * Update an extra field value for a given user.
1916
     *
1917
     * @param int    $userId   User ID
1918
     * @param string $variable Field variable name
1919
     * @param string $value    Field value
1920
     *
1921
     * @return bool true if field updated, false otherwise
1922
     */
1923
    public static function update_extra_field_value($userId, $variable, $value = '')
1924
    {
1925
        $extraFieldValue = new ExtraFieldValue('user');
1926
        $params = [
1927
            'item_id' => $userId,
1928
            'variable' => $variable,
1929
            'field_value' => $value,
1930
        ];
1931
1932
        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...
1933
    }
1934
1935
    /**
1936
     * Get an array of extra fields with field details (type, default value and options).
1937
     *
1938
     * @param    int    Offset (from which row)
1939
     * @param    int    Number of items
1940
     * @param    int    Column on which sorting is made
1941
     * @param    string    Sorting direction
1942
     * @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...
1943
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
1944
     *
1945
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
1946
     */
1947
    public static function get_extra_fields(
1948
        $from = 0,
1949
        $number_of_items = 0,
1950
        $column = 5,
1951
        $direction = 'ASC',
1952
        $all_visibility = true,
1953
        $field_filter = null
1954
    ) {
1955
        $fields = [];
1956
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1957
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
1958
        $columns = [
1959
            'id',
1960
            'variable',
1961
            'value_type',
1962
            'display_text',
1963
            'default_value',
1964
            'field_order',
1965
            'filter',
1966
        ];
1967
        $column = (int) $column;
1968
        $sort_direction = '';
1969
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
1970
            $sort_direction = strtoupper($direction);
1971
        }
1972
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1973
        $sqlf = "SELECT * FROM $t_uf WHERE item_type = $extraFieldType ";
1974
        if (!$all_visibility) {
1975
            $sqlf .= " AND visible_to_self = 1 ";
1976
        }
1977
        if (!is_null($field_filter)) {
1978
            $field_filter = (int) $field_filter;
1979
            $sqlf .= " AND filter = $field_filter ";
1980
        }
1981
        $sqlf .= " ORDER BY `".$columns[$column]."` $sort_direction ";
1982
        if (0 != $number_of_items) {
1983
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
1984
        }
1985
        $resf = Database::query($sqlf);
1986
        if (Database::num_rows($resf) > 0) {
1987
            while ($rowf = Database::fetch_array($resf)) {
1988
                $fields[$rowf['id']] = [
1989
                    0 => $rowf['id'],
1990
                    1 => $rowf['variable'],
1991
                    2 => $rowf['value_type'],
1992
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
1993
                    4 => $rowf['default_value'],
1994
                    5 => $rowf['field_order'],
1995
                    6 => $rowf['visible_to_self'],
1996
                    7 => $rowf['changeable'],
1997
                    8 => $rowf['filter'],
1998
                    9 => [],
1999
                    10 => '<a name="'.$rowf['id'].'"></a>',
2000
                ];
2001
2002
                $sqlo = "SELECT * FROM $t_ufo
2003
                         WHERE field_id = ".$rowf['id']."
2004
                         ORDER BY option_order ASC";
2005
                $reso = Database::query($sqlo);
2006
                if (Database::num_rows($reso) > 0) {
2007
                    while ($rowo = Database::fetch_array($reso)) {
2008
                        $fields[$rowf['id']][9][$rowo['id']] = [
2009
                            0 => $rowo['id'],
2010
                            1 => $rowo['option_value'],
2011
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2012
                            3 => $rowo['option_order'],
2013
                        ];
2014
                    }
2015
                }
2016
            }
2017
        }
2018
2019
        return $fields;
2020
    }
2021
2022
    /**
2023
     * Creates a new extra field.
2024
     *
2025
     * @param string $variable    Field's internal variable name
2026
     * @param int    $fieldType   Field's type
2027
     * @param string $displayText Field's language var name
2028
     * @param string $default     Field's default value
2029
     *
2030
     * @return int
2031
     */
2032
    public static function create_extra_field(
2033
        $variable,
2034
        $valueType,
2035
        $displayText,
2036
        $default
2037
    ) {
2038
        $extraField = new ExtraField('user');
2039
        $params = [
2040
            'variable' => $variable,
2041
            'value_type' => $valueType,
2042
            'display_text' => $displayText,
2043
            'default_value' => $default,
2044
        ];
2045
2046
        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...
2047
    }
2048
2049
    /**
2050
     * Check if a field is available.
2051
     *
2052
     * @param string $variable
2053
     *
2054
     * @return bool
2055
     */
2056
    public static function is_extra_field_available($variable)
2057
    {
2058
        $extraField = new ExtraField('user');
2059
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2060
2061
        return !empty($data) ? true : false;
2062
    }
2063
2064
    /**
2065
     * Gets user extra fields data.
2066
     *
2067
     * @param    int    User ID
2068
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2069
     * @param    bool    Whether to return invisible fields as well
2070
     * @param    bool    Whether to split multiple-selection fields or not
2071
     *
2072
     * @return array Array of fields => value for the given user
2073
     */
2074
    public static function get_extra_user_data(
2075
        $user_id,
2076
        $prefix = false,
2077
        $allVisibility = true,
2078
        $splitMultiple = false,
2079
        $fieldFilter = null
2080
    ) {
2081
        $user_id = (int) $user_id;
2082
2083
        if (empty($user_id)) {
2084
            return [];
2085
        }
2086
2087
        $extra_data = [];
2088
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2089
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2090
        $sql = "SELECT f.id as id, f.variable as fvar, f.value_type as type
2091
                FROM $t_uf f
2092
                WHERE
2093
                    item_type = ".EntityExtraField::USER_FIELD_TYPE."
2094
                ";
2095
        $filter_cond = '';
2096
2097
        if (!$allVisibility) {
2098
            if (isset($fieldFilter)) {
2099
                $fieldFilter = (int) $fieldFilter;
2100
                $filter_cond .= " AND filter = $fieldFilter ";
2101
            }
2102
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2103
        } else {
2104
            if (isset($fieldFilter)) {
2105
                $fieldFilter = (int) $fieldFilter;
2106
                $sql .= " AND filter = $fieldFilter ";
2107
            }
2108
        }
2109
2110
        $sql .= ' ORDER BY f.field_order';
2111
2112
        $res = Database::query($sql);
2113
        if (Database::num_rows($res) > 0) {
2114
            while ($row = Database::fetch_array($res)) {
2115
                if (self::USER_FIELD_TYPE_TAG == $row['type']) {
2116
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2117
                    $extra_data['extra_'.$row['fvar']] = $tags;
2118
                } else {
2119
                    $sqlu = "SELECT field_value as fval
2120
                            FROM $t_ufv
2121
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2122
                    $resu = Database::query($sqlu);
2123
                    // get default value
2124
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2125
                               WHERE id=".$row['id'];
2126
                    $res_df = Database::query($sql_df);
2127
2128
                    if (Database::num_rows($resu) > 0) {
2129
                        $rowu = Database::fetch_array($resu);
2130
                        $fval = $rowu['fval'];
2131
                        if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2132
                            $fval = explode(';', $rowu['fval']);
2133
                        }
2134
                    } else {
2135
                        $row_df = Database::fetch_array($res_df);
2136
                        $fval = $row_df['fval_df'];
2137
                    }
2138
                    // We get here (and fill the $extra_data array) even if there
2139
                    // is no user with data (we fill it with default values)
2140
                    if ($prefix) {
2141
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2142
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2143
                        } else {
2144
                            $extra_data['extra_'.$row['fvar']] = $fval;
2145
                        }
2146
                    } else {
2147
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2148
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2149
                        } else {
2150
                            $extra_data[$row['fvar']] = $fval;
2151
                        }
2152
                    }
2153
                }
2154
            }
2155
        }
2156
2157
        return $extra_data;
2158
    }
2159
2160
    /** Get extra user data by field.
2161
     * @param int    user ID
2162
     * @param string the internal variable name of the field
2163
     *
2164
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2165
     */
2166
    public static function get_extra_user_data_by_field(
2167
        $user_id,
2168
        $field_variable,
2169
        $prefix = false,
2170
        $all_visibility = true,
2171
        $splitmultiple = false
2172
    ) {
2173
        $user_id = (int) $user_id;
2174
2175
        if (empty($user_id)) {
2176
            return [];
2177
        }
2178
2179
        $extra_data = [];
2180
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2181
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2182
2183
        $sql = "SELECT f.id as id, f.variable as fvar, f.value_type as type
2184
                FROM $t_uf f
2185
                WHERE f.variable = '$field_variable' ";
2186
2187
        if (!$all_visibility) {
2188
            $sql .= " AND f.visible_to_self = 1 ";
2189
        }
2190
2191
        $sql .= " AND item_type = ".EntityExtraField::USER_FIELD_TYPE;
2192
        $sql .= " ORDER BY f.field_order ";
2193
2194
        $res = Database::query($sql);
2195
        if (Database::num_rows($res) > 0) {
2196
            while ($row = Database::fetch_array($res)) {
2197
                $sqlu = "SELECT field_value as fval FROM $t_ufv v
2198
                         INNER JOIN $t_uf f
2199
                         ON (v.field_id = f.id)
2200
                         WHERE
2201
                            item_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2202
                            field_id = ".$row['id']." AND
2203
                            item_id = ".$user_id;
2204
                $resu = Database::query($sqlu);
2205
                $fval = '';
2206
                if (Database::num_rows($resu) > 0) {
2207
                    $rowu = Database::fetch_array($resu);
2208
                    $fval = $rowu['fval'];
2209
                    if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2210
                        $fval = explode(';', $rowu['fval']);
2211
                    }
2212
                }
2213
                if ($prefix) {
2214
                    $extra_data['extra_'.$row['fvar']] = $fval;
2215
                } else {
2216
                    $extra_data[$row['fvar']] = $fval;
2217
                }
2218
            }
2219
        }
2220
2221
        return $extra_data;
2222
    }
2223
2224
    /**
2225
     * Get the extra field information for a certain field (the options as well).
2226
     *
2227
     * @param int $variable The name of the field we want to know everything about
2228
     *
2229
     * @return array Array containing all the information about the extra profile field
2230
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2231
     *               as returned by the database)
2232
     *
2233
     * @author Julio Montoya
2234
     *
2235
     * @since v1.8.6
2236
     */
2237
    public static function get_extra_field_information_by_name($variable)
2238
    {
2239
        $extraField = new ExtraField('user');
2240
2241
        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...
2242
    }
2243
2244
    /**
2245
     * Get the extra field information for user tag (the options as well).
2246
     *
2247
     * @param int $variable The name of the field we want to know everything about
2248
     *
2249
     * @return array Array containing all the information about the extra profile field
2250
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2251
     *               as returned by the database)
2252
     *
2253
     * @author José Loguercio
2254
     *
2255
     * @since v1.11.0
2256
     */
2257
    public static function get_extra_field_tags_information_by_name($variable)
2258
    {
2259
        $extraField = new ExtraField('user');
2260
2261
        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...
2262
    }
2263
2264
    /**
2265
     * Get all the extra field information of a certain field (also the options).
2266
     *
2267
     * @param int $fieldId the ID of the field we want to know everything of
2268
     *
2269
     * @return array $return containing all th information about the extra profile field
2270
     *
2271
     * @author Julio Montoya
2272
     *
2273
     * @deprecated
2274
     * @since v1.8.6
2275
     */
2276
    public static function get_extra_field_information($fieldId)
2277
    {
2278
        $extraField = new ExtraField('user');
2279
2280
        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...
2281
    }
2282
2283
    /**
2284
     * Get extra user data by value.
2285
     *
2286
     * @param string $variable       the internal variable name of the field
2287
     * @param string $value          the internal value of the field
2288
     * @param bool   $all_visibility
2289
     *
2290
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2291
     */
2292
    public static function get_extra_user_data_by_value($variable, $value, $all_visibility = true)
2293
    {
2294
        $extraFieldValue = new ExtraFieldValue('user');
2295
        $extraField = new ExtraField('user');
2296
2297
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2298
2299
        if (false === $info) {
2300
            return [];
2301
        }
2302
2303
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2304
            $variable,
2305
            $value,
2306
            false,
2307
            false,
2308
            true
2309
        );
2310
2311
        $result = [];
2312
        if (!empty($data)) {
2313
            foreach ($data as $item) {
2314
                $result[] = $item['item_id'];
2315
            }
2316
        }
2317
2318
        return $result;
2319
    }
2320
2321
    /**
2322
     * Get extra user data by tags value.
2323
     *
2324
     * @param int    $fieldId the ID of the field we want to know everything of
2325
     * @param string $tag     the tag name for search
2326
     *
2327
     * @return array with extra data info of a user
2328
     *
2329
     * @author José Loguercio
2330
     *
2331
     * @since v1.11.0
2332
     */
2333
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2334
    {
2335
        $extraField = new ExtraField('user');
2336
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2337
        $array = [];
2338
        foreach ($result as $index => $user) {
2339
            $array[] = $user['user_id'];
2340
        }
2341
2342
        return $array;
2343
    }
2344
2345
    /**
2346
     * Get extra user data by field variable.
2347
     *
2348
     * @param string $variable field variable
2349
     *
2350
     * @return array data
2351
     */
2352
    public static function get_extra_user_data_by_field_variable($variable)
2353
    {
2354
        $extraInfo = self::get_extra_field_information_by_name($variable);
2355
        $field_id = (int) $extraInfo['id'];
2356
2357
        $extraField = new ExtraFieldValue('user');
2358
        $data = $extraField->getValuesByFieldId($field_id);
2359
2360
        if (!empty($data)) {
2361
            foreach ($data as $row) {
2362
                $user_id = $row['item_id'];
2363
                $data[$user_id] = $row;
2364
            }
2365
        }
2366
2367
        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...
2368
    }
2369
2370
    /**
2371
     * Get extra user data tags by field variable.
2372
     *
2373
     * @param string $variable field variable
2374
     *
2375
     * @return array
2376
     */
2377
    public static function get_extra_user_data_for_tags($variable)
2378
    {
2379
        $data = self::get_extra_field_tags_information_by_name($variable);
2380
2381
        return $data;
2382
    }
2383
2384
    /**
2385
     * Gives a list of [session_category][session_id] for the current user.
2386
     *
2387
     * @param int  $user_id
2388
     * @param bool $is_time_over                 whether to fill the first element or not
2389
     *                                           (to give space for courses out of categories)
2390
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2391
     * @param bool $ignoreTimeLimit              ignore time start/end
2392
     * @param bool $getCount
2393
     *
2394
     * @return array list of statuses [session_category][session_id]
2395
     *
2396
     * @todo ensure multiple access urls are managed correctly
2397
     */
2398
    public static function get_sessions_by_category(
2399
        $user_id,
2400
        $is_time_over = true,
2401
        $ignore_visibility_for_admins = false,
2402
        $ignoreTimeLimit = false,
2403
        $getCount = false
2404
    ) {
2405
        $user_id = (int) $user_id;
2406
2407
        if (empty($user_id)) {
2408
            return [];
2409
        }
2410
2411
        // Get the list of sessions per user
2412
        $now = new DateTime('now', new DateTimeZone('UTC'));
2413
2414
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2415
        // join would not catch session-courses where the user is general
2416
        // session coach but which do not have students nor coaches registered
2417
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
2418
2419
        if (!$getCount) {
2420
            $dqlSelect = " DISTINCT
2421
                s.id,
2422
                s.title,
2423
                s.accessStartDate AS access_start_date,
2424
                s.accessEndDate AS access_end_date,
2425
                s.duration,
2426
                sc.id AS session_category_id,
2427
                sc.title AS session_category_title,
2428
                sc.dateStart AS session_category_date_start,
2429
                sc.dateEnd AS session_category_date_end,
2430
                s.coachAccessStartDate AS coach_access_start_date,
2431
                s.coachAccessEndDate AS coach_access_end_date,
2432
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
2433
                , s.position AS position
2434
            ";
2435
        }
2436
2437
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
2438
        // is awfully inefficient for large sets of data (1m25s for 58K
2439
        // sessions, BT#14115) but executing a similar query twice and grouping
2440
        // the results afterwards in PHP takes about 1/1000th of the time
2441
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
2442
        $dqlStudent = "SELECT $dqlSelect
2443
            FROM ChamiloCoreBundle:Session AS s
2444
            LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2445
            INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2446
            LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2447
            WHERE scu.user = :user AND url.url = :url ";
2448
        $dqlCoach = "SELECT $dqlSelect
2449
            FROM ChamiloCoreBundle:Session AS s
2450
            INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2451
            LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2452
            INNER JOIN ChamiloCoreBundle:SessionRelUser AS su WITH su.session = s
2453
            WHERE (su.user = :user AND su.relationType = ".SessionEntity::GENERAL_COACH.") AND url.url = :url ";
2454
2455
        // Default order
2456
        $order = 'ORDER BY sc.title, s.title';
2457
2458
        // Order by date if showing all sessions
2459
        $showAllSessions = ('true' === api_get_setting('course.show_all_sessions_on_my_course_page'));
2460
        if ($showAllSessions) {
2461
            $order = 'ORDER BY s.accessStartDate';
2462
        }
2463
2464
        // Order by position
2465
        if ('true' === api_get_setting('session.session_list_order')) {
2466
            $order = 'ORDER BY s.position';
2467
        }
2468
2469
        // Order by dates according to settings
2470
        $orderBySettings = api_get_setting('session.my_courses_session_order', true);
2471
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
2472
            $field = $orderBySettings['field'];
2473
            $orderSetting = $orderBySettings['order'];
2474
            switch ($field) {
2475
                case 'start_date':
2476
                    $order = " ORDER BY s.accessStartDate $orderSetting";
2477
                    break;
2478
                case 'end_date':
2479
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
2480
                    if ('asc' == $orderSetting) {
2481
                        // Put null values at the end
2482
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
2483
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
2484
                    }
2485
                    break;
2486
                case 'name':
2487
                case 'title':
2488
                    $order = " ORDER BY s.title $orderSetting ";
2489
                    break;
2490
            }
2491
        }
2492
2493
        $dqlStudent .= $order;
2494
        $dqlCoach .= $order;
2495
2496
        $accessUrlId = api_get_current_access_url_id();
2497
        $dqlStudent = Database::getManager()
2498
            ->createQuery($dqlStudent)
2499
            ->setParameters(
2500
                ['user' => $user_id, 'url' => $accessUrlId]
2501
            )
2502
        ;
2503
        $dqlCoach = Database::getManager()
2504
            ->createQuery($dqlCoach)
2505
            ->setParameters(
2506
                ['user' => $user_id, 'url' => $accessUrlId]
2507
            )
2508
        ;
2509
2510
        if ($getCount) {
2511
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
2512
        }
2513
2514
        $sessionDataStudent = $dqlStudent->getResult();
2515
        $sessionDataCoach = $dqlCoach->getResult();
2516
2517
        $sessionData = [];
2518
        // First fill $sessionData with student sessions
2519
        if (!empty($sessionDataStudent)) {
2520
            foreach ($sessionDataStudent as $row) {
2521
                $sessionData[$row['id']] = $row;
2522
            }
2523
        }
2524
        // Overwrite session data of the user as a student with session data
2525
        // of the user as a coach.
2526
        // There shouldn't be such duplicate rows, but just in case...
2527
        if (!empty($sessionDataCoach)) {
2528
            foreach ($sessionDataCoach as $row) {
2529
                $sessionData[$row['id']] = $row;
2530
            }
2531
        }
2532
2533
        $collapsable = ('true' === api_get_setting('session.allow_user_session_collapsable'));
2534
2535
2536
2537
        $extraField = new ExtraFieldValue('session');
2538
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
2539
2540
        if (empty($sessionData)) {
2541
            return [];
2542
        }
2543
        $categories = [];
2544
        foreach ($sessionData as $row) {
2545
            $session_id = $row['id'];
2546
            $coachList = SessionManager::getCoachesBySession($session_id);
2547
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
2548
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
2549
            $courseList = self::get_courses_list_by_session($user_id, $session_id);
2550
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
2551
2552
            // User portal filters:
2553
            if (false === $ignoreTimeLimit) {
2554
                if ($is_time_over) {
2555
                    // History
2556
                    if ($row['duration']) {
2557
                        if ($daysLeft >= 0) {
2558
                            continue;
2559
                        }
2560
                    } else {
2561
                        if (empty($row['access_end_date'])) {
2562
                            continue;
2563
                        } else {
2564
                            if ($row['access_end_date'] > $now) {
2565
                                continue;
2566
                            }
2567
                        }
2568
                    }
2569
                } else {
2570
                    // Current user portal
2571
                    $isGeneralCoach = api_get_session_entity($row['id'])->hasUserAsGeneralCoach(api_get_user_entity($user_id));
2572
                    $isCoachOfCourse = in_array($user_id, $coachList);
2573
2574
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
2575
                        // Teachers can access the session depending in the access_coach date
2576
                    } else {
2577
                        if ($row['duration']) {
2578
                            if ($daysLeft <= 0) {
2579
                                continue;
2580
                            }
2581
                        } else {
2582
                            if (isset($row['access_end_date']) &&
2583
                                !empty($row['access_end_date'])
2584
                            ) {
2585
                                if ($row['access_end_date'] <= $now) {
2586
                                    continue;
2587
                                }
2588
                            }
2589
                        }
2590
                    }
2591
                }
2592
            }
2593
2594
            $categories[$row['session_category_id']]['session_category'] = [
2595
                'id' => $row['session_category_id'],
2596
                'name' => $row['session_category_title'],
2597
                'date_start' => $categoryStart,
2598
                'date_end' => $categoryEnd,
2599
            ];
2600
2601
            $visibility = api_get_session_visibility(
2602
                $session_id,
2603
                null,
2604
                $ignore_visibility_for_admins
2605
            );
2606
2607
            if (SESSION_VISIBLE != $visibility) {
2608
                // Course Coach session visibility.
2609
                $blockedCourseCount = 0;
2610
                $closedVisibilityList = [
2611
                    COURSE_VISIBILITY_CLOSED,
2612
                    COURSE_VISIBILITY_HIDDEN,
2613
                ];
2614
2615
                foreach ($courseList as $course) {
2616
                    // Checking session visibility
2617
                    $sessionCourseVisibility = api_get_session_visibility(
2618
                        $session_id,
2619
                        $course['real_id'],
2620
                        $ignore_visibility_for_admins
2621
                    );
2622
2623
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
2624
                    if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
2625
                        $blockedCourseCount++;
2626
                    }
2627
                }
2628
2629
                // If all courses are blocked then no show in the list.
2630
                if ($blockedCourseCount === count($courseList)) {
2631
                    $visibility = SESSION_INVISIBLE;
2632
                } else {
2633
                    $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...
2634
                }
2635
            }
2636
2637
            switch ($visibility) {
2638
                case SESSION_VISIBLE_READ_ONLY:
2639
                case SESSION_VISIBLE:
2640
                case SESSION_AVAILABLE:
2641
                    break;
2642
                case SESSION_INVISIBLE:
2643
                    if (false === $ignore_visibility_for_admins) {
2644
                        continue 2;
2645
                    }
2646
            }
2647
2648
            $collapsed = '';
2649
            $collapsedAction = '';
2650
            if ($collapsable) {
2651
                $collapsableData = SessionManager::getCollapsableData(
2652
                    $user_id,
2653
                    $session_id,
2654
                    $extraField,
2655
                    $collapsableLink
2656
                );
2657
                $collapsed = $collapsableData['collapsed'];
2658
                $collapsedAction = $collapsableData['collapsable_link'];
2659
            }
2660
2661
            $categories[$row['session_category_id']]['sessions'][] = [
2662
                'session_name' => $row['title'],
2663
                'session_id' => $row['id'],
2664
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
2665
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
2666
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
2667
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
2668
                'courses' => $courseList,
2669
                'collapsed' => $collapsed,
2670
                'collapsable_link' => $collapsedAction,
2671
                'duration' => $row['duration'],
2672
            ];
2673
        }
2674
2675
        return $categories;
2676
    }
2677
2678
    /**
2679
     * Gives a list of [session_id-course_code] => [status] for the current user.
2680
     *
2681
     * @param int $user_id
2682
     * @param int $sessionLimit
2683
     *
2684
     * @return array list of statuses (session_id-course_code => status)
2685
     */
2686
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
2687
    {
2688
        // Database Table Definitions
2689
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2690
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2691
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2692
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
2693
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2694
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2695
2696
        $user_id = (int) $user_id;
2697
2698
        if (empty($user_id)) {
2699
            return [];
2700
        }
2701
2702
        $sessionRepo = Container::getSessionRepository();
2703
2704
        $user = api_get_user_entity($user_id);
2705
        $url = null;
2706
        $formattedUserName = Container::$container->get(NameConvention::class)->getPersonName($user);
2707
2708
        // We filter the courses from the URL
2709
        $join_access_url = $where_access_url = '';
2710
        if (api_get_multiple_access_url()) {
2711
            $access_url_id = api_get_current_access_url_id();
2712
            if (-1 != $access_url_id) {
2713
                $url = api_get_url_entity($access_url_id);
2714
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2715
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
2716
                $where_access_url = " AND access_url_id = $access_url_id ";
2717
            }
2718
        }
2719
2720
        // Courses in which we subscribed out of any session
2721
2722
        $sql = "SELECT
2723
                    course.code,
2724
                    course_rel_user.status course_rel_status,
2725
                    course_rel_user.sort sort,
2726
                    course_rel_user.user_course_cat user_course_cat
2727
                 FROM $tbl_course_user course_rel_user
2728
                 LEFT JOIN $tbl_course course
2729
                 ON course.id = course_rel_user.c_id
2730
                 $join_access_url
2731
                 WHERE
2732
                    course_rel_user.user_id = '".$user_id."' AND
2733
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
2734
                    $where_access_url
2735
                 ORDER BY course_rel_user.sort, course.title ASC";
2736
2737
        $course_list_sql_result = Database::query($sql);
2738
        $personal_course_list = [];
2739
        if (Database::num_rows($course_list_sql_result) > 0) {
2740
            while ($result_row = Database::fetch_assoc($course_list_sql_result)) {
2741
                $course_info = api_get_course_info($result_row['code']);
2742
                $result_row['course_info'] = $course_info;
2743
                $personal_course_list[] = $result_row;
2744
            }
2745
        }
2746
2747
        $coachCourseConditions = '';
2748
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
2749
        if (api_is_allowed_to_create_course()) {
2750
            $sessionListFromCourseCoach = [];
2751
            $sql = " SELECT DISTINCT session_id
2752
                    FROM $tbl_session_course_user
2753
                    WHERE user_id = $user_id AND status = ".SessionEntity::COURSE_COACH;
2754
2755
            $result = Database::query($sql);
2756
            if (Database::num_rows($result)) {
2757
                $result = Database::store_result($result);
2758
                foreach ($result as $session) {
2759
                    $sessionListFromCourseCoach[] = $session['session_id'];
2760
                }
2761
            }
2762
            if (!empty($sessionListFromCourseCoach)) {
2763
                $condition = implode("','", $sessionListFromCourseCoach);
2764
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
2765
            }
2766
        }
2767
2768
        // Get the list of sessions where the user is subscribed
2769
        // This is divided into two different queries
2770
        $sessions = [];
2771
        $sessionLimitRestriction = '';
2772
        if (!empty($sessionLimit)) {
2773
            $sessionLimit = (int) $sessionLimit;
2774
            $sessionLimitRestriction = "LIMIT $sessionLimit";
2775
        }
2776
2777
        $sql = "SELECT DISTINCT s.id, s.title, access_start_date, access_end_date
2778
                FROM $tbl_session_user su INNER JOIN $tbl_session s
2779
                ON (s.id = su.session_id)
2780
                WHERE (
2781
                    su.user_id = $user_id AND
2782
                    su.relation_type = ".SessionEntity::STUDENT."
2783
                )
2784
                $coachCourseConditions
2785
                ORDER BY access_start_date, access_end_date, s.title
2786
                $sessionLimitRestriction
2787
        ";
2788
2789
        $result = Database::query($sql);
2790
        if (Database::num_rows($result) > 0) {
2791
            while ($row = Database::fetch_assoc($result)) {
2792
                $sessions[$row['id']] = $row;
2793
            }
2794
        }
2795
2796
        $sql = "SELECT DISTINCT
2797
                s.id, s.title, s.access_start_date, s.access_end_date
2798
                FROM $tbl_session s
2799
                INNER JOIN $tbl_session_user sru ON sru.session_id = s.id
2800
                WHERE (
2801
                    sru.user_id = $user_id AND sru.relation_type = ".SessionEntity::GENERAL_COACH."
2802
                )
2803
                $coachCourseConditions
2804
                ORDER BY s.access_start_date, s.access_end_date, s.title";
2805
2806
        $result = Database::query($sql);
2807
        if (Database::num_rows($result) > 0) {
2808
            while ($row = Database::fetch_assoc($result)) {
2809
                if (empty($sessions[$row['id']])) {
2810
                    $sessions[$row['id']] = $row;
2811
                }
2812
            }
2813
        }
2814
2815
        if (api_is_allowed_to_create_course()) {
2816
            foreach ($sessions as $enreg) {
2817
                $session_id = $enreg['id'];
2818
                $session_visibility = api_get_session_visibility($session_id);
2819
                $session = api_get_session_entity($session_id);
2820
2821
                if (SESSION_INVISIBLE == $session_visibility) {
2822
                    continue;
2823
                }
2824
2825
                $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
2826
                    $user,
2827
                    $session,
2828
                    SessionEntity::GENERAL_COACH,
2829
                    $url
2830
                );
2831
                $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
2832
                    $user,
2833
                    $session,
2834
                    SessionEntity::COURSE_COACH,
2835
                    $url
2836
                );
2837
2838
                // This query is horribly slow when more than a few thousand
2839
                // users and just a few sessions to which they are subscribed
2840
                $coursesInSession = array_map(
2841
                    function (SessionRelCourse $courseInSession) {
2842
                        $course = $courseInSession->getCourse();
2843
2844
                        return [
2845
                            'code' => $course->getCode(),
2846
                            'i' => $course->getTitle(),
2847
                            'l' => $course->getCourseLanguage(),
2848
                            'sort' => 1,
2849
                        ];
2850
                    },
2851
                    array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
2852
                );
2853
2854
                foreach ($coursesInSession as $result_row) {
2855
                    $result_row['t'] = $formattedUserName;
2856
                    $result_row['email'] = $user->getEmail();
2857
                    $result_row['access_start_date'] = $session->getAccessStartDate()?->format('Y-m-d H:i:s');
2858
                    $result_row['access_end_date'] = $session->getAccessEndDate()?->format('Y-m-d H:i:s');
2859
                    $result_row['session_id'] = $session->getId();
2860
                    $result_row['session_name'] = $session->getTitle();
2861
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
2862
                    $key = $result_row['session_id'].' - '.$result_row['code'];
2863
                    $personal_course_list[$key] = $result_row;
2864
                }
2865
            }
2866
        }
2867
2868
        foreach ($sessions as $enreg) {
2869
            $session_id = $enreg['id'];
2870
            $session_visibility = api_get_session_visibility($session_id);
2871
            if (SESSION_INVISIBLE == $session_visibility) {
2872
                continue;
2873
            }
2874
2875
            /* This query is very similar to the above query,
2876
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
2877
            $sql = "SELECT DISTINCT
2878
                course.code code,
2879
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
2880
                email,
2881
                course.course_language l,
2882
                1 sort,
2883
                access_start_date,
2884
                access_end_date,
2885
                session.id as session_id,
2886
                session.title as session_name,
2887
                IF((session_course_user.user_id = 3 AND session_course_user.status = ".SessionEntity::COURSE_COACH."),'2', '5')
2888
            FROM $tbl_session_course_user as session_course_user
2889
            INNER JOIN $tbl_course AS course
2890
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
2891
            INNER JOIN $tbl_session as session
2892
            ON session_course_user.session_id = session.id
2893
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
2894
            WHERE session_course_user.user_id = $user_id
2895
            ORDER BY i";
2896
2897
            $course_list_sql_result = Database::query($sql);
2898
            while ($result_row = Database::fetch_assoc($course_list_sql_result)) {
2899
                $result_row['course_info'] = api_get_course_info($result_row['code']);
2900
                $key = $result_row['session_id'].' - '.$result_row['code'];
2901
                if (!isset($personal_course_list[$key])) {
2902
                    $personal_course_list[$key] = $result_row;
2903
                }
2904
            }
2905
        }
2906
2907
        return $personal_course_list;
2908
    }
2909
2910
    /**
2911
     * Gives a list of courses for the given user in the given session.
2912
     *
2913
     * @param int $user_id
2914
     * @param int $session_id
2915
     *
2916
     * @return array list of statuses (session_id-course_code => status)
2917
     */
2918
    public static function get_courses_list_by_session($user_id, $session_id)
2919
    {
2920
        // Database Table Definitions
2921
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
2922
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2923
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
2924
2925
        $user_id = (int) $user_id;
2926
        $session_id = (int) $session_id;
2927
2928
        $sessionRepo = Container::getSessionRepository();
2929
2930
        $user = api_get_user_entity($user_id);
2931
        $session = api_get_session_entity($session_id);
2932
        $url = null;
2933
2934
        // We filter the courses from the URL
2935
        $join_access_url = $where_access_url = '';
2936
        if (api_get_multiple_access_url()) {
2937
            $urlId = api_get_current_access_url_id();
2938
            if (-1 != $urlId) {
2939
                $url = api_get_url_entity($urlId);
2940
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
2941
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
2942
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
2943
            }
2944
        }
2945
2946
        /* This query is very similar to the query below, but it will check the
2947
        session_rel_course_user table if there are courses registered
2948
        to our user or not */
2949
        $sql = "SELECT DISTINCT
2950
                    c.title,
2951
                    c.visibility,
2952
                    c.id as real_id,
2953
                    c.code as course_code,
2954
                    sc.position,
2955
                    c.unsubscribe
2956
                FROM $tbl_session_course_user as scu
2957
                INNER JOIN $tbl_session_course sc
2958
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
2959
                INNER JOIN $tableCourse as c
2960
                ON (scu.c_id = c.id)
2961
                $join_access_url
2962
                WHERE
2963
                    scu.user_id = $user_id AND
2964
                    scu.session_id = $session_id
2965
                    $where_access_url
2966
                ORDER BY sc.position ASC";
2967
2968
        $myCourseList = [];
2969
        $courses = [];
2970
        $result = Database::query($sql);
2971
        if (Database::num_rows($result) > 0) {
2972
            while ($result_row = Database::fetch_assoc($result)) {
2973
                $result_row['status'] = 5;
2974
                if (!in_array($result_row['real_id'], $courses)) {
2975
                    $position = $result_row['position'];
2976
                    if (!isset($myCourseList[$position])) {
2977
                        $myCourseList[$position] = $result_row;
2978
                    } else {
2979
                        $myCourseList[] = $result_row;
2980
                    }
2981
                    $courses[] = $result_row['real_id'];
2982
                }
2983
            }
2984
        }
2985
2986
        if (api_is_allowed_to_create_course()) {
2987
            $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
2988
                $user,
2989
                $session,
2990
                SessionEntity::GENERAL_COACH,
2991
                $url
2992
            );
2993
            $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
2994
                $user,
2995
                $session,
2996
                SessionEntity::COURSE_COACH,
2997
                $url
2998
            );
2999
3000
            $coursesInSession = array_map(
3001
                function (SessionRelCourse $courseInSession) {
3002
                    $course = $courseInSession->getCourse();
3003
3004
                    return [
3005
                        'title' => $course->getTitle(),
3006
                        'visibility' => $course->getVisibility(),
3007
                        'real_id' => $course->getId(),
3008
                        'course_code' => $course->getCode(),
3009
                        'position' => $courseInSession->getPosition(),
3010
                        'unsubscribe' => $course->getUnsubscribe(),
3011
                    ];
3012
                },
3013
                array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
3014
            );
3015
3016
            foreach ($coursesInSession as $result_row) {
3017
                $result_row['status'] = 2;
3018
                if (!in_array($result_row['real_id'], $courses)) {
3019
                    $position = $result_row['position'];
3020
                    if (!isset($myCourseList[$position])) {
3021
                        $myCourseList[$position] = $result_row;
3022
                    } else {
3023
                        $myCourseList[] = $result_row;
3024
                    }
3025
                    $courses[] = $result_row['real_id'];
3026
                }
3027
            }
3028
        }
3029
3030
        if (api_is_drh()) {
3031
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3032
            $sessionList = array_keys($sessionList);
3033
            if (in_array($session_id, $sessionList)) {
3034
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3035
                if (!empty($courseList)) {
3036
                    foreach ($courseList as $course) {
3037
                        if (!in_array($course['id'], $courses)) {
3038
                            $position = $course['position'];
3039
                            if (!isset($myCourseList[$position])) {
3040
                                $myCourseList[$position] = $course;
3041
                            } else {
3042
                                $myCourseList[] = $course;
3043
                            }
3044
                        }
3045
                    }
3046
                }
3047
            }
3048
        } else {
3049
            //check if user is general coach for this session
3050
            if ($session && $session->hasUserAsGeneralCoach($user)) {
3051
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3052
                if (!empty($courseList)) {
3053
                    foreach ($courseList as $course) {
3054
                        if (!in_array($course['id'], $courses)) {
3055
                            $position = $course['position'];
3056
                            if (!isset($myCourseList[$position])) {
3057
                                $myCourseList[$position] = $course;
3058
                            } else {
3059
                                $myCourseList[] = $course;
3060
                            }
3061
                        }
3062
                    }
3063
                }
3064
            }
3065
        }
3066
3067
        if (!empty($myCourseList)) {
3068
            ksort($myCourseList);
3069
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
3070
            if (empty($checkPosition)) {
3071
                // The session course list doesn't have any position,
3072
                // then order the course list by course code
3073
                $list = array_column($myCourseList, 'course_code');
3074
                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

3074
                array_multisort($myCourseList, /** @scrutinizer ignore-type */ SORT_ASC, $list);
Loading history...
3075
            }
3076
        }
3077
3078
        return $myCourseList;
3079
    }
3080
3081
        /**
3082
     * Gives a list of session for the given user in the given course id.
3083
     *
3084
     * @param int $user_id
3085
     * @param int $course_id
3086
     *
3087
     * @return array list of session
3088
     */
3089
    public static function get_sessions_list_by_course($user_id, $course_id) : array
3090
    {
3091
        $course_id = (int) $course_id;
3092
        $user_id = (int) $user_id;
3093
3094
        if (empty($course_id)) {
3095
            return [];
3096
        }
3097
3098
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3099
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3100
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3101
3102
        $sql = "SELECT DISTINCT
3103
                    s.id,
3104
                    s.name
3105
                FROM $tbl_session s
3106
                INNER JOIN $tbl_session_course_user scu
3107
                ON s.id = scu.session_id
3108
                WHERE scu.c_id = $course_id";
3109
        // prepare array
3110
        $sessions = [];
3111
        $result = Database::query($sql);
3112
        if (Database::num_rows($result) > 0) {
3113
            while ($row = Database::fetch_assoc($result)) {
3114
                $sessions[$row['id']] = $row;
3115
            }
3116
        }
3117
3118
        return $sessions;
3119
3120
    }
3121
3122
    /**
3123
     * Get user id from a username.
3124
     *
3125
     * @param string $username
3126
     *
3127
     * @return int User ID (or false if not found)
3128
     */
3129
    public static function get_user_id_from_username($username)
3130
    {
3131
        if (empty($username)) {
3132
            return false;
3133
        }
3134
        $username = trim($username);
3135
        $username = Database::escape_string($username);
3136
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3137
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3138
        $res = Database::query($sql);
3139
3140
        if (false === $res) {
3141
            return false;
3142
        }
3143
        if (1 !== Database::num_rows($res)) {
3144
            return false;
3145
        }
3146
        $row = Database::fetch_array($res);
3147
3148
        return $row['id'];
3149
    }
3150
3151
    /**
3152
     * Get the users files upload from his share_folder.
3153
     *
3154
     * @param string $user_id      User ID
3155
     * @param string $course       course directory
3156
     * @param string $resourceType resource type: images, all
3157
     *
3158
     * @return string
3159
     */
3160
    /*public static function get_user_upload_files_by_course(
3161
        $user_id,
3162
        $course,
3163
        $resourceType = 'all'
3164
    ) {
3165
        $return = '';
3166
        $user_id = (int) $user_id;
3167
3168
        if (!empty($user_id) && !empty($course)) {
3169
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3170
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3171
            $file_list = [];
3172
3173
            if (is_dir($path)) {
3174
                $handle = opendir($path);
3175
                while ($file = readdir($handle)) {
3176
                    if ('.' == $file || '..' == $file || '.htaccess' == $file || is_dir($path.$file)) {
3177
                        continue; // skip current/parent directory and .htaccess
3178
                    }
3179
                    $file_list[] = $file;
3180
                }
3181
                if (count($file_list) > 0) {
3182
                    $return = "<h4>$course</h4>";
3183
                    $return .= '<ul class="thumbnails">';
3184
                }
3185
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
3186
                foreach ($file_list as $file) {
3187
                    if ('all' == $resourceType) {
3188
                        $return .= '<li>
3189
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
3190
                    } elseif ('images' == $resourceType) {
3191
                        //get extension
3192
                        $ext = explode('.', $file);
3193
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
3194
                            $return .= '<li class="span2">
3195
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
3196
                                                <img src="'.$web_path.urlencode($file).'" >
3197
                                            </a>
3198
                                        </li>';
3199
                        }
3200
                    }
3201
                }
3202
                if (count($file_list) > 0) {
3203
                    $return .= '</ul>';
3204
                }
3205
            }
3206
        }
3207
3208
        return $return;
3209
    }*/
3210
3211
    /**
3212
     * Gets the API key (or keys) and return them into an array.
3213
     *
3214
     * @param int     Optional user id (defaults to the result of api_get_user_id())
3215
     * @param string $api_service
3216
     *
3217
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
3218
     */
3219
    public static function get_api_keys($user_id = null, $api_service = 'default')
3220
    {
3221
        if ($user_id != strval(intval($user_id))) {
3222
            return false;
3223
        }
3224
        if (empty($user_id)) {
3225
            $user_id = api_get_user_id();
3226
        }
3227
        if (false === $user_id) {
3228
            return false;
3229
        }
3230
        $service_name = Database::escape_string($api_service);
3231
        if (false === is_string($service_name)) {
3232
            return false;
3233
        }
3234
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3235
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
3236
        $res = Database::query($sql);
3237
        if (false === $res) {
3238
            return false;
3239
        } //error during query
3240
        $num = Database::num_rows($res);
3241
        if (0 == $num) {
3242
            return false;
3243
        }
3244
        $list = [];
3245
        while ($row = Database::fetch_array($res)) {
3246
            $list[$row['id']] = $row['api_key'];
3247
        }
3248
3249
        return $list;
3250
    }
3251
3252
    /**
3253
     * Adds a new API key to the users' account.
3254
     *
3255
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
3256
     * @param string $api_service
3257
     *
3258
     * @return bool True on success, false on failure
3259
     */
3260
    public static function add_api_key($user_id = null, $api_service = 'default')
3261
    {
3262
        if ($user_id != strval(intval($user_id))) {
3263
            return false;
3264
        }
3265
        if (empty($user_id)) {
3266
            $user_id = api_get_user_id();
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
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
3277
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
3278
        $res = Database::query($sql);
3279
        if (false === $res) {
3280
            return false;
3281
        } //error during query
3282
        $num = Database::insert_id();
3283
3284
        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...
3285
    }
3286
3287
    /**
3288
     * Deletes an API key from the user's account.
3289
     *
3290
     * @param   int     API key's internal ID
3291
     *
3292
     * @return bool True on success, false on failure
3293
     */
3294
    public static function delete_api_key($key_id)
3295
    {
3296
        if ($key_id != strval(intval($key_id))) {
3297
            return false;
3298
        }
3299
        if (false === $key_id) {
3300
            return false;
3301
        }
3302
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3303
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
3304
        $res = Database::query($sql);
3305
        if (false === $res) {
3306
            return false;
3307
        } //error during query
3308
        $num = Database::num_rows($res);
3309
        if (1 !== $num) {
3310
            return false;
3311
        }
3312
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
3313
        $res = Database::query($sql);
3314
        if (false === $res) {
3315
            return false;
3316
        } //error during query
3317
3318
        return true;
3319
    }
3320
3321
    /**
3322
     * Regenerate an API key from the user's account.
3323
     *
3324
     * @param   int     user ID (defaults to the results of api_get_user_id())
3325
     * @param   string  API key's internal ID
3326
     *
3327
     * @return int num
3328
     */
3329
    public static function update_api_key($user_id, $api_service)
3330
    {
3331
        if ($user_id != strval(intval($user_id))) {
3332
            return false;
3333
        }
3334
        if (false === $user_id) {
3335
            return false;
3336
        }
3337
        $service_name = Database::escape_string($api_service);
3338
        if (false === is_string($service_name)) {
3339
            return false;
3340
        }
3341
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3342
        $sql = "SELECT id FROM $t_api
3343
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3344
        $res = Database::query($sql);
3345
        $num = Database::num_rows($res);
3346
        if (1 == $num) {
3347
            $id_key = Database::fetch_assoc($res);
3348
            self::delete_api_key($id_key['id']);
3349
            $num = self::add_api_key($user_id, $api_service);
3350
        } elseif (0 == $num) {
3351
            $num = self::add_api_key($user_id, $api_service);
3352
        }
3353
3354
        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...
3355
    }
3356
3357
    /**
3358
     * @param   int     user ID (defaults to the results of api_get_user_id())
3359
     * @param   string    API key's internal ID
3360
     *
3361
     * @return int row ID, or return false if not found
3362
     */
3363
    public static function get_api_key_id($user_id, $api_service)
3364
    {
3365
        if ($user_id != strval(intval($user_id))) {
3366
            return false;
3367
        }
3368
        if (false === $user_id) {
3369
            return false;
3370
        }
3371
        if (empty($api_service)) {
3372
            return false;
3373
        }
3374
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3375
        $api_service = Database::escape_string($api_service);
3376
        $sql = "SELECT id FROM $t_api
3377
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3378
        $res = Database::query($sql);
3379
        if (Database::num_rows($res) < 1) {
3380
            return false;
3381
        }
3382
        $row = Database::fetch_assoc($res);
3383
3384
        return $row['id'];
3385
    }
3386
3387
    /**
3388
     * Checks if a user_id is platform admin.
3389
     *
3390
     * @param   int user ID
3391
     *
3392
     * @return bool True if is admin, false otherwise
3393
     *
3394
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
3395
     */
3396
    public static function is_admin($user_id)
3397
    {
3398
        $user_id = (int) $user_id;
3399
        if (empty($user_id)) {
3400
            return false;
3401
        }
3402
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3403
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3404
        $res = Database::query($sql);
3405
3406
        return 1 === Database::num_rows($res);
3407
    }
3408
3409
    /**
3410
     * Get the total count of users.
3411
     *
3412
     * @param ?int $status Status of users to be counted
3413
     * @param ?int $access_url_id Access URL ID (optional)
3414
     * @param ?int $active
3415
     *
3416
     * @return mixed Number of users or false on error
3417
     * @throws \Doctrine\DBAL\Exception
3418
     */
3419
    public static function get_number_of_users(
3420
        ?int $status = 0,
3421
        ?int $access_url_id = 1,
3422
        ?int $active = null,
3423
        ?string $dateFrom = null,
3424
        ?string $dateUntil = null
3425
    ): mixed {
3426
        $tableUser = Database::get_main_table(TABLE_MAIN_USER);
3427
        $tableAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3428
3429
        if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

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

3429
        if (/** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
3430
            $sql = "SELECT count(u.id)
3431
                    FROM $tableUser u
3432
                    INNER JOIN $tableAccessUrlRelUser url_user
3433
                    ON (u.id = url_user.user_id)
3434
                    WHERE url_user.access_url_id = $access_url_id
3435
            ";
3436
        } else {
3437
            $sql = "SELECT count(u.id)
3438
                    FROM $tableUser u
3439
                    WHERE 1 = 1 ";
3440
        }
3441
3442
        $status = (int) $status;
3443
        if (!empty($status) && $status > 0) {
3444
            $sql .= " AND u.status = $status ";
3445
        }
3446
3447
        if (isset($active)) {
3448
            $active = (int) $active;
3449
            $sql .= " AND u.active = $active ";
3450
        }
3451
3452
        if (!empty($dateFrom)) {
3453
            $dateFrom = api_get_utc_datetime("$dateFrom 00:00:00");
3454
            $sql .= " AND u.registration_date >= '$dateFrom' ";
3455
        }
3456
        if (!empty($dateUntil)) {
3457
            $dateUntil = api_get_utc_datetime("$dateUntil 23:59:59");
3458
            $sql .= " AND u.registration_date <= '$dateUntil' ";
3459
        }
3460
3461
        $res = Database::query($sql);
3462
        if (1 === Database::num_rows($res)) {
3463
            return (int) Database::result($res, 0, 0);
3464
        }
3465
3466
        return false;
3467
    }
3468
3469
    /**
3470
     * Gets the tags of a specific field_id
3471
     * USER TAGS.
3472
     *
3473
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
3474
     *
3475
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
3476
     *    Called it "books" for example.
3477
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
3478
     * 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
3479
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
3480
     * 5. Test and enjoy.
3481
     *
3482
     * @param string $tag
3483
     * @param int    $field_id      field_id
3484
     * @param string $return_format how we are going to result value in array or in a string (json)
3485
     * @param $limit
3486
     *
3487
     * @return mixed
3488
     */
3489
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
3490
    {
3491
        // database table definition
3492
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3493
        $field_id = (int) $field_id;
3494
        $limit = (int) $limit;
3495
        $tag = trim(Database::escape_string($tag));
3496
3497
        // all the information of the field
3498
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
3499
                WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag LIMIT $limit";
3500
        $result = Database::query($sql);
3501
        $return = [];
3502
        if (Database::num_rows($result) > 0) {
3503
            while ($row = Database::fetch_assoc($result)) {
3504
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
3505
            }
3506
        }
3507
        if ('json' === $return_format) {
3508
            $return = json_encode($return);
3509
        }
3510
3511
        return $return;
3512
    }
3513
3514
    /**
3515
     * @param int $field_id
3516
     * @param int $limit
3517
     *
3518
     * @return array
3519
     */
3520
    public static function get_top_tags($field_id, $limit = 100)
3521
    {
3522
        // database table definition
3523
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3524
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3525
        $field_id = (int) $field_id;
3526
        $limit = (int) $limit;
3527
        // all the information of the field
3528
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
3529
                INNER JOIN $table_user_tag ut
3530
                ON (ut.id = uv.tag_id)
3531
                WHERE field_id = $field_id
3532
                GROUP BY tag_id
3533
                ORDER BY count DESC
3534
                LIMIT $limit";
3535
        $result = Database::query($sql);
3536
        $return = [];
3537
        if (Database::num_rows($result) > 0) {
3538
            while ($row = Database::fetch_assoc($result)) {
3539
                $return[] = $row;
3540
            }
3541
        }
3542
3543
        return $return;
3544
    }
3545
3546
    /**
3547
     * Get user's tags.
3548
     *
3549
     * @param int $user_id
3550
     * @param int $field_id
3551
     *
3552
     * @return array
3553
     */
3554
    public static function get_user_tags($user_id, $field_id)
3555
    {
3556
        // database table definition
3557
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3558
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3559
        $field_id = (int) $field_id;
3560
        $user_id = (int) $user_id;
3561
3562
        // all the information of the field
3563
        $sql = "SELECT ut.id, tag, count
3564
                FROM $table_user_tag ut
3565
                INNER JOIN $table_user_tag_values uv
3566
                ON (uv.tag_id=ut.ID)
3567
                WHERE field_id = $field_id AND user_id = $user_id
3568
                ORDER BY tag";
3569
        $result = Database::query($sql);
3570
        $return = [];
3571
        if (Database::num_rows($result) > 0) {
3572
            while ($row = Database::fetch_assoc($result)) {
3573
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3574
            }
3575
        }
3576
3577
        return $return;
3578
    }
3579
3580
    /**
3581
     * Get user's tags.
3582
     *
3583
     * @param int  $user_id
3584
     * @param int  $field_id
3585
     * @param bool $show_links show links or not
3586
     *
3587
     * @return string
3588
     */
3589
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
3590
    {
3591
        // database table definition
3592
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3593
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3594
        $field_id = (int) $field_id;
3595
        $user_id = (int) $user_id;
3596
3597
        // all the information of the field
3598
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
3599
                INNER JOIN $table_user_tag_values uv
3600
                ON (uv.tag_id = ut.id)
3601
                WHERE field_id = $field_id AND user_id = $user_id
3602
                ORDER BY tag";
3603
3604
        $result = Database::query($sql);
3605
        $return = [];
3606
        if (Database::num_rows($result) > 0) {
3607
            while ($row = Database::fetch_assoc($result)) {
3608
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3609
            }
3610
        }
3611
        $user_tags = $return;
3612
        $tag_tmp = [];
3613
        foreach ($user_tags as $tag) {
3614
            if ($show_links) {
3615
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
3616
                    $tag['tag'].
3617
                '</a>';
3618
            } else {
3619
                $tag_tmp[] = $tag['tag'];
3620
            }
3621
        }
3622
3623
        if (is_array($user_tags) && count($user_tags) > 0) {
3624
            return implode(', ', $tag_tmp);
3625
        } else {
3626
            return '';
3627
        }
3628
    }
3629
3630
    /**
3631
     * Get the tag id.
3632
     *
3633
     * @param int $tag
3634
     * @param int $field_id
3635
     *
3636
     * @return int returns 0 if fails otherwise the tag id
3637
     */
3638
    public static function get_tag_id($tag, $field_id)
3639
    {
3640
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3641
        $tag = Database::escape_string($tag);
3642
        $field_id = (int) $field_id;
3643
        //with COLLATE latin1_bin to select query in a case sensitive mode
3644
        $sql = "SELECT id FROM $table_user_tag
3645
                WHERE tag LIKE '$tag' AND field_id = $field_id";
3646
        $result = Database::query($sql);
3647
        if (Database::num_rows($result) > 0) {
3648
            $row = Database::fetch_assoc($result);
3649
3650
            return $row['id'];
3651
        } else {
3652
            return 0;
3653
        }
3654
    }
3655
3656
    /**
3657
     * Get the tag id.
3658
     *
3659
     * @param int $tag_id
3660
     * @param int $field_id
3661
     *
3662
     * @return int 0 if fails otherwise the tag id
3663
     */
3664
    public static function get_tag_id_from_id($tag_id, $field_id)
3665
    {
3666
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3667
        $tag_id = (int) $tag_id;
3668
        $field_id = (int) $field_id;
3669
        $sql = "SELECT id FROM $table_user_tag
3670
                WHERE id = '$tag_id' AND field_id = $field_id";
3671
        $result = Database::query($sql);
3672
        if (Database::num_rows($result) > 0) {
3673
            $row = Database::fetch_assoc($result);
3674
3675
            return $row['id'];
3676
        } else {
3677
            return false;
3678
        }
3679
    }
3680
3681
    /**
3682
     * Adds a user-tag value.
3683
     *
3684
     * @param mixed $tag
3685
     * @param int   $user_id
3686
     * @param int   $field_id field id of the tag
3687
     *
3688
     * @return bool True if the tag was inserted or updated. False otherwise.
3689
     *              The return value doesn't take into account *values* added to the tag.
3690
     *              Only the creation/update of the tag field itself.
3691
     */
3692
    public static function add_tag($tag, $user_id, $field_id)
3693
    {
3694
        // database table definition
3695
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3696
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3697
        $tag = trim(Database::escape_string($tag));
3698
        $user_id = (int) $user_id;
3699
        $field_id = (int) $field_id;
3700
3701
        $tag_id = self::get_tag_id($tag, $field_id);
3702
3703
        /* IMPORTANT
3704
         *  @todo we don't create tags with numbers
3705
         *
3706
         */
3707
3708
        //this is a new tag
3709
        if (0 == $tag_id) {
3710
            //the tag doesn't exist
3711
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
3712
            Database::query($sql);
3713
            $last_insert_id = Database::insert_id();
3714
        } else {
3715
            //the tag exists we update it
3716
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
3717
            Database::query($sql);
3718
            $last_insert_id = $tag_id;
3719
        }
3720
3721
        if (!empty($last_insert_id) && (0 != $last_insert_id)) {
3722
            //we insert the relationship user-tag
3723
            $sql = "SELECT tag_id FROM $table_user_tag_values
3724
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
3725
            $result = Database::query($sql);
3726
            //if the relationship does not exist we create it
3727
            if (0 == Database::num_rows($result)) {
3728
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
3729
                Database::query($sql);
3730
            }
3731
3732
            return true;
3733
        }
3734
3735
        return false;
3736
    }
3737
3738
    /**
3739
     * Deletes an user tag.
3740
     *
3741
     * @param int $user_id
3742
     * @param int $field_id
3743
     */
3744
    public static function delete_user_tags($user_id, $field_id)
3745
    {
3746
        // database table definition
3747
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3748
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3749
        $user_id = (int) $user_id;
3750
3751
        $tags = self::get_user_tags($user_id, $field_id);
3752
        if (is_array($tags) && count($tags) > 0) {
3753
            foreach ($tags as $key => $tag) {
3754
                if ($tag['count'] > '0') {
3755
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
3756
                    Database::query($sql);
3757
                }
3758
                $sql = "DELETE FROM $table_user_tag_values
3759
                        WHERE user_id = $user_id AND tag_id = $key";
3760
                Database::query($sql);
3761
            }
3762
        }
3763
    }
3764
3765
    /**
3766
     * Process the tag list comes from the UserManager::update_extra_field_value() function.
3767
     *
3768
     * @param array $tags     the tag list that will be added
3769
     * @param int   $user_id
3770
     * @param int   $field_id
3771
     *
3772
     * @return bool
3773
     */
3774
    public static function process_tags($tags, $user_id, $field_id)
3775
    {
3776
        // We loop the tags and add it to the DB
3777
        if (is_array($tags)) {
3778
            foreach ($tags as $tag) {
3779
                self::add_tag($tag, $user_id, $field_id);
3780
            }
3781
        } else {
3782
            self::add_tag($tags, $user_id, $field_id);
3783
        }
3784
3785
        return true;
3786
    }
3787
3788
    /**
3789
     * Returns a list of all administrators.
3790
     *
3791
     * @return array
3792
     */
3793
    public static function get_all_administrators()
3794
    {
3795
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3796
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
3797
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3798
        $access_url_id = api_get_current_access_url_id();
3799
        if (api_get_multiple_access_url()) {
3800
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3801
                    FROM $tbl_url_rel_user as url
3802
                    INNER JOIN $table_admin as admin
3803
                    ON (admin.user_id=url.user_id)
3804
                    INNER JOIN $table_user u
3805
                    ON (u.id=admin.user_id)
3806
                    WHERE access_url_id ='".$access_url_id."'";
3807
        } else {
3808
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3809
                    FROM $table_admin as admin
3810
                    INNER JOIN $table_user u
3811
                    ON (u.id=admin.user_id)";
3812
        }
3813
        $sql .= !str_contains($sql, 'WHERE') ? ' WHERE u.active <> '.USER_SOFT_DELETED : ' AND u.active <> '.USER_SOFT_DELETED;
3814
        $result = Database::query($sql);
3815
        $return = [];
3816
        if (Database::num_rows($result) > 0) {
3817
            while ($row = Database::fetch_assoc($result)) {
3818
                $return[$row['user_id']] = $row;
3819
            }
3820
        }
3821
3822
        return $return;
3823
    }
3824
3825
    /**
3826
     * Search an user (tags, first name, last name and email ).
3827
     *
3828
     * @param string $tag
3829
     * @param int    $field_id        field id of the tag
3830
     * @param int    $from            where to start in the query
3831
     * @param int    $number_of_items
3832
     * @param bool   $getCount        get count or not
3833
     *
3834
     * @return array
3835
     */
3836
    public static function get_all_user_tags(
3837
        $tag,
3838
        $field_id = 0,
3839
        $from = 0,
3840
        $number_of_items = 10,
3841
        $getCount = false
3842
    ) {
3843
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
3844
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3845
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3846
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3847
3848
        $field_id = intval($field_id);
3849
        $from = intval($from);
3850
        $number_of_items = intval($number_of_items);
3851
3852
        $where_field = "";
3853
        $where_extra_fields = self::get_search_form_where_extra_fields();
3854
        if (0 != $field_id) {
3855
            $where_field = " field_id = $field_id AND ";
3856
        }
3857
3858
        // all the information of the field
3859
        if ($getCount) {
3860
            $select = "SELECT count(DISTINCT u.id) count";
3861
        } else {
3862
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, tag, picture_uri";
3863
        }
3864
3865
        $sql = " $select
3866
                FROM $user_table u
3867
                INNER JOIN $access_url_rel_user_table url_rel_user
3868
                ON (u.id = url_rel_user.user_id)
3869
                LEFT JOIN $table_user_tag_values uv
3870
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
3871
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
3872
                WHERE
3873
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
3874
                    (
3875
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
3876
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
3877
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
3878
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
3879
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
3880
                     )
3881
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
3882
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
3883
3884
        $keyword_active = true;
3885
        // only active users
3886
        if ($keyword_active) {
3887
            $sql .= " AND u.active='1'";
3888
        }
3889
        // avoid anonymous
3890
        $sql .= " AND u.status <> 6 ";
3891
        $sql .= " ORDER BY username";
3892
        $sql .= " LIMIT $from , $number_of_items";
3893
3894
        $result = Database::query($sql);
3895
        $return = [];
3896
3897
        if (Database::num_rows($result) > 0) {
3898
            if ($getCount) {
3899
                $row = Database::fetch_assoc($result);
3900
3901
                return $row['count'];
3902
            }
3903
            while ($row = Database::fetch_assoc($result)) {
3904
                $return[$row['id']] = $row;
3905
            }
3906
        }
3907
3908
        return $return;
3909
    }
3910
3911
    /**
3912
     * Get extra filterable user fields (only type select).
3913
     *
3914
     * @return array Array of extra fields as [int => ['name' => ..., 'variable' => ..., 'data' => ...]] (
3915
     *               or empty array if no extra field)
3916
     */
3917
    public static function getExtraFilterableFields()
3918
    {
3919
        $extraFieldList = self::get_extra_fields();
3920
        $fields = [];
3921
        if (is_array($extraFieldList)) {
3922
            foreach ($extraFieldList as $extraField) {
3923
                // If is enabled to filter and is a "<select>" field type
3924
                if (1 == $extraField[8] && 4 == $extraField[2]) {
3925
                    $fields[] = [
3926
                        'name' => $extraField[3],
3927
                        'variable' => $extraField[1],
3928
                        'data' => $extraField[9],
3929
                    ];
3930
                }
3931
            }
3932
        }
3933
3934
        return $fields;
3935
    }
3936
3937
    /**
3938
     * Get extra where clauses for finding users based on extra filterable user fields (type select).
3939
     *
3940
     * @return string With AND clauses based on user's ID which have the values to search in extra user fields
3941
     *                (or empty if no extra field exists)
3942
     */
3943
    public static function get_search_form_where_extra_fields()
3944
    {
3945
        $useExtraFields = false;
3946
        $extraFields = self::getExtraFilterableFields();
3947
        $extraFieldResult = [];
3948
        if (is_array($extraFields) && count($extraFields) > 0) {
3949
            foreach ($extraFields as $extraField) {
3950
                $varName = 'field_'.$extraField['variable'];
3951
                if (self::is_extra_field_available($extraField['variable'])) {
3952
                    if (isset($_GET[$varName]) && '0' != $_GET[$varName]) {
3953
                        $useExtraFields = true;
3954
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
3955
                            $extraField['variable'],
3956
                            $_GET[$varName]
3957
                        );
3958
                    }
3959
                }
3960
            }
3961
        }
3962
3963
        if ($useExtraFields) {
3964
            $finalResult = [];
3965
            if (count($extraFieldResult) > 1) {
3966
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
3967
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
3968
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
3969
                    }
3970
                }
3971
            } else {
3972
                $finalResult = $extraFieldResult[0];
3973
            }
3974
3975
            if (is_array($finalResult) && count($finalResult) > 0) {
3976
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
3977
            } else {
3978
                //no results
3979
                $whereFilter = " AND u.id  = -1 ";
3980
            }
3981
3982
            return $whereFilter;
3983
        }
3984
3985
        return '';
3986
    }
3987
3988
    /**
3989
     * Show the search form.
3990
     *
3991
     * @param string $query the value of the search box
3992
     *
3993
     * @throws Exception
3994
     *
3995
     * @return string HTML form
3996
     */
3997
    public static function get_search_form($query, $defaultParams = [])
3998
    {
3999
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
4000
        $form = new FormValidator(
4001
            'search_user',
4002
            'get',
4003
            api_get_path(WEB_PATH).'main/social/search.php',
4004
            '',
4005
            [],
4006
            FormValidator::LAYOUT_HORIZONTAL
4007
        );
4008
4009
        $query = Security::remove_XSS($query);
4010
4011
        if (!empty($query)) {
4012
            $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

4012
            $form->addHeader(get_lang('Results and feedback').' "'./** @scrutinizer ignore-type */ $query.'"');
Loading history...
4013
        }
4014
4015
        $form->addText(
4016
            'q',
4017
            get_lang('Users, Groups'),
4018
            false,
4019
            [
4020
                'id' => 'q',
4021
            ]
4022
        );
4023
        $options = [
4024
            0 => get_lang('Select'),
4025
            1 => get_lang('User'),
4026
            2 => get_lang('Group'),
4027
        ];
4028
        $form->addSelect(
4029
            'search_type',
4030
            get_lang('Type'),
4031
            $options,
4032
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
4033
        );
4034
4035
        // Extra fields
4036
        $extraFields = self::getExtraFilterableFields();
4037
        $defaults = [];
4038
        if (is_array($extraFields) && count($extraFields) > 0) {
4039
            foreach ($extraFields as $extraField) {
4040
                $varName = 'field_'.$extraField['variable'];
4041
                $options = [
4042
                    0 => get_lang('Select'),
4043
                ];
4044
                foreach ($extraField['data'] as $option) {
4045
                    if (isset($_GET[$varName])) {
4046
                        if ($_GET[$varName] == $option[1]) {
4047
                            $defaults[$option[1]] = true;
4048
                        }
4049
                    }
4050
4051
                    $options[$option[1]] = $option[1];
4052
                }
4053
                $form->addSelect($varName, $extraField['name'], $options);
4054
            }
4055
        }
4056
4057
        $defaults['search_type'] = (int) $searchType;
4058
        $defaults['q'] = $query;
4059
4060
        if (!empty($defaultParams)) {
4061
            $defaults = array_merge($defaults, $defaultParams);
4062
        }
4063
        $form->setDefaults($defaults);
4064
        $form->addButtonSearch(get_lang('Search'));
4065
4066
        $js = '<script>
4067
        extra_field_toogle();
4068
        function extra_field_toogle() {
4069
            if (jQuery("select[name=search_type]").val() != "1") {
4070
                jQuery(".extra_field").hide();
4071
            } else {
4072
                jQuery(".extra_field").show();
4073
            }
4074
        }
4075
        </script>';
4076
4077
        return $js.$form->returnForm();
4078
    }
4079
4080
    /**
4081
     * @param int $userId
4082
     *
4083
     * @return array
4084
     */
4085
    public static function getDrhListFromUser($userId)
4086
    {
4087
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4088
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4089
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4090
        $userId = (int) $userId;
4091
4092
        $orderBy = null;
4093
        if (api_is_western_name_order()) {
4094
            $orderBy .= ' ORDER BY firstname, lastname ';
4095
        } else {
4096
            $orderBy .= ' ORDER BY lastname, firstname ';
4097
        }
4098
4099
        $sql = "SELECT u.id, username, u.firstname, u.lastname
4100
                FROM $tblUser u
4101
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
4102
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
4103
                WHERE
4104
                    access_url_id = ".api_get_current_access_url_id()." AND
4105
                    uru.user_id = '$userId' AND
4106
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4107
                    $orderBy
4108
                ";
4109
        $result = Database::query($sql);
4110
4111
        return Database::store_result($result);
4112
    }
4113
4114
    /**
4115
     * get users followed by human resource manager.
4116
     *
4117
     * @param int    $userId
4118
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
4119
     * @param bool   $getOnlyUserId
4120
     * @param bool   $getSql
4121
     * @param bool   $getCount
4122
     * @param int    $from
4123
     * @param int    $numberItems
4124
     * @param int    $column
4125
     * @param string $direction
4126
     * @param int    $active
4127
     * @param string $lastConnectionDate
4128
     *
4129
     * @return array users
4130
     */
4131
    public static function get_users_followed_by_drh(
4132
        $userId,
4133
        $userStatus = 0,
4134
        $getOnlyUserId = false,
4135
        $getSql = false,
4136
        $getCount = false,
4137
        $from = null,
4138
        $numberItems = null,
4139
        $column = null,
4140
        $direction = null,
4141
        $active = null,
4142
        $lastConnectionDate = null
4143
    ) {
4144
        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...
4145
            $userId,
4146
            $userStatus,
4147
            $getOnlyUserId,
4148
            $getSql,
4149
            $getCount,
4150
            $from,
4151
            $numberItems,
4152
            $column,
4153
            $direction,
4154
            $active,
4155
            $lastConnectionDate,
4156
            DRH
4157
        );
4158
    }
4159
4160
    /**
4161
     * Get users followed by human resource manager.
4162
     *
4163
     * @param int    $userId
4164
     * @param int    $userStatus             Filter users by status (STUDENT, COURSEMANAGER, etc)
4165
     * @param bool   $getOnlyUserId
4166
     * @param bool   $getSql
4167
     * @param bool   $getCount
4168
     * @param int    $from
4169
     * @param int    $numberItems
4170
     * @param int    $column
4171
     * @param string $direction
4172
     * @param int    $active
4173
     * @param string $lastConnectionDate
4174
     * @param int    $status                 the function is called by who? COURSEMANAGER, DRH?
4175
     * @param string $keyword
4176
     * @param bool   $checkSessionVisibility
4177
     *
4178
     * @return mixed Users list (array) or the SQL query if $getSQL was set to true
4179
     */
4180
    public static function getUsersFollowedByUser(
4181
        $userId,
4182
        $userStatus = null,
4183
        $getOnlyUserId = false,
4184
        $getSql = false,
4185
        $getCount = false,
4186
        $from = null,
4187
        $numberItems = null,
4188
        $column = null,
4189
        $direction = null,
4190
        $active = null,
4191
        $lastConnectionDate = null,
4192
        $status = null,
4193
        $keyword = null,
4194
        $checkSessionVisibility = false
4195
    ) {
4196
        // Database Table Definitions
4197
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4198
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4199
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4200
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4201
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4202
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4203
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4204
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4205
4206
        $userId = (int) $userId;
4207
        $limitCondition = '';
4208
4209
        if (isset($from) && isset($numberItems)) {
4210
            $from = (int) $from;
4211
            $numberItems = (int) $numberItems;
4212
            $limitCondition = "LIMIT $from, $numberItems";
4213
        }
4214
4215
        $column = Database::escape_string($column);
4216
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
4217
4218
        $userConditions = '';
4219
        if (!empty($userStatus)) {
4220
            $userConditions .= ' AND u.status = '.intval($userStatus);
4221
        }
4222
4223
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
4224
        if ($getOnlyUserId) {
4225
            $select = " SELECT DISTINCT u.id user_id";
4226
        }
4227
4228
        $masterSelect = "SELECT DISTINCT * FROM ";
4229
4230
        if ($getCount) {
4231
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
4232
            $select = " SELECT DISTINCT(u.id) user_id";
4233
        }
4234
4235
        if (!is_null($active)) {
4236
            $active = intval($active);
4237
            $userConditions .= " AND u.active = $active ";
4238
        }
4239
4240
        if (!empty($keyword)) {
4241
            $keyword = trim(Database::escape_string($keyword));
4242
            $keywordParts = array_filter(explode(' ', $keyword));
4243
            $extraKeyword = '';
4244
            if (!empty($keywordParts)) {
4245
                $keywordPartsFixed = Database::escape_string(implode('%', $keywordParts));
4246
                if (!empty($keywordPartsFixed)) {
4247
                    $extraKeyword .= " OR
4248
                        CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keywordPartsFixed%' OR
4249
                        CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keywordPartsFixed%' ";
4250
                }
4251
            }
4252
            $userConditions .= " AND (
4253
                u.username LIKE '%$keyword%' OR
4254
                u.firstname LIKE '%$keyword%' OR
4255
                u.lastname LIKE '%$keyword%' OR
4256
                u.official_code LIKE '%$keyword%' OR
4257
                u.email LIKE '%$keyword%' OR
4258
                CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keyword%' OR
4259
                CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keyword%'
4260
                $extraKeyword
4261
            )";
4262
        }
4263
4264
        if (!empty($lastConnectionDate)) {
4265
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
4266
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
4267
        }
4268
4269
        $sessionConditionsCoach = null;
4270
        $dateCondition = '';
4271
        $drhConditions = null;
4272
        $teacherSelect = null;
4273
        $urlId = api_get_current_access_url_id();
4274
4275
        switch ($status) {
4276
            case DRH:
4277
                $drhConditions .= " AND
4278
                    friend_user_id = '$userId' AND
4279
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4280
                ";
4281
                break;
4282
            case COURSEMANAGER:
4283
                $drhConditions .= " AND
4284
                    friend_user_id = '$userId' AND
4285
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4286
                ";
4287
4288
                $sessionConditionsTeacher = " AND
4289
                    (scu.status = ".SessionEntity::COURSE_COACH." AND scu.user_id = '$userId')
4290
                ";
4291
4292
                if ($checkSessionVisibility) {
4293
                    $today = api_strtotime('now', 'UTC');
4294
                    $today = date('Y-m-d', $today);
4295
                    $dateCondition = "
4296
                        AND
4297
                        (
4298
                            (s.access_start_date <= '$today' AND '$today' <= s.access_end_date) OR
4299
                            (s.access_start_date IS NULL AND s.access_end_date IS NULL) OR
4300
                            (s.access_start_date <= '$today' AND s.access_end_date IS NULL) OR
4301
                            ('$today' <= s.access_end_date AND s.access_start_date IS NULL)
4302
                        )
4303
					";
4304
                }
4305
4306
                // Use $tbl_session_rel_course_rel_user instead of $tbl_session_rel_user
4307
                /*
4308
                INNER JOIN $tbl_session_rel_user sru
4309
                ON (sru.user_id = u.id)
4310
                INNER JOIN $tbl_session_rel_course_rel_user scu
4311
                ON (scu.user_id = u.id AND scu.c_id IS NOT NULL AND visibility = 1)*/
4312
                $teacherSelect =
4313
                "UNION ALL (
4314
                        $select
4315
                        FROM $tbl_user u
4316
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
4317
                        WHERE
4318
                            (
4319
                                sru.session_id IN (
4320
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
4321
                                    $tbl_session_rel_access_url session_rel_access_rel_user
4322
                                    ON session_rel_access_rel_user.session_id = s.id
4323
                                    INNER JOIN $tbl_session_rel_user sru ON s.id = sru.session_id
4324
                                    WHERE access_url_id = ".$urlId."
4325
                                        AND (sru.relation_type = ".SessionEntity::GENERAL_COACH."
4326
                                        AND sru.user_id = $userId)
4327
                                ) OR sru.session_id IN (
4328
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
4329
                                    INNER JOIN $tbl_session_rel_access_url url
4330
                                    ON (url.session_id = s.id)
4331
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
4332
                                    ON (scu.session_id = s.id)
4333
                                    WHERE access_url_id = ".$urlId."
4334
                                    $sessionConditionsTeacher
4335
                                    $dateCondition
4336
                                )
4337
                            )
4338
                            $userConditions
4339
                    )
4340
                    UNION ALL(
4341
                        $select
4342
                        FROM $tbl_user u
4343
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
4344
                        WHERE cu.c_id IN (
4345
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
4346
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
4347
                        )
4348
                        $userConditions
4349
                    )"
4350
                ;
4351
                break;
4352
            case STUDENT_BOSS:
4353
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".UserRelUser::USER_RELATION_TYPE_BOSS;
4354
                break;
4355
            case HRM_REQUEST:
4356
                $drhConditions .= " AND
4357
                    friend_user_id = '$userId' AND
4358
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_HRM_REQUEST."'
4359
                ";
4360
                break;
4361
        }
4362
4363
        $join = null;
4364
        $sql = " $masterSelect
4365
                (
4366
                    (
4367
                        $select
4368
                        FROM $tbl_user u
4369
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
4370
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
4371
                        $join
4372
                        WHERE
4373
                            access_url_id = ".$urlId."
4374
                            $drhConditions
4375
                            $userConditions
4376
                    )
4377
                    $teacherSelect
4378
4379
                ) as t1";
4380
4381
        if ($getSql) {
4382
            return $sql;
4383
        }
4384
        if ($getCount) {
4385
            $result = Database::query($sql);
4386
            $row = Database::fetch_array($result);
4387
4388
            return $row['count'];
4389
        }
4390
4391
        $orderBy = null;
4392
        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...
4393
            if (api_is_western_name_order()) {
4394
                $orderBy .= " ORDER BY firstname, lastname ";
4395
            } else {
4396
                $orderBy .= " ORDER BY lastname, firstname ";
4397
            }
4398
4399
            if (!empty($column) && !empty($direction)) {
4400
                // Fixing order due the UNIONs
4401
                $column = str_replace('u.', '', $column);
4402
                $orderBy = " ORDER BY `$column` $direction ";
4403
            }
4404
        }
4405
4406
        $sql .= $orderBy;
4407
        $sql .= $limitCondition;
4408
4409
        $result = Database::query($sql);
4410
        $users = [];
4411
        if (Database::num_rows($result) > 0) {
4412
            while ($row = Database::fetch_array($result)) {
4413
                $users[$row['user_id']] = $row;
4414
            }
4415
        }
4416
4417
        return $users;
4418
    }
4419
4420
    /**
4421
     * Subscribes users to human resource manager (Dashboard feature).
4422
     *
4423
     * @param int   $hr_dept_id
4424
     * @param array $users_id
4425
     * @param bool  $deleteOtherAssignedUsers
4426
     */
4427
    public static function subscribeUsersToHRManager(
4428
        $hr_dept_id,
4429
        $users_id,
4430
        $deleteOtherAssignedUsers = true
4431
    ): void {
4432
        self::subscribeUsersToUser(
4433
            $hr_dept_id,
4434
            $users_id,
4435
            UserRelUser::USER_RELATION_TYPE_RRHH,
4436
            false,
4437
            $deleteOtherAssignedUsers
4438
        );
4439
    }
4440
4441
    /**
4442
     * Register request to assign users to HRM.
4443
     *
4444
     * @param int   $hrmId   The HRM ID
4445
     * @param array $usersId The users IDs
4446
     */
4447
    public static function requestUsersToHRManager($hrmId, $usersId): void
4448
    {
4449
        self::subscribeUsersToUser(
4450
            $hrmId,
4451
            $usersId,
4452
            UserRelUser::USER_RELATION_TYPE_HRM_REQUEST,
4453
            false,
4454
            false
4455
        );
4456
    }
4457
4458
    /**
4459
     * Remove the requests for assign a user to a HRM.
4460
     *
4461
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
4462
     */
4463
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
4464
    {
4465
        $users = implode(', ', $usersId);
4466
        Database::getManager()
4467
            ->createQuery('
4468
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
4469
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
4470
            ')
4471
            ->execute(['hrm_id' => $hrmId, 'relation_type' => UserRelUser::USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
4472
    }
4473
4474
    /**
4475
     * Add subscribed users to a user by relation type.
4476
     *
4477
     * @param int   $userId                   The user id
4478
     * @param array $subscribedUsersId        The id of subscribed users
4479
     * @param int   $relationType             The relation type
4480
     * @param bool  $deleteUsersBeforeInsert
4481
     * @param bool  $deleteOtherAssignedUsers
4482
     */
4483
    public static function subscribeUsersToUser(
4484
        $userId,
4485
        $subscribedUsersId,
4486
        $relationType,
4487
        $deleteUsersBeforeInsert = false,
4488
        $deleteOtherAssignedUsers = true
4489
    ): void {
4490
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4491
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4492
4493
        $userId = (int) $userId;
4494
        $relationType = (int) $relationType;
4495
4496
        if ($deleteOtherAssignedUsers) {
4497
            if (api_get_multiple_access_url()) {
4498
                // Deleting assigned users to hrm_id
4499
                $sql = "SELECT s.user_id
4500
                        FROM $userRelUserTable s
4501
                        INNER JOIN $userRelAccessUrlTable a
4502
                        ON (a.user_id = s.user_id)
4503
                        WHERE
4504
                            friend_user_id = $userId AND
4505
                            relation_type = $relationType AND
4506
                            access_url_id = ".api_get_current_access_url_id();
4507
            } else {
4508
                $sql = "SELECT user_id
4509
                        FROM $userRelUserTable
4510
                        WHERE
4511
                            friend_user_id = $userId AND
4512
                            relation_type = $relationType";
4513
            }
4514
            $result = Database::query($sql);
4515
4516
            if (Database::num_rows($result) > 0) {
4517
                while ($row = Database::fetch_array($result)) {
4518
                    $sql = "DELETE FROM $userRelUserTable
4519
                            WHERE
4520
                                user_id = {$row['user_id']} AND
4521
                                friend_user_id = $userId AND
4522
                                relation_type = $relationType";
4523
                    Database::query($sql);
4524
                }
4525
            }
4526
        }
4527
4528
        if ($deleteUsersBeforeInsert) {
4529
            $sql = "DELETE FROM $userRelUserTable
4530
                    WHERE
4531
                        user_id = $userId AND
4532
                        relation_type = $relationType";
4533
            Database::query($sql);
4534
        }
4535
4536
        // Inserting new user list.
4537
        if (is_array($subscribedUsersId)) {
4538
            foreach ($subscribedUsersId as $subscribedUserId) {
4539
                $subscribedUserId = (int) $subscribedUserId;
4540
                $sql = "SELECT id
4541
                        FROM $userRelUserTable
4542
                        WHERE
4543
                            user_id = $subscribedUserId AND
4544
                            friend_user_id = $userId AND
4545
                            relation_type = $relationType";
4546
4547
                $result = Database::query($sql);
4548
                $num = Database::num_rows($result);
4549
                if (0 === $num) {
4550
                    $userRelUser = (new UserRelUser())
4551
                        ->setUser(api_get_user_entity($subscribedUserId))
4552
                        ->setFriend(api_get_user_entity($userId))
4553
                        ->setRelationType($relationType)
4554
                    ;
4555
                    $em = Database::getManager();
4556
                    $em->persist($userRelUser);
4557
                    $em->flush();
4558
                }
4559
            }
4560
        }
4561
    }
4562
4563
    /**
4564
     * This function checks if a user is followed by provided human resources managers.
4565
     *
4566
     * @param int $user_id
4567
     * @param int $hr_dept_id Human resources manager
4568
     *
4569
     * @return bool
4570
     * @throws Exception
4571
     * @throws \Doctrine\DBAL\Exception
4572
     */
4573
    public static function is_user_followed_by_drh(int $user_id, int $hr_dept_id): bool
4574
    {
4575
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4576
        $result = false;
4577
4578
        $sql = "SELECT user_id FROM $tbl_user_rel_user
4579
                WHERE
4580
                    user_id = $user_id AND
4581
                    friend_user_id = $hr_dept_id AND
4582
                    relation_type = ".UserRelUser::USER_RELATION_TYPE_RRHH;
4583
        $rs = Database::query($sql);
4584
        if (Database::num_rows($rs) > 0) {
4585
            $result = true;
4586
        }
4587
4588
        return $result;
4589
    }
4590
4591
    /**
4592
     * Return the user id of teacher or session administrator.
4593
     *
4594
     * @param array $courseInfo
4595
     *
4596
     * @return int The user id, or 0 if the session ID was negative
4597
     * @throws Exception
4598
     * @throws \Doctrine\DBAL\Exception
4599
     */
4600
    public static function get_user_id_of_course_admin_or_session_admin(array $courseInfo): int
4601
    {
4602
        $session = api_get_session_id();
4603
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4604
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4605
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4606
4607
        if (empty($courseInfo)) {
4608
            return 0;
4609
        }
4610
4611
        $courseId = $courseInfo['real_id'];
4612
4613
        if (0 == $session) {
4614
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
4615
                    INNER JOIN '.$table_course_user.' ru
4616
                    ON ru.user_id = u.id
4617
                    WHERE
4618
                        ru.status = 1 AND
4619
                        ru.c_id = "'.$courseId.'" ';
4620
            $rs = Database::query($sql);
4621
            $num_rows = Database::num_rows($rs);
4622
            if (1 == $num_rows) {
4623
                $row = Database::fetch_array($rs);
4624
4625
                return (int) $row['uid'];
4626
            } else {
4627
                $my_num_rows = $num_rows;
4628
4629
                return (int) Database::result($rs, $my_num_rows - 1, 'uid');
4630
            }
4631
        } elseif ($session > 0) {
4632
            $sql = 'SELECT u.id as uid FROM '.$table_user.' u
4633
                    INNER JOIN '.$table_session_course_user.' sru
4634
                    ON sru.user_id = u.id
4635
                    WHERE
4636
                        sru.c_id = '.$courseId.' AND
4637
                        sru.status = '.SessionEntity::COURSE_COACH;
4638
            $rs = Database::query($sql);
4639
            if (Database::num_rows($rs) > 0) {
4640
                $row = Database::fetch_assoc($rs);
4641
4642
                return (int) $row['uid'];
4643
            }
4644
        }
4645
4646
        return 0;
4647
    }
4648
4649
    /**
4650
     * Determines if a user is a gradebook certified.
4651
     *
4652
     * @param int $cat_id  The category id of gradebook
4653
     * @param int $user_id The user id
4654
     *
4655
     * @return bool
4656
     */
4657
    public static function is_user_certified($cat_id, $user_id)
4658
    {
4659
        $cat_id = (int) $cat_id;
4660
        $user_id = (int) $user_id;
4661
4662
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4663
        $sql = 'SELECT path_certificate
4664
                FROM '.$table.'
4665
                WHERE
4666
                    cat_id = "'.$cat_id.'" AND
4667
                    user_id = "'.$user_id.'"';
4668
        $rs = Database::query($sql);
4669
        $row = Database::fetch_array($rs);
4670
4671
        if (!isset($row['path_certificate']) || '' == $row['path_certificate'] || is_null($row['path_certificate'])) {
4672
            return false;
4673
        }
4674
4675
        return true;
4676
    }
4677
4678
    /**
4679
     * Gets the info about a gradebook certificate for a user by course.
4680
     *
4681
     * @param array $course_info The course code
4682
     * @param int   $session_id
4683
     * @param int   $user_id     The user id
4684
     *
4685
     * @return array if there is not information return false
4686
     */
4687
    public static function get_info_gradebook_certificate($course_info, $session_id, $user_id)
4688
    {
4689
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4690
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
4691
        $session_id = (int) $session_id;
4692
        $user_id = (int) $user_id;
4693
        $courseId = $course_info['real_id'];
4694
4695
        if (empty($session_id)) {
4696
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
4697
        } else {
4698
            $session_condition = " AND session_id = $session_id";
4699
        }
4700
4701
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.'
4702
                WHERE cat_id = (
4703
                    SELECT id FROM '.$tbl_grade_category.'
4704
                    WHERE
4705
                        c_id = "'.$courseId.'" '.$session_condition.'
4706
                    LIMIT 1
4707
                ) AND user_id='.$user_id;
4708
4709
        $rs = Database::query($sql);
4710
        if (Database::num_rows($rs) > 0) {
4711
            $row = Database::fetch_assoc($rs);
4712
            $score = $row['score_certificate'];
4713
            $category_id = $row['cat_id'];
4714
            $cat = Category::load($category_id);
4715
            $displayscore = ScoreDisplay::instance();
4716
            if (isset($cat) && $displayscore->is_custom()) {
4717
                $grade = $displayscore->display_score(
4718
                    [$score, $cat[0]->get_weight()],
4719
                    SCORE_DIV_PERCENT_WITH_CUSTOM
4720
                );
4721
            } else {
4722
                $grade = $displayscore->display_score(
4723
                    [$score, $cat[0]->get_weight()]
4724
                );
4725
            }
4726
            $row['grade'] = $grade;
4727
4728
            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...
4729
        }
4730
4731
        return false;
4732
    }
4733
4734
    /**
4735
     * This function check if the user is a coach inside session course.
4736
     *
4737
     * @param int $user_id    User id
4738
     * @param int $courseId
4739
     * @param int $session_id
4740
     *
4741
     * @return bool True if the user is a coach
4742
     */
4743
    public static function is_session_course_coach($user_id, $courseId, $session_id)
4744
    {
4745
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4746
        // Protect data
4747
        $user_id = intval($user_id);
4748
        $courseId = intval($courseId);
4749
        $session_id = intval($session_id);
4750
        $result = false;
4751
4752
        $sql = "SELECT session_id FROM $table
4753
                WHERE
4754
                  session_id = $session_id AND
4755
                  c_id = $courseId AND
4756
                  user_id = $user_id AND
4757
                  status = ".SessionEntity::COURSE_COACH;
4758
        $res = Database::query($sql);
4759
4760
        if (Database::num_rows($res) > 0) {
4761
            $result = true;
4762
        }
4763
4764
        return $result;
4765
    }
4766
4767
    /**
4768
     * This function returns an icon path that represents the favicon of the website of which the url given.
4769
     * Defaults to the current Chamilo favicon.
4770
     *
4771
     * @param string $url1 URL of website where to look for favicon.ico
4772
     * @param string $url2 Optional second URL of website where to look for favicon.ico
4773
     *
4774
     * @return string Path of icon to load
4775
     */
4776
    public static function get_favicon_from_url($url1, $url2 = null)
4777
    {
4778
        $icon_link = '';
4779
        $url = $url1;
4780
        if (empty($url1)) {
4781
            $url = $url2;
4782
            if (empty($url)) {
4783
                $url = api_get_access_url(api_get_current_access_url_id());
4784
                $url = $url[0];
4785
            }
4786
        }
4787
        if (!empty($url)) {
4788
            $pieces = parse_url($url);
4789
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
4790
        }
4791
4792
        return $icon_link;
4793
    }
4794
4795
    public static function addUserAsAdmin(User $user)
4796
    {
4797
        $userId = $user->getId();
4798
4799
        if (!self::is_admin($userId)) {
4800
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
4801
            $sql = "INSERT INTO $table SET user_id = $userId";
4802
            Database::query($sql);
4803
        }
4804
4805
        $user->addRole('ROLE_ADMIN');
4806
        self::getRepository()->updateUser($user, true);
4807
    }
4808
4809
    public static function removeUserAdmin(User $user)
4810
    {
4811
        $userId = (int) $user->getId();
4812
        if (self::is_admin($userId)) {
4813
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
4814
            $sql = "DELETE FROM $table WHERE user_id = $userId";
4815
            Database::query($sql);
4816
            $user->removeRole('ROLE_ADMIN');
4817
            self::getRepository()->updateUser($user, true);
4818
        }
4819
    }
4820
4821
    /**
4822
     * @param string $from
4823
     * @param string $to
4824
     */
4825
    public static function update_all_user_languages($from, $to)
4826
    {
4827
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4828
        $from = Database::escape_string($from);
4829
        $to = Database::escape_string($to);
4830
4831
        if (!empty($to) && !empty($from)) {
4832
            $sql = "UPDATE $table_user SET language = '$to'
4833
                    WHERE language = '$from'";
4834
            Database::query($sql);
4835
        }
4836
    }
4837
4838
    /**
4839
     * Subscribe boss to students.
4840
     *
4841
     * @param int   $bossId                   The boss id
4842
     * @param array $usersId                  The users array
4843
     * @param bool  $deleteOtherAssignedUsers
4844
     */
4845
    public static function subscribeBossToUsers($bossId, $usersId, $deleteOtherAssignedUsers = true): void
4846
    {
4847
        self::subscribeUsersToUser(
4848
            $bossId,
4849
            $usersId,
4850
            UserRelUser::USER_RELATION_TYPE_BOSS,
4851
            false,
4852
            $deleteOtherAssignedUsers
4853
        );
4854
    }
4855
4856
    /**
4857
     * @param int $userId
4858
     *
4859
     * @return bool
4860
     */
4861
    public static function removeAllBossFromStudent($userId)
4862
    {
4863
        $userId = (int) $userId;
4864
4865
        if (empty($userId)) {
4866
            return false;
4867
        }
4868
4869
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4870
        $sql = "DELETE FROM $userRelUserTable
4871
                WHERE user_id = $userId AND relation_type = ".UserRelUser::USER_RELATION_TYPE_BOSS;
4872
        Database::query($sql);
4873
4874
        return true;
4875
    }
4876
4877
    /**
4878
     * Subscribe boss to students, if $bossList is empty then the boss list will be empty too.
4879
     *
4880
     * @param int   $studentId
4881
     * @param array $bossList
4882
     * @param bool  $sendNotification
4883
     *
4884
     * @return mixed Affected rows or false on failure
4885
     */
4886
    public static function subscribeUserToBossList(
4887
        $studentId,
4888
        $bossList,
4889
        $sendNotification = false
4890
    ) {
4891
        $inserted = 0;
4892
        if (!empty($bossList)) {
4893
            sort($bossList);
4894
            $studentId = (int) $studentId;
4895
            $studentInfo = api_get_user_info($studentId);
4896
4897
            if (empty($studentInfo)) {
4898
                return false;
4899
            }
4900
4901
            $previousBossList = self::getStudentBossList($studentId);
4902
            $previousBossList = !empty($previousBossList) ? array_column($previousBossList, 'boss_id') : [];
4903
            sort($previousBossList);
4904
4905
            // Boss list is the same, nothing changed.
4906
            if ($bossList == $previousBossList) {
4907
                return false;
4908
            }
4909
4910
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4911
            self::removeAllBossFromStudent($studentId);
4912
4913
            foreach ($bossList as $bossId) {
4914
                $bossId = (int) $bossId;
4915
                $bossInfo = api_get_user_info($bossId);
4916
4917
                if (empty($bossInfo)) {
4918
                    continue;
4919
                }
4920
4921
                $bossLanguage = $bossInfo['locale'];
4922
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
4923
                        VALUES ($studentId, $bossId, ".UserRelUser::USER_RELATION_TYPE_BOSS.")";
4924
                $insertId = Database::query($sql);
4925
4926
                if ($insertId) {
4927
                    if ($sendNotification) {
4928
                        $name = $studentInfo['complete_name'];
4929
                        $url = api_get_path(WEB_CODE_PATH).'my_space/myStudents.php?student='.$studentId;
4930
                        $url = Display::url($url, $url);
4931
                        $subject = sprintf(get_lang('You have been assigned the learner %s', $bossLanguage), $name);
4932
                        $message = sprintf(get_lang('You have been assigned the learner %s with url %s', $bossLanguage), $name, $url);
4933
                        MessageManager::send_message_simple(
4934
                            $bossId,
4935
                            $subject,
4936
                            $message
4937
                        );
4938
                    }
4939
                    $inserted++;
4940
                }
4941
            }
4942
        } else {
4943
            self::removeAllBossFromStudent($studentId);
4944
        }
4945
4946
        return $inserted;
4947
    }
4948
4949
    /**
4950
     * Get users followed by student boss.
4951
     *
4952
     * @param int    $userId
4953
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
4954
     * @param bool   $getOnlyUserId
4955
     * @param bool   $getSql
4956
     * @param bool   $getCount
4957
     * @param int    $from
4958
     * @param int    $numberItems
4959
     * @param int    $column
4960
     * @param string $direction
4961
     * @param int    $active
4962
     * @param string $lastConnectionDate
4963
     *
4964
     * @return array users
4965
     */
4966
    public static function getUsersFollowedByStudentBoss(
4967
        $userId,
4968
        $userStatus = 0,
4969
        $getOnlyUserId = false,
4970
        $getSql = false,
4971
        $getCount = false,
4972
        $from = null,
4973
        $numberItems = null,
4974
        $column = null,
4975
        $direction = null,
4976
        $active = null,
4977
        $lastConnectionDate = null
4978
    ) {
4979
        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...
4980
            $userId,
4981
            $userStatus,
4982
            $getOnlyUserId,
4983
            $getSql,
4984
            $getCount,
4985
            $from,
4986
            $numberItems,
4987
            $column,
4988
            $direction,
4989
            $active,
4990
            $lastConnectionDate,
4991
            STUDENT_BOSS
4992
        );
4993
    }
4994
4995
    /**
4996
     * @return array
4997
     */
4998
    public static function getOfficialCodeGrouped()
4999
    {
5000
        $user = Database::get_main_table(TABLE_MAIN_USER);
5001
        $sql = "SELECT DISTINCT official_code
5002
                FROM $user
5003
                GROUP BY official_code";
5004
        $result = Database::query($sql);
5005
        $values = Database::store_result($result, 'ASSOC');
5006
        $result = [];
5007
        foreach ($values as $value) {
5008
            $result[$value['official_code']] = $value['official_code'];
5009
        }
5010
5011
        return $result;
5012
    }
5013
5014
    /**
5015
     * @param string $officialCode
5016
     *
5017
     * @return array
5018
     */
5019
    public static function getUsersByOfficialCode($officialCode)
5020
    {
5021
        $user = Database::get_main_table(TABLE_MAIN_USER);
5022
        $officialCode = Database::escape_string($officialCode);
5023
5024
        $sql = "SELECT DISTINCT id
5025
                FROM $user
5026
                WHERE official_code = '$officialCode'
5027
                ";
5028
        $result = Database::query($sql);
5029
5030
        $users = [];
5031
        while ($row = Database::fetch_array($result)) {
5032
            $users[] = $row['id'];
5033
        }
5034
5035
        return $users;
5036
    }
5037
5038
    /**
5039
     * Calc the expended time (in seconds) by a user in a course.
5040
     *
5041
     * @param int    $userId    The user id
5042
     * @param int    $courseId  The course id
5043
     * @param int    $sessionId Optional. The session id
5044
     * @param string $from      Optional. From date
5045
     * @param string $until     Optional. Until date
5046
     *
5047
     * @return int The time
5048
     */
5049
    public static function getTimeSpentInCourses(
5050
        $userId,
5051
        $courseId,
5052
        $sessionId = 0,
5053
        $from = '',
5054
        $until = ''
5055
    ) {
5056
        $userId = (int) $userId;
5057
        $sessionId = (int) $sessionId;
5058
5059
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5060
        $whereConditions = [
5061
            'user_id = ? ' => $userId,
5062
            'AND c_id = ? ' => $courseId,
5063
            'AND session_id = ? ' => $sessionId,
5064
        ];
5065
5066
        if (!empty($from) && !empty($until)) {
5067
            $whereConditions["AND (login_course_date >= '?' "] = $from;
5068
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
5069
        }
5070
5071
        $trackResult = Database::select(
5072
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
5073
            $trackCourseAccessTable,
5074
            [
5075
                'where' => $whereConditions,
5076
            ],
5077
            'first'
5078
        );
5079
5080
        if (false != $trackResult) {
5081
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
5082
        }
5083
5084
        return 0;
5085
    }
5086
5087
    /**
5088
     * Get the boss user ID from a followed user id.
5089
     *
5090
     * @param $userId
5091
     *
5092
     * @return bool
5093
     */
5094
    public static function getFirstStudentBoss($userId)
5095
    {
5096
        $userId = (int) $userId;
5097
        if ($userId > 0) {
5098
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5099
            $row = Database::select(
5100
                'DISTINCT friend_user_id AS boss_id',
5101
                $userRelTable,
5102
                [
5103
                    'where' => [
5104
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
5105
                            $userId,
5106
                            UserRelUser::USER_RELATION_TYPE_BOSS,
5107
                        ],
5108
                    ],
5109
                ]
5110
            );
5111
            if (!empty($row)) {
5112
                return $row[0]['boss_id'];
5113
            }
5114
        }
5115
5116
        return false;
5117
    }
5118
5119
    /**
5120
     * Get the boss user ID from a followed user id.
5121
     *
5122
     * @param int $userId student id
5123
     *
5124
     * @return array
5125
     */
5126
    public static function getStudentBossList($userId)
5127
    {
5128
        $userId = (int) $userId;
5129
5130
        if ($userId > 0) {
5131
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5132
5133
            return Database::select(
5134
                'DISTINCT friend_user_id AS boss_id',
5135
                $userRelTable,
5136
                [
5137
                    'where' => [
5138
                        'user_id = ? AND relation_type = ? ' => [
5139
                            $userId,
5140
                            UserRelUser::USER_RELATION_TYPE_BOSS,
5141
                        ],
5142
                    ],
5143
                ]
5144
            );
5145
        }
5146
5147
        return [];
5148
    }
5149
5150
    /**
5151
     * @param int $bossId
5152
     * @param int $studentId
5153
     *
5154
     * @return bool
5155
     */
5156
    public static function userIsBossOfStudent($bossId, $studentId)
5157
    {
5158
        $result = false;
5159
        $bossList = self::getStudentBossList($studentId);
5160
        if (!empty($bossList)) {
5161
            $bossList = array_column($bossList, 'boss_id');
5162
            if (in_array($bossId, $bossList)) {
5163
                $result = true;
5164
            }
5165
        }
5166
5167
        return $result;
5168
    }
5169
5170
    /**
5171
     * Displays the name of the user and makes the link to the user profile.
5172
     *
5173
     * @param array $userInfo
5174
     *
5175
     * @return string
5176
     */
5177
    public static function getUserProfileLink($userInfo)
5178
    {
5179
        if (isset($userInfo) && isset($userInfo['user_id'])) {
5180
            return Display::url(
5181
                $userInfo['complete_name_with_username'],
5182
                $userInfo['profile_url']
5183
            );
5184
        }
5185
5186
        return get_lang('Anonymous');
5187
    }
5188
5189
    /**
5190
     * Get users whose name matches $firstname and $lastname.
5191
     *
5192
     * @param string $firstname Firstname to search
5193
     * @param string $lastname  Lastname to search
5194
     *
5195
     * @return array The user list
5196
     */
5197
    public static function getUsersByName($firstname, $lastname)
5198
    {
5199
        $firstname = Database::escape_string($firstname);
5200
        $lastname = Database::escape_string($lastname);
5201
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
5202
5203
        $sql = <<<SQL
5204
            SELECT id, username, lastname, firstname
5205
            FROM $userTable
5206
            WHERE
5207
                firstname LIKE '$firstname%' AND
5208
                lastname LIKE '$lastname%'
5209
SQL;
5210
        $result = Database::query($sql);
5211
        $users = [];
5212
        while ($resultData = Database::fetch_object($result)) {
5213
            $users[] = $resultData;
5214
        }
5215
5216
        return $users;
5217
    }
5218
5219
    /**
5220
     * @param int $optionSelected
5221
     *
5222
     * @return string
5223
     */
5224
    public static function getUserSubscriptionTab($optionSelected = 1)
5225
    {
5226
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
5227
        if (('true' === $allowAdmin && api_is_allowed_to_edit()) ||
5228
            api_is_platform_admin()
5229
        ) {
5230
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
5231
5232
            $headers = [
5233
                [
5234
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
5235
                    'content' => get_lang('Learners'),
5236
                ],
5237
                [
5238
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
5239
                    'content' => get_lang('Trainers'),
5240
                ],
5241
                /*[
5242
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
5243
                    'content' => get_lang('Learners'),
5244
                ],
5245
                [
5246
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
5247
                    'content' => get_lang('Trainers'),
5248
                ],*/
5249
                [
5250
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
5251
                    'content' => get_lang('Groups'),
5252
                ],
5253
                [
5254
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
5255
                    'content' => get_lang('Classes'),
5256
                ],
5257
            ];
5258
5259
            return Display::tabsOnlyLink($headers, $optionSelected);
5260
        }
5261
5262
        return '';
5263
    }
5264
5265
    /**
5266
     * Make sure this function is protected because it does NOT check password!
5267
     *
5268
     * This function defines globals.
5269
     *
5270
     * @param int  $userId
5271
     * @param bool $checkIfUserCanLoginAs
5272
     *
5273
     * @return bool
5274
     *
5275
     * @author Evie Embrechts
5276
     * @author Yannick Warnier <[email protected]>
5277
     */
5278
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
5279
    {
5280
        $userId = (int) $userId;
5281
        $userInfo = api_get_user_info($userId);
5282
5283
        // Check if the user is allowed to 'login_as'
5284
        $canLoginAs = true;
5285
        if ($checkIfUserCanLoginAs) {
5286
            $canLoginAs = api_can_login_as($userId);
5287
        }
5288
5289
        if (!$canLoginAs || empty($userInfo)) {
5290
            return false;
5291
        }
5292
5293
        if ($userId) {
5294
            $logInfo = [
5295
                'tool' => 'logout',
5296
                'tool_id' => 0,
5297
                'tool_id_detail' => 0,
5298
                'action' => '',
5299
                'info' => 'Change user (login as)',
5300
            ];
5301
            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

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

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

6163
                    /** @scrutinizer ignore-call */ 
6164
                    $gb = Category::createCategoryObjectFromEntity($gradebook);

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...
6164
                    $finished = $gb->is_certificate_available($row['user_id']);
6165
                    if (!empty($finished)) {
6166
                        $courses[$row['code']]['finished']++;
6167
                    }
6168
                }
6169
            }
6170
        }
6171
        return $courses;
6172
    }
6173
6174
    /**
6175
     * Count users in sessions and if they have certificate.
6176
     * This function is resource intensive.
6177
     *
6178
     * @return array
6179
     * @throws Exception
6180
     * @throws \Doctrine\DBAL\Exception
6181
     */
6182
    public static function countUsersWhoFinishedCoursesInSessions()
6183
    {
6184
        $coursesInSessions = [];
6185
        $currentAccessUrlId = api_get_current_access_url_id();
6186
        $sql = "SELECT course.code, srcru.session_id, srcru.user_id, session.title
6187
                FROM session_rel_course_rel_user srcru
6188
                    JOIN course ON srcru.c_id = course.id
6189
                    JOIN access_url_rel_session aurs on srcru.session_id = aurs.session_id
6190
                    JOIN session ON srcru.session_id = session.id
6191
                WHERE aurs.access_url_id = $currentAccessUrlId
6192
                ORDER BY course.code, session.title
6193
        ";
6194
        $res = Database::query($sql);
6195
        if (Database::num_rows($res) > 0) {
6196
            while ($row = Database::fetch_array($res)) {
6197
                $index = $row['code'].' ('.$row['title'].')';
6198
                if (!isset($coursesInSessions[$index])) {
6199
                    $coursesInSessions[$index] = [
6200
                        'subscribed' => 0,
6201
                        'finished' => 0,
6202
                    ];
6203
                }
6204
                $coursesInSessions[$index]['subscribed']++;
6205
                $entityManager = Database::getManager();
6206
                $repository = $entityManager->getRepository('ChamiloCoreBundle:GradebookCategory');
6207
                /** @var \Chamilo\CoreBundle\Entity\GradebookCategory $gradebook */
6208
                $gradebook = $repository->findOneBy(
6209
                    [
6210
                        'courseCode' => $row['code'],
6211
                        'sessionId' => $row['session_id'],
6212
                    ]
6213
                );
6214
                if (!empty($gradebook)) {
6215
                    $finished = 0;
6216
                    $gb = Category::createCategoryObjectFromEntity($gradebook);
6217
                    $finished = $gb->is_certificate_available($row['user_id']);
6218
                    if (!empty($finished)) {
6219
                        $coursesInSessions[$index]['finished']++;
6220
                    }
6221
                }
6222
            }
6223
        }
6224
        return $coursesInSessions;
6225
    }
6226
6227
}
6228