UserManager::is_session_course_coach()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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

70
        /** @scrutinizer ignore-call */ 
71
        $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...
71
72
        return $hasher->isPasswordValid($user, $plainPassword);
73
    }
74
75
    /**
76
     * @param int    $userId
77
     * @param string $password
78
     */
79
    public static function updatePassword($userId, $password)
80
    {
81
        $user = api_get_user_entity($userId);
82
        $userManager = self::getRepository();
83
        $user->setPlainPassword($password);
84
        $userManager->updateUser($user, true);
85
    }
86
87
    /**
88
     * Creates a new user for the platform.
89
     *
90
     * @param string        $firstName
91
     * @param string        $lastName
92
     * @param int           $status                  (1 for course tutor, 5 for student, 6 for anonymous)
93
     * @param string        $email
94
     * @param string        $loginName
95
     * @param string        $password
96
     * @param string        $officialCode           Any official code (optional)
97
     * @param string        $language                User language    (optional)
98
     * @param string        $phone                   Phone number    (optional)
99
     * @param string        $pictureUri             Picture URI        (optional)
100
     * @param string        $authSources              Authentication source (defaults to 'platform', dependind on constant)
101
     * @param string $expirationDate          Account expiration date (optional, defaults to null)
102
     * @param int           $active                  Whether the account is enabled or disabled by default
103
     * @param int           $hrDeptId              The department of HR in which the user is registered (defaults to 0)
104
     * @param array         $extra                   Extra fields (labels must be prefixed by "extra_")
105
     * @param string        $encryptMethod          Used if password is given encrypted. Set to an empty string by default
106
     * @param bool          $sendMail
107
     * @param bool          $isAdmin
108
     * @param string        $address
109
     * @param bool          $sendEmailToAllAdmins
110
     * @param FormValidator $form
111
     * @param int           $creatorId
112
     * @param array         $emailTemplate
113
     * @param string        $redirectToURLAfterLogin
114
     *
115
     * @return mixed new user id - if the new user creation succeeds, false otherwise
116
     * @desc The function tries to retrieve user id from the session.
117
     * If it exists, the current user id is the creator id. If a problem arises,
118
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
119
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
120
     *@author Hugues Peeters <[email protected]>,
121
     * @author Roan Embrechts <[email protected]>
122
     *
123
     */
124
    public static function create_user(
125
        $firstName,
126
        $lastName,
127
        $status,
128
        $email,
129
        $loginName,
130
        $password,
131
        $officialCode = '',
132
        $language = '',
133
        $phone = '',
134
        $pictureUri = '',
135
        ?array $authSources = [],
136
        $expirationDate = null,
137
        $active = 1,
138
        $hrDeptId = 0,
139
        $extra = [],
140
        $encryptMethod = '',
141
        $sendMail = false,
142
        $isAdmin = false,
143
        $address = '',
144
        $sendEmailToAllAdmins = false,
145
        $form = null,
146
        $creatorId = 0,
147
        $emailTemplate = [],
148
        $redirectToURLAfterLogin = ''
149
    ) {
150
        $authSources = !empty($authSources) ? $authSources : [UserAuthSource::PLATFORM];
151
        $creatorId = empty($creatorId) ? api_get_user_id() : $creatorId;
152
153
        if (0 === $creatorId) {
154
            Display::addFlash(
155
                Display::return_message(get_lang('A user creator is required'))
156
            );
157
158
            return false;
159
        }
160
161
        $creatorInfo = api_get_user_info($creatorId);
162
        $creatorEmail = $creatorInfo['email'] ?? '';
163
164
        // First check if the login exists.
165
        if (!Container::getUserRepository()->isUsernameAvailable($loginName)) {
166
            Display::addFlash(
167
                Display::return_message(get_lang('This login is already taken !'))
168
            );
169
170
            return false;
171
        }
172
173
        Container::getEventDispatcher()
174
            ->dispatch(
175
                new UserCreatedEvent([], AbstractEvent::TYPE_PRE),
176
                Events::USER_CREATED
177
            )
178
        ;
179
180
        $original_password = $password;
181
182
        $accessUrl = Container::getAccessUrlUtil()->getCurrent();
183
        $access_url_id = $accessUrl->getId();
184
185
        $hostingLimitUsers = get_hosting_limit($access_url_id, 'users');
186
187
        if ($hostingLimitUsers !== null && $hostingLimitUsers > 0) {
188
            $num = self::get_number_of_users();
189
            if ($num >= $hostingLimitUsers) {
190
                api_warn_hosting_contact('users');
191
                Display::addFlash(
192
                    Display::return_message(
193
                        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.'),
194
                        'warning'
195
                    )
196
                );
197
198
                return false;
199
            }
200
        }
201
202
        if (1 === $status) {
203
            $hostingLimitTeachers = get_hosting_limit($access_url_id, 'teachers');
204
205
            if ($hostingLimitTeachers !== null && $hostingLimitTeachers > 0) {
206
                $num = self::get_number_of_users(1);
207
                if ($num >= $hostingLimitTeachers) {
208
                    Display::addFlash(
209
                        Display::return_message(
210
                            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.'),
211
                            'warning'
212
                        )
213
                    );
214
                    api_warn_hosting_contact('hosting_limit_teachers');
215
216
                    return false;
217
                }
218
            }
219
        }
220
221
        if (empty($password)) {
222
            if (in_array(UserAuthSource::PLATFORM, $authSources)) {
223
                Display::addFlash(
224
                    Display::return_message(
225
                        get_lang('Required field').': '.get_lang(
226
                            'Password'
227
                        ),
228
                        'warning'
229
                    )
230
                );
231
232
                return false;
233
            }
234
            // Use authSource as pseudo-password (validated by external auth).
235
            $password = $authSources;
236
        }
237
238
        // Checking the user language
239
        $languages = api_get_languages();
240
        if (!in_array($language, array_keys($languages), true)) {
241
            $language = 'en_US'; // default
242
        }
243
244
        $now = new DateTime();
245
        if (empty($expirationDate) || '0000-00-00 00:00:00' === $expirationDate) {
246
            $expirationDate = null;
247
        } else {
248
            $expirationDate = api_get_utc_datetime($expirationDate, true, true);
249
        }
250
251
        $repo = Container::getUserRepository();
252
        $user = $repo->createUser()
253
            ->setLastname($lastName)
254
            ->setFirstname($firstName)
255
            ->setUsername($loginName)
256
            ->setStatus($status)
257
            ->setPlainPassword($password)
258
            ->setEmail($email)
259
            ->setOfficialCode($officialCode)
260
            ->setCreatorId($creatorId)
261
            ->setPhone($phone)
262
            ->setAddress($address)
263
            ->setLocale($language)
264
            ->setHrDeptId($hrDeptId)
265
            ->setActive($active)
266
            ->setTimezone(api_get_timezone())
267
        ;
268
269
        foreach ($authSources as $authSource) {
270
            $user->addAuthSourceByAuthentication($authSource, $accessUrl);
271
        }
272
273
        if (null !== $expirationDate) {
274
            $user->setExpirationDate($expirationDate);
275
        }
276
277
        $user->setRoleFromStatus($status);
278
        $dobStr = $_POST['date_of_birth'] ?? null;
279
        if ($dobStr) {
280
            $dob = \DateTime::createFromFormat('Y-m-d', $dobStr)
281
                ?: \DateTime::createFromFormat('d/m/Y', $dobStr)
282
                    ?: \DateTime::createFromFormat('d-m-Y', $dobStr);
283
284
            if ($dob instanceof \DateTime) {
0 ignored issues
show
introduced by
$dob is always a sub-type of DateTime.
Loading history...
285
                $user->setDateOfBirth($dob);
286
            }
287
        }
288
289
        $repo->updateUser($user, true);
290
        $userId = $user->getId();
291
292
        if (empty($userId)) {
293
            Display::addFlash(
294
                Display::return_message(get_lang('There happened an unknown error. Please contact the platform administrator.'))
295
            );
296
            return false;
297
        }
298
299
        $userLocale = $user->getLocale();
300
        if ($isAdmin) {
301
            self::addUserAsAdmin($user);
302
        }
303
304
        if (api_get_multiple_access_url()) {
305
            UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
306
        } else {
307
            UrlManager::add_user_to_url($userId, 1);
308
        }
309
310
        if (is_array($extra) && count($extra) > 0) {
311
            $extra['item_id'] = $userId;
312
            $userFieldValue = new ExtraFieldValue('user');
313
            /* Force saving of extra fields (otherwise, if the current
314
                user is not admin, fields not visible to the user - most
315
                of them - are just ignored) */
316
            $userFieldValue->saveFieldValues(
317
                $extra,
318
                true,
319
                false,
320
                [],
321
                [],
322
                true
323
            );
324
        } else {
325
            // Create notify settings by default
326
            self::update_extra_field_value(
327
                $userId,
328
                'mail_notify_invitation',
329
                '1'
330
            );
331
            self::update_extra_field_value(
332
                $userId,
333
                'mail_notify_message',
334
                '1'
335
            );
336
            self::update_extra_field_value(
337
                $userId,
338
                'mail_notify_group_message',
339
                '1'
340
            );
341
        }
342
343
        self::update_extra_field_value(
344
            $userId,
345
            'already_logged_in',
346
            'false'
347
        );
348
349
        if (!empty($redirectToURLAfterLogin) && ('true' === api_get_setting('workflows.plugin_redirection_enabled'))) {
350
            RedirectionPlugin::insert($userId, $redirectToURLAfterLogin);
351
        }
352
353
        if (!empty($email) && $sendMail) {
354
            $recipient_name = api_get_person_name(
355
                $firstName,
356
                $lastName,
357
                null,
358
                PERSON_NAME_EMAIL_ADDRESS
359
            );
360
            $tpl = Container::getTwig();
361
            $emailSubject = $tpl->render('@ChamiloCore/Mailer/Legacy/subject_registration_platform.html.twig', ['locale' => $userLocale]);
362
            $sender_name = api_get_person_name(
363
                api_get_setting('administratorName'),
364
                api_get_setting('administratorSurname'),
365
                null,
366
                PERSON_NAME_EMAIL_ADDRESS
367
            );
368
            $email_admin = api_get_setting('emailAdministrator');
369
370
            $url = api_get_path(WEB_PATH);
371
            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 AccessUrlUtil::isMultiple ( Ignorable by Annotation )

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

371
            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...
372
                $access_url_id = api_get_current_access_url_id();
373
                if (-1 != $access_url_id) {
374
                    $urlInfo = api_get_access_url($access_url_id);
375
                    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...
376
                        $url = $urlInfo['url'];
377
                    }
378
                }
379
            }
380
381
            // variables for the default template
382
            $params = [
383
                'complete_name' => stripslashes(api_get_person_name($firstName, $lastName)),
384
                'login_name' => $loginName,
385
                'original_password' => stripslashes($original_password),
386
                'mailWebPath' => $url,
387
                'new_user' => $user,
388
                'search_link' => $url,
389
                'locale' => $userLocale,
390
            ];
391
392
            // ofaj
393
            if ('true' === api_get_setting('session.allow_search_diagnostic')) {
394
                $urlSearch = api_get_path(WEB_CODE_PATH).'search/search.php';
395
                $linkSearch = Display::url($urlSearch, $urlSearch);
396
                $params['search_link'] = $linkSearch;
397
            }
398
399
            $emailBody = $tpl->render(
400
                '@ChamiloCore/Mailer/Legacy/content_registration_platform.html.twig',
401
                $params
402
            );
403
404
            $userInfo = api_get_user_info($userId);
405
            $mailTemplateManager = new MailTemplateManager();
406
            $phoneNumber = $extra['mobile_phone_number'] ?? null;
407
408
            $emailBodyTemplate = '';
409
            if (!empty($emailTemplate)) {
410
                if (isset($emailTemplate['content_registration_platform.tpl']) &&
411
                    !empty($emailTemplate['content_registration_platform.tpl'])
412
                ) {
413
                    $emailBodyTemplate = $mailTemplateManager->parseTemplate(
414
                        $emailTemplate['content_registration_platform.tpl'],
415
                        $userInfo
416
                    );
417
                }
418
            }
419
420
            $twoEmail = ('true' === api_get_setting('mail.send_two_inscription_confirmation_mail'));
421
            if (true === $twoEmail) {
422
                $emailBody = $tpl->render('@ChamiloCore/Mailer/Legacy/new_user_first_email_confirmation.html.twig');
423
                if (!empty($emailBodyTemplate) &&
424
                    isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
425
                    !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
426
                ) {
427
                    $emailBody = $mailTemplateManager->parseTemplate(
428
                        $emailTemplate['new_user_first_email_confirmation.tpl'],
429
                        $userInfo
430
                    );
431
                }
432
433
                api_mail_html(
434
                    $recipient_name,
435
                    $email,
436
                    $emailSubject,
437
                    $emailBody,
438
                    $sender_name,
439
                    $email_admin,
440
                    [],
441
                    [],
442
                    false,
443
                    [],
444
                    $creatorEmail
445
                );
446
447
                $emailBody = $tpl->render('@ChamiloCore/Mailer/Legacy/new_user_second_email_confirmation.html.twig');
448
                if (!empty($emailBodyTemplate) &&
449
                    isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
450
                    !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
451
                ) {
452
                    $emailBody = $mailTemplateManager->parseTemplate(
453
                        $emailTemplate['new_user_second_email_confirmation.tpl'],
454
                        $userInfo
455
                    );
456
                }
457
458
                api_mail_html(
459
                    $recipient_name,
460
                    $email,
461
                    $emailSubject,
462
                    $emailBody,
463
                    $sender_name,
464
                    $email_admin,
465
                    null,
466
                    null,
467
                    null,
468
                    [],
469
                    $creatorEmail
470
                );
471
            } else {
472
                if (!empty($emailBodyTemplate)) {
473
                    $emailBody = $emailBodyTemplate;
474
                }
475
                $sendToInbox = ('true' === api_get_setting('registration.send_inscription_msg_to_inbox'));
476
                if ($sendToInbox) {
477
                    $adminList = self::get_all_administrators();
478
                    $senderId = 1;
479
                    if (!empty($adminList)) {
480
                        $adminInfo = current($adminList);
481
                        $senderId = $adminInfo['user_id'];
482
                    }
483
484
                    MessageManager::send_message_simple(
485
                        $userId,
486
                        $emailSubject,
487
                        $emailBody,
488
                        $senderId
489
                    );
490
                } else {
491
                    api_mail_html(
492
                        $recipient_name,
493
                        $email,
494
                        $emailSubject,
495
                        $emailBody,
496
                        $sender_name,
497
                        $email_admin,
498
                        [],
499
                        [],
500
                        false,
501
                        [],
502
                        $creatorEmail
503
                    );
504
                }
505
            }
506
507
            $notification = api_get_setting('profile.send_notification_when_user_added', true);
508
            if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
509
                foreach ($notification['admins'] as $adminId) {
510
                    $emailSubjectToAdmin = get_lang('The user has been added').': '.
511
                        api_get_person_name($firstName, $lastName);
512
                    MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
513
                }
514
            }
515
516
            /** @var TranslatorInterface $translator */
517
            $translator   = Container::$container->get('translator');
518
            $currentLocale = $translator->getLocale();
519
520
            $visibleCoreFields = [];
521
            $visibleExtraVars  = [];
522
523
            if ($form) {
524
                $formNames = [];
525
                if (property_exists($form, '_elements') && is_array($form->_elements)) {
526
                    foreach ($form->_elements as $el) {
527
                        if (is_object($el) && method_exists($el, 'getName')) {
528
                            $formNames[] = (string) $el->getName();
529
                        }
530
                    }
531
                }
532
533
                $cfg = api_get_setting('registration.allow_fields_inscription', true);
534
                $cfgCore = (is_array($cfg) && isset($cfg['fields']) && is_array($cfg['fields']))
535
                    ? $cfg['fields']
536
                    : [];
537
538
                $visibleCoreFields = array_values(array_intersect($cfgCore, $formNames));
539
540
                foreach ($formNames as $n) {
541
                    if (strpos($n, 'extra_') === 0) {
542
                        $visibleExtraVars[] = substr($n, 6);
543
                    }
544
                }
545
            }
546
547
            if ($sendEmailToAllAdmins) {
548
                $adminList = self::get_all_administrators();
549
                $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
550
551
                foreach ($adminList as $adminId => $adminData) {
552
                    $adminLocale = $adminData['locale'] ?? 'en_US';
553
                    $translator->setLocale($adminLocale);
554
555
                    $profileHtml      = self::renderRegistrationProfileHtml(
556
                        $user,
557
                        $extra ?? [],
558
                        $adminLocale,
559
                        $visibleCoreFields,
560
                        $visibleExtraVars
561
                    );
562
                    $userLanguageName = self::resolveLanguageName($user->getLocale());
563
564
                    $paramsAdmin = [
565
                        'complete_name' => stripslashes(api_get_person_name($firstName, $lastName)),
566
                        'user_added'    => $user,
567
                        'link'          => Display::url($url, $url),
568
                        'form'          => $profileHtml,
569
                        'user_language' => $userLanguageName,
570
                    ];
571
572
                    $emailBodyAdmin = $tpl->render(
573
                        '@ChamiloCore/Mailer/Legacy/content_registration_platform_to_admin.html.twig',
574
                        $paramsAdmin
575
                    );
576
577
                    $subject = get_lang('The user has been added', $adminLocale);
578
                    MessageManager::send_message_simple(
579
                        $adminId,
580
                        $subject,
581
                        $emailBodyAdmin,
582
                        $userId
583
                    );
584
585
                    $translator->setLocale($currentLocale);
586
                }
587
            }
588
        }
589
590
        Container::getEventDispatcher()
591
            ->dispatch(
592
                new UserCreatedEvent(
593
                    ['return' => $user, 'originalPassword' => $original_password],
594
                    AbstractEvent::TYPE_POST
595
                ),
596
                Events::USER_CREATED
597
            )
598
        ;
599
600
        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

600
        Event::/** @scrutinizer ignore-call */ 
601
               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...
601
602
        return $userId;
603
    }
604
605
    /**
606
     * Returns the human-readable language name for a given ISO code.
607
     *
608
     * Accepts ISO 639-1/2 codes (e.g. "en", "es", "eng"). If $iso is null or the
609
     * code is unknown, an empty string is returned.
610
     *
611
     * @param string|null $iso Two- or three-letter ISO 639 language code.
612
     * @return string          Language name in English (e.g. "English", "Spanish").
613
     */
614
    private static function resolveLanguageName(?string $iso): string
615
    {
616
        if (empty($iso)) {
617
            return '';
618
        }
619
620
        /** @var LanguageRepository $langRepo */
621
        $langRepo = Container::$container->get(LanguageRepository::class);
622
        $entity   = $langRepo->findOneBy(['isocode' => $iso]);
623
624
        return $entity ? $entity->getOriginalName() : $iso;
625
    }
626
627
    /**
628
     * Build the “profile” HTML (core + dynamic extra fields) for the admin email, located in $adminLocale.
629
     *
630
     * @param User $user
631
     * @param array $extraParams Raw POST values from registration (keys: "extra_*").
632
     * @param string $adminLocale e.g. "es_ES", "fr_FR".
633
     * @param array $visibleCoreFields Visible core names (email, firstname, status, date_of_birth, etc.)
634
     * @param array $visibleExtraVars Visible extra variables (without the "extra_" prefix)
635
     */
636
    private static function renderRegistrationProfileHtml(
637
        User $user,
638
        array $extraParams,
639
        string $adminLocale,
640
        array $visibleCoreFields = [],
641
        array $visibleExtraVars = []
642
    ): string {
643
        $showAllCore  = empty($visibleCoreFields);
644
        $showAllExtra = empty($visibleExtraVars);
645
646
        $coreVisible = array_fill_keys($visibleCoreFields, true);
647
        $extraVisible = array_fill_keys($visibleExtraVars, true);
648
649
        $languageName = self::resolveLanguageName($user->getLocale());
650
651
        $corePairs = [];
652
653
        if ($showAllCore || !empty($coreVisible['email'])) {
654
            $corePairs[get_lang('E-mail', $adminLocale)] = $user->getEmail();
655
        }
656
        if ($showAllCore || !empty($coreVisible['firstname'])) {
657
            $corePairs[get_lang('First name', $adminLocale)] = (string) $user->getFirstname();
658
        }
659
        if ($showAllCore || !empty($coreVisible['lastname'])) {
660
            $corePairs[get_lang('Last name', $adminLocale)] = (string) $user->getLastname();
661
        }
662
        if ($showAllCore || !empty($coreVisible['username'])) {
663
            $corePairs[get_lang('Username', $adminLocale)] = $user->getUsername();
664
        }
665
        if ($showAllCore || !empty($coreVisible['official_code'])) {
666
            $corePairs[get_lang('Official code', $adminLocale)] = $user->getOfficialCode() ?? '';
667
        }
668
        if ($showAllCore || !empty($coreVisible['phone'])) {
669
            $corePairs[get_lang('Phone', $adminLocale)] = $user->getPhone() ?? '';
670
        }
671
        if ($showAllCore || !empty($coreVisible['address'])) {
672
            $corePairs[get_lang('User address', $adminLocale)] = $user->getAddress() ?? '';
673
        }
674
        if ($showAllCore || !empty($coreVisible['language'])) {
675
            $corePairs[get_lang('Language', $adminLocale)] = $languageName;
676
        }
677
678
        if ($showAllCore || !empty($coreVisible['status'])) {
679
            $statusLabel = ((int) $user->getStatus() === COURSEMANAGER)
680
                ? get_lang('Teach courses', $adminLocale)
681
                : get_lang('Follow courses', $adminLocale);
682
            $corePairs[get_lang('What do you want to do?', $adminLocale)] = $statusLabel;
683
        }
684
685
        if (($showAllCore || !empty($coreVisible['date_of_birth'])) &&
686
            $user->getDateOfBirth() instanceof \DateTimeInterface
687
        ) {
688
            $corePairs[get_lang('Date of birth', $adminLocale)] = $user->getDateOfBirth()->format('Y-m-d');
689
        }
690
691
        $efv = new \ExtraFieldValue('user');
692
        $ef  = new \ExtraField('user');
693
694
        $extraPairs = [];
695
        $presentVars = [];
696
        $rows = $efv->getAllValuesByItem((int) $user->getId());
697
698
        if (is_array($rows)) {
699
            foreach ($rows as $row) {
700
                $fieldId   = (int)$row['id'];
701
                $variable  = (string)$row['variable'];
702
                $presentVars[$variable] = true;
703
704
                if (!$showAllExtra && empty($extraVisible[$variable])) {
705
                    continue;
706
                }
707
708
                $tr = $efv->get_values_by_handler_and_field_id((int) $user->getId(), $fieldId, true);
709
                $val = null;
710
711
                if ($tr && array_key_exists('value', $tr)) {
712
                    $val = $tr['value'];
713
                } else {
714
                    $val = $row['value'];
715
                }
716
717
                $type = (int)$row['value_type'];
718
                if ($type === \ExtraField::FIELD_TYPE_CHECKBOX) {
719
                    $val = ((string)$val === '1') ? get_lang('Yes', $adminLocale) : get_lang('No', $adminLocale);
720
                }
721
                if ($type === \ExtraField::FIELD_TYPE_TAG && is_array($val)) {
722
                    $val = implode(', ', array_map(fn($t) => is_array($t) ? (string)($t['value'] ?? '') : (string)$t, $val));
723
                }
724
                if (is_string($val)) {
725
                    $val = str_replace(['<br />','<br>','<br/>'], ', ', $val);
726
                }
727
728
                $label = !empty($row['display_text'])
729
                    ? get_lang($row['display_text'], $adminLocale)
730
                    : get_lang(ucwords(str_replace('_',' ',$variable)), $adminLocale);
731
732
                if ($val !== '' && $val !== null) {
733
                    $extraPairs[$label] = (string)$val;
734
                }
735
            }
736
        }
737
738
        foreach ($extraParams as $k => $v) {
739
            if (strpos($k, 'extra_') !== 0) continue;
740
            $variable = substr($k, 6);
741
            if (isset($presentVars[$variable])) continue;
742
            if (!$showAllExtra && empty($extraVisible[$variable])) continue;
743
744
            $def = $ef->get_handler_field_info_by_field_variable($variable);
745
            $label = $def && !empty($def['display_text'])
746
                ? get_lang($def['display_text'], $adminLocale)
747
                : get_lang(ucwords(str_replace('_',' ',$variable)), $adminLocale);
748
749
            $val = $v;
750
            if (is_array($val)) {
751
                $val = implode(', ', array_map('strval', $val));
752
            } elseif ($val === '1') {
753
                $val = get_lang('Yes', $adminLocale);
754
            } elseif ($val === '0') {
755
                $val = get_lang('No', $adminLocale);
756
            }
757
758
            if ($val !== '' && $val !== null) {
759
                $extraPairs[$label] = (string)$val;
760
            }
761
        }
762
763
        $html = '<div class="form-horizontal">';
764
        foreach ($corePairs as $k => $v) {
765
            if ($v === '' || $v === null) continue;
766
            $html .= '<div>'.$k.': '.\Security::remove_XSS((string)$v).'</div>';
767
        }
768
        foreach ($extraPairs as $k => $v) {
769
            $html .= '<div>'.$k.': '.\Security::remove_XSS((string)$v).'</div>';
770
        }
771
        $html .= '</div>';
772
773
        return $html;
774
    }
775
776
    /**
777
     * Can user be deleted? This function checks whether there's a course
778
     * in which the given user is the
779
     * only course administrator. If that is the case, the user can't be
780
     * deleted because the course would remain without a course admin.
781
     *
782
     * @param int $user_id The user id
783
     *
784
     * @return bool true if user can be deleted
785
     *
786
     * @assert (null) === false
787
     * @assert (-1) === false
788
     * @assert ('abc') === false
789
     */
790
    public static function canDeleteUser($user_id)
791
    {
792
        $deny = api_get_env_variable('DENY_DELETE_USERS', false);
793
794
        if ($deny) {
795
            return false;
796
        }
797
798
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
799
        $user_id = (int) $user_id;
800
801
        if (empty($user_id)) {
802
            return false;
803
        }
804
805
        $sql = "SELECT * FROM $table_course_user
806
                WHERE status = 1 AND user_id = ".$user_id;
807
        $res = Database::query($sql);
808
        while ($course = Database::fetch_object($res)) {
809
            $sql = "SELECT id FROM $table_course_user
810
                    WHERE status=1 AND c_id = ".intval($course->c_id);
811
            $res2 = Database::query($sql);
812
            if (1 == Database::num_rows($res2)) {
813
                return false;
814
            }
815
        }
816
817
        return true;
818
    }
819
820
    /**
821
     * Deletes a user from the system or marks the user as deleted based on the $destroy flag.
822
     * If $destroy is false, the user is only marked as deleted (e.g., active = -1) but not actually removed from the database.
823
     * This allows for the possibility of restoring the user at a later time. If $destroy is true, the user and all their relations
824
     * are permanently removed from the database.
825
     *
826
     * Note: When $destroy is false, the user's relations are not removed, allowing for potential restoration. When $destroy is true,
827
     * the function proceeds to remove all the user's relations, effectively cleaning up all references to the user in the system.
828
     */
829
    public static function delete_user(int $user_id, bool $destroy = false): bool
830
    {
831
        $user_id = (int) $user_id;
832
833
        if (empty($user_id)) {
834
            return false;
835
        }
836
837
        if (!self::canDeleteUser($user_id)) {
838
            return false;
839
        }
840
841
        $repository = Container::getUserRepository();
842
843
        /** @var User $user */
844
        $user = $repository->find($user_id);
845
846
        if (!$user) {
0 ignored issues
show
introduced by
$user is of type Chamilo\CoreBundle\Entity\User, thus it always evaluated to true.
Loading history...
847
            return false;
848
        }
849
850
        $fallbackUser = $repository->getFallbackUser();
851
        $fallbackId = (int) $fallbackUser->getId();
852
        $affectedIds = [];
853
        if ($destroy && $fallbackId && $fallbackId !== $user_id) {
854
            $em = Database::getManager();
855
            $rows = $em->createQuery(
856
                'SELECT u.id
857
                     FROM ' . User::class . ' u
858
                     WHERE u.creatorId = :oldCreator AND u.id <> :fallbackId'
859
            )
860
                ->setParameter('oldCreator', $user_id)
861
                ->setParameter('fallbackId', $fallbackId)
862
                ->getScalarResult();
863
864
            $affectedIds = array_map(
865
                static fn(array $r): int => (int) $r['id'],
866
                $rows
867
            );
868
869
            $em->createQuery(
870
                'UPDATE ' . User::class . ' u
871
                 SET u.creatorId = :newCreator
872
                 WHERE u.creatorId = :oldCreator AND u.id <> :fallbackId'
873
            )
874
                ->setParameter('newCreator', $fallbackId)
875
                ->setParameter('oldCreator', $user_id)
876
                ->setParameter('fallbackId', $fallbackId)
877
                ->execute();
878
        }
879
880
        $repository->deleteUser($user, $destroy);
881
882
        if ($destroy) {
883
            Event::addEvent(
884
                LOG_USER_DELETE,
885
                LOG_USER_OBJECT,
886
                api_get_user_info($user_id),
887
                api_get_utc_datetime(),
888
                api_get_user_id()
889
            );
890
891
            // Log one event per affected user AFTER the deletion
892
            if (!empty($affectedIds) && $fallbackId && $fallbackId !== $user_id) {
893
                $nowUtc = api_get_utc_datetime();
894
                $actor  = api_get_user_id();
895
                foreach ($affectedIds as $affectedId) {
896
                    Event::addEvent(
897
                        LOG_USER_CREATOR_DELETED,
898
                        LOG_USER_ID,
899
                        [
900
                            'user_id'        => $affectedId,
901
                            'old_creator_id' => $user_id,
902
                            'new_creator_id' => $fallbackId,
903
                        ],
904
                        $nowUtc,
905
                        $actor
906
                    );
907
                }
908
            }
909
        }
910
911
        return true;
912
    }
913
914
    /**
915
     * Deletes users completely. Can be called either as:
916
     * - UserManager::delete_users(1, 2, 3); or
917
     * - UserManager::delete_users(array(1, 2, 3));.
918
     *
919
     * @param array|int $ids
920
     *
921
     * @return bool True if at least one user was successfuly deleted. False otherwise.
922
     *
923
     * @author Laurent Opprecht
924
     *
925
     * @uses \UserManager::delete_user() to actually delete each user
926
     * @assert (null) === false
927
     * @assert (-1) === false
928
     * @assert (array(-1)) === false
929
     */
930
    public static function delete_users($ids = [])
931
    {
932
        $result = false;
933
        $ids = is_array($ids) ? $ids : func_get_args();
934
        if (!is_array($ids) || 0 == count($ids)) {
935
            return false;
936
        }
937
        $ids = array_map('intval', $ids);
938
        foreach ($ids as $id) {
939
            if (empty($id) || $id < 1) {
940
                continue;
941
            }
942
            $deleted = self::delete_user($id);
943
            $result = $deleted || $result;
944
        }
945
946
        return $result;
947
    }
948
949
    /**
950
     * Disable users. Can be called either as:
951
     * - UserManager::deactivate_users(1, 2, 3);
952
     * - UserManager::deactivate_users(array(1, 2, 3));.
953
     *
954
     * @param array|int $ids
955
     *
956
     * @return bool
957
     *
958
     * @author Laurent Opprecht
959
     * @assert (null) === false
960
     * @assert (array(-1)) === false
961
     */
962
    public static function deactivate_users($ids = [])
963
    {
964
        if (empty($ids)) {
965
            return false;
966
        }
967
968
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
969
970
        $ids = is_array($ids) ? $ids : func_get_args();
971
        $ids = array_map('intval', $ids);
972
        $ids = implode(',', $ids);
973
974
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
975
        $r = Database::query($sql);
976
        if (false !== $r) {
977
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
978
979
            return true;
980
        }
981
982
        return false;
983
    }
984
985
    /**
986
     * Enable users. Can be called either as:
987
     * - UserManager::activate_users(1, 2, 3);
988
     * - UserManager::activate_users(array(1, 2, 3));.
989
     *
990
     * @param array|int IDs of the users to enable
991
     *
992
     * @return bool
993
     *
994
     * @author Laurent Opprecht
995
     * @assert (null) === false
996
     * @assert (array(-1)) === false
997
     */
998
    public static function activate_users($ids = [])
999
    {
1000
        if (empty($ids)) {
1001
            return false;
1002
        }
1003
1004
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1005
1006
        $ids = is_array($ids) ? $ids : func_get_args();
1007
        $ids = array_map('intval', $ids);
1008
        $ids = implode(',', $ids);
1009
1010
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
1011
        $r = Database::query($sql);
1012
        if (false !== $r) {
1013
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
1014
1015
            return true;
1016
        }
1017
1018
        return false;
1019
    }
1020
1021
    /**
1022
     * Update user information with all the parameters passed to this function.
1023
     *
1024
     * @param int    $user_id         The ID of the user to be updated
1025
     * @param string $firstname       The user's firstname
1026
     * @param string $lastname        The user's lastname
1027
     * @param string $username        The user's username (login)
1028
     * @param string $password        The user's password
1029
     * @param string $auth_sources     The authentication source (default: "platform")
1030
     * @param string $email           The user's e-mail address
1031
     * @param int    $status          The user's status
1032
     * @param string $official_code   The user's official code (usually just an internal institutional code)
1033
     * @param string $phone           The user's phone number
1034
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
1035
     * @param string $expiration_date The date at which this user will be automatically disabled
1036
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
1037
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
1038
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
1039
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
1040
     * @param string $language        The language to which the user account will be set
1041
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
1042
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
1043
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
1044
     * @param string $address
1045
     * @param array  $emailTemplate
1046
     *
1047
     * @return bool|int False on error, or the user ID if the user information was updated
1048
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
1049
     */
1050
    public static function update_user(
1051
        $user_id,
1052
        $firstname,
1053
        $lastname,
1054
        $username,
1055
        $password,
1056
        array $auth_sources,
1057
        $email,
1058
        $status,
1059
        $official_code,
1060
        $phone,
1061
        $picture_uri,
1062
        $expiration_date,
1063
        $active,
1064
        $creator_id = null,
1065
        $hr_dept_id = 0,
1066
        $extra = null,
1067
        $language = 'en_US',
1068
        $encrypt_method = '',
1069
        $send_email = false,
1070
        $reset_password = 0,
1071
        $address = null,
1072
        $emailTemplate = []
1073
    ) {
1074
        $eventDispatcher = Container::getEventDispatcher();
1075
1076
        $eventDispatcher->dispatch(
1077
            new UserUpdatedEvent([], AbstractEvent::TYPE_PRE),
1078
            Events::USER_UPDATED
1079
        );
1080
1081
        $original_password = $password;
1082
        $user_id = (int) $user_id;
1083
        $creator_id = (int) $creator_id;
1084
1085
        if (empty($user_id)) {
1086
            return false;
1087
        }
1088
1089
        $userManager = self::getRepository();
1090
        $user = api_get_user_entity($user_id);
1091
1092
        if (null === $user) {
1093
            return false;
1094
        }
1095
1096
        $accessUrl = Container::getAccessUrlUtil()->getCurrent();
1097
1098
        if (0 == $reset_password) {
1099
            $password = null;
1100
            $auth_sources = $user->getAuthSourcesAuthentications($accessUrl);
1101
        } elseif (1 == $reset_password) {
1102
            $original_password = $password = api_generate_password();
1103
            $auth_sources = [UserAuthSource::PLATFORM];
1104
        } elseif (2 == $reset_password) {
1105
            $auth_sources = [UserAuthSource::PLATFORM];
1106
        }
1107
1108
        // Checking the user language
1109
        $languages = array_keys(api_get_languages());
1110
        if (!in_array($language, $languages)) {
1111
            $language = api_get_setting('platformLanguage');
1112
        }
1113
1114
        $change_active = 0;
1115
        $isUserActive = $user->isActive();
1116
        if ($active != USER_SOFT_DELETED) {
1117
            if ($isUserActive != $active) {
1118
                $change_active = 1;
1119
            }
1120
        }
1121
1122
        $originalUsername = $user->getUsername();
1123
1124
        // If username is different from original then check if it exists.
1125
        if ($originalUsername !== $username) {
1126
            $available = Container::getUserRepository()->isUsernameAvailable($username);
1127
            if (false === $available) {
1128
                return false;
1129
            }
1130
        }
1131
1132
        if (!empty($expiration_date)) {
1133
            $expiration_date = api_get_utc_datetime($expiration_date);
1134
            $expiration_date = new \DateTime($expiration_date, new DateTimeZone('UTC'));
1135
        }
1136
1137
        $previousStatus = $user->getStatus();
1138
        $previousRole = $user->getRoleFromStatus($previousStatus);
1139
1140
        $user
1141
            ->removeRole($previousRole)
1142
            ->setLastname($lastname)
1143
            ->setFirstname($firstname)
1144
            ->setUsername($username)
1145
            ->setStatus($status)
1146
            ->setLocale($language)
1147
            ->setEmail($email)
1148
            ->setOfficialCode($official_code)
1149
            ->setPhone($phone)
1150
            ->setAddress($address)
1151
            ->setExpirationDate($expiration_date)
1152
            ->setActive($active)
1153
            ->setHrDeptId((int) $hr_dept_id)
1154
            ->removeAuthSources()
1155
        ;
1156
1157
        foreach ($auth_sources as $authSource) {
1158
            $user->addAuthSourceByAuthentication($authSource, $accessUrl);
1159
        }
1160
1161
        if (!is_null($password)) {
1162
            $user->setPlainPassword($password);
1163
        }
1164
1165
        $user->setRoleFromStatus($status);
1166
        $userManager->updateUser($user, true);
1167
        Event::addEvent(LOG_USER_UPDATE, LOG_USER_ID, $user_id);
1168
1169
        if (1 == $change_active) {
1170
            $event_title = LOG_USER_DISABLE;
1171
            if (1 == $active) {
1172
                $event_title = LOG_USER_ENABLE;
1173
            }
1174
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
1175
        }
1176
1177
        if (is_array($extra) && count($extra) > 0) {
1178
            $res = true;
1179
            foreach ($extra as $fname => $fvalue) {
1180
                $res = $res && self::update_extra_field_value(
1181
                    $user_id,
1182
                    $fname,
1183
                    $fvalue
1184
                );
1185
            }
1186
        }
1187
1188
        if (!empty($email) && $send_email) {
1189
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
1190
            $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

1190
            $emailsubject = '['./** @scrutinizer ignore-type */ api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
Loading history...
1191
            $sender_name = api_get_person_name(
1192
                api_get_setting('administratorName'),
1193
                api_get_setting('administratorSurname'),
1194
                null,
1195
                PERSON_NAME_EMAIL_ADDRESS
1196
            );
1197
            $email_admin = api_get_setting('emailAdministrator');
1198
            $url = api_get_path(WEB_PATH);
1199
            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 AccessUrlUtil::isMultiple ( Ignorable by Annotation )

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

1199
            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...
1200
                $access_url_id = api_get_current_access_url_id();
1201
                if (-1 != $access_url_id) {
1202
                    $url = api_get_access_url($access_url_id);
1203
                    $url = $url['url'];
1204
                }
1205
            }
1206
1207
            $tplContent = new Template(
1208
                null,
1209
                false,
1210
                false,
1211
                false,
1212
                false,
1213
                false
1214
            );
1215
            // variables for the default template
1216
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
1217
            $tplContent->assign('login_name', $username);
1218
1219
            $originalPassword = '';
1220
            if ($reset_password > 0) {
1221
                $originalPassword = stripslashes($original_password);
1222
            }
1223
            $tplContent->assign('original_password', $originalPassword);
1224
            $tplContent->assign('portal_url', $url);
1225
1226
            $emailBody = $tplContent->fetch('@ChamiloCore/Mailer/Legacy/user_edit_content.html.twig');
1227
1228
            $mailTemplateManager = new MailTemplateManager();
1229
1230
            if (!empty($emailTemplate) &&
1231
                isset($emailTemplate['user_edit_content.tpl']) &&
1232
                !empty($emailTemplate['user_edit_content.tpl'])
1233
            ) {
1234
                $userInfo = api_get_user_info($user_id);
1235
                $emailBody = $mailTemplateManager->parseTemplate($emailTemplate['user_edit_content.tpl'], $userInfo);
1236
            }
1237
1238
            $creatorInfo = api_get_user_info($creator_id);
1239
            $creatorEmail = $creatorInfo['email'] ?? '';
1240
1241
            api_mail_html(
1242
                $recipient_name,
1243
                $email,
1244
                $emailsubject,
1245
                $emailBody,
1246
                $sender_name,
1247
                $email_admin,
1248
                null,
1249
                null,
1250
                null,
1251
                null,
1252
                $creatorEmail
1253
            );
1254
        }
1255
1256
        $eventDispatcher->dispatch(
1257
            new UserUpdatedEvent(['user' => $user], AbstractEvent::TYPE_POST),
1258
            Events::USER_UPDATED
1259
        );
1260
1261
        return $user->getId();
1262
    }
1263
1264
    /**
1265
     * Disables a user.
1266
     *
1267
     * @param int User id
1268
     *
1269
     * @return bool
1270
     *
1271
     * @uses \UserManager::change_active_state() to actually disable the user
1272
     * @assert (0) === false
1273
     */
1274
    public static function disable($user_id)
1275
    {
1276
        if (empty($user_id)) {
1277
            return false;
1278
        }
1279
        self::change_active_state($user_id, 0);
1280
1281
        return true;
1282
    }
1283
1284
    /**
1285
     * Enable a user.
1286
     *
1287
     * @param int User id
1288
     *
1289
     * @return bool
1290
     *
1291
     * @uses \UserManager::change_active_state() to actually disable the user
1292
     * @assert (0) === false
1293
     */
1294
    public static function enable($user_id)
1295
    {
1296
        if (empty($user_id)) {
1297
            return false;
1298
        }
1299
        self::change_active_state($user_id, 1);
1300
1301
        return true;
1302
    }
1303
1304
    /**
1305
     * Returns the user's id based on the original id and field name in
1306
     * the extra fields. Returns 0 if no user was found. This function is
1307
     * mostly useful in the context of a web services-based sinchronization.
1308
     *
1309
     * @param string Original user id
1310
     * @param string Original field name
1311
     *
1312
     * @return int User id
1313
     * @assert ('0','---') === 0
1314
     */
1315
    public static function get_user_id_from_original_id(
1316
        $original_user_id_value,
1317
        $original_user_id_name
1318
    ) {
1319
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1320
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1321
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1322
1323
        $original_user_id_name = Database::escape_string($original_user_id_name);
1324
        $original_user_id_value = Database::escape_string($original_user_id_value);
1325
1326
        $sql = "SELECT item_id as user_id
1327
                FROM $t_uf uf
1328
                INNER JOIN $t_ufv ufv
1329
                ON ufv.field_id = uf.id
1330
                WHERE
1331
                    variable = '$original_user_id_name' AND
1332
                    field_value = '$original_user_id_value' AND
1333
                    item_type = $extraFieldType
1334
                ";
1335
        $res = Database::query($sql);
1336
        $row = Database::fetch_object($res);
1337
        if ($row) {
1338
            return $row->user_id;
1339
        }
1340
1341
        return 0;
1342
    }
1343
1344
    /**
1345
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1346
     *
1347
     * @param string $firstname the first name of the user
1348
     * @param string $lastname  the last name of the user
1349
     *
1350
     * @return string suggests a username that contains only ASCII-letters and digits,
1351
     *                without check for uniqueness within the system
1352
     *
1353
     * @author Julio Montoya Armas
1354
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1355
     * @assert ('','') === false
1356
     * @assert ('a','b') === 'ab'
1357
     */
1358
    public static function create_username($firstname, $lastname)
1359
    {
1360
        if (empty($firstname) && empty($lastname)) {
1361
            return false;
1362
        }
1363
1364
        // The first letter only.
1365
        $firstname = api_substr(
1366
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1367
            0,
1368
            1
1369
        );
1370
        //Looking for a space in the lastname
1371
        $pos = api_strpos($lastname, ' ');
1372
        if (false !== $pos) {
1373
            $lastname = api_substr($lastname, 0, $pos);
1374
        }
1375
1376
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1377
        $username = $firstname.$lastname;
1378
        if (empty($username)) {
1379
            $username = 'user';
1380
        }
1381
1382
        $username = URLify::transliterate($username);
1383
1384
        return strtolower(substr($username, 0, User::USERNAME_MAX_LENGTH - 3));
1385
    }
1386
1387
    /**
1388
     * Creates a unique username, using:
1389
     * 1. the first name and the last name of a user;
1390
     * 2. an already created username but not checked for uniqueness yet.
1391
     *
1392
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1393
     *                          parameter is treated as username which is to be checked f
1394
     *                          or uniqueness and to be modified when it is necessary.
1395
     * @param string $lastname  the last name of the user
1396
     *
1397
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1398
     *                Note: When the method is called several times with same parameters,
1399
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1400
     *
1401
     * @author Ivan Tcholakov, 2009
1402
     */
1403
    public static function create_unique_username($firstname, $lastname = null)
1404
    {
1405
        if (is_null($lastname)) {
1406
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1407
            // For making this method tolerant of mistakes,
1408
            // let us transliterate and purify the suggested input username anyway.
1409
            // So, instead of the sentence $username = $firstname; we place the following:
1410
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1411
        } else {
1412
            $username = self::create_username($firstname, $lastname);
1413
        }
1414
        if (!Container::getUserRepository()->isUsernameAvailable($username)) {
1415
            $i = 2;
1416
            $temp_username = substr($username, 0, User::USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1417
            while (!Container::getUserRepository()->isUsernameAvailable($temp_username)) {
1418
                $i++;
1419
                $temp_username = substr($username, 0, User::USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1420
            }
1421
            $username = $temp_username;
1422
        }
1423
1424
        $username = URLify::transliterate($username);
1425
1426
        return $username;
1427
    }
1428
1429
    /**
1430
     * Modifies a given username accordingly to the specification for valid characters and length.
1431
     *
1432
     * @param $username string          The input username
1433
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1434
     *                     otherwise compliance may be partial. The default value is FALSE.
1435
     *
1436
     * @return string the resulting purified username
1437
     */
1438
    public static function purify_username($username, $strict = false)
1439
    {
1440
        if ($strict) {
1441
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1442
            // into ASCII letters in order they not to be totally removed.
1443
            // 2. Applying the strict purifier.
1444
            // 3. Length limitation.
1445
            $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);
1446
            $return = URLify::transliterate($return);
1447
1448
            // We want everything transliterate() does except converting @ to '(at)'. This is a hack to avoid this.
1449
            $return = str_replace(' (at) ', '@', $return);
1450
1451
            return $return;
1452
        }
1453
1454
        // 1. Applying the shallow purifier.
1455
        // 2. Length limitation.
1456
        return substr(
1457
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1458
            0,
1459
            User::USERNAME_MAX_LENGTH
1460
        );
1461
    }
1462
1463
    /**
1464
     * Checks whether the user id exists in the database.
1465
     *
1466
     * @param int $userId User id
1467
     *
1468
     * @return bool True if user id was found, false otherwise
1469
     */
1470
    public static function is_user_id_valid($userId)
1471
    {
1472
        $resultData = Database::select(
1473
            'COUNT(1) AS count',
1474
            Database::get_main_table(TABLE_MAIN_USER),
1475
            [
1476
                'where' => ['id = ?' => (int) $userId],
1477
            ],
1478
            'first'
1479
        );
1480
1481
        if (false === $resultData) {
1482
            return false;
1483
        }
1484
1485
        return $resultData['count'] > 0;
1486
    }
1487
1488
    /**
1489
     * Checks whether a given username matches to the specification strictly.
1490
     * The empty username is assumed here as invalid.
1491
     * Mostly this function is to be used in the user interface built-in validation routines
1492
     * for providing feedback while usernames are enterd manually.
1493
     *
1494
     * @param string $username the input username
1495
     *
1496
     * @return bool returns TRUE if the username is valid, FALSE otherwise
1497
     */
1498
    public static function is_username_valid($username)
1499
    {
1500
        return !empty($username) && $username == self::purify_username($username, true);
1501
    }
1502
1503
    /**
1504
     * Checks whether a username is empty. If the username contains whitespace characters,
1505
     * such as spaces, tabulators, newlines, etc.,
1506
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
1507
     *
1508
     * @param string $username the given username
1509
     *
1510
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1511
     */
1512
    public static function is_username_empty($username)
1513
    {
1514
        return 0 == strlen(self::purify_username($username, false));
1515
    }
1516
1517
    /**
1518
     * Checks whether a username is too long or not.
1519
     *
1520
     * @param string $username the given username, it should contain only ASCII-letters and digits
1521
     *
1522
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
1523
     */
1524
    public static function is_username_too_long($username)
1525
    {
1526
        return strlen($username) > User::USERNAME_MAX_LENGTH;
1527
    }
1528
1529
    /**
1530
     * Get the users by ID.
1531
     *
1532
     * @param array  $ids    student ids
1533
     * @param bool   $active
1534
     * @param string $order
1535
     * @param string $limit
1536
     *
1537
     * @return array $result student information
1538
     */
1539
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
1540
    {
1541
        if (empty($ids)) {
1542
            return [];
1543
        }
1544
1545
        $ids = is_array($ids) ? $ids : [$ids];
1546
        $ids = array_map('intval', $ids);
1547
        $ids = implode(',', $ids);
1548
1549
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
1550
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
1551
        if (!is_null($active)) {
1552
            $sql .= ' AND active='.($active ? '1' : '0');
1553
        }
1554
1555
        if (!is_null($order)) {
1556
            $order = Database::escape_string($order);
1557
            $sql .= ' ORDER BY '.$order;
1558
        }
1559
1560
        if (!is_null($limit)) {
1561
            $limit = Database::escape_string($limit);
1562
            $sql .= ' LIMIT '.$limit;
1563
        }
1564
1565
        $rs = Database::query($sql);
1566
        $result = [];
1567
        while ($row = Database::fetch_array($rs)) {
1568
            $result[] = $row;
1569
        }
1570
1571
        return $result;
1572
    }
1573
1574
    /**
1575
     * Get a list of users of which the given conditions match with an = 'cond'.
1576
     *
1577
     * @param array $conditions a list of condition (example : status=>STUDENT)
1578
     * @param array $order_by   a list of fields on which sort
1579
     *
1580
     * @return array an array with all users of the platform
1581
     *
1582
     * @todo security filter order by
1583
     */
1584
    public static function get_user_list(
1585
        $conditions = [],
1586
        $order_by = [],
1587
        $limit_from = false,
1588
        $limit_to = false,
1589
        $idCampus = null
1590
    ) {
1591
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1592
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1593
        $return_array = [];
1594
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1595
1596
        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 AccessUrlUtil::isMultiple ( Ignorable by Annotation )

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

1596
        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...
1597
            if ($idCampus) {
1598
                $urlId = $idCampus;
1599
            } else {
1600
                $urlId = api_get_current_access_url_id();
1601
            }
1602
            $sql .= " INNER JOIN $userUrlTable url_user
1603
                      ON (user.id = url_user.user_id)
1604
                      WHERE url_user.access_url_id = $urlId";
1605
        } else {
1606
            $sql .= " WHERE 1=1 ";
1607
        }
1608
1609
        if (count($conditions) > 0) {
1610
            foreach ($conditions as $field => $value) {
1611
                $field = Database::escape_string($field);
1612
                $value = Database::escape_string($value);
1613
                $sql .= " AND $field = '$value'";
1614
            }
1615
        }
1616
1617
        if (count($order_by) > 0) {
1618
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1619
        }
1620
1621
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1622
            $limit_from = (int) $limit_from;
1623
            $limit_to = (int) $limit_to;
1624
            $sql .= " LIMIT $limit_from, $limit_to";
1625
        }
1626
        $sql_result = Database::query($sql);
1627
        while ($result = Database::fetch_array($sql_result)) {
1628
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1629
            $return_array[] = $result;
1630
        }
1631
1632
        return $return_array;
1633
    }
1634
1635
    public static function getUserListExtraConditions(
1636
        $conditions = [],
1637
        $order_by = [],
1638
        $limit_from = false,
1639
        $limit_to = false,
1640
        $idCampus = null,
1641
        $extraConditions = '',
1642
        $getCount = false
1643
    ) {
1644
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1645
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1646
        $return_array = [];
1647
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1648
1649
        if ($getCount) {
1650
            $sql = "SELECT count(user.id) count FROM $user_table user ";
1651
        }
1652
1653
        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 AccessUrlUtil::isMultiple ( Ignorable by Annotation )

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

1653
        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...
1654
            if ($idCampus) {
1655
                $urlId = $idCampus;
1656
            } else {
1657
                $urlId = api_get_current_access_url_id();
1658
            }
1659
            $sql .= " INNER JOIN $userUrlTable url_user
1660
                      ON (user.id = url_user.user_id)
1661
                      WHERE url_user.access_url_id = $urlId";
1662
        } else {
1663
            $sql .= " WHERE 1=1 ";
1664
        }
1665
1666
        $sql .= " AND status <> ".ANONYMOUS." ";
1667
1668
        if (count($conditions) > 0) {
1669
            foreach ($conditions as $field => $value) {
1670
                $field = Database::escape_string($field);
1671
                $value = Database::escape_string($value);
1672
                $sql .= " AND $field = '$value'";
1673
            }
1674
        }
1675
1676
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
1677
1678
        if (!empty($order_by) && count($order_by) > 0) {
1679
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1680
        }
1681
1682
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1683
            $limit_from = (int) $limit_from;
1684
            $limit_to = (int) $limit_to;
1685
            $sql .= " LIMIT $limit_from, $limit_to";
1686
        }
1687
1688
        $sql_result = Database::query($sql);
1689
1690
        if ($getCount) {
1691
            $result = Database::fetch_array($sql_result);
1692
1693
            return $result['count'];
1694
        }
1695
1696
        while ($result = Database::fetch_array($sql_result)) {
1697
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1698
            $return_array[] = $result;
1699
        }
1700
1701
        return $return_array;
1702
    }
1703
1704
    /**
1705
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
1706
     *
1707
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
1708
     * @param array  $order_by         a list of fields on which sort
1709
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
1710
     * @param string $condition        Whether we want the filters to be combined by AND or OR
1711
     * @param array  $onlyThisUserList
1712
     *
1713
     * @return array an array with all users of the platform
1714
     *
1715
     * @todo optional course code parameter, optional sorting parameters...
1716
     * @todo security filter order_by
1717
     */
1718
    public static function getUserListLike(
1719
        $conditions = [],
1720
        $order_by = [],
1721
        $simple_like = false,
1722
        $condition = 'AND',
1723
        $onlyThisUserList = [],
1724
        int $limit = 0,
1725
        int $offset = 0
1726
    ) {
1727
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1728
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1729
        $return_array = [];
1730
        $sql_query = "SELECT user.id, user.username, user.firstname, user.lastname, user.official_code, user.status
1731
                  FROM $user_table user ";
1732
1733
        if (api_get_multiple_access_url()) {
1734
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
1735
        }
1736
1737
        $sql_query .= ' WHERE 1 = 1 ';
1738
        if (count($conditions) > 0) {
1739
            $temp_conditions = [];
1740
            foreach ($conditions as $field => $value) {
1741
                $field = Database::escape_string($field);
1742
                $value = Database::escape_string($value);
1743
                if ($simple_like) {
1744
                    $temp_conditions[] = "$field LIKE '$value%'";
1745
                } else {
1746
                    if (in_array($field, ['user.id', 'user.status'])) {
1747
                        $temp_conditions[] = "$field = '$value'";
1748
                    } else {
1749
                        $temp_conditions[] = "$field LIKE '%$value%'";
1750
                    }
1751
                }
1752
            }
1753
            if (!empty($temp_conditions)) {
1754
                $sql_query .= ' AND '.implode(" $condition ", $temp_conditions);
1755
            }
1756
1757
            if (api_get_multiple_access_url()) {
1758
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1759
            }
1760
        } else {
1761
            if (api_get_multiple_access_url()) {
1762
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1763
            }
1764
        }
1765
1766
        if (!empty($onlyThisUserList)) {
1767
            $onlyThisUserListToString = implode("','", array_map('intval', $onlyThisUserList));
1768
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
1769
        }
1770
1771
        if (!empty($order_by)) {
1772
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1773
        }
1774
1775
        if ($limit > 0) {
1776
            $sql_query .= ' LIMIT '.intval($limit);
1777
            if ($offset > 0) {
1778
                $sql_query .= ' OFFSET '.intval($offset);
1779
            }
1780
        }
1781
1782
        $sql_result = Database::query($sql_query);
1783
        while ($result = Database::fetch_array($sql_result)) {
1784
            $return_array[] = $result;
1785
        }
1786
1787
        return $return_array;
1788
    }
1789
1790
    /**
1791
     * Get user path from user ID (returns an array).
1792
     * The return format is a complete path to a folder ending with "/"
1793
     * In case the first level of subdirectory of users/ does not exist, the
1794
     * function will attempt to create it. Probably not the right place to do it
1795
     * but at least it avoids headaches in many other places.
1796
     *
1797
     * @param int    $id   User ID
1798
     * @param string $type Type of path to return (can be 'system', 'web', 'last')
1799
     *
1800
     * @return string User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
1801
     */
1802
    public static function getUserPathById($id, $type)
1803
    {
1804
        $id = (int) $id;
1805
        if (!$id) {
1806
            return null;
1807
        }
1808
1809
        $userPath = "users/$id/";
1810
        if (api_get_setting('split_users_upload_directory') === 'true') {
1811
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
1812
            // In exceptional cases, on some portals, the intermediate base user
1813
            // directory might not have been created. Make sure it is before
1814
            // going further.
1815
1816
            $rootPath = api_get_path(SYS_PATH).'../app/upload/users/'.substr((string) $id, 0, 1);
1817
            if (!is_dir($rootPath)) {
1818
                $perm = api_get_permissions_for_new_directories();
1819
                try {
1820
                    mkdir($rootPath, $perm);
1821
                } catch (Exception $e) {
1822
                    error_log($e->getMessage());
1823
                }
1824
            }
1825
        }
1826
        switch ($type) {
1827
            case 'system': // Base: absolute system path.
1828
                $userPath = api_get_path(SYS_PATH).'../app/upload/'.$userPath;
1829
                break;
1830
            case 'web': // Base: absolute web path.
1831
                $userPath = api_get_path(WEB_PATH).'../app/upload/'.$userPath;
1832
                break;
1833
            case 'last': // Only the last part starting with users/
1834
                break;
1835
        }
1836
1837
        return $userPath;
1838
    }
1839
1840
    /**
1841
     * Gets the current user image.
1842
     *
1843
     * @param string $userId
1844
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
1845
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
1846
     * @param bool   $addRandomId
1847
     * @param array  $userInfo    to avoid query the DB
1848
     *
1849
     * @todo add gravatar support
1850
     * @todo replace $userId with User entity
1851
     *
1852
     * @return string
1853
     */
1854
    public static function getUserPicture(
1855
        $userId,
1856
        int $size = USER_IMAGE_SIZE_MEDIUM,
1857
        $addRandomId = true,
1858
        $userInfo = []
1859
    ) {
1860
        $user = api_get_user_entity($userId);
1861
        $illustrationRepo = Container::getIllustrationRepository();
1862
1863
        switch ($size) {
1864
            case USER_IMAGE_SIZE_SMALL:
1865
                $width = 32;
1866
                break;
1867
            case USER_IMAGE_SIZE_MEDIUM:
1868
                $width = 64;
1869
                break;
1870
            case USER_IMAGE_SIZE_BIG:
1871
                $width = 128;
1872
                break;
1873
            case USER_IMAGE_SIZE_ORIGINAL:
1874
            default:
1875
                $width = 0;
1876
                break;
1877
        }
1878
1879
        $url = $illustrationRepo->getIllustrationUrl($user);
1880
        $params = [];
1881
        if (!empty($width)) {
1882
            $params['w'] = $width;
1883
        }
1884
1885
        if ($addRandomId) {
1886
            $params['rand'] = uniqid('u_', true);
1887
        }
1888
1889
        $paramsToString = '';
1890
        if (!empty($params)) {
1891
            $paramsToString = '?'.http_build_query($params);
1892
        }
1893
1894
        return $url.$paramsToString;
1895
1896
        /*
1897
        // Make sure userInfo is defined. Otherwise, define it!
1898
        if (empty($userInfo) || !is_array($userInfo) || 0 == count($userInfo)) {
1899
            if (empty($user_id)) {
1900
                return '';
1901
            } else {
1902
                $userInfo = api_get_user_info($user_id);
1903
            }
1904
        }
1905
1906
        $imageWebPath = self::get_user_picture_path_by_id(
1907
            $user_id,
1908
            'web',
1909
            $userInfo
1910
        );
1911
        $pictureWebFile = $imageWebPath['file'];
1912
        $pictureWebDir = $imageWebPath['dir'];
1913
1914
        $pictureAnonymousSize = '128';
1915
        $gravatarSize = 22;
1916
        $realSizeName = 'small_';
1917
1918
        switch ($size) {
1919
            case USER_IMAGE_SIZE_SMALL:
1920
                $pictureAnonymousSize = '32';
1921
                $realSizeName = 'small_';
1922
                $gravatarSize = 32;
1923
                break;
1924
            case USER_IMAGE_SIZE_MEDIUM:
1925
                $pictureAnonymousSize = '64';
1926
                $realSizeName = 'medium_';
1927
                $gravatarSize = 64;
1928
                break;
1929
            case USER_IMAGE_SIZE_ORIGINAL:
1930
                $pictureAnonymousSize = '128';
1931
                $realSizeName = '';
1932
                $gravatarSize = 128;
1933
                break;
1934
            case USER_IMAGE_SIZE_BIG:
1935
                $pictureAnonymousSize = '128';
1936
                $realSizeName = 'big_';
1937
                $gravatarSize = 128;
1938
                break;
1939
        }
1940
1941
        $gravatarEnabled = api_get_setting('gravatar_enabled');
1942
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
1943
        if ('unknown.jpg' == $pictureWebFile || empty($pictureWebFile)) {
1944
            if ('true' === $gravatarEnabled) {
1945
                $file = self::getGravatar(
1946
                    $imageWebPath['email'],
1947
                    $gravatarSize,
1948
                    api_get_setting('gravatar_type')
1949
                );
1950
1951
                if ($addRandomId) {
1952
                    $file .= '&rand='.uniqid();
1953
                }
1954
1955
                return $file;
1956
            }
1957
1958
            return $anonymousPath;
1959
        }
1960
1961
        if ($addRandomId) {
1962
            $picture .= '?rand='.uniqid();
1963
        }
1964
1965
        return $picture;*/
1966
    }
1967
1968
    /**
1969
     * Creates new user photos in various sizes of a user, or deletes user photos.
1970
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
1971
     *
1972
     * @param int    $user_id the user internal identification number
1973
     * @param string $file    The common file name for the newly created photos.
1974
     *                        It will be checked and modified for compatibility with the file system.
1975
     *                        If full name is provided, path component is ignored.
1976
     *                        If an empty name is provided, then old user photos are deleted only,
1977
     *
1978
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
1979
     *
1980
     * @param string $source_file    the full system name of the image from which user photos will be created
1981
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
1982
     *
1983
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
1984
     *              When deletion is requested returns empty string.
1985
     *              In case of internal error or negative validation returns FALSE.
1986
     */
1987
    public static function update_user_picture($userId, UploadedFile $file, string $crop = '')
1988
    {
1989
        if (empty($userId) || empty($file)) {
1990
            return false;
1991
        }
1992
1993
        $repo = Container::getUserRepository();
1994
        $user = $repo->find($userId);
1995
        if ($user) {
1996
            $repoIllustration = Container::getIllustrationRepository();
1997
            $repoIllustration->addIllustration($user, $user, $file, $crop);
1998
        }
1999
    }
2000
2001
    /**
2002
     * Deletes user photos.
2003
     *
2004
     * @param int $userId the user internal identification number
2005
     *
2006
     * @return mixed returns empty string on success, FALSE on error
2007
     */
2008
    public static function deleteUserPicture($userId)
2009
    {
2010
        $repo = Container::getUserRepository();
2011
        $user = $repo->find($userId);
2012
        if ($user) {
2013
            $illustrationRepo = Container::getIllustrationRepository();
2014
            $illustrationRepo->deleteIllustration($user);
2015
        }
2016
    }
2017
2018
    /**
2019
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
2020
     * doesn't have any.
2021
     *
2022
     * If there has been a request to remove a production, the function will return
2023
     * without building the list unless forced to do so by the optional second
2024
     * parameter. This increases performance by avoiding to read through the
2025
     * productions on the filesystem before the removal request has been carried
2026
     * out because they'll have to be re-read afterwards anyway.
2027
     *
2028
     * @deprecated This method is being removed from chamilo 2.0
2029
     * @param int  $user_id    User id
2030
     * @param bool $force      Optional parameter to force building after a removal request
2031
     * @param bool $showDelete
2032
     *
2033
     * @return string A string containing the XHTML code to display the production list, or FALSE
2034
     */
2035
    public static function build_production_list($user_id, $force = false, $showDelete = false)
2036
    {
2037
        if (!$force && !empty($_POST['remove_production'])) {
2038
            return true; // postpone reading from the filesystem
2039
        }
2040
2041
        $productions = self::get_user_productions($user_id);
2042
2043
        if (empty($productions)) {
2044
            return false;
2045
        }
2046
2047
        return false;
2048
2049
        $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...
2050
        $del_image = Display::returnIconPath('delete.png');
2051
        $add_image = Display::returnIconPath('archive.png');
2052
        $del_text = get_lang('Delete');
2053
        $production_list = '';
2054
        if (count($productions) > 0) {
2055
            $production_list = '<div class="files-production"><ul id="productions">';
2056
            foreach ($productions as $file) {
2057
                $production_list .= '<li>
2058
                    <img src="'.$add_image.'" />
2059
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2060
                        '.htmlentities($file).'
2061
                    </a>';
2062
                if ($showDelete) {
2063
                    $production_list .= '&nbsp;&nbsp;
2064
                        <input
2065
                            style="width:16px;"
2066
                            type="image"
2067
                            name="remove_production['.urlencode($file).']"
2068
                            src="'.$del_image.'"
2069
                            alt="'.$del_text.'"
2070
                            title="'.$del_text.' '.htmlentities($file).'"
2071
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2072
                }
2073
            }
2074
            $production_list .= '</ul></div>';
2075
        }
2076
2077
        return $production_list;
2078
    }
2079
2080
    /**
2081
     * Returns an array with the user's productions.
2082
     *
2083
     * @param int $user_id User id
2084
     *
2085
     * @return array An array containing the user's productions
2086
     */
2087
    public static function get_user_productions($user_id)
2088
    {
2089
        return [];
2090
2091
        $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...
2092
        $productions = [];
2093
2094
        if (is_dir($production_repository)) {
2095
            $handle = opendir($production_repository);
2096
            while ($file = readdir($handle)) {
2097
                if ('.' == $file ||
2098
                    '..' == $file ||
2099
                    '.htaccess' == $file ||
2100
                    is_dir($production_repository.$file)
2101
                ) {
2102
                    // skip current/parent directory and .htaccess
2103
                    continue;
2104
                }
2105
2106
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2107
                    // User's photos should not be listed as productions.
2108
                    continue;
2109
                }
2110
                $productions[] = $file;
2111
            }
2112
        }
2113
2114
        return $productions;
2115
    }
2116
2117
    /**
2118
     * Remove a user production.
2119
     *
2120
     * @param int    $user_id    User id
2121
     * @param string $production The production to remove
2122
     *
2123
     * @return bool
2124
     */
2125
    public static function remove_user_production($user_id, $production)
2126
    {
2127
        throw new Exception('remove_user_production');
2128
        /*$production_path = self::get_user_picture_path_by_id($user_id, 'system');
2129
        $production_file = $production_path['dir'].$production;
2130
        if (is_file($production_file)) {
2131
            unlink($production_file);
2132
2133
            return true;
2134
        }
2135
2136
        return false;*/
2137
    }
2138
2139
    /**
2140
     * Update an extra field value for a given user.
2141
     *
2142
     * @param int    $userId   User ID
2143
     * @param string $variable Field variable name
2144
     * @param string $value    Field value
2145
     *
2146
     * @return bool true if field updated, false otherwise
2147
     */
2148
    public static function update_extra_field_value($userId, $variable, $value = '')
2149
    {
2150
        $extraFieldValue = new ExtraFieldValue('user');
2151
        $params = [
2152
            'item_id' => $userId,
2153
            'variable' => $variable,
2154
            'field_value' => $value,
2155
        ];
2156
2157
        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...
2158
    }
2159
2160
    /**
2161
     * Get an array of extra fields with field details (type, default value and options).
2162
     *
2163
     * @param    int    Offset (from which row)
2164
     * @param    int    Number of items
2165
     * @param    int    Column on which sorting is made
2166
     * @param    string    Sorting direction
2167
     * @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...
2168
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2169
     *
2170
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2171
     */
2172
    public static function get_extra_fields(
2173
        $from = 0,
2174
        $number_of_items = 0,
2175
        $column = 5,
2176
        $direction = 'ASC',
2177
        $all_visibility = true,
2178
        $field_filter = null
2179
    ) {
2180
        $fields = [];
2181
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2182
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2183
        $columns = [
2184
            'id',
2185
            'variable',
2186
            'value_type',
2187
            'display_text',
2188
            'default_value',
2189
            'field_order',
2190
            'filter',
2191
        ];
2192
        $column = (int) $column;
2193
        $sort_direction = '';
2194
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2195
            $sort_direction = strtoupper($direction);
2196
        }
2197
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2198
        $sqlf = "SELECT * FROM $t_uf WHERE item_type = $extraFieldType ";
2199
        if (!$all_visibility) {
2200
            $sqlf .= " AND visible_to_self = 1 ";
2201
        }
2202
        if (!is_null($field_filter)) {
2203
            $field_filter = (int) $field_filter;
2204
            $sqlf .= " AND filter = $field_filter ";
2205
        }
2206
        $sqlf .= " ORDER BY `".$columns[$column]."` $sort_direction ";
2207
        if (0 != $number_of_items) {
2208
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2209
        }
2210
        $resf = Database::query($sqlf);
2211
        if (Database::num_rows($resf) > 0) {
2212
            while ($rowf = Database::fetch_array($resf)) {
2213
                $fields[$rowf['id']] = [
2214
                    0 => $rowf['id'],
2215
                    1 => $rowf['variable'],
2216
                    2 => $rowf['value_type'],
2217
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2218
                    4 => $rowf['default_value'],
2219
                    5 => $rowf['field_order'],
2220
                    6 => $rowf['visible_to_self'],
2221
                    7 => $rowf['changeable'],
2222
                    8 => $rowf['filter'],
2223
                    9 => [],
2224
                    10 => '<a name="'.$rowf['id'].'"></a>',
2225
                ];
2226
2227
                $sqlo = "SELECT * FROM $t_ufo
2228
                         WHERE field_id = ".$rowf['id']."
2229
                         ORDER BY option_order ASC";
2230
                $reso = Database::query($sqlo);
2231
                if (Database::num_rows($reso) > 0) {
2232
                    while ($rowo = Database::fetch_array($reso)) {
2233
                        $fields[$rowf['id']][9][$rowo['id']] = [
2234
                            0 => $rowo['id'],
2235
                            1 => $rowo['option_value'],
2236
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2237
                            3 => $rowo['option_order'],
2238
                        ];
2239
                    }
2240
                }
2241
            }
2242
        }
2243
2244
        return $fields;
2245
    }
2246
2247
    /**
2248
     * Creates a new extra field.
2249
     *
2250
     * @param string $variable    Field's internal variable name
2251
     * @param int    $fieldType   Field's type
2252
     * @param string $displayText Field's language var name
2253
     * @param string $default     Field's default value
2254
     *
2255
     * @return int
2256
     */
2257
    public static function create_extra_field(
2258
        $variable,
2259
        $valueType,
2260
        $displayText,
2261
        $default
2262
    ) {
2263
        $extraField = new ExtraField('user');
2264
        $params = [
2265
            'variable' => $variable,
2266
            'value_type' => $valueType,
2267
            'display_text' => $displayText,
2268
            'default_value' => $default,
2269
        ];
2270
2271
        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...
2272
    }
2273
2274
    /**
2275
     * Check if a field is available.
2276
     *
2277
     * @param string $variable
2278
     *
2279
     * @return bool
2280
     */
2281
    public static function is_extra_field_available($variable)
2282
    {
2283
        $extraField = new ExtraField('user');
2284
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2285
2286
        return !empty($data) ? true : false;
2287
    }
2288
2289
    /**
2290
     * Gets user extra fields data.
2291
     *
2292
     * @param    int    User ID
2293
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2294
     * @param    bool    Whether to return invisible fields as well
2295
     * @param    bool    Whether to split multiple-selection fields or not
2296
     *
2297
     * @return array Array of fields => value for the given user
2298
     */
2299
    public static function get_extra_user_data(
2300
        $user_id,
2301
        $prefix = false,
2302
        $allVisibility = true,
2303
        $splitMultiple = false,
2304
        $fieldFilter = null
2305
    ) {
2306
        $user_id = (int) $user_id;
2307
2308
        if (empty($user_id)) {
2309
            return [];
2310
        }
2311
2312
        $extra_data = [];
2313
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2314
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2315
        $sql = "SELECT f.id as id, f.variable as fvar, f.value_type as type
2316
                FROM $t_uf f
2317
                WHERE
2318
                    item_type = ".EntityExtraField::USER_FIELD_TYPE."
2319
                ";
2320
        $filter_cond = '';
2321
2322
        if (!$allVisibility) {
2323
            if (isset($fieldFilter)) {
2324
                $fieldFilter = (int) $fieldFilter;
2325
                $filter_cond .= " AND filter = $fieldFilter ";
2326
            }
2327
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2328
        } else {
2329
            if (isset($fieldFilter)) {
2330
                $fieldFilter = (int) $fieldFilter;
2331
                $sql .= " AND filter = $fieldFilter ";
2332
            }
2333
        }
2334
2335
        $sql .= ' ORDER BY f.field_order';
2336
2337
        $res = Database::query($sql);
2338
        if (Database::num_rows($res) > 0) {
2339
            while ($row = Database::fetch_array($res)) {
2340
                if (self::USER_FIELD_TYPE_TAG == $row['type']) {
2341
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2342
                    $extra_data['extra_'.$row['fvar']] = $tags;
2343
                } else {
2344
                    $sqlu = "SELECT field_value as fval
2345
                            FROM $t_ufv
2346
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2347
                    $resu = Database::query($sqlu);
2348
                    // get default value
2349
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2350
                               WHERE id=".$row['id'];
2351
                    $res_df = Database::query($sql_df);
2352
2353
                    if (Database::num_rows($resu) > 0) {
2354
                        $rowu = Database::fetch_array($resu);
2355
                        $fval = $rowu['fval'];
2356
                        if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2357
                            $fval = explode(';', $rowu['fval']);
2358
                        }
2359
                    } else {
2360
                        $row_df = Database::fetch_array($res_df);
2361
                        $fval = $row_df['fval_df'];
2362
                    }
2363
                    // We get here (and fill the $extra_data array) even if there
2364
                    // is no user with data (we fill it with default values)
2365
                    if ($prefix) {
2366
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2367
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2368
                        } else {
2369
                            $extra_data['extra_'.$row['fvar']] = $fval;
2370
                        }
2371
                    } else {
2372
                        if (self::USER_FIELD_TYPE_RADIO == $row['type']) {
2373
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2374
                        } else {
2375
                            $extra_data[$row['fvar']] = $fval;
2376
                        }
2377
                    }
2378
                }
2379
            }
2380
        }
2381
2382
        return $extra_data;
2383
    }
2384
2385
    /** Get extra user data by field.
2386
     * @param int    user ID
2387
     * @param string the internal variable name of the field
2388
     *
2389
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2390
     */
2391
    public static function get_extra_user_data_by_field(
2392
        $user_id,
2393
        $field_variable,
2394
        $prefix = false,
2395
        $all_visibility = true,
2396
        $splitmultiple = false
2397
    ) {
2398
        $user_id = (int) $user_id;
2399
2400
        if (empty($user_id)) {
2401
            return [];
2402
        }
2403
2404
        $extra_data = [];
2405
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2406
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2407
2408
        $sql = "SELECT f.id as id, f.variable as fvar, f.value_type as type
2409
                FROM $t_uf f
2410
                WHERE f.variable = '$field_variable' ";
2411
2412
        if (!$all_visibility) {
2413
            $sql .= " AND f.visible_to_self = 1 ";
2414
        }
2415
2416
        $sql .= " AND item_type = ".EntityExtraField::USER_FIELD_TYPE;
2417
        $sql .= " ORDER BY f.field_order ";
2418
2419
        $res = Database::query($sql);
2420
        if (Database::num_rows($res) > 0) {
2421
            while ($row = Database::fetch_array($res)) {
2422
                $sqlu = "SELECT field_value as fval FROM $t_ufv v
2423
                         INNER JOIN $t_uf f
2424
                         ON (v.field_id = f.id)
2425
                         WHERE
2426
                            item_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2427
                            field_id = ".$row['id']." AND
2428
                            item_id = ".$user_id;
2429
                $resu = Database::query($sqlu);
2430
                $fval = '';
2431
                if (Database::num_rows($resu) > 0) {
2432
                    $rowu = Database::fetch_array($resu);
2433
                    $fval = $rowu['fval'];
2434
                    if (self::USER_FIELD_TYPE_SELECT_MULTIPLE == $row['type']) {
2435
                        $fval = explode(';', $rowu['fval']);
2436
                    }
2437
                }
2438
                if ($prefix) {
2439
                    $extra_data['extra_'.$row['fvar']] = $fval;
2440
                } else {
2441
                    $extra_data[$row['fvar']] = $fval;
2442
                }
2443
            }
2444
        }
2445
2446
        return $extra_data;
2447
    }
2448
2449
    /**
2450
     * Get the extra field information for a certain field (the options as well).
2451
     *
2452
     * @param int $variable The name of the field we want to know everything about
2453
     *
2454
     * @return array Array containing all the information about the extra profile field
2455
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2456
     *               as returned by the database)
2457
     *
2458
     * @author Julio Montoya
2459
     *
2460
     * @since v1.8.6
2461
     */
2462
    public static function get_extra_field_information_by_name($variable)
2463
    {
2464
        $extraField = new ExtraField('user');
2465
2466
        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...
2467
    }
2468
2469
    /**
2470
     * Get the extra field information for user tag (the options as well).
2471
     *
2472
     * @param int $variable The name of the field we want to know everything about
2473
     *
2474
     * @return array Array containing all the information about the extra profile field
2475
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2476
     *               as returned by the database)
2477
     *
2478
     * @author José Loguercio
2479
     *
2480
     * @since v1.11.0
2481
     */
2482
    public static function get_extra_field_tags_information_by_name($variable)
2483
    {
2484
        $extraField = new ExtraField('user');
2485
2486
        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...
2487
    }
2488
2489
    /**
2490
     * Get all the extra field information of a certain field (also the options).
2491
     *
2492
     * @param int $fieldId the ID of the field we want to know everything of
2493
     *
2494
     * @return array $return containing all th information about the extra profile field
2495
     *
2496
     * @author Julio Montoya
2497
     *
2498
     * @deprecated
2499
     * @since v1.8.6
2500
     */
2501
    public static function get_extra_field_information($fieldId)
2502
    {
2503
        $extraField = new ExtraField('user');
2504
2505
        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...
2506
    }
2507
2508
    /**
2509
     * Get extra user data by value.
2510
     *
2511
     * @param string $variable       the internal variable name of the field
2512
     * @param string $value          the internal value of the field
2513
     * @param bool   $all_visibility
2514
     *
2515
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2516
     */
2517
    public static function get_extra_user_data_by_value($variable, $value, $all_visibility = true)
2518
    {
2519
        $extraFieldValue = new ExtraFieldValue('user');
2520
        $extraField = new ExtraField('user');
2521
2522
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2523
2524
        if (false === $info) {
2525
            return [];
2526
        }
2527
2528
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2529
            $variable,
2530
            $value,
2531
            false,
2532
            false,
2533
            true
2534
        );
2535
2536
        $result = [];
2537
        if (!empty($data)) {
2538
            foreach ($data as $item) {
2539
                $result[] = $item['item_id'];
2540
            }
2541
        }
2542
2543
        return $result;
2544
    }
2545
2546
    /**
2547
     * Get extra user data by tags value.
2548
     *
2549
     * @param int    $fieldId the ID of the field we want to know everything of
2550
     * @param string $tag     the tag name for search
2551
     *
2552
     * @return array with extra data info of a user
2553
     *
2554
     * @author José Loguercio
2555
     *
2556
     * @since v1.11.0
2557
     */
2558
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2559
    {
2560
        $extraField = new ExtraField('user');
2561
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2562
        $array = [];
2563
        foreach ($result as $index => $user) {
2564
            $array[] = $user['user_id'];
2565
        }
2566
2567
        return $array;
2568
    }
2569
2570
    /**
2571
     * Get extra user data by field variable.
2572
     *
2573
     * @param string $variable field variable
2574
     *
2575
     * @return array data
2576
     */
2577
    public static function get_extra_user_data_by_field_variable($variable)
2578
    {
2579
        $extraInfo = self::get_extra_field_information_by_name($variable);
2580
        $field_id = (int) $extraInfo['id'];
2581
2582
        $extraField = new ExtraFieldValue('user');
2583
        $data = $extraField->getValuesByFieldId($field_id);
2584
2585
        if (!empty($data)) {
2586
            foreach ($data as $row) {
2587
                $user_id = $row['item_id'];
2588
                $data[$user_id] = $row;
2589
            }
2590
        }
2591
2592
        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...
2593
    }
2594
2595
    /**
2596
     * Get extra user data tags by field variable.
2597
     *
2598
     * @param string $variable field variable
2599
     *
2600
     * @return array
2601
     */
2602
    public static function get_extra_user_data_for_tags($variable)
2603
    {
2604
        $data = self::get_extra_field_tags_information_by_name($variable);
2605
2606
        return $data;
2607
    }
2608
2609
    /**
2610
     * Gives a list of [session_category][session_id] for the current user.
2611
     *
2612
     * @param int  $user_id
2613
     * @param bool $is_time_over                 whether to fill the first element or not
2614
     *                                           (to give space for courses out of categories)
2615
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2616
     * @param bool $ignoreTimeLimit              ignore time start/end
2617
     * @param bool $getCount
2618
     *
2619
     * @return array list of statuses [session_category][session_id]
2620
     *
2621
     * @todo ensure multiple access urls are managed correctly
2622
     */
2623
    public static function get_sessions_by_category(
2624
        $user_id,
2625
        $is_time_over = true,
2626
        $ignore_visibility_for_admins = false,
2627
        $ignoreTimeLimit = false,
2628
        $getCount = false
2629
    ) {
2630
        $user_id = (int) $user_id;
2631
2632
        if (empty($user_id)) {
2633
            return [];
2634
        }
2635
2636
        // Get the list of sessions per user
2637
        $now = new DateTime('now', new DateTimeZone('UTC'));
2638
2639
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2640
        // join would not catch session-courses where the user is general
2641
        // session coach but which do not have students nor coaches registered
2642
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
2643
2644
        if (!$getCount) {
2645
            $dqlSelect = " DISTINCT
2646
                s.id,
2647
                s.title,
2648
                s.accessStartDate AS access_start_date,
2649
                s.accessEndDate AS access_end_date,
2650
                s.duration,
2651
                sc.id AS session_category_id,
2652
                sc.title AS session_category_title,
2653
                sc.dateStart AS session_category_date_start,
2654
                sc.dateEnd AS session_category_date_end,
2655
                s.coachAccessStartDate AS coach_access_start_date,
2656
                s.coachAccessEndDate AS coach_access_end_date,
2657
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
2658
                , s.position AS position
2659
            ";
2660
        }
2661
2662
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
2663
        // is awfully inefficient for large sets of data (1m25s for 58K
2664
        // sessions, BT#14115) but executing a similar query twice and grouping
2665
        // the results afterwards in PHP takes about 1/1000th of the time
2666
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
2667
        $dqlStudent = "SELECT $dqlSelect
2668
            FROM ChamiloCoreBundle:Session AS s
2669
            LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2670
            INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2671
            LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2672
            WHERE scu.user = :user AND url.url = :url ";
2673
        $dqlCoach = "SELECT $dqlSelect
2674
            FROM ChamiloCoreBundle:Session AS s
2675
            INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2676
            LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2677
            INNER JOIN ChamiloCoreBundle:SessionRelUser AS su WITH su.session = s
2678
            WHERE (su.user = :user AND su.relationType = ".SessionEntity::GENERAL_COACH.") AND url.url = :url ";
2679
2680
        // Default order
2681
        $order = 'ORDER BY sc.title, s.title';
2682
2683
        // Order by date if showing all sessions
2684
        $showAllSessions = ('true' === api_get_setting('session.show_all_sessions_on_my_course_page'));
2685
        if ($showAllSessions) {
2686
            $order = 'ORDER BY s.accessStartDate';
2687
        }
2688
2689
        // Order by position
2690
        if ('true' === api_get_setting('session.session_list_order')) {
2691
            $order = 'ORDER BY s.position';
2692
        }
2693
2694
        // Order by dates according to settings
2695
        $orderBySettings = api_get_setting('session.my_courses_session_order', true);
2696
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
2697
            $field = $orderBySettings['field'];
2698
            $orderSetting = $orderBySettings['order'];
2699
            switch ($field) {
2700
                case 'start_date':
2701
                    $order = " ORDER BY s.accessStartDate $orderSetting";
2702
                    break;
2703
                case 'end_date':
2704
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
2705
                    if ('asc' == $orderSetting) {
2706
                        // Put null values at the end
2707
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
2708
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
2709
                    }
2710
                    break;
2711
                case 'name':
2712
                case 'title':
2713
                    $order = " ORDER BY s.title $orderSetting ";
2714
                    break;
2715
            }
2716
        }
2717
2718
        $dqlStudent .= $order;
2719
        $dqlCoach .= $order;
2720
2721
        $accessUrlId = api_get_current_access_url_id();
2722
        $dqlStudent = Database::getManager()
2723
            ->createQuery($dqlStudent)
2724
            ->setParameters(
2725
                ['user' => $user_id, 'url' => $accessUrlId]
2726
            )
2727
        ;
2728
        $dqlCoach = Database::getManager()
2729
            ->createQuery($dqlCoach)
2730
            ->setParameters(
2731
                ['user' => $user_id, 'url' => $accessUrlId]
2732
            )
2733
        ;
2734
2735
        if ($getCount) {
2736
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
2737
        }
2738
2739
        $sessionDataStudent = $dqlStudent->getResult();
2740
        $sessionDataCoach = $dqlCoach->getResult();
2741
2742
        $sessionData = [];
2743
        // First fill $sessionData with student sessions
2744
        if (!empty($sessionDataStudent)) {
2745
            foreach ($sessionDataStudent as $row) {
2746
                $sessionData[$row['id']] = $row;
2747
            }
2748
        }
2749
        // Overwrite session data of the user as a student with session data
2750
        // of the user as a coach.
2751
        // There shouldn't be such duplicate rows, but just in case...
2752
        if (!empty($sessionDataCoach)) {
2753
            foreach ($sessionDataCoach as $row) {
2754
                $sessionData[$row['id']] = $row;
2755
            }
2756
        }
2757
2758
        $collapsable = ('true' === api_get_setting('session.allow_user_session_collapsable'));
2759
2760
2761
2762
        $extraField = new ExtraFieldValue('session');
2763
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
2764
2765
        if (empty($sessionData)) {
2766
            return [];
2767
        }
2768
        $categories = [];
2769
        foreach ($sessionData as $row) {
2770
            $session_id = $row['id'];
2771
            $coachList = SessionManager::getCoachesBySession($session_id);
2772
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
2773
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
2774
            $courseList = self::get_courses_list_by_session($user_id, $session_id);
2775
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
2776
2777
            // User portal filters:
2778
            if (false === $ignoreTimeLimit) {
2779
                if ($is_time_over) {
2780
                    // History
2781
                    if ($row['duration']) {
2782
                        if ($daysLeft >= 0) {
2783
                            continue;
2784
                        }
2785
                    } else {
2786
                        if (empty($row['access_end_date'])) {
2787
                            continue;
2788
                        } else {
2789
                            if ($row['access_end_date'] > $now) {
2790
                                continue;
2791
                            }
2792
                        }
2793
                    }
2794
                } else {
2795
                    // Current user portal
2796
                    $isGeneralCoach = api_get_session_entity($row['id'])->hasUserAsGeneralCoach(api_get_user_entity($user_id));
2797
                    $isCoachOfCourse = in_array($user_id, $coachList);
2798
2799
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
2800
                        // Teachers can access the session depending in the access_coach date
2801
                    } else {
2802
                        if ($row['duration']) {
2803
                            if ($daysLeft <= 0) {
2804
                                continue;
2805
                            }
2806
                        } else {
2807
                            if (isset($row['access_end_date']) &&
2808
                                !empty($row['access_end_date'])
2809
                            ) {
2810
                                if ($row['access_end_date'] <= $now) {
2811
                                    continue;
2812
                                }
2813
                            }
2814
                        }
2815
                    }
2816
                }
2817
            }
2818
2819
            $categories[$row['session_category_id']]['session_category'] = [
2820
                'id' => $row['session_category_id'],
2821
                'name' => $row['session_category_title'],
2822
                'date_start' => $categoryStart,
2823
                'date_end' => $categoryEnd,
2824
            ];
2825
2826
            $visibility = api_get_session_visibility(
0 ignored issues
show
Deprecated Code introduced by
The function api_get_session_visibility() has been deprecated: Use Session::setAccessVisibilityByUser() instead. ( Ignorable by Annotation )

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

2826
            $visibility = /** @scrutinizer ignore-deprecated */ api_get_session_visibility(

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...
2827
                $session_id,
2828
                null,
2829
                $ignore_visibility_for_admins
2830
            );
2831
2832
            if (SESSION_VISIBLE != $visibility) {
2833
                // Course Coach session visibility.
2834
                $blockedCourseCount = 0;
2835
                $closedVisibilityList = [
2836
                    COURSE_VISIBILITY_CLOSED,
2837
                    COURSE_VISIBILITY_HIDDEN,
2838
                ];
2839
2840
                foreach ($courseList as $course) {
2841
                    // Checking session visibility
2842
                    $sessionCourseVisibility = api_get_session_visibility(
0 ignored issues
show
Deprecated Code introduced by
The function api_get_session_visibility() has been deprecated: Use Session::setAccessVisibilityByUser() instead. ( Ignorable by Annotation )

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

2842
                    $sessionCourseVisibility = /** @scrutinizer ignore-deprecated */ api_get_session_visibility(

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...
2843
                        $session_id,
2844
                        $course['real_id'],
2845
                        $ignore_visibility_for_admins
2846
                    );
2847
2848
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
2849
                    if (false === $courseIsVisible || SESSION_INVISIBLE == $sessionCourseVisibility) {
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

2849
                    if (false === $courseIsVisible || /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $sessionCourseVisibility) {
Loading history...
2850
                        $blockedCourseCount++;
2851
                    }
2852
                }
2853
2854
                // If all courses are blocked then no show in the list.
2855
                if ($blockedCourseCount === count($courseList)) {
2856
                    $visibility = SESSION_INVISIBLE;
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

2856
                    $visibility = /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE;
Loading history...
2857
                } else {
2858
                    $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...
2859
                }
2860
            }
2861
2862
            switch ($visibility) {
2863
                case SESSION_VISIBLE_READ_ONLY:
2864
                case SESSION_VISIBLE:
2865
                case SESSION_AVAILABLE:
2866
                    break;
2867
                case SESSION_INVISIBLE:
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

2867
                case /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE:
Loading history...
2868
                    if (false === $ignore_visibility_for_admins) {
2869
                        continue 2;
2870
                    }
2871
            }
2872
2873
            $collapsed = '';
2874
            $collapsedAction = '';
2875
            if ($collapsable) {
2876
                $collapsableData = SessionManager::getCollapsableData(
2877
                    $user_id,
2878
                    $session_id,
2879
                    $extraField,
2880
                    $collapsableLink
2881
                );
2882
                $collapsed = $collapsableData['collapsed'];
2883
                $collapsedAction = $collapsableData['collapsable_link'];
2884
            }
2885
2886
            $categories[$row['session_category_id']]['sessions'][] = [
2887
                'session_name' => $row['title'],
2888
                'session_id' => $row['id'],
2889
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
2890
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
2891
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
2892
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
2893
                'courses' => $courseList,
2894
                'collapsed' => $collapsed,
2895
                'collapsable_link' => $collapsedAction,
2896
                'duration' => $row['duration'],
2897
            ];
2898
        }
2899
2900
        return $categories;
2901
    }
2902
2903
    /**
2904
     * Gives a list of [session_id-course_code] => [status] for the current user.
2905
     *
2906
     * @param  int  $user_id
2907
     * @param  int  $sessionLimit
2908
     *
2909
     * @return array list of statuses (session_id-course_code => status)
2910
     *
2911
     * @throws Exception
2912
     */
2913
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
2914
    {
2915
        // Database Table Definitions
2916
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2917
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2918
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2919
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
2920
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2921
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2922
2923
        $user_id = (int) $user_id;
2924
2925
        if (empty($user_id)) {
2926
            return [];
2927
        }
2928
2929
        $sessionRepo = Container::getSessionRepository();
2930
2931
        $user = api_get_user_entity($user_id);
2932
        $url = null;
2933
        $formattedUserName = Container::$container->get(NameConventionHelper::class)->getPersonName($user);
2934
2935
        // We filter the courses from the URL
2936
        $join_access_url = $where_access_url = '';
2937
        if (api_get_multiple_access_url()) {
2938
            $access_url_id = api_get_current_access_url_id();
2939
            if (-1 != $access_url_id) {
2940
                $url = api_get_url_entity($access_url_id);
2941
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2942
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
2943
                $where_access_url = " AND access_url_id = $access_url_id ";
2944
            }
2945
        }
2946
2947
        // Courses in which we subscribed out of any session
2948
2949
        $sql = "SELECT
2950
                    course.code,
2951
                    course_rel_user.status course_rel_status,
2952
                    course_rel_user.sort sort,
2953
                    course_rel_user.user_course_cat user_course_cat
2954
                 FROM $tbl_course_user course_rel_user
2955
                 LEFT JOIN $tbl_course course
2956
                 ON course.id = course_rel_user.c_id
2957
                 $join_access_url
2958
                 WHERE
2959
                    course_rel_user.user_id = '".$user_id."' AND
2960
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
2961
                    $where_access_url
2962
                 ORDER BY course_rel_user.sort, course.title ASC";
2963
2964
        $course_list_sql_result = Database::query($sql);
2965
        $personal_course_list = [];
2966
        if (Database::num_rows($course_list_sql_result) > 0) {
2967
            while ($result_row = Database::fetch_assoc($course_list_sql_result)) {
2968
                $course_info = api_get_course_info($result_row['code']);
2969
                $result_row['course_info'] = $course_info;
2970
                $personal_course_list[] = $result_row;
2971
            }
2972
        }
2973
2974
        $coachCourseConditions = '';
2975
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
2976
        if (api_is_allowed_to_create_course()) {
2977
            $sessionListFromCourseCoach = [];
2978
            $sql = " SELECT DISTINCT session_id
2979
                    FROM $tbl_session_course_user
2980
                    WHERE user_id = $user_id AND status = ".SessionEntity::COURSE_COACH;
2981
2982
            $result = Database::query($sql);
2983
            if (Database::num_rows($result)) {
2984
                $result = Database::store_result($result);
2985
                foreach ($result as $session) {
2986
                    $sessionListFromCourseCoach[] = $session['session_id'];
2987
                }
2988
            }
2989
            if (!empty($sessionListFromCourseCoach)) {
2990
                $condition = implode("','", $sessionListFromCourseCoach);
2991
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
2992
            }
2993
        }
2994
2995
        // Get the list of sessions where the user is subscribed
2996
        // This is divided into two different queries
2997
        $sessions = [];
2998
        $sessionLimitRestriction = '';
2999
        if (!empty($sessionLimit)) {
3000
            $sessionLimit = (int) $sessionLimit;
3001
            $sessionLimitRestriction = "LIMIT $sessionLimit";
3002
        }
3003
3004
        $sql = "SELECT DISTINCT s.id, s.title, access_start_date, access_end_date
3005
                FROM $tbl_session_user su INNER JOIN $tbl_session s
3006
                ON (s.id = su.session_id)
3007
                WHERE (
3008
                    su.user_id = $user_id AND
3009
                    su.relation_type = ".SessionEntity::STUDENT."
3010
                )
3011
                $coachCourseConditions
3012
                ORDER BY access_start_date, access_end_date, s.title
3013
                $sessionLimitRestriction
3014
        ";
3015
3016
        $result = Database::query($sql);
3017
        if (Database::num_rows($result) > 0) {
3018
            while ($row = Database::fetch_assoc($result)) {
3019
                $sessions[$row['id']] = $row;
3020
            }
3021
        }
3022
3023
        $sql = "SELECT DISTINCT
3024
                s.id, s.title, s.access_start_date, s.access_end_date
3025
                FROM $tbl_session s
3026
                INNER JOIN $tbl_session_user sru ON sru.session_id = s.id
3027
                WHERE (
3028
                    sru.user_id = $user_id AND sru.relation_type = ".SessionEntity::GENERAL_COACH."
3029
                )
3030
                $coachCourseConditions
3031
                ORDER BY s.access_start_date, s.access_end_date, s.title";
3032
3033
        $result = Database::query($sql);
3034
        if (Database::num_rows($result) > 0) {
3035
            while ($row = Database::fetch_assoc($result)) {
3036
                if (empty($sessions[$row['id']])) {
3037
                    $sessions[$row['id']] = $row;
3038
                }
3039
            }
3040
        }
3041
3042
        if (api_is_allowed_to_create_course()) {
3043
            foreach ($sessions as $enreg) {
3044
                $session_id = $enreg['id'];
3045
                $session_visibility = api_get_session_visibility($session_id);
0 ignored issues
show
Deprecated Code introduced by
The function api_get_session_visibility() has been deprecated: Use Session::setAccessVisibilityByUser() instead. ( Ignorable by Annotation )

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

3045
                $session_visibility = /** @scrutinizer ignore-deprecated */ api_get_session_visibility($session_id);

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...
3046
                $session = api_get_session_entity($session_id);
3047
3048
                if (SESSION_INVISIBLE == $session_visibility) {
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

3048
                if (/** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $session_visibility) {
Loading history...
3049
                    continue;
3050
                }
3051
3052
                $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
3053
                    $user,
3054
                    $session,
3055
                    SessionEntity::GENERAL_COACH,
3056
                    $url
3057
                );
3058
                $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
3059
                    $user,
3060
                    $session,
3061
                    SessionEntity::COURSE_COACH,
3062
                    $url
3063
                );
3064
3065
                // This query is horribly slow when more than a few thousand
3066
                // users and just a few sessions to which they are subscribed
3067
                $coursesInSession = array_map(
3068
                    function (SessionRelCourse $courseInSession) {
3069
                        $course = $courseInSession->getCourse();
3070
3071
                        return [
3072
                            'code' => $course->getCode(),
3073
                            'i' => $course->getTitle(),
3074
                            'l' => $course->getCourseLanguage(),
3075
                            'sort' => 1,
3076
                        ];
3077
                    },
3078
                    array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
3079
                );
3080
3081
                foreach ($coursesInSession as $result_row) {
3082
                    $result_row['t'] = $formattedUserName;
3083
                    $result_row['email'] = $user->getEmail();
3084
                    $result_row['access_start_date'] = $session->getAccessStartDate()?->format('Y-m-d H:i:s');
3085
                    $result_row['access_end_date'] = $session->getAccessEndDate()?->format('Y-m-d H:i:s');
3086
                    $result_row['session_id'] = $session->getId();
3087
                    $result_row['session_name'] = $session->getTitle();
3088
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3089
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3090
                    $personal_course_list[$key] = $result_row;
3091
                }
3092
            }
3093
        }
3094
3095
        foreach ($sessions as $enreg) {
3096
            $session_id = $enreg['id'];
3097
            $session_visibility = api_get_session_visibility($session_id);
0 ignored issues
show
Deprecated Code introduced by
The function api_get_session_visibility() has been deprecated: Use Session::setAccessVisibilityByUser() instead. ( Ignorable by Annotation )

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

3097
            $session_visibility = /** @scrutinizer ignore-deprecated */ api_get_session_visibility($session_id);

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...
3098
            if (SESSION_INVISIBLE == $session_visibility) {
0 ignored issues
show
introduced by
The constant SESSION_INVISIBLE has been deprecated: Use Session::INVISIBLE ( Ignorable by Annotation )

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

3098
            if (/** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $session_visibility) {
Loading history...
3099
                continue;
3100
            }
3101
3102
            /* This query is very similar to the above query,
3103
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3104
            $sql = "SELECT DISTINCT
3105
                course.code code,
3106
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
3107
                email,
3108
                course.course_language l,
3109
                1 sort,
3110
                access_start_date,
3111
                access_end_date,
3112
                session.id as session_id,
3113
                session.title as session_name,
3114
                IF((session_course_user.user_id = 3 AND session_course_user.status = ".SessionEntity::COURSE_COACH."),'2', '5')
3115
            FROM $tbl_session_course_user as session_course_user
3116
            INNER JOIN $tbl_course AS course
3117
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
3118
            INNER JOIN $tbl_session as session
3119
            ON session_course_user.session_id = session.id
3120
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
3121
            WHERE session_course_user.user_id = $user_id
3122
            ORDER BY i";
3123
3124
            $course_list_sql_result = Database::query($sql);
3125
            while ($result_row = Database::fetch_assoc($course_list_sql_result)) {
3126
                $result_row['course_info'] = api_get_course_info($result_row['code']);
3127
                $key = $result_row['session_id'].' - '.$result_row['code'];
3128
                if (!isset($personal_course_list[$key])) {
3129
                    $personal_course_list[$key] = $result_row;
3130
                }
3131
            }
3132
        }
3133
3134
        return $personal_course_list;
3135
    }
3136
3137
    /**
3138
     * Gives a list of courses for the given user in the given session.
3139
     *
3140
     * @param int $user_id
3141
     * @param int $session_id
3142
     *
3143
     * @return array list of statuses (session_id-course_code => status)
3144
     */
3145
    public static function get_courses_list_by_session($user_id, $session_id)
3146
    {
3147
        // Database Table Definitions
3148
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
3149
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3150
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3151
3152
        $user_id = (int) $user_id;
3153
        $session_id = (int) $session_id;
3154
3155
        $sessionRepo = Container::getSessionRepository();
3156
3157
        $user = api_get_user_entity($user_id);
3158
        $session = api_get_session_entity($session_id);
3159
        $url = null;
3160
3161
        // We filter the courses from the URL
3162
        $join_access_url = $where_access_url = '';
3163
        if (api_get_multiple_access_url()) {
3164
            $urlId = api_get_current_access_url_id();
3165
            if (-1 != $urlId) {
3166
                $url = api_get_url_entity($urlId);
3167
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3168
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
3169
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
3170
            }
3171
        }
3172
3173
        /* This query is very similar to the query below, but it will check the
3174
        session_rel_course_user table if there are courses registered
3175
        to our user or not */
3176
        $sql = "SELECT DISTINCT
3177
                    c.title,
3178
                    c.visibility,
3179
                    c.id as real_id,
3180
                    c.code as course_code,
3181
                    sc.position,
3182
                    c.unsubscribe
3183
                FROM $tbl_session_course_user as scu
3184
                INNER JOIN $tbl_session_course sc
3185
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3186
                INNER JOIN $tableCourse as c
3187
                ON (scu.c_id = c.id)
3188
                $join_access_url
3189
                WHERE
3190
                    scu.user_id = $user_id AND
3191
                    scu.session_id = $session_id
3192
                    $where_access_url
3193
                ORDER BY sc.position ASC";
3194
3195
        $myCourseList = [];
3196
        $courses = [];
3197
        $result = Database::query($sql);
3198
        if (Database::num_rows($result) > 0) {
3199
            while ($result_row = Database::fetch_assoc($result)) {
3200
                $result_row['status'] = 5;
3201
                if (!in_array($result_row['real_id'], $courses)) {
3202
                    $position = $result_row['position'];
3203
                    if (!isset($myCourseList[$position])) {
3204
                        $myCourseList[$position] = $result_row;
3205
                    } else {
3206
                        $myCourseList[] = $result_row;
3207
                    }
3208
                    $courses[] = $result_row['real_id'];
3209
                }
3210
            }
3211
        }
3212
3213
        if (api_is_allowed_to_create_course()) {
3214
            $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
3215
                $user,
3216
                $session,
3217
                SessionEntity::GENERAL_COACH,
3218
                $url
3219
            );
3220
            $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
3221
                $user,
3222
                $session,
3223
                SessionEntity::COURSE_COACH,
3224
                $url
3225
            );
3226
3227
            $coursesInSession = array_map(
3228
                function (SessionRelCourse $courseInSession) {
3229
                    $course = $courseInSession->getCourse();
3230
3231
                    return [
3232
                        'title' => $course->getTitle(),
3233
                        'visibility' => $course->getVisibility(),
3234
                        'real_id' => $course->getId(),
3235
                        'course_code' => $course->getCode(),
3236
                        'position' => $courseInSession->getPosition(),
3237
                        'unsubscribe' => $course->getUnsubscribe(),
3238
                    ];
3239
                },
3240
                array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
3241
            );
3242
3243
            foreach ($coursesInSession as $result_row) {
3244
                $result_row['status'] = 2;
3245
                if (!in_array($result_row['real_id'], $courses)) {
3246
                    $position = $result_row['position'];
3247
                    if (!isset($myCourseList[$position])) {
3248
                        $myCourseList[$position] = $result_row;
3249
                    } else {
3250
                        $myCourseList[] = $result_row;
3251
                    }
3252
                    $courses[] = $result_row['real_id'];
3253
                }
3254
            }
3255
        }
3256
3257
        if (api_is_drh()) {
3258
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3259
            $sessionList = array_keys($sessionList);
3260
            if (in_array($session_id, $sessionList)) {
3261
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3262
                if (!empty($courseList)) {
3263
                    foreach ($courseList as $course) {
3264
                        if (!in_array($course['id'], $courses)) {
3265
                            $position = $course['position'];
3266
                            if (!isset($myCourseList[$position])) {
3267
                                $myCourseList[$position] = $course;
3268
                            } else {
3269
                                $myCourseList[] = $course;
3270
                            }
3271
                        }
3272
                    }
3273
                }
3274
            }
3275
        } else {
3276
            //check if user is general coach for this session
3277
            if ($session && $session->hasUserAsGeneralCoach($user)) {
3278
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3279
                if (!empty($courseList)) {
3280
                    foreach ($courseList as $course) {
3281
                        if (!in_array($course['id'], $courses)) {
3282
                            $position = $course['position'];
3283
                            if (!isset($myCourseList[$position])) {
3284
                                $myCourseList[$position] = $course;
3285
                            } else {
3286
                                $myCourseList[] = $course;
3287
                            }
3288
                        }
3289
                    }
3290
                }
3291
            }
3292
        }
3293
3294
        if (!empty($myCourseList)) {
3295
            ksort($myCourseList);
3296
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
3297
            if (empty($checkPosition)) {
3298
                // The session course list doesn't have any position,
3299
                // then order the course list by course code
3300
                $list = array_column($myCourseList, 'course_code');
3301
                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

3301
                array_multisort($myCourseList, /** @scrutinizer ignore-type */ SORT_ASC, $list);
Loading history...
3302
            }
3303
        }
3304
3305
        return $myCourseList;
3306
    }
3307
3308
    /**
3309
     * Get user id from a username.
3310
     *
3311
     * @param string $username
3312
     *
3313
     * @return int User ID (or false if not found)
3314
     */
3315
    public static function get_user_id_from_username($username)
3316
    {
3317
        if (empty($username)) {
3318
            return false;
3319
        }
3320
        $username = trim($username);
3321
        $username = Database::escape_string($username);
3322
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3323
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3324
        $res = Database::query($sql);
3325
3326
        if (false === $res) {
3327
            return false;
3328
        }
3329
        if (1 !== Database::num_rows($res)) {
3330
            return false;
3331
        }
3332
        $row = Database::fetch_array($res);
3333
3334
        return $row['id'];
3335
    }
3336
3337
    /**
3338
     * Get the users files upload from his share_folder.
3339
     *
3340
     * @param string $user_id      User ID
3341
     * @param string $course       course directory
3342
     * @param string $resourceType resource type: images, all
3343
     *
3344
     * @return string
3345
     */
3346
    /*public static function get_user_upload_files_by_course(
3347
        $user_id,
3348
        $course,
3349
        $resourceType = 'all'
3350
    ) {
3351
        $return = '';
3352
        $user_id = (int) $user_id;
3353
3354
        if (!empty($user_id) && !empty($course)) {
3355
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3356
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3357
            $file_list = [];
3358
3359
            if (is_dir($path)) {
3360
                $handle = opendir($path);
3361
                while ($file = readdir($handle)) {
3362
                    if ('.' == $file || '..' == $file || '.htaccess' == $file || is_dir($path.$file)) {
3363
                        continue; // skip current/parent directory and .htaccess
3364
                    }
3365
                    $file_list[] = $file;
3366
                }
3367
                if (count($file_list) > 0) {
3368
                    $return = "<h4>$course</h4>";
3369
                    $return .= '<ul class="thumbnails">';
3370
                }
3371
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
3372
                foreach ($file_list as $file) {
3373
                    if ('all' == $resourceType) {
3374
                        $return .= '<li>
3375
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
3376
                    } elseif ('images' == $resourceType) {
3377
                        //get extension
3378
                        $ext = explode('.', $file);
3379
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
3380
                            $return .= '<li class="span2">
3381
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
3382
                                                <img src="'.$web_path.urlencode($file).'" >
3383
                                            </a>
3384
                                        </li>';
3385
                        }
3386
                    }
3387
                }
3388
                if (count($file_list) > 0) {
3389
                    $return .= '</ul>';
3390
                }
3391
            }
3392
        }
3393
3394
        return $return;
3395
    }*/
3396
3397
    /**
3398
     * Gets the API key (or keys) and return them into an array.
3399
     *
3400
     * @param int     Optional user id (defaults to the result of api_get_user_id())
3401
     * @param string $api_service
3402
     *
3403
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
3404
     */
3405
    public static function get_api_keys($user_id = null, $api_service = 'default')
3406
    {
3407
        if ($user_id != strval(intval($user_id))) {
3408
            return false;
3409
        }
3410
        if (empty($user_id)) {
3411
            $user_id = api_get_user_id();
3412
        }
3413
        if (false === $user_id) {
3414
            return false;
3415
        }
3416
        $service_name = Database::escape_string($api_service);
3417
        if (false === is_string($service_name)) {
3418
            return false;
3419
        }
3420
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3421
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
3422
        $res = Database::query($sql);
3423
        if (false === $res) {
3424
            return false;
3425
        } //error during query
3426
        $num = Database::num_rows($res);
3427
        if (0 == $num) {
3428
            return false;
3429
        }
3430
        $list = [];
3431
        while ($row = Database::fetch_array($res)) {
3432
            $list[$row['id']] = $row['api_key'];
3433
        }
3434
3435
        return $list;
3436
    }
3437
3438
    /**
3439
     * Adds a new API key to the users' account.
3440
     *
3441
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
3442
     * @param string $api_service
3443
     *
3444
     * @return bool True on success, false on failure
3445
     */
3446
    public static function add_api_key($user_id = null, $api_service = 'default')
3447
    {
3448
        if ($user_id != strval(intval($user_id))) {
3449
            return false;
3450
        }
3451
        if (empty($user_id)) {
3452
            $user_id = api_get_user_id();
3453
        }
3454
        if (false === $user_id) {
3455
            return false;
3456
        }
3457
        $service_name = Database::escape_string($api_service);
3458
        if (false === is_string($service_name)) {
3459
            return false;
3460
        }
3461
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3462
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
3463
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
3464
        $res = Database::query($sql);
3465
        if (false === $res) {
3466
            return false;
3467
        } //error during query
3468
        $num = Database::insert_id();
3469
3470
        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...
3471
    }
3472
3473
    /**
3474
     * Deletes an API key from the user's account.
3475
     *
3476
     * @param   int     API key's internal ID
3477
     *
3478
     * @return bool True on success, false on failure
3479
     */
3480
    public static function delete_api_key($key_id)
3481
    {
3482
        if ($key_id != strval(intval($key_id))) {
3483
            return false;
3484
        }
3485
        if (false === $key_id) {
3486
            return false;
3487
        }
3488
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3489
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
3490
        $res = Database::query($sql);
3491
        if (false === $res) {
3492
            return false;
3493
        } //error during query
3494
        $num = Database::num_rows($res);
3495
        if (1 !== $num) {
3496
            return false;
3497
        }
3498
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
3499
        $res = Database::query($sql);
3500
        if (false === $res) {
3501
            return false;
3502
        } //error during query
3503
3504
        return true;
3505
    }
3506
3507
    /**
3508
     * Regenerate an API key from the user's account.
3509
     *
3510
     * @param   int     user ID (defaults to the results of api_get_user_id())
3511
     * @param   string  API key's internal ID
3512
     *
3513
     * @return int num
3514
     */
3515
    public static function update_api_key($user_id, $api_service)
3516
    {
3517
        if ($user_id != strval(intval($user_id))) {
3518
            return false;
3519
        }
3520
        if (false === $user_id) {
3521
            return false;
3522
        }
3523
        $service_name = Database::escape_string($api_service);
3524
        if (false === is_string($service_name)) {
3525
            return false;
3526
        }
3527
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3528
        $sql = "SELECT id FROM $t_api
3529
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3530
        $res = Database::query($sql);
3531
        $num = Database::num_rows($res);
3532
        if (1 == $num) {
3533
            $id_key = Database::fetch_assoc($res);
3534
            self::delete_api_key($id_key['id']);
3535
            $num = self::add_api_key($user_id, $api_service);
3536
        } elseif (0 == $num) {
3537
            $num = self::add_api_key($user_id, $api_service);
3538
        }
3539
3540
        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...
3541
    }
3542
3543
    /**
3544
     * @param   int     user ID (defaults to the results of api_get_user_id())
3545
     * @param   string    API key's internal ID
3546
     *
3547
     * @return int row ID, or return false if not found
3548
     */
3549
    public static function get_api_key_id($user_id, $api_service)
3550
    {
3551
        if ($user_id != strval(intval($user_id))) {
3552
            return false;
3553
        }
3554
        if (false === $user_id) {
3555
            return false;
3556
        }
3557
        if (empty($api_service)) {
3558
            return false;
3559
        }
3560
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3561
        $api_service = Database::escape_string($api_service);
3562
        $sql = "SELECT id FROM $t_api
3563
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3564
        $res = Database::query($sql);
3565
        if (Database::num_rows($res) < 1) {
3566
            return false;
3567
        }
3568
        $row = Database::fetch_assoc($res);
3569
3570
        return $row['id'];
3571
    }
3572
3573
    /**
3574
     * Checks if a user_id is platform admin.
3575
     *
3576
     * @param   int user ID
3577
     *
3578
     * @return bool True if is admin, false otherwise
3579
     *
3580
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
3581
     */
3582
    public static function is_admin($user_id)
3583
    {
3584
        $user_id = (int) $user_id;
3585
        if (empty($user_id)) {
3586
            return false;
3587
        }
3588
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3589
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3590
        $res = Database::query($sql);
3591
3592
        return 1 === Database::num_rows($res);
3593
    }
3594
3595
    /**
3596
     * Get the total count of users.
3597
     *
3598
     * @param ?int $status Status of users to be counted
3599
     * @param ?int $access_url_id Access URL ID (optional)
3600
     * @param ?int $active
3601
     *
3602
     * @return mixed Number of users or false on error
3603
     * @throws \Doctrine\DBAL\Exception
3604
     */
3605
    public static function get_number_of_users(
3606
        ?int $status = 0,
3607
        ?int $access_url_id = 1,
3608
        ?int $active = null,
3609
        ?string $dateFrom = null,
3610
        ?string $dateUntil = null
3611
    ): mixed {
3612
        $tableUser = Database::get_main_table(TABLE_MAIN_USER);
3613
        $tableAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3614
3615
        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 AccessUrlUtil::isMultiple ( Ignorable by Annotation )

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

3615
        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...
3616
            $sql = "SELECT count(u.id)
3617
                    FROM $tableUser u
3618
                    INNER JOIN $tableAccessUrlRelUser url_user
3619
                    ON (u.id = url_user.user_id)
3620
                    WHERE url_user.access_url_id = $access_url_id
3621
            ";
3622
        } else {
3623
            $sql = "SELECT count(u.id)
3624
                    FROM $tableUser u
3625
                    WHERE 1 = 1 ";
3626
        }
3627
3628
        $status = (int) $status;
3629
        if (!empty($status) && $status > 0) {
3630
            $sql .= " AND u.status = $status ";
3631
        }
3632
3633
        if (isset($active)) {
3634
            $active = (int) $active;
3635
            $sql .= " AND u.active = $active ";
3636
        }
3637
3638
        if (!empty($dateFrom)) {
3639
            $dateFrom = api_get_utc_datetime("$dateFrom 00:00:00");
3640
            $sql .= " AND u.created_at >= '$dateFrom' ";
3641
        }
3642
        if (!empty($dateUntil)) {
3643
            $dateUntil = api_get_utc_datetime("$dateUntil 23:59:59");
3644
            $sql .= " AND u.created_at <= '$dateUntil' ";
3645
        }
3646
3647
        $res = Database::query($sql);
3648
        if (1 === Database::num_rows($res)) {
3649
            return (int) Database::result($res, 0, 0);
3650
        }
3651
3652
        return false;
3653
    }
3654
3655
    /**
3656
     * Gets the tags of a specific field_id
3657
     * USER TAGS.
3658
     *
3659
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
3660
     *
3661
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
3662
     *    Called it "books" for example.
3663
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
3664
     * 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
3665
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
3666
     * 5. Test and enjoy.
3667
     *
3668
     * @param string $tag
3669
     * @param int    $field_id      field_id
3670
     * @param string $return_format how we are going to result value in array or in a string (json)
3671
     * @param $limit
3672
     *
3673
     * @return mixed
3674
     */
3675
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
3676
    {
3677
        // database table definition
3678
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3679
        $field_id = (int) $field_id;
3680
        $limit = (int) $limit;
3681
        $tag = trim(Database::escape_string($tag));
3682
3683
        // all the information of the field
3684
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
3685
                WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag LIMIT $limit";
3686
        $result = Database::query($sql);
3687
        $return = [];
3688
        if (Database::num_rows($result) > 0) {
3689
            while ($row = Database::fetch_assoc($result)) {
3690
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
3691
            }
3692
        }
3693
        if ('json' === $return_format) {
3694
            $return = json_encode($return);
3695
        }
3696
3697
        return $return;
3698
    }
3699
3700
    /**
3701
     * @param int $field_id
3702
     * @param int $limit
3703
     *
3704
     * @return array
3705
     */
3706
    public static function get_top_tags($field_id, $limit = 100)
3707
    {
3708
        // database table definition
3709
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3710
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3711
        $field_id = (int) $field_id;
3712
        $limit = (int) $limit;
3713
        // all the information of the field
3714
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
3715
                INNER JOIN $table_user_tag ut
3716
                ON (ut.id = uv.tag_id)
3717
                WHERE field_id = $field_id
3718
                GROUP BY tag_id
3719
                ORDER BY count DESC
3720
                LIMIT $limit";
3721
        $result = Database::query($sql);
3722
        $return = [];
3723
        if (Database::num_rows($result) > 0) {
3724
            while ($row = Database::fetch_assoc($result)) {
3725
                $return[] = $row;
3726
            }
3727
        }
3728
3729
        return $return;
3730
    }
3731
3732
    /**
3733
     * Get user's tags.
3734
     *
3735
     * @param int $user_id
3736
     * @param int $field_id
3737
     *
3738
     * @return array
3739
     */
3740
    public static function get_user_tags($user_id, $field_id)
3741
    {
3742
        // database table definition
3743
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3744
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3745
        $field_id = (int) $field_id;
3746
        $user_id = (int) $user_id;
3747
3748
        // all the information of the field
3749
        $sql = "SELECT ut.id, tag, count
3750
                FROM $table_user_tag ut
3751
                INNER JOIN $table_user_tag_values uv
3752
                ON (uv.tag_id=ut.ID)
3753
                WHERE field_id = $field_id AND user_id = $user_id
3754
                ORDER BY tag";
3755
        $result = Database::query($sql);
3756
        $return = [];
3757
        if (Database::num_rows($result) > 0) {
3758
            while ($row = Database::fetch_assoc($result)) {
3759
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3760
            }
3761
        }
3762
3763
        return $return;
3764
    }
3765
3766
    /**
3767
     * Get user's tags.
3768
     *
3769
     * @param int  $user_id
3770
     * @param int  $field_id
3771
     * @param bool $show_links show links or not
3772
     *
3773
     * @return string
3774
     */
3775
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
3776
    {
3777
        // database table definition
3778
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3779
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3780
        $field_id = (int) $field_id;
3781
        $user_id = (int) $user_id;
3782
3783
        // all the information of the field
3784
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
3785
                INNER JOIN $table_user_tag_values uv
3786
                ON (uv.tag_id = ut.id)
3787
                WHERE field_id = $field_id AND user_id = $user_id
3788
                ORDER BY tag";
3789
3790
        $result = Database::query($sql);
3791
        $return = [];
3792
        if (Database::num_rows($result) > 0) {
3793
            while ($row = Database::fetch_assoc($result)) {
3794
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3795
            }
3796
        }
3797
        $user_tags = $return;
3798
        $tag_tmp = [];
3799
        foreach ($user_tags as $tag) {
3800
            if ($show_links) {
3801
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
3802
                    $tag['tag'].
3803
                '</a>';
3804
            } else {
3805
                $tag_tmp[] = $tag['tag'];
3806
            }
3807
        }
3808
3809
        if (is_array($user_tags) && count($user_tags) > 0) {
3810
            return implode(', ', $tag_tmp);
3811
        } else {
3812
            return '';
3813
        }
3814
    }
3815
3816
    /**
3817
     * Get the tag id.
3818
     *
3819
     * @param int $tag
3820
     * @param int $field_id
3821
     *
3822
     * @return int returns 0 if fails otherwise the tag id
3823
     */
3824
    public static function get_tag_id($tag, $field_id)
3825
    {
3826
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3827
        $tag = Database::escape_string($tag);
3828
        $field_id = (int) $field_id;
3829
        //with COLLATE latin1_bin to select query in a case sensitive mode
3830
        $sql = "SELECT id FROM $table_user_tag
3831
                WHERE tag LIKE '$tag' AND field_id = $field_id";
3832
        $result = Database::query($sql);
3833
        if (Database::num_rows($result) > 0) {
3834
            $row = Database::fetch_assoc($result);
3835
3836
            return $row['id'];
3837
        } else {
3838
            return 0;
3839
        }
3840
    }
3841
3842
    /**
3843
     * Get the tag id.
3844
     *
3845
     * @param int $tag_id
3846
     * @param int $field_id
3847
     *
3848
     * @return int 0 if fails otherwise the tag id
3849
     */
3850
    public static function get_tag_id_from_id($tag_id, $field_id)
3851
    {
3852
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3853
        $tag_id = (int) $tag_id;
3854
        $field_id = (int) $field_id;
3855
        $sql = "SELECT id FROM $table_user_tag
3856
                WHERE id = '$tag_id' AND field_id = $field_id";
3857
        $result = Database::query($sql);
3858
        if (Database::num_rows($result) > 0) {
3859
            $row = Database::fetch_assoc($result);
3860
3861
            return $row['id'];
3862
        } else {
3863
            return false;
3864
        }
3865
    }
3866
3867
    /**
3868
     * Adds a user-tag value.
3869
     *
3870
     * @param mixed $tag
3871
     * @param int   $user_id
3872
     * @param int   $field_id field id of the tag
3873
     *
3874
     * @return bool True if the tag was inserted or updated. False otherwise.
3875
     *              The return value doesn't take into account *values* added to the tag.
3876
     *              Only the creation/update of the tag field itself.
3877
     */
3878
    public static function add_tag($tag, $user_id, $field_id)
3879
    {
3880
        // database table definition
3881
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3882
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3883
        $tag = trim(Database::escape_string($tag));
3884
        $user_id = (int) $user_id;
3885
        $field_id = (int) $field_id;
3886
3887
        $tag_id = self::get_tag_id($tag, $field_id);
3888
3889
        /* IMPORTANT
3890
         *  @todo we don't create tags with numbers
3891
         *
3892
         */
3893
3894
        //this is a new tag
3895
        if (0 == $tag_id) {
3896
            //the tag doesn't exist
3897
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
3898
            Database::query($sql);
3899
            $last_insert_id = Database::insert_id();
3900
        } else {
3901
            //the tag exists we update it
3902
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
3903
            Database::query($sql);
3904
            $last_insert_id = $tag_id;
3905
        }
3906
3907
        if (!empty($last_insert_id) && (0 != $last_insert_id)) {
3908
            //we insert the relationship user-tag
3909
            $sql = "SELECT tag_id FROM $table_user_tag_values
3910
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
3911
            $result = Database::query($sql);
3912
            //if the relationship does not exist we create it
3913
            if (0 == Database::num_rows($result)) {
3914
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
3915
                Database::query($sql);
3916
            }
3917
3918
            return true;
3919
        }
3920
3921
        return false;
3922
    }
3923
3924
    /**
3925
     * Deletes an user tag.
3926
     *
3927
     * @param int $user_id
3928
     * @param int $field_id
3929
     */
3930
    public static function delete_user_tags($user_id, $field_id)
3931
    {
3932
        // database table definition
3933
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3934
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3935
        $user_id = (int) $user_id;
3936
3937
        $tags = self::get_user_tags($user_id, $field_id);
3938
        if (is_array($tags) && count($tags) > 0) {
3939
            foreach ($tags as $key => $tag) {
3940
                if ($tag['count'] > '0') {
3941
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
3942
                    Database::query($sql);
3943
                }
3944
                $sql = "DELETE FROM $table_user_tag_values
3945
                        WHERE user_id = $user_id AND tag_id = $key";
3946
                Database::query($sql);
3947
            }
3948
        }
3949
    }
3950
3951
    /**
3952
     * Process the tag list comes from the UserManager::update_extra_field_value() function.
3953
     *
3954
     * @param array $tags     the tag list that will be added
3955
     * @param int   $user_id
3956
     * @param int   $field_id
3957
     *
3958
     * @return bool
3959
     */
3960
    public static function process_tags($tags, $user_id, $field_id)
3961
    {
3962
        // We loop the tags and add it to the DB
3963
        if (is_array($tags)) {
3964
            foreach ($tags as $tag) {
3965
                self::add_tag($tag, $user_id, $field_id);
3966
            }
3967
        } else {
3968
            self::add_tag($tags, $user_id, $field_id);
3969
        }
3970
3971
        return true;
3972
    }
3973
3974
    /**
3975
     * Returns a list of all administrators.
3976
     *
3977
     * @return array
3978
     */
3979
    public static function get_all_administrators()
3980
    {
3981
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3982
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
3983
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3984
        $access_url_id = api_get_current_access_url_id();
3985
        if (api_get_multiple_access_url()) {
3986
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active, locale
3987
                    FROM $tbl_url_rel_user as url
3988
                    INNER JOIN $table_admin as admin
3989
                    ON (admin.user_id=url.user_id)
3990
                    INNER JOIN $table_user u
3991
                    ON (u.id=admin.user_id)
3992
                    WHERE access_url_id ='".$access_url_id."'";
3993
        } else {
3994
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active, locale
3995
                    FROM $table_admin as admin
3996
                    INNER JOIN $table_user u
3997
                    ON (u.id=admin.user_id)";
3998
        }
3999
        $sql .= !str_contains($sql, 'WHERE') ? ' WHERE u.active <> '.USER_SOFT_DELETED : ' AND u.active <> '.USER_SOFT_DELETED;
4000
        $result = Database::query($sql);
4001
        $return = [];
4002
        if (Database::num_rows($result) > 0) {
4003
            while ($row = Database::fetch_assoc($result)) {
4004
                $return[$row['user_id']] = $row;
4005
            }
4006
        }
4007
4008
        return $return;
4009
    }
4010
4011
    /**
4012
     * Search an user (tags, first name, last name and email ).
4013
     *
4014
     * @param string $tag
4015
     * @param int    $field_id        field id of the tag
4016
     * @param int    $from            where to start in the query
4017
     * @param int    $number_of_items
4018
     * @param bool   $getCount        get count or not
4019
     *
4020
     * @return array
4021
     */
4022
    public static function get_all_user_tags(
4023
        $tag,
4024
        $field_id = 0,
4025
        $from = 0,
4026
        $number_of_items = 10,
4027
        $getCount = false
4028
    ) {
4029
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
4030
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4031
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4032
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4033
4034
        $field_id = intval($field_id);
4035
        $from = intval($from);
4036
        $number_of_items = intval($number_of_items);
4037
4038
        $where_field = "";
4039
        $where_extra_fields = self::get_search_form_where_extra_fields();
4040
        if (0 != $field_id) {
4041
            $where_field = " field_id = $field_id AND ";
4042
        }
4043
4044
        // all the information of the field
4045
        if ($getCount) {
4046
            $select = "SELECT count(DISTINCT u.id) count";
4047
        } else {
4048
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, tag, picture_uri";
4049
        }
4050
4051
        $sql = " $select
4052
                FROM $user_table u
4053
                INNER JOIN $access_url_rel_user_table url_rel_user
4054
                ON (u.id = url_rel_user.user_id)
4055
                LEFT JOIN $table_user_tag_values uv
4056
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
4057
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
4058
                WHERE
4059
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
4060
                    (
4061
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
4062
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
4063
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
4064
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
4065
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
4066
                     )
4067
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
4068
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
4069
4070
        $keyword_active = true;
4071
        // only active users
4072
        if ($keyword_active) {
4073
            $sql .= " AND u.active='1'";
4074
        }
4075
        // avoid anonymous
4076
        $sql .= " AND u.status <> 6 ";
4077
        $sql .= " ORDER BY username";
4078
        $sql .= " LIMIT $from , $number_of_items";
4079
4080
        $result = Database::query($sql);
4081
        $return = [];
4082
4083
        if (Database::num_rows($result) > 0) {
4084
            if ($getCount) {
4085
                $row = Database::fetch_assoc($result);
4086
4087
                return $row['count'];
4088
            }
4089
            while ($row = Database::fetch_assoc($result)) {
4090
                $return[$row['id']] = $row;
4091
            }
4092
        }
4093
4094
        return $return;
4095
    }
4096
4097
    /**
4098
     * Get extra filterable user fields (only type select).
4099
     *
4100
     * @return array Array of extra fields as [int => ['name' => ..., 'variable' => ..., 'data' => ...]] (
4101
     *               or empty array if no extra field)
4102
     */
4103
    public static function getExtraFilterableFields()
4104
    {
4105
        $extraFieldList = self::get_extra_fields();
4106
        $fields = [];
4107
        if (is_array($extraFieldList)) {
4108
            foreach ($extraFieldList as $extraField) {
4109
                // If is enabled to filter and is a "<select>" field type
4110
                if (1 == $extraField[8] && 4 == $extraField[2]) {
4111
                    $fields[] = [
4112
                        'name' => $extraField[3],
4113
                        'variable' => $extraField[1],
4114
                        'data' => $extraField[9],
4115
                    ];
4116
                }
4117
            }
4118
        }
4119
4120
        return $fields;
4121
    }
4122
4123
    /**
4124
     * Get extra where clauses for finding users based on extra filterable user fields (type select).
4125
     *
4126
     * @return string With AND clauses based on user's ID which have the values to search in extra user fields
4127
     *                (or empty if no extra field exists)
4128
     */
4129
    public static function get_search_form_where_extra_fields()
4130
    {
4131
        $useExtraFields = false;
4132
        $extraFields = self::getExtraFilterableFields();
4133
        $extraFieldResult = [];
4134
        if (is_array($extraFields) && count($extraFields) > 0) {
4135
            foreach ($extraFields as $extraField) {
4136
                $varName = 'field_'.$extraField['variable'];
4137
                if (self::is_extra_field_available($extraField['variable'])) {
4138
                    if (isset($_GET[$varName]) && '0' != $_GET[$varName]) {
4139
                        $useExtraFields = true;
4140
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
4141
                            $extraField['variable'],
4142
                            $_GET[$varName]
4143
                        );
4144
                    }
4145
                }
4146
            }
4147
        }
4148
4149
        if ($useExtraFields) {
4150
            $finalResult = [];
4151
            if (count($extraFieldResult) > 1) {
4152
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
4153
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
4154
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
4155
                    }
4156
                }
4157
            } else {
4158
                $finalResult = $extraFieldResult[0];
4159
            }
4160
4161
            if (is_array($finalResult) && count($finalResult) > 0) {
4162
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
4163
            } else {
4164
                //no results
4165
                $whereFilter = " AND u.id  = -1 ";
4166
            }
4167
4168
            return $whereFilter;
4169
        }
4170
4171
        return '';
4172
    }
4173
4174
    /**
4175
     * Show the search form.
4176
     *
4177
     * @param string $query the value of the search box
4178
     *
4179
     * @throws Exception
4180
     *
4181
     * @return string HTML form
4182
     */
4183
    public static function get_search_form($query, $defaultParams = [])
4184
    {
4185
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
4186
        $form = new FormValidator(
4187
            'search_user',
4188
            'get',
4189
            api_get_path(WEB_PATH).'main/social/search.php',
4190
            '',
4191
            [],
4192
            FormValidator::LAYOUT_HORIZONTAL
4193
        );
4194
4195
        $query = Security::remove_XSS($query);
4196
4197
        if (!empty($query)) {
4198
            $form->addHeader(get_lang('Results and feedback').' "'.$query.'"');
4199
        }
4200
4201
        $form->addText(
4202
            'q',
4203
            get_lang('Users, Groups'),
4204
            false,
4205
            [
4206
                'id' => 'q',
4207
            ]
4208
        );
4209
        $options = [
4210
            0 => get_lang('Select'),
4211
            1 => get_lang('User'),
4212
            2 => get_lang('Group'),
4213
        ];
4214
        $form->addSelect(
4215
            'search_type',
4216
            get_lang('Type'),
4217
            $options,
4218
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
4219
        );
4220
4221
        // Extra fields
4222
        $extraFields = self::getExtraFilterableFields();
4223
        $defaults = [];
4224
        if (is_array($extraFields) && count($extraFields) > 0) {
4225
            foreach ($extraFields as $extraField) {
4226
                $varName = 'field_'.$extraField['variable'];
4227
                $options = [
4228
                    0 => get_lang('Select'),
4229
                ];
4230
                foreach ($extraField['data'] as $option) {
4231
                    if (isset($_GET[$varName])) {
4232
                        if ($_GET[$varName] == $option[1]) {
4233
                            $defaults[$option[1]] = true;
4234
                        }
4235
                    }
4236
4237
                    $options[$option[1]] = $option[1];
4238
                }
4239
                $form->addSelect($varName, $extraField['name'], $options);
4240
            }
4241
        }
4242
4243
        $defaults['search_type'] = (int) $searchType;
4244
        $defaults['q'] = $query;
4245
4246
        if (!empty($defaultParams)) {
4247
            $defaults = array_merge($defaults, $defaultParams);
4248
        }
4249
        $form->setDefaults($defaults);
4250
        $form->addButtonSearch(get_lang('Search'));
4251
4252
        $js = '<script>
4253
        extra_field_toogle();
4254
        function extra_field_toogle() {
4255
            if (jQuery("select[name=search_type]").val() != "1") {
4256
                jQuery(".extra_field").hide();
4257
            } else {
4258
                jQuery(".extra_field").show();
4259
            }
4260
        }
4261
        </script>';
4262
4263
        return $js.$form->returnForm();
4264
    }
4265
4266
    /**
4267
     * @param int $userId
4268
     *
4269
     * @return array
4270
     */
4271
    public static function getDrhListFromUser($userId)
4272
    {
4273
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4274
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4275
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4276
        $userId = (int) $userId;
4277
4278
        $orderBy = null;
4279
        if (api_is_western_name_order()) {
4280
            $orderBy .= ' ORDER BY firstname, lastname ';
4281
        } else {
4282
            $orderBy .= ' ORDER BY lastname, firstname ';
4283
        }
4284
4285
        $sql = "SELECT u.id, username, u.firstname, u.lastname
4286
                FROM $tblUser u
4287
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
4288
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
4289
                WHERE
4290
                    access_url_id = ".api_get_current_access_url_id()." AND
4291
                    uru.user_id = '$userId' AND
4292
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4293
                    $orderBy
4294
                ";
4295
        $result = Database::query($sql);
4296
4297
        return Database::store_result($result);
4298
    }
4299
4300
    /**
4301
     * get users followed by human resource manager.
4302
     *
4303
     * @param int    $userId
4304
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
4305
     * @param bool   $getOnlyUserId
4306
     * @param bool   $getSql
4307
     * @param bool   $getCount
4308
     * @param int    $from
4309
     * @param int    $numberItems
4310
     * @param int    $column
4311
     * @param string $direction
4312
     * @param int    $active
4313
     * @param string $lastConnectionDate
4314
     *
4315
     * @return array users
4316
     */
4317
    public static function get_users_followed_by_drh(
4318
        $userId,
4319
        $userStatus = 0,
4320
        $getOnlyUserId = false,
4321
        $getSql = false,
4322
        $getCount = false,
4323
        $from = null,
4324
        $numberItems = null,
4325
        $column = null,
4326
        $direction = null,
4327
        $active = null,
4328
        $lastConnectionDate = null
4329
    ) {
4330
        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...
4331
            $userId,
4332
            $userStatus,
4333
            $getOnlyUserId,
4334
            $getSql,
4335
            $getCount,
4336
            $from,
4337
            $numberItems,
4338
            $column,
4339
            $direction,
4340
            $active,
4341
            $lastConnectionDate,
4342
            DRH
4343
        );
4344
    }
4345
4346
    /**
4347
     * Get users followed by human resource manager.
4348
     *
4349
     * @param int    $userId
4350
     * @param int    $userStatus             Filter users by status (STUDENT, COURSEMANAGER, etc)
4351
     * @param bool   $getOnlyUserId
4352
     * @param bool   $getSql
4353
     * @param bool   $getCount
4354
     * @param int    $from
4355
     * @param int    $numberItems
4356
     * @param int    $column
4357
     * @param string $direction
4358
     * @param int    $active
4359
     * @param string $lastConnectionDate
4360
     * @param int    $status                 the function is called by who? COURSEMANAGER, DRH?
4361
     * @param string $keyword
4362
     * @param bool   $checkSessionVisibility
4363
     *
4364
     * @return mixed Users list (array) or the SQL query if $getSQL was set to true
4365
     */
4366
    public static function getUsersFollowedByUser(
4367
        $userId,
4368
        $userStatus = null,
4369
        $getOnlyUserId = false,
4370
        $getSql = false,
4371
        $getCount = false,
4372
        $from = null,
4373
        $numberItems = null,
4374
        $column = null,
4375
        $direction = null,
4376
        $active = null,
4377
        $lastConnectionDate = null,
4378
        $status = null,
4379
        $keyword = null,
4380
        $checkSessionVisibility = false
4381
    ) {
4382
        // Database Table Definitions
4383
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4384
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4385
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4386
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4387
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4388
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4389
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4390
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4391
4392
        $userId = (int) $userId;
4393
        $limitCondition = '';
4394
4395
        if (isset($from) && isset($numberItems)) {
4396
            $from = (int) $from;
4397
            $numberItems = (int) $numberItems;
4398
            $limitCondition = "LIMIT $from, $numberItems";
4399
        }
4400
4401
        $column = Database::escape_string($column);
4402
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
4403
4404
        $userConditions = '';
4405
        if (!empty($userStatus)) {
4406
            $userConditions .= ' AND u.status = '.intval($userStatus);
4407
        }
4408
4409
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
4410
        if ($getOnlyUserId) {
4411
            $select = " SELECT DISTINCT u.id user_id";
4412
        }
4413
4414
        $masterSelect = "SELECT DISTINCT * FROM ";
4415
4416
        if ($getCount) {
4417
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
4418
            $select = " SELECT DISTINCT(u.id) user_id";
4419
        }
4420
4421
        if (!is_null($active)) {
4422
            $active = intval($active);
4423
            $userConditions .= " AND u.active = $active ";
4424
        }
4425
4426
        if (!empty($keyword)) {
4427
            $keyword = trim(Database::escape_string($keyword));
4428
            $keywordParts = array_filter(explode(' ', $keyword));
4429
            $extraKeyword = '';
4430
            if (!empty($keywordParts)) {
4431
                $keywordPartsFixed = Database::escape_string(implode('%', $keywordParts));
4432
                if (!empty($keywordPartsFixed)) {
4433
                    $extraKeyword .= " OR
4434
                        CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keywordPartsFixed%' OR
4435
                        CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keywordPartsFixed%' ";
4436
                }
4437
            }
4438
            $userConditions .= " AND (
4439
                u.username LIKE '%$keyword%' OR
4440
                u.firstname LIKE '%$keyword%' OR
4441
                u.lastname LIKE '%$keyword%' OR
4442
                u.official_code LIKE '%$keyword%' OR
4443
                u.email LIKE '%$keyword%' OR
4444
                CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keyword%' OR
4445
                CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keyword%'
4446
                $extraKeyword
4447
            )";
4448
        }
4449
4450
        if (!empty($lastConnectionDate)) {
4451
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
4452
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
4453
        }
4454
4455
        $sessionConditionsCoach = null;
4456
        $dateCondition = '';
4457
        $drhConditions = null;
4458
        $teacherSelect = null;
4459
        $urlId = api_get_current_access_url_id();
4460
4461
        switch ($status) {
4462
            case DRH:
4463
                $drhConditions .= " AND
4464
                    friend_user_id = '$userId' AND
4465
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4466
                ";
4467
                break;
4468
            case COURSEMANAGER:
4469
                $drhConditions .= " AND
4470
                    friend_user_id = '$userId' AND
4471
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_RRHH."'
4472
                ";
4473
4474
                $sessionConditionsTeacher = " AND
4475
                    (scu.status = ".SessionEntity::COURSE_COACH." AND scu.user_id = '$userId')
4476
                ";
4477
4478
                if ($checkSessionVisibility) {
4479
                    $today = api_strtotime('now', 'UTC');
4480
                    $today = date('Y-m-d', $today);
4481
                    $dateCondition = "
4482
                        AND
4483
                        (
4484
                            (s.access_start_date <= '$today' AND '$today' <= s.access_end_date) OR
4485
                            (s.access_start_date IS NULL AND s.access_end_date IS NULL) OR
4486
                            (s.access_start_date <= '$today' AND s.access_end_date IS NULL) OR
4487
                            ('$today' <= s.access_end_date AND s.access_start_date IS NULL)
4488
                        )
4489
					";
4490
                }
4491
4492
                // Use $tbl_session_rel_course_rel_user instead of $tbl_session_rel_user
4493
                /*
4494
                INNER JOIN $tbl_session_rel_user sru
4495
                ON (sru.user_id = u.id)
4496
                INNER JOIN $tbl_session_rel_course_rel_user scu
4497
                ON (scu.user_id = u.id AND scu.c_id IS NOT NULL AND visibility = 1)*/
4498
                $teacherSelect =
4499
                "UNION ALL (
4500
                        $select
4501
                        FROM $tbl_user u
4502
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
4503
                        WHERE
4504
                            (
4505
                                sru.session_id IN (
4506
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
4507
                                    $tbl_session_rel_access_url session_rel_access_rel_user
4508
                                    ON session_rel_access_rel_user.session_id = s.id
4509
                                    INNER JOIN $tbl_session_rel_user sru ON s.id = sru.session_id
4510
                                    WHERE access_url_id = ".$urlId."
4511
                                        AND (sru.relation_type = ".SessionEntity::GENERAL_COACH."
4512
                                        AND sru.user_id = $userId)
4513
                                ) OR sru.session_id IN (
4514
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
4515
                                    INNER JOIN $tbl_session_rel_access_url url
4516
                                    ON (url.session_id = s.id)
4517
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
4518
                                    ON (scu.session_id = s.id)
4519
                                    WHERE access_url_id = ".$urlId."
4520
                                    $sessionConditionsTeacher
4521
                                    $dateCondition
4522
                                )
4523
                            )
4524
                            $userConditions
4525
                    )
4526
                    UNION ALL(
4527
                        $select
4528
                        FROM $tbl_user u
4529
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
4530
                        WHERE cu.c_id IN (
4531
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
4532
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
4533
                        )
4534
                        $userConditions
4535
                    )"
4536
                ;
4537
                break;
4538
            case STUDENT_BOSS:
4539
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".UserRelUser::USER_RELATION_TYPE_BOSS;
4540
                break;
4541
            case HRM_REQUEST:
4542
                $drhConditions .= " AND
4543
                    friend_user_id = '$userId' AND
4544
                    relation_type = '".UserRelUser::USER_RELATION_TYPE_HRM_REQUEST."'
4545
                ";
4546
                break;
4547
        }
4548
4549
        $join = null;
4550
        $sql = " $masterSelect
4551
                (
4552
                    (
4553
                        $select
4554
                        FROM $tbl_user u
4555
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
4556
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
4557
                        $join
4558
                        WHERE
4559
                            access_url_id = ".$urlId."
4560
                            $drhConditions
4561
                            $userConditions
4562
                    )
4563
                    $teacherSelect
4564
4565
                ) as t1";
4566
4567
        if ($getSql) {
4568
            return $sql;
4569
        }
4570
        if ($getCount) {
4571
            $result = Database::query($sql);
4572
            $row = Database::fetch_array($result);
4573
4574
            return $row['count'];
4575
        }
4576
4577
        $orderBy = null;
4578
        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...
4579
            if (api_is_western_name_order()) {
4580
                $orderBy .= " ORDER BY firstname, lastname ";
4581
            } else {
4582
                $orderBy .= " ORDER BY lastname, firstname ";
4583
            }
4584
4585
            if (!empty($column) && !empty($direction)) {
4586
                // Fixing order due the UNIONs
4587
                $column = str_replace('u.', '', $column);
4588
                $orderBy = " ORDER BY `$column` $direction ";
4589
            }
4590
        }
4591
4592
        $sql .= $orderBy;
4593
        $sql .= $limitCondition;
4594
4595
        $result = Database::query($sql);
4596
        $users = [];
4597
        if (Database::num_rows($result) > 0) {
4598
            while ($row = Database::fetch_array($result)) {
4599
                $users[$row['user_id']] = $row;
4600
            }
4601
        }
4602
4603
        return $users;
4604
    }
4605
4606
    /**
4607
     * Subscribes users to human resource manager (Dashboard feature).
4608
     *
4609
     * @param int   $hr_dept_id
4610
     * @param array $users_id
4611
     * @param bool  $deleteOtherAssignedUsers
4612
     */
4613
    public static function subscribeUsersToHRManager(
4614
        $hr_dept_id,
4615
        $users_id,
4616
        $deleteOtherAssignedUsers = true
4617
    ): void {
4618
        self::subscribeUsersToUser(
4619
            $hr_dept_id,
4620
            $users_id,
4621
            UserRelUser::USER_RELATION_TYPE_RRHH,
4622
            false,
4623
            $deleteOtherAssignedUsers
4624
        );
4625
    }
4626
4627
    /**
4628
     * Register request to assign users to HRM.
4629
     *
4630
     * @param int   $hrmId   The HRM ID
4631
     * @param array $usersId The users IDs
4632
     */
4633
    public static function requestUsersToHRManager($hrmId, $usersId): void
4634
    {
4635
        self::subscribeUsersToUser(
4636
            $hrmId,
4637
            $usersId,
4638
            UserRelUser::USER_RELATION_TYPE_HRM_REQUEST,
4639
            false,
4640
            false
4641
        );
4642
    }
4643
4644
    /**
4645
     * Remove the requests for assign a user to a HRM.
4646
     *
4647
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
4648
     */
4649
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
4650
    {
4651
        $users = implode(', ', $usersId);
4652
        Database::getManager()
4653
            ->createQuery('
4654
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
4655
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
4656
            ')
4657
            ->execute(['hrm_id' => $hrmId, 'relation_type' => UserRelUser::USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
4658
    }
4659
4660
    /**
4661
     * Add subscribed users to a user by relation type.
4662
     *
4663
     * @param int   $userId                   The user id
4664
     * @param array $subscribedUsersId        The id of subscribed users
4665
     * @param int   $relationType             The relation type
4666
     * @param bool  $deleteUsersBeforeInsert
4667
     * @param bool  $deleteOtherAssignedUsers
4668
     */
4669
    public static function subscribeUsersToUser(
4670
        $userId,
4671
        $subscribedUsersId,
4672
        $relationType,
4673
        $deleteUsersBeforeInsert = false,
4674
        $deleteOtherAssignedUsers = true
4675
    ): void {
4676
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4677
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4678
4679
        $userId = (int) $userId;
4680
        $relationType = (int) $relationType;
4681
4682
        if ($deleteOtherAssignedUsers) {
4683
            if (api_get_multiple_access_url()) {
4684
                // Deleting assigned users to hrm_id
4685
                $sql = "SELECT s.user_id
4686
                        FROM $userRelUserTable s
4687
                        INNER JOIN $userRelAccessUrlTable a
4688
                        ON (a.user_id = s.user_id)
4689
                        WHERE
4690
                            friend_user_id = $userId AND
4691
                            relation_type = $relationType AND
4692
                            access_url_id = ".api_get_current_access_url_id();
4693
            } else {
4694
                $sql = "SELECT user_id
4695
                        FROM $userRelUserTable
4696
                        WHERE
4697
                            friend_user_id = $userId AND
4698
                            relation_type = $relationType";
4699
            }
4700
            $result = Database::query($sql);
4701
4702
            if (Database::num_rows($result) > 0) {
4703
                while ($row = Database::fetch_array($result)) {
4704
                    $sql = "DELETE FROM $userRelUserTable
4705
                            WHERE
4706
                                user_id = {$row['user_id']} AND
4707
                                friend_user_id = $userId AND
4708
                                relation_type = $relationType";
4709
                    Database::query($sql);
4710
                }
4711
            }
4712
        }
4713
4714
        if ($deleteUsersBeforeInsert) {
4715
            $sql = "DELETE FROM $userRelUserTable
4716
                    WHERE
4717
                        user_id = $userId AND
4718
                        relation_type = $relationType";
4719
            Database::query($sql);
4720
        }
4721
4722
        // Inserting new user list.
4723
        if (is_array($subscribedUsersId)) {
4724
            foreach ($subscribedUsersId as $subscribedUserId) {
4725
                $subscribedUserId = (int) $subscribedUserId;
4726
                $sql = "SELECT id
4727
                        FROM $userRelUserTable
4728
                        WHERE
4729
                            user_id = $subscribedUserId AND
4730
                            friend_user_id = $userId AND
4731
                            relation_type = $relationType";
4732
4733
                $result = Database::query($sql);
4734
                $num = Database::num_rows($result);
4735
                if (0 === $num) {
4736
                    $userRelUser = (new UserRelUser())
4737
                        ->setUser(api_get_user_entity($subscribedUserId))
4738
                        ->setFriend(api_get_user_entity($userId))
4739
                        ->setRelationType($relationType)
4740
                    ;
4741
                    $em = Database::getManager();
4742
                    $em->persist($userRelUser);
4743
                    $em->flush();
4744
                }
4745
            }
4746
        }
4747
    }
4748
4749
    /**
4750
     * This function checks if a user is followed by provided human resources managers.
4751
     *
4752
     * @param int $user_id
4753
     * @param int $hr_dept_id Human resources manager
4754
     *
4755
     * @return bool
4756
     * @throws Exception
4757
     * @throws \Doctrine\DBAL\Exception
4758
     */
4759
    public static function is_user_followed_by_drh(int $user_id, int $hr_dept_id): bool
4760
    {
4761
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4762
        $result = false;
4763
4764
        $sql = "SELECT user_id FROM $tbl_user_rel_user
4765
                WHERE
4766
                    user_id = $user_id AND
4767
                    friend_user_id = $hr_dept_id AND
4768
                    relation_type = ".UserRelUser::USER_RELATION_TYPE_RRHH;
4769
        $rs = Database::query($sql);
4770
        if (Database::num_rows($rs) > 0) {
4771
            $result = true;
4772
        }
4773
4774
        return $result;
4775
    }
4776
4777
    /**
4778
     * Return the user id of teacher or session administrator.
4779
     *
4780
     * @param array $courseInfo
4781
     *
4782
     * @return int The user id, or 0 if the session ID was negative
4783
     * @throws Exception
4784
     * @throws \Doctrine\DBAL\Exception
4785
     */
4786
    public static function get_user_id_of_course_admin_or_session_admin(array $courseInfo): int
4787
    {
4788
        $session = api_get_session_id();
4789
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4790
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4791
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4792
4793
        if (empty($courseInfo)) {
4794
            return 0;
4795
        }
4796
4797
        $courseId = $courseInfo['real_id'];
4798
4799
        if (0 == $session) {
4800
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
4801
                    INNER JOIN '.$table_course_user.' ru
4802
                    ON ru.user_id = u.id
4803
                    WHERE
4804
                        ru.status = 1 AND
4805
                        ru.c_id = "'.$courseId.'" ';
4806
            $rs = Database::query($sql);
4807
            $num_rows = Database::num_rows($rs);
4808
            if (1 == $num_rows) {
4809
                $row = Database::fetch_array($rs);
4810
4811
                return (int) $row['uid'];
4812
            } else {
4813
                $my_num_rows = $num_rows;
4814
4815
                return (int) Database::result($rs, $my_num_rows - 1, 'uid');
4816
            }
4817
        } elseif ($session > 0) {
4818
            $sql = 'SELECT u.id as uid FROM '.$table_user.' u
4819
                    INNER JOIN '.$table_session_course_user.' sru
4820
                    ON sru.user_id = u.id
4821
                    WHERE
4822
                        sru.c_id = '.$courseId.' AND
4823
                        sru.status = '.SessionEntity::COURSE_COACH;
4824
            $rs = Database::query($sql);
4825
            if (Database::num_rows($rs) > 0) {
4826
                $row = Database::fetch_assoc($rs);
4827
4828
                return (int) $row['uid'];
4829
            }
4830
        }
4831
4832
        return 0;
4833
    }
4834
4835
    /**
4836
     * Determines if a user is a gradebook certified.
4837
     *
4838
     * @param int $cat_id  The category id of gradebook
4839
     * @param int $user_id The user id
4840
     *
4841
     * @return bool
4842
     */
4843
    public static function is_user_certified($cat_id, $user_id)
4844
    {
4845
        $cat_id = (int) $cat_id;
4846
        $user_id = (int) $user_id;
4847
4848
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4849
        $sql = 'SELECT path_certificate
4850
                FROM '.$table.'
4851
                WHERE
4852
                    cat_id = "'.$cat_id.'" AND
4853
                    user_id = "'.$user_id.'"';
4854
        $rs = Database::query($sql);
4855
        $row = Database::fetch_array($rs);
4856
4857
        if (!isset($row['path_certificate']) || '' == $row['path_certificate'] || is_null($row['path_certificate'])) {
4858
            return false;
4859
        }
4860
4861
        return true;
4862
    }
4863
4864
    /**
4865
     * Gets the info about a gradebook certificate for a user by course.
4866
     *
4867
     * @param array $course_info The course code
4868
     * @param int   $session_id
4869
     * @param int   $user_id     The user id
4870
     *
4871
     * @return array if there is not information return false
4872
     */
4873
    public static function get_info_gradebook_certificate($course_info, $session_id, $user_id)
4874
    {
4875
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4876
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
4877
        $session_id = (int) $session_id;
4878
        $user_id = (int) $user_id;
4879
        $courseId = $course_info['real_id'];
4880
4881
        if (empty($session_id)) {
4882
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
4883
        } else {
4884
            $session_condition = " AND session_id = $session_id";
4885
        }
4886
4887
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.'
4888
                WHERE cat_id = (
4889
                    SELECT id FROM '.$tbl_grade_category.'
4890
                    WHERE
4891
                        c_id = "'.$courseId.'" '.$session_condition.'
4892
                    LIMIT 1
4893
                ) AND user_id='.$user_id;
4894
4895
        $rs = Database::query($sql);
4896
        if (Database::num_rows($rs) > 0) {
4897
            $row = Database::fetch_assoc($rs);
4898
            $score = $row['score_certificate'];
4899
            $category_id = $row['cat_id'];
4900
            $cat = Category::load($category_id);
4901
            $displayscore = ScoreDisplay::instance();
4902
            if (isset($cat) && $displayscore->is_custom()) {
4903
                $grade = $displayscore->display_score(
4904
                    [$score, $cat[0]->get_weight()],
4905
                    SCORE_DIV_PERCENT_WITH_CUSTOM
4906
                );
4907
            } else {
4908
                $grade = $displayscore->display_score(
4909
                    [$score, $cat[0]->get_weight()]
4910
                );
4911
            }
4912
            $row['grade'] = $grade;
4913
4914
            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...
4915
        }
4916
4917
        return false;
4918
    }
4919
4920
    /**
4921
     * This function check if the user is a coach inside session course.
4922
     *
4923
     * @param int $user_id    User id
4924
     * @param int $courseId
4925
     * @param int $session_id
4926
     *
4927
     * @return bool True if the user is a coach
4928
     */
4929
    public static function is_session_course_coach($user_id, $courseId, $session_id)
4930
    {
4931
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4932
        // Protect data
4933
        $user_id = intval($user_id);
4934
        $courseId = intval($courseId);
4935
        $session_id = intval($session_id);
4936
        $result = false;
4937
4938
        $sql = "SELECT session_id FROM $table
4939
                WHERE
4940
                  session_id = $session_id AND
4941
                  c_id = $courseId AND
4942
                  user_id = $user_id AND
4943
                  status = ".SessionEntity::COURSE_COACH;
4944
        $res = Database::query($sql);
4945
4946
        if (Database::num_rows($res) > 0) {
4947
            $result = true;
4948
        }
4949
4950
        return $result;
4951
    }
4952
4953
    /**
4954
     * This function returns an icon path that represents the favicon of the website of which the url given.
4955
     * Defaults to the current Chamilo favicon.
4956
     *
4957
     * @param string $url1 URL of website where to look for favicon.ico
4958
     * @param string $url2 Optional second URL of website where to look for favicon.ico
4959
     *
4960
     * @return string Path of icon to load
4961
     */
4962
    public static function get_favicon_from_url($url1, $url2 = null)
4963
    {
4964
        $icon_link = '';
4965
        $url = $url1;
4966
        if (empty($url1)) {
4967
            $url = $url2;
4968
            if (empty($url)) {
4969
                $url = api_get_access_url(api_get_current_access_url_id());
4970
                $url = $url[0];
4971
            }
4972
        }
4973
        if (!empty($url)) {
4974
            $pieces = parse_url($url);
4975
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
4976
        }
4977
4978
        return $icon_link;
4979
    }
4980
4981
    public static function addUserAsAdmin(User $user)
4982
    {
4983
        $userId = $user->getId();
4984
4985
        if (!self::is_admin($userId)) {
4986
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
4987
            $sql = "INSERT INTO $table SET user_id = $userId";
4988
            Database::query($sql);
4989
        }
4990
4991
        $user->addRole('ROLE_ADMIN');
4992
        self::getRepository()->updateUser($user, true);
4993
    }
4994
4995
    public static function removeUserAdmin(User $user)
4996
    {
4997
        $userId = (int) $user->getId();
4998
        if (self::is_admin($userId)) {
4999
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
5000
            $sql = "DELETE FROM $table WHERE user_id = $userId";
5001
            Database::query($sql);
5002
            $user->removeRole('ROLE_ADMIN');
5003
            self::getRepository()->updateUser($user, true);
5004
        }
5005
    }
5006
5007
    /**
5008
     * @param string $from
5009
     * @param string $to
5010
     */
5011
    public static function update_all_user_languages($from, $to)
5012
    {
5013
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5014
        $from = Database::escape_string($from);
5015
        $to = Database::escape_string($to);
5016
5017
        if (!empty($to) && !empty($from)) {
5018
            $sql = "UPDATE $table_user SET language = '$to'
5019
                    WHERE language = '$from'";
5020
            Database::query($sql);
5021
        }
5022
    }
5023
5024
    /**
5025
     * Subscribe boss to students.
5026
     *
5027
     * @param int   $bossId                   The boss id
5028
     * @param array $usersId                  The users array
5029
     * @param bool  $deleteOtherAssignedUsers
5030
     */
5031
    public static function subscribeBossToUsers($bossId, $usersId, $deleteOtherAssignedUsers = true): void
5032
    {
5033
        self::subscribeUsersToUser(
5034
            $bossId,
5035
            $usersId,
5036
            UserRelUser::USER_RELATION_TYPE_BOSS,
5037
            false,
5038
            $deleteOtherAssignedUsers
5039
        );
5040
    }
5041
5042
    /**
5043
     * @param int $userId
5044
     *
5045
     * @return bool
5046
     */
5047
    public static function removeAllBossFromStudent($userId)
5048
    {
5049
        $userId = (int) $userId;
5050
5051
        if (empty($userId)) {
5052
            return false;
5053
        }
5054
5055
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5056
        $sql = "DELETE FROM $userRelUserTable
5057
                WHERE user_id = $userId AND relation_type = ".UserRelUser::USER_RELATION_TYPE_BOSS;
5058
        Database::query($sql);
5059
5060
        return true;
5061
    }
5062
5063
    /**
5064
     * Subscribe boss to students, if $bossList is empty then the boss list will be empty too.
5065
     *
5066
     * @param int   $studentId
5067
     * @param array $bossList
5068
     * @param bool  $sendNotification
5069
     *
5070
     * @return mixed Affected rows or false on failure
5071
     */
5072
    public static function subscribeUserToBossList(
5073
        $studentId,
5074
        $bossList,
5075
        $sendNotification = false
5076
    ) {
5077
        $inserted = 0;
5078
        if (!empty($bossList)) {
5079
            sort($bossList);
5080
            $studentId = (int) $studentId;
5081
            $studentInfo = api_get_user_info($studentId);
5082
5083
            if (empty($studentInfo)) {
5084
                return false;
5085
            }
5086
5087
            $previousBossList = self::getStudentBossList($studentId);
5088
            $previousBossList = !empty($previousBossList) ? array_column($previousBossList, 'boss_id') : [];
5089
            sort($previousBossList);
5090
5091
            // Boss list is the same, nothing changed.
5092
            if ($bossList == $previousBossList) {
5093
                return false;
5094
            }
5095
5096
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5097
            self::removeAllBossFromStudent($studentId);
5098
5099
            foreach ($bossList as $bossId) {
5100
                $bossId = (int) $bossId;
5101
                $bossInfo = api_get_user_info($bossId);
5102
5103
                if (empty($bossInfo)) {
5104
                    continue;
5105
                }
5106
5107
                $bossLanguage = $bossInfo['locale'];
5108
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
5109
                        VALUES ($studentId, $bossId, ".UserRelUser::USER_RELATION_TYPE_BOSS.")";
5110
                $insertId = Database::query($sql);
5111
5112
                if ($insertId) {
5113
                    if ($sendNotification) {
5114
                        $name = $studentInfo['complete_name'];
5115
                        $url = api_get_path(WEB_CODE_PATH).'my_space/myStudents.php?student='.$studentId;
5116
                        $url = Display::url($url, $url);
5117
                        $subject = sprintf(get_lang('You have been assigned the learner %s', $bossLanguage), $name);
5118
                        $message = sprintf(get_lang('You have been assigned the learner %s with url %s', $bossLanguage), $name, $url);
5119
                        MessageManager::send_message_simple(
5120
                            $bossId,
5121
                            $subject,
5122
                            $message
5123
                        );
5124
                    }
5125
                    $inserted++;
5126
                }
5127
            }
5128
        } else {
5129
            self::removeAllBossFromStudent($studentId);
5130
        }
5131
5132
        return $inserted;
5133
    }
5134
5135
    /**
5136
     * Get users followed by student boss.
5137
     *
5138
     * @param int    $userId
5139
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
5140
     * @param bool   $getOnlyUserId
5141
     * @param bool   $getSql
5142
     * @param bool   $getCount
5143
     * @param int    $from
5144
     * @param int    $numberItems
5145
     * @param int    $column
5146
     * @param string $direction
5147
     * @param int    $active
5148
     * @param string $lastConnectionDate
5149
     *
5150
     * @return array users
5151
     */
5152
    public static function getUsersFollowedByStudentBoss(
5153
        $userId,
5154
        $userStatus = 0,
5155
        $getOnlyUserId = false,
5156
        $getSql = false,
5157
        $getCount = false,
5158
        $from = null,
5159
        $numberItems = null,
5160
        $column = null,
5161
        $direction = null,
5162
        $active = null,
5163
        $lastConnectionDate = null
5164
    ) {
5165
        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...
5166
            $userId,
5167
            $userStatus,
5168
            $getOnlyUserId,
5169
            $getSql,
5170
            $getCount,
5171
            $from,
5172
            $numberItems,
5173
            $column,
5174
            $direction,
5175
            $active,
5176
            $lastConnectionDate,
5177
            STUDENT_BOSS
5178
        );
5179
    }
5180
5181
    /**
5182
     * @return array
5183
     */
5184
    public static function getOfficialCodeGrouped()
5185
    {
5186
        $user = Database::get_main_table(TABLE_MAIN_USER);
5187
        $sql = "SELECT DISTINCT official_code
5188
                FROM $user
5189
                GROUP BY official_code";
5190
        $result = Database::query($sql);
5191
        $values = Database::store_result($result, 'ASSOC');
5192
        $result = [];
5193
        foreach ($values as $value) {
5194
            $result[$value['official_code']] = $value['official_code'];
5195
        }
5196
5197
        return $result;
5198
    }
5199
5200
    /**
5201
     * @param string $officialCode
5202
     *
5203
     * @return array
5204
     */
5205
    public static function getUsersByOfficialCode($officialCode)
5206
    {
5207
        $user = Database::get_main_table(TABLE_MAIN_USER);
5208
        $officialCode = Database::escape_string($officialCode);
5209
5210
        $sql = "SELECT DISTINCT id
5211
                FROM $user
5212
                WHERE official_code = '$officialCode'
5213
                ";
5214
        $result = Database::query($sql);
5215
5216
        $users = [];
5217
        while ($row = Database::fetch_array($result)) {
5218
            $users[] = $row['id'];
5219
        }
5220
5221
        return $users;
5222
    }
5223
5224
    /**
5225
     * Calc the expended time (in seconds) by a user in a course.
5226
     *
5227
     * @param int    $userId    The user id
5228
     * @param int    $courseId  The course id
5229
     * @param int    $sessionId Optional. The session id
5230
     * @param string $from      Optional. From date
5231
     * @param string $until     Optional. Until date
5232
     *
5233
     * @return int The time
5234
     */
5235
    public static function getTimeSpentInCourses(
5236
        $userId,
5237
        $courseId,
5238
        $sessionId = 0,
5239
        $from = '',
5240
        $until = ''
5241
    ) {
5242
        $userId = (int) $userId;
5243
        $sessionId = (int) $sessionId;
5244
5245
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5246
        $whereConditions = [
5247
            'user_id = ? ' => $userId,
5248
            'AND c_id = ? ' => $courseId,
5249
            'AND session_id = ? ' => $sessionId,
5250
        ];
5251
5252
        if (!empty($from) && !empty($until)) {
5253
            $whereConditions["AND (login_course_date >= '?' "] = $from;
5254
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
5255
        }
5256
5257
        $trackResult = Database::select(
5258
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
5259
            $trackCourseAccessTable,
5260
            [
5261
                'where' => $whereConditions,
5262
            ],
5263
            'first'
5264
        );
5265
5266
        if (false != $trackResult) {
5267
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
5268
        }
5269
5270
        return 0;
5271
    }
5272
5273
    /**
5274
     * Get the boss user ID from a followed user id.
5275
     *
5276
     * @param $userId
5277
     *
5278
     * @return bool
5279
     */
5280
    public static function getFirstStudentBoss($userId)
5281
    {
5282
        $userId = (int) $userId;
5283
        if ($userId > 0) {
5284
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5285
            $row = Database::select(
5286
                'DISTINCT friend_user_id AS boss_id',
5287
                $userRelTable,
5288
                [
5289
                    'where' => [
5290
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
5291
                            $userId,
5292
                            UserRelUser::USER_RELATION_TYPE_BOSS,
5293
                        ],
5294
                    ],
5295
                ]
5296
            );
5297
            if (!empty($row)) {
5298
                return $row[0]['boss_id'];
5299
            }
5300
        }
5301
5302
        return false;
5303
    }
5304
5305
    /**
5306
     * Get the boss user ID from a followed user id.
5307
     *
5308
     * @param int $userId student id
5309
     *
5310
     * @return array
5311
     */
5312
    public static function getStudentBossList($userId)
5313
    {
5314
        $userId = (int) $userId;
5315
5316
        if ($userId > 0) {
5317
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5318
5319
            return Database::select(
5320
                'DISTINCT friend_user_id AS boss_id',
5321
                $userRelTable,
5322
                [
5323
                    'where' => [
5324
                        'user_id = ? AND relation_type = ? ' => [
5325
                            $userId,
5326
                            UserRelUser::USER_RELATION_TYPE_BOSS,
5327
                        ],
5328
                    ],
5329
                ]
5330
            );
5331
        }
5332
5333
        return [];
5334
    }
5335
5336
    /**
5337
     * @param int $bossId
5338
     * @param int $studentId
5339
     *
5340
     * @return bool
5341
     */
5342
    public static function userIsBossOfStudent($bossId, $studentId)
5343
    {
5344
        $result = false;
5345
        $bossList = self::getStudentBossList($studentId);
5346
        if (!empty($bossList)) {
5347
            $bossList = array_column($bossList, 'boss_id');
5348
            if (in_array($bossId, $bossList)) {
5349
                $result = true;
5350
            }
5351
        }
5352
5353
        return $result;
5354
    }
5355
5356
    /**
5357
     * Displays the name of the user and makes the link to the user profile.
5358
     *
5359
     * @param array $userInfo
5360
     *
5361
     * @return string
5362
     */
5363
    public static function getUserProfileLink($userInfo)
5364
    {
5365
        if (isset($userInfo) && isset($userInfo['user_id'])) {
5366
            return Display::url(
5367
                $userInfo['complete_name_with_username'],
5368
                $userInfo['profile_url']
5369
            );
5370
        }
5371
5372
        return get_lang('Anonymous');
5373
    }
5374
5375
    /**
5376
     * Get users whose name matches $firstname and $lastname.
5377
     *
5378
     * @param string $firstname Firstname to search
5379
     * @param string $lastname  Lastname to search
5380
     *
5381
     * @return array The user list
5382
     */
5383
    public static function getUsersByName($firstname, $lastname)
5384
    {
5385
        $firstname = Database::escape_string($firstname);
5386
        $lastname = Database::escape_string($lastname);
5387
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
5388
5389
        $sql = <<<SQL
5390
            SELECT id, username, lastname, firstname
5391
            FROM $userTable
5392
            WHERE
5393
                firstname LIKE '$firstname%' AND
5394
                lastname LIKE '$lastname%'
5395
SQL;
5396
        $result = Database::query($sql);
5397
        $users = [];
5398
        while ($resultData = Database::fetch_object($result)) {
5399
            $users[] = $resultData;
5400
        }
5401
5402
        return $users;
5403
    }
5404
5405
    /**
5406
     * @param int $optionSelected
5407
     *
5408
     * @return string
5409
     */
5410
    public static function getUserSubscriptionTab($optionSelected = 1)
5411
    {
5412
        $allowAdmin = api_get_setting('workflows.allow_user_course_subscription_by_course_admin');
5413
        if (('true' === $allowAdmin && api_is_allowed_to_edit()) ||
5414
            api_is_platform_admin()
5415
        ) {
5416
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
5417
5418
            $headers = [
5419
                [
5420
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
5421
                    'content' => get_lang('Learners'),
5422
                ],
5423
                [
5424
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
5425
                    'content' => get_lang('Trainers'),
5426
                ],
5427
                /*[
5428
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
5429
                    'content' => get_lang('Learners'),
5430
                ],
5431
                [
5432
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
5433
                    'content' => get_lang('Trainers'),
5434
                ],*/
5435
                [
5436
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
5437
                    'content' => get_lang('Groups'),
5438
                ],
5439
                [
5440
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
5441
                    'content' => get_lang('Classes'),
5442
                ],
5443
            ];
5444
5445
            return Display::tabsOnlyLink($headers, $optionSelected);
5446
        }
5447
5448
        return '';
5449
    }
5450
5451
    /**
5452
     * Make sure this function is protected because it does NOT check password!
5453
     *
5454
     * This function defines globals.
5455
     *
5456
     * @param int  $userId
5457
     * @param bool $checkIfUserCanLoginAs
5458
     *
5459
     * @return bool
5460
     *
5461
     * @author Evie Embrechts
5462
     * @author Yannick Warnier <[email protected]>
5463
     */
5464
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
5465
    {
5466
        $userId = (int) $userId;
5467
        $userInfo = api_get_user_info($userId);
5468
5469
        // Check if the user is allowed to 'login_as'
5470
        $canLoginAs = true;
5471
        if ($checkIfUserCanLoginAs) {
5472
            $canLoginAs = api_can_login_as($userId);
5473
        }
5474
5475
        if (!$canLoginAs || empty($userInfo)) {
5476
            return false;
5477
        }
5478
5479
        if ($userId) {
5480
            Event::registerLog([
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

5480
            Event::/** @scrutinizer ignore-call */ 
5481
                   registerLog([

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...
5481
                'tool' => 'logout',
5482
                'tool_id' => 0,
5483
                'tool_id_detail' => 0,
5484
                'action' => '',
5485
                'info' => 'Change user (login as)',
5486
            ]);
5487
5488
            // Logout current user
5489
            self::loginDelete(api_get_user_id());
5490
5491
            return true;
5492
        }
5493
5494
        return false;
5495
    }
5496
5497
    /**
5498
     * Remove all login records from the track_e_online stats table,
5499
     * for the given user ID.
5500
     *
5501
     * @param int $userId User ID
5502
     */
5503
    public static function loginDelete($userId)
5504
    {
5505
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5506
        $userId = (int) $userId;
5507
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
5508
        Database::query($query);
5509
    }
5510
5511
    /**
5512
     * Login as first admin user registered in the platform.
5513
     *
5514
     * @return array
5515
     */
5516
    public static function logInAsFirstAdmin()
5517
    {
5518
        $adminList = self::get_all_administrators();
5519
5520
        if (!empty($adminList)) {
5521
            $userInfo = current($adminList);
5522
            if (!empty($userInfo)) {
5523
                $result = self::loginAsUser($userInfo['user_id'], false);
5524
                if ($result && api_is_platform_admin()) {
5525
                    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...
5526
                }
5527
            }
5528
        }
5529
5530
        return [];
5531
    }
5532
5533
    /**
5534
     * Check if user is teacher of a student based in their courses.
5535
     *
5536
     * @param $teacherId
5537
     * @param $studentId
5538
     *
5539
     * @return array
5540
     */
5541
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
5542
    {
5543
        $courses = CourseManager::getCoursesFollowedByUser(
5544
            $teacherId,
5545
            COURSEMANAGER
5546
        );
5547
        if (empty($courses)) {
5548
            return false;
5549
        }
5550
5551
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
5552
        if (empty($coursesFromUser)) {
5553
            return false;
5554
        }
5555
5556
        $coursesCodeList = array_column($courses, 'code');
5557
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
5558
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
5559
        $commonCourses = array_filter($commonCourses);
5560
5561
        if (!empty($commonCourses)) {
5562
            return $commonCourses;
5563
        }
5564
5565
        return [];
5566
    }
5567
5568
    /**
5569
     * @param int $teacherId
5570
     * @param int $studentId
5571
     *
5572
     * @return bool
5573
     */
5574
    public static function isTeacherOfStudent($teacherId, $studentId)
5575
    {
5576
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
5577
            $teacherId,
5578
            $studentId
5579
        );
5580
5581
        if (!empty($courses)) {
5582
            return true;
5583
        }
5584
5585
        return false;
5586
    }
5587
5588
    /**
5589
     * Send user confirmation mail.
5590
     *
5591
     * @throws Exception
5592
     */
5593
    public static function sendUserConfirmationMail(User $user)
5594
    {
5595
        $uniqueId = api_get_unique_id();
5596
        $user->setConfirmationToken($uniqueId);
5597
5598
        Database::getManager()->persist($user);
5599
        Database::getManager()->flush();
5600
5601
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
5602
5603
        // Check if the user was originally set for an automated subscription to a course or session
5604
        $courseCodeToRedirect = Session::read('course_redirect');
5605
        $sessionToRedirect = Session::read('session_redirect');
5606
        if (!empty($courseCodeToRedirect)) {
5607
            $url .= '&c='.$courseCodeToRedirect;
5608
        }
5609
        if (!empty($sessionToRedirect)) {
5610
            $url .= '&s='.$sessionToRedirect;
5611
        }
5612
        $mailSubject = get_lang('Registration confirmation');
5613
        $mailBody = get_lang('To complete your platform registration you need confirm your account by clicking the following link')
5614
            .PHP_EOL
5615
            .Display::url($url, $url);
5616
5617
        api_mail_html(
5618
            self::formatUserFullName($user),
5619
            $user->getEmail(),
5620
            $mailSubject,
5621
            $mailBody
5622
        );
5623
        Display::addFlash(Display::return_message(get_lang('Check your e-mail and follow the instructions.')));
5624
    }
5625
5626
    /**
5627
     * Anonymize a user. Replace personal info by anonymous info.
5628
     *
5629
     * @param int  $userId   User id
5630
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
5631
     *
5632
     * @throws \Exception
5633
     *
5634
     * @return bool
5635
     * @assert (0) === false
5636
     */
5637
    public static function anonymize($userId, $deleteIP = true)
5638
    {
5639
        global $debug;
5640
5641
        $userId = (int) $userId;
5642
5643
        if (empty($userId)) {
5644
            return false;
5645
        }
5646
5647
        $em = Database::getManager();
5648
        $user = api_get_user_entity($userId);
5649
        $uniqueId = uniqid('anon', true);
5650
        $user
5651
            ->setFirstname($uniqueId)
5652
            ->setLastname($uniqueId)
5653
            ->setBiography('')
5654
            ->setAddress('')
5655
            //->setCurriculumItems(null)
5656
            ->setDateOfBirth(null)
5657
            ->setCompetences('')
5658
            ->setDiplomas('')
5659
            ->setOpenarea('')
5660
            ->setTeach('')
5661
            ->setProductions(null)
5662
            ->setOpenid('')
5663
            ->setEmailCanonical($uniqueId.'@example.com')
5664
            ->setEmail($uniqueId.'@example.com')
5665
            ->setUsername($uniqueId)
5666
            ->setUsernameCanonical($uniqueId)
5667
            ->setPhone('')
5668
            ->setOfficialCode('')
5669
        ;
5670
5671
        self::deleteUserPicture($userId);
5672
        self::cleanUserRequestsOfRemoval($userId);
5673
5674
        // The IP address is a border-case personal data, as it does
5675
        // not directly allow for personal identification (it is not
5676
        // a completely safe value in most countries - the IP could
5677
        // be used by neighbours and crackers)
5678
        if ($deleteIP) {
5679
            $substitute = '127.0.0.1';
5680
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
5681
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
5682
            $res = Database::query($sql);
5683
            if (false === $res && $debug > 0) {
5684
                error_log("Could not anonymize IP address for user $userId ($sql)");
5685
            }
5686
5687
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5688
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5689
            $res = Database::query($sql);
5690
            if (false === $res && $debug > 0) {
5691
                error_log("Could not anonymize IP address for user $userId ($sql)");
5692
            }
5693
5694
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
5695
            $sql = "UPDATE $table SET user_ip = '$substitute' WHERE exe_user_id = $userId";
5696
            $res = Database::query($sql);
5697
            if (false === $res && $debug > 0) {
5698
                error_log("Could not anonymize IP address for user $userId ($sql)");
5699
            }
5700
5701
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
5702
            $sql = "UPDATE $table SET user_ip = '$substitute' WHERE login_user_id = $userId";
5703
            $res = Database::query($sql);
5704
            if (false === $res && $debug > 0) {
5705
                error_log("Could not anonymize IP address for user $userId ($sql)");
5706
            }
5707
5708
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5709
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
5710
            $res = Database::query($sql);
5711
            if (false === $res && $debug > 0) {
5712
                error_log("Could not anonymize IP address for user $userId ($sql)");
5713
            }
5714
5715
            $table = Database::get_course_table(TABLE_WIKI);
5716
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5717
            $res = Database::query($sql);
5718
            if (false === $res && $debug > 0) {
5719
                error_log("Could not anonymize IP address for user $userId ($sql)");
5720
            }
5721
5722
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
5723
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
5724
            $res = Database::query($sql);
5725
            if (false === $res && $debug > 0) {
5726
                error_log("Could not anonymize IP address for user $userId ($sql)");
5727
            }
5728
5729
            $table = Database::get_course_table(TABLE_WIKI);
5730
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
5731
            $res = Database::query($sql);
5732
            if (false === $res && $debug > 0) {
5733
                error_log("Could not anonymize IP address for user $userId ($sql)");
5734
            }
5735
        }
5736
5737
        $extraFieldRepository = $em->getRepository(EntityExtraField::class);
5738
        $autoRemoveFields = $extraFieldRepository->findBy([
5739
            'autoRemove' => 1,
5740
            'itemType' => EntityExtraField::USER_FIELD_TYPE
5741
        ]);
5742
5743
        foreach ($autoRemoveFields as $field) {
5744
            $extraFieldValueRepository = $em->getRepository(EntityExtraFieldValues::class);
5745
            $extraFieldValue = $extraFieldValueRepository->findOneBy([
5746
                'field' => $field,
5747
                'itemId' => $userId
5748
            ]);
5749
5750
            if ($extraFieldValue) {
5751
                $em->remove($extraFieldValue);
5752
            }
5753
        }
5754
5755
        $em->persist($user);
5756
        $em->flush();
5757
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
5758
5759
        return true;
5760
    }
5761
5762
    /**
5763
     * @param int $userId
5764
     *
5765
     * @throws Exception
5766
     *
5767
     * @return string
5768
     */
5769
    public static function anonymizeUserWithVerification($userId)
5770
    {
5771
        $allowDelete = ('true' === api_get_setting('session.allow_delete_user_for_session_admin'));
5772
5773
        $message = '';
5774
        if (api_is_platform_admin() ||
5775
            ($allowDelete && api_is_session_admin())
5776
        ) {
5777
            $userToUpdateInfo = api_get_user_info($userId);
5778
            $currentUserId = api_get_user_id();
5779
5780
            if ($userToUpdateInfo &&
5781
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
5782
            ) {
5783
                if ($userId != $currentUserId &&
5784
                    self::anonymize($userId)
5785
                ) {
5786
                    $message = Display::return_message(
5787
                        sprintf(get_lang("User %s's information anonymized."), $userToUpdateInfo['complete_name_with_username']),
5788
                        'confirmation'
5789
                    );
5790
                } else {
5791
                    $message = Display::return_message(
5792
                        sprintf(get_lang("We could not anonymize user %s's information. Please try again or check the logs."), $userToUpdateInfo['complete_name_with_username']),
5793
                        'error'
5794
                    );
5795
                }
5796
            } else {
5797
                $message = Display::return_message(
5798
                    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']),
5799
                    'error'
5800
                );
5801
            }
5802
        }
5803
5804
        return $message;
5805
    }
5806
5807
    /**
5808
     * @param int $userId
5809
     *
5810
     * @throws Exception
5811
     *
5812
     * @return string
5813
     */
5814
    public static function deleteUserWithVerification($userId, bool $destroy = false)
5815
    {
5816
        $allowDelete = ('true' === api_get_setting('session.allow_delete_user_for_session_admin'));
5817
        $message = Display::return_message(get_lang('You cannot delete this user'), 'error');
5818
        $userToUpdateInfo = api_get_user_info($userId);
5819
5820
        // User must exist.
5821
        if (empty($userToUpdateInfo)) {
5822
            return $message;
5823
        }
5824
5825
        $currentUserId = api_get_user_id();
5826
5827
        // Cannot delete myself.
5828
        if ($userId == $currentUserId) {
5829
            return $message;
5830
        }
5831
5832
        if (api_is_platform_admin() ||
5833
            ($allowDelete && api_is_session_admin())
5834
        ) {
5835
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
5836
                if (self::delete_user($userId, $destroy)) {
5837
                    $message = Display::return_message(
5838
                        get_lang('The user has been deleted').': '.$userToUpdateInfo['complete_name_with_username'],
5839
                        'confirmation'
5840
                    );
5841
                } else {
5842
                    $message = Display::return_message(get_lang('This user cannot be deleted because he is still teacher in a course. You can either remove his teacher status from these courses and then delete his account, or disable his account instead of deleting it.'), 'error');
5843
                }
5844
            }
5845
        }
5846
5847
        return $message;
5848
    }
5849
5850
    /**
5851
     * @return array
5852
     */
5853
    public static function createDataPrivacyExtraFields()
5854
    {
5855
        self::create_extra_field(
5856
            'request_for_legal_agreement_consent_removal_justification',
5857
            1, //text
5858
            'Request for legal agreement consent removal justification	',
5859
            ''
5860
        );
5861
5862
        self::create_extra_field(
5863
            'request_for_delete_account_justification',
5864
            1, //text
5865
            'Request for delete account justification',
5866
            ''
5867
        );
5868
5869
        $extraFieldId = self::create_extra_field(
5870
            'request_for_legal_agreement_consent_removal',
5871
            1, //text
5872
            'Request for legal agreement consent removal',
5873
            ''
5874
        );
5875
5876
        $extraFieldIdDeleteAccount = self::create_extra_field(
5877
            'request_for_delete_account',
5878
            1, //text
5879
            'Request for delete user account',
5880
            ''
5881
        );
5882
5883
        return [
5884
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
5885
            'delete_legal' => $extraFieldId,
5886
        ];
5887
    }
5888
5889
    /**
5890
     * @param int $userId
5891
     */
5892
    public static function cleanUserRequestsOfRemoval($userId)
5893
    {
5894
        $userId = (int) $userId;
5895
5896
        $extraFieldValue = new ExtraFieldValue('user');
5897
        $extraFieldsToDelete = [
5898
            'legal_accept',
5899
            'request_for_legal_agreement_consent_removal',
5900
            'request_for_legal_agreement_consent_removal_justification',
5901
            'request_for_delete_account_justification', // just in case delete also this
5902
            'request_for_delete_account',
5903
        ];
5904
5905
        foreach ($extraFieldsToDelete as $variable) {
5906
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
5907
                $userId,
5908
                $variable
5909
            );
5910
            if ($value && isset($value['id'])) {
5911
                $extraFieldValue->delete($value['id']);
5912
            }
5913
        }
5914
    }
5915
5916
    /**
5917
     * @param int $searchYear
5918
     *
5919
     * @throws Exception
5920
     *
5921
     * @return array
5922
     */
5923
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
5924
    {
5925
        $timezone = new DateTimeZone(api_get_timezone());
5926
5927
        $sessions = [];
5928
        if (DRH == $userInfo['status']) {
5929
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
5930
        } elseif (api_is_platform_admin(true)) {
5931
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
5932
        } else {
5933
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
5934
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
5935
5936
            foreach ($sessionsByCategory as $sessionsInCategory) {
5937
                $sessions = array_merge($sessions, $sessionsInCategory);
5938
            }
5939
        }
5940
5941
        $sessions = array_map(
5942
            function ($sessionInfo) {
5943
                if (!isset($sessionInfo['session_id'])) {
5944
                    $sessionInfo['session_id'] = $sessionInfo['id'];
5945
                }
5946
                if (!isset($sessionInfo['session_name'])) {
5947
                    $sessionInfo['session_name'] = $sessionInfo['name'];
5948
                }
5949
5950
                return $sessionInfo;
5951
            },
5952
            $sessions
5953
        );
5954
5955
        $calendarSessions = [];
5956
5957
        foreach ($sessions as $sessionInfo) {
5958
            if (!empty($sessionInfo['duration'])) {
5959
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
5960
                    $sessionInfo['session_id'],
5961
                    $userInfo['id']
5962
                );
5963
5964
                if (empty($courseAccess)) {
5965
                    continue;
5966
                }
5967
5968
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
5969
                $lastAccessDate = clone $firstAcessDate;
5970
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
5971
5972
                $firstAccessYear = (int) $firstAcessDate->format('Y');
5973
                $lastAccessYear = (int) $lastAccessDate->format('Y');
5974
5975
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
5976
                    $calendarSessions[$sessionInfo['session_id']] = [
5977
                        'name' => $sessionInfo['session_name'],
5978
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
5979
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
5980
                    ];
5981
                }
5982
5983
                continue;
5984
            }
5985
5986
            $accessStartDate = !empty($sessionInfo['access_start_date'])
5987
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
5988
                : null;
5989
            $accessEndDate = !empty($sessionInfo['access_end_date'])
5990
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
5991
                : null;
5992
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
5993
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
5994
5995
            $isValid = false;
5996
5997
            if ($accessStartYear && $accessEndYear) {
5998
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
5999
                    $isValid = true;
6000
                }
6001
            }
6002
6003
            if ($accessStartYear && !$accessEndYear) {
6004
                if ($accessStartYear == $searchYear) {
6005
                    $isValid = true;
6006
                }
6007
            }
6008
6009
            if (!$accessStartYear && $accessEndYear) {
6010
                if ($accessEndYear == $searchYear) {
6011
                    $isValid = true;
6012
                }
6013
            }
6014
6015
            if ($isValid) {
6016
                $calendarSessions[$sessionInfo['session_id']] = [
6017
                    'name' => $sessionInfo['session_name'],
6018
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
6019
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
6020
                ];
6021
            }
6022
        }
6023
6024
        return $calendarSessions;
6025
    }
6026
6027
    /**
6028
     * Get sessions info for planification calendar.
6029
     *
6030
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
6031
     * @param int   $searchYear
6032
     *
6033
     * @throws Exception
6034
     *
6035
     * @return array
6036
     */
6037
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
6038
    {
6039
        $timezone = new DateTimeZone(api_get_timezone());
6040
        $calendar = [];
6041
6042
        foreach ($sessionsList as $sessionId => $sessionInfo) {
6043
            $startDate = $sessionInfo['access_start_date']
6044
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6045
                : null;
6046
            $endDate = $sessionInfo['access_end_date']
6047
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6048
                : null;
6049
6050
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
6051
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
6052
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
6053
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
6054
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
6055
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
6056
6057
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
6058
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
6059
6060
            $calendar[] = [
6061
                'id' => $sessionId,
6062
                'name' => $sessionInfo['name'],
6063
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
6064
                'start_in_last_year' => $startYear < $searchYear,
6065
                'end_in_next_year' => $endYear > $searchYear,
6066
                'no_start' => !$startWeek,
6067
                'no_end' => !$endWeek,
6068
                'start' => $start,
6069
                'duration' => $duration > 0 ? $duration : 1,
6070
            ];
6071
        }
6072
6073
        usort(
6074
            $calendar,
6075
            function ($sA, $sB) {
6076
                if ($sA['start'] == $sB['start']) {
6077
                    return 0;
6078
                }
6079
6080
                if ($sA['start'] < $sB['start']) {
6081
                    return -1;
6082
                }
6083
6084
                return 1;
6085
            }
6086
        );
6087
6088
        return $calendar;
6089
    }
6090
6091
    /**
6092
     * Return the user's full name. Optionally with the username.
6093
     */
6094
    public static function formatUserFullName(User $user, bool $includeUsername = false): string
6095
    {
6096
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
6097
6098
        if ($includeUsername && 'false' === api_get_setting('profile.hide_username_with_complete_name')) {
6099
            $username = $user->getUsername();
6100
6101
            return "$fullName ($username)";
6102
        }
6103
6104
        return $fullName;
6105
    }
6106
6107
    /**
6108
     * @param int $userId
6109
     *
6110
     * @return array
6111
     */
6112
    public static function getUserCareers($userId)
6113
    {
6114
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6115
        $tableCareer = Database::get_main_table(TABLE_CAREER);
6116
        $userId = (int) $userId;
6117
6118
        $sql = "SELECT c.id, c.title
6119
                FROM $table uc
6120
                INNER JOIN $tableCareer c
6121
                ON uc.career_id = c.id
6122
                WHERE user_id = $userId
6123
                ORDER BY uc.created_at
6124
                ";
6125
        $result = Database::query($sql);
6126
6127
        return Database::store_result($result, 'ASSOC');
6128
    }
6129
6130
    /**
6131
     * @param int $userId
6132
     * @param int $careerId
6133
     */
6134
    public static function addUserCareer($userId, $careerId)
6135
    {
6136
        if ('true' !== api_get_setting('session.allow_career_users')) {
6137
            return false;
6138
        }
6139
6140
        if (false === self::userHasCareer($userId, $careerId)) {
6141
            $params = ['user_id' => $userId, 'career_id' => $careerId, 'created_at' => api_get_utc_datetime(), 'updated_at' => api_get_utc_datetime()];
6142
            $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6143
            Database::insert($table, $params);
6144
        }
6145
6146
        return true;
6147
    }
6148
6149
    /**
6150
     * @param int   $userCareerId
6151
     * @param array $data
6152
     *
6153
     * @return bool
6154
     */
6155
    public static function updateUserCareer($userCareerId, $data)
6156
    {
6157
        if ('true' !== api_get_setting('session.allow_career_users')) {
6158
            return false;
6159
        }
6160
6161
        $params = ['extra_data' => $data, 'updated_at' => api_get_utc_datetime()];
6162
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6163
        Database::update(
6164
            $table,
6165
            $params,
6166
            ['id = ?' => (int) $userCareerId]
6167
        );
6168
6169
        return true;
6170
    }
6171
6172
    /**
6173
     * @param int $userId
6174
     * @param int $careerId
6175
     *
6176
     * @return array
6177
     */
6178
    public static function getUserCareer($userId, $careerId)
6179
    {
6180
        $userId = (int) $userId;
6181
        $careerId = (int) $careerId;
6182
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6183
6184
        $sql = "SELECT * FROM $table WHERE user_id = $userId AND career_id = $careerId";
6185
        $result = Database::query($sql);
6186
6187
        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...
6188
    }
6189
6190
    /**
6191
     * @param int $userId
6192
     * @param int $careerId
6193
     *
6194
     * @return bool
6195
     */
6196
    public static function userHasCareer($userId, $careerId)
6197
    {
6198
        $userId = (int) $userId;
6199
        $careerId = (int) $careerId;
6200
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
6201
6202
        $sql = "SELECT id FROM $table WHERE user_id = $userId AND career_id = $careerId";
6203
        $result = Database::query($sql);
6204
6205
        return Database::num_rows($result) > 0;
6206
    }
6207
6208
    /**
6209
     * Disables or enables a user.
6210
     *
6211
     * @param int $user_id
6212
     * @param int $active  Enable or disable
6213
     *
6214
     * @return bool True on success, false on failure
6215
     * @assert (-1,0) === false
6216
     * @assert (1,1) === true
6217
     */
6218
    public static function change_active_state($user_id, $active)
6219
    {
6220
        $user_id = (int) $user_id;
6221
        $active = (int) $active;
6222
6223
        if (empty($user_id)) {
6224
            return false;
6225
        }
6226
6227
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6228
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
6229
        $r = Database::query($sql);
6230
        $ev = LOG_USER_DISABLE;
6231
        if (1 == $active) {
6232
            $ev = LOG_USER_ENABLE;
6233
        }
6234
        if (false !== $r) {
6235
            Event::addEvent($ev, LOG_USER_ID, $user_id);
6236
        }
6237
6238
        return $r;
6239
    }
6240
6241
    /**
6242
     * Get either a Gravatar URL or complete image tag for a specified email address.
6243
     *
6244
     * @param string $email The email address
6245
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
6246
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
6247
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
6248
     * @param bool   $img   True to return a complete IMG tag False for just the URL
6249
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
6250
     *
6251
     * @return string containing either just a URL or a complete image tag
6252
     * @source http://gravatar.com/site/implement/images/php/
6253
     */
6254
    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...
6255
        $email,
6256
        $s = 80,
6257
        $d = 'mm',
6258
        $r = 'g',
6259
        $img = false,
6260
        $atts = []
6261
    ) {
6262
        $url = 'http://www.gravatar.com/avatar/';
6263
        if (!empty($_SERVER['HTTPS'])) {
6264
            $url = 'https://secure.gravatar.com/avatar/';
6265
        }
6266
        $url .= md5(strtolower(trim($email)));
6267
        $url .= "?s=$s&d=$d&r=$r";
6268
        if ($img) {
6269
            $url = '<img src="'.$url.'"';
6270
            foreach ($atts as $key => $val) {
6271
                $url .= ' '.$key.'="'.$val.'"';
6272
            }
6273
            $url .= ' />';
6274
        }
6275
6276
        return $url;
6277
    }
6278
6279
    /**
6280
     * Count users in courses and if they have certificate.
6281
     * This function is resource intensive.
6282
     *
6283
     * @return array
6284
     * @throws Exception
6285
     * @throws \Doctrine\DBAL\Exception
6286
     */
6287
    public static function countUsersWhoFinishedCourses()
6288
    {
6289
        $courses = [];
6290
        $currentAccessUrlId = api_get_current_access_url_id();
6291
        $sql = "SELECT course.code, course.id as cid, cru.user_id
6292
                FROM course_rel_user cru
6293
                    JOIN course ON cru.c_id = course.id
6294
                    JOIN access_url_rel_user auru on cru.user_id = auru.user_id
6295
                    JOIN access_url_rel_course ON course.id = access_url_rel_course.c_id
6296
                WHERE access_url_rel_course.access_url_id = $currentAccessUrlId
6297
                ORDER BY course.code
6298
        ";
6299
        $res = Database::query($sql);
6300
        if (Database::num_rows($res) > 0) {
6301
            while ($row = Database::fetch_array($res)) {
6302
                if (!isset($courses[$row['code']])) {
6303
                    $courses[$row['code']] = [
6304
                        'subscribed' => 0,
6305
                        'finished' => 0,
6306
                    ];
6307
                }
6308
                $courses[$row['code']]['subscribed']++;
6309
                $entityManager = Database::getManager();
6310
                $repository = $entityManager->getRepository(GradebookCategory::class);
6311
                //todo check when have more than 1 gradebook
6312
                /** @var GradebookCategory $gradebook */
6313
                $gradebook = $repository->findOneBy(['course' => $row['cid']]);
6314
                if (!empty($gradebook)) {
6315
                    $finished = 0;
6316
                    Database::getManager()->persist($gradebook);
6317
                    $certificateRepo = $entityManager->getRepository(\Chamilo\CoreBundle\Entity\GradebookCertificate::class);
6318
                    $finished = $certificateRepo->getCertificateByUserId($gradebook->getId(), $row['user_id']);
6319
                    if (!empty($finished)) {
6320
                        $courses[$row['code']]['finished']++;
6321
                    }
6322
                }
6323
            }
6324
        }
6325
        return $courses;
6326
    }
6327
6328
    /**
6329
     * Count users in sessions and if they have certificate.
6330
     * This function is resource intensive.
6331
     *
6332
     * @return array
6333
     * @throws Exception
6334
     * @throws \Doctrine\DBAL\Exception
6335
     */
6336
    public static function countUsersWhoFinishedCoursesInSessions()
6337
    {
6338
        $coursesInSessions = [];
6339
        $currentAccessUrlId = api_get_current_access_url_id();
6340
        $sql = "SELECT course.code, srcru.session_id, srcru.user_id, session.title
6341
                FROM session_rel_course_rel_user srcru
6342
                    JOIN course ON srcru.c_id = course.id
6343
                    JOIN access_url_rel_session aurs on srcru.session_id = aurs.session_id
6344
                    JOIN session ON srcru.session_id = session.id
6345
                WHERE aurs.access_url_id = $currentAccessUrlId
6346
                ORDER BY course.code, session.title
6347
        ";
6348
        $res = Database::query($sql);
6349
        if (Database::num_rows($res) > 0) {
6350
            while ($row = Database::fetch_array($res)) {
6351
                $index = $row['code'].' ('.$row['title'].')';
6352
                if (!isset($coursesInSessions[$index])) {
6353
                    $coursesInSessions[$index] = [
6354
                        'subscribed' => 0,
6355
                        'finished' => 0,
6356
                    ];
6357
                }
6358
                $coursesInSessions[$index]['subscribed']++;
6359
                $entityManager = Database::getManager();
6360
                $repository = $entityManager->getRepository(GradebookCategory::class);
6361
                /** @var GradebookCategory $gradebook */
6362
                $gradebook = $repository->findOneBy(
6363
                    [
6364
                        'course' => $row['cid'],
6365
                        'sessionId' => $row['session_id'],
6366
                    ]
6367
                );
6368
                if (!empty($gradebook)) {
6369
                    $finished = 0;
6370
                    Database::getManager()->persist($gradebook);
6371
                    $certificateRepo = $entityManager->getRepository(\Chamilo\CoreBundle\Entity\GradebookCertificate::class);
6372
                    $finished = $certificateRepo->getCertificateByUserId($gradebook->getId(), $row['user_id']);
6373
                    if (!empty($finished)) {
6374
                        $coursesInSessions[$index]['finished']++;
6375
                    }
6376
                }
6377
            }
6378
        }
6379
        return $coursesInSessions;
6380
    }
6381
6382
    public static function redirectToResetPassword($userId): void
6383
    {
6384
        if ('true' !== api_get_setting('security.force_renew_password_at_first_login')) {
6385
            return;
6386
        }
6387
        $askPassword = self::get_extra_user_data_by_field(
6388
            $userId,
6389
            'ask_new_password'
6390
        );
6391
        if (!empty($askPassword) && isset($askPassword['ask_new_password']) &&
6392
            1 === (int) $askPassword['ask_new_password']
6393
        ) {
6394
            $uniqueId = api_get_unique_id();
6395
            $userObj = api_get_user_entity($userId);
6396
            $userObj->setConfirmationToken($uniqueId);
6397
            $userObj->setPasswordRequestedAt(new \DateTime());
6398
            Database::getManager()->persist($userObj);
6399
            Database::getManager()->flush();
6400
            $url = api_get_path(WEB_CODE_PATH).'auth/reset.php?token='.$uniqueId;
6401
            api_location($url);
6402
        }
6403
    }
6404
6405
}
6406