UserManager::getSubscribedSessionsByYear()   F
last analyzed

Complexity

Conditions 28
Paths 6492

Size

Total Lines 102
Code Lines 62

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 28
eloc 62
nop 2
dl 0
loc 102
rs 0
c 0
b 0
f 0
nc 6492

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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
     * Validates the password.
54
     */
55
    public static function isPasswordValid(User $user, string $plainPassword): bool
56
    {
57
        /**
58
         * @psalm-suppress PrivateService
59
         */
60
        $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

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

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

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

1178
            $emailsubject = '['./** @scrutinizer ignore-type */ api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
Loading history...
1179
            $sender_name = api_get_person_name(
1180
                api_get_setting('administratorName'),
1181
                api_get_setting('administratorSurname'),
1182
                null,
1183
                PERSON_NAME_EMAIL_ADDRESS
1184
            );
1185
            $email_admin = api_get_setting('emailAdministrator');
1186
            $url = api_get_path(WEB_PATH);
1187
            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

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

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

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

2814
            $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...
2815
                $session_id,
2816
                null,
2817
                $ignore_visibility_for_admins
2818
            );
2819
2820
            if (SESSION_VISIBLE != $visibility) {
2821
                // Course Coach session visibility.
2822
                $blockedCourseCount = 0;
2823
                $closedVisibilityList = [
2824
                    COURSE_VISIBILITY_CLOSED,
2825
                    COURSE_VISIBILITY_HIDDEN,
2826
                ];
2827
2828
                foreach ($courseList as $course) {
2829
                    // Checking session visibility
2830
                    $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

2830
                    $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...
2831
                        $session_id,
2832
                        $course['real_id'],
2833
                        $ignore_visibility_for_admins
2834
                    );
2835
2836
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
2837
                    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

2837
                    if (false === $courseIsVisible || /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $sessionCourseVisibility) {
Loading history...
2838
                        $blockedCourseCount++;
2839
                    }
2840
                }
2841
2842
                // If all courses are blocked then no show in the list.
2843
                if ($blockedCourseCount === count($courseList)) {
2844
                    $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

2844
                    $visibility = /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE;
Loading history...
2845
                } else {
2846
                    $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...
2847
                }
2848
            }
2849
2850
            switch ($visibility) {
2851
                case SESSION_VISIBLE_READ_ONLY:
2852
                case SESSION_VISIBLE:
2853
                case SESSION_AVAILABLE:
2854
                    break;
2855
                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

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

3033
                $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...
3034
                $session = api_get_session_entity($session_id);
3035
3036
                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

3036
                if (/** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $session_visibility) {
Loading history...
3037
                    continue;
3038
                }
3039
3040
                $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
3041
                    $user,
3042
                    $session,
3043
                    SessionEntity::GENERAL_COACH,
3044
                    $url
3045
                );
3046
                $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
3047
                    $user,
3048
                    $session,
3049
                    SessionEntity::COURSE_COACH,
3050
                    $url
3051
                );
3052
3053
                // This query is horribly slow when more than a few thousand
3054
                // users and just a few sessions to which they are subscribed
3055
                $coursesInSession = array_map(
3056
                    function (SessionRelCourse $courseInSession) {
3057
                        $course = $courseInSession->getCourse();
3058
3059
                        return [
3060
                            'code' => $course->getCode(),
3061
                            'i' => $course->getTitle(),
3062
                            'l' => $course->getCourseLanguage(),
3063
                            'sort' => 1,
3064
                        ];
3065
                    },
3066
                    array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
3067
                );
3068
3069
                foreach ($coursesInSession as $result_row) {
3070
                    $result_row['t'] = $formattedUserName;
3071
                    $result_row['email'] = $user->getEmail();
3072
                    $result_row['access_start_date'] = $session->getAccessStartDate()?->format('Y-m-d H:i:s');
3073
                    $result_row['access_end_date'] = $session->getAccessEndDate()?->format('Y-m-d H:i:s');
3074
                    $result_row['session_id'] = $session->getId();
3075
                    $result_row['session_name'] = $session->getTitle();
3076
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3077
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3078
                    $personal_course_list[$key] = $result_row;
3079
                }
3080
            }
3081
        }
3082
3083
        foreach ($sessions as $enreg) {
3084
            $session_id = $enreg['id'];
3085
            $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

3085
            $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...
3086
            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

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

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

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

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