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
     * Repository is use to query the DB, selects, etc.
54
     *
55
     * @return UserRepository
56
     */
57
    public static function getRepository()
58
    {
59
        return Container::getUserRepository();
60
    }
61
62
    /**
63
     * Validates the password.
64
     */
65
    public static function isPasswordValid(User $user, string $plainPassword): bool
66
    {
67
        /**
68
         * @psalm-suppress PrivateService
69
         */
70
        $hasher = Container::$container->get('security.user_password_hasher');
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

Loading history...
372
                $access_url_id = api_get_current_access_url_id();
373
                if (-1 != $access_url_id) {
374
                    $urlInfo = api_get_access_url($access_url_id);
375
                    if ($urlInfo) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $urlInfo of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

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

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

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

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

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

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

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

1190
            $emailsubject = '['./** @scrutinizer ignore-type */ api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
Loading history...
1191
            $sender_name = api_get_person_name(
1192
                api_get_setting('administratorName'),
1193
                api_get_setting('administratorSurname'),
1194
                null,
1195
                PERSON_NAME_EMAIL_ADDRESS
1196
            );
1197
            $email_admin = api_get_setting('emailAdministrator');
1198
            $url = api_get_path(WEB_PATH);
1199
            if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlUtil::isMultiple ( Ignorable by Annotation )

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

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

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

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

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

1618
        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...
1619
            if ($idCampus) {
1620
                $urlId = $idCampus;
1621
            } else {
1622
                $urlId = api_get_current_access_url_id();
1623
            }
1624
            $sql .= " INNER JOIN $userUrlTable url_user
1625
                      ON (user.id = url_user.user_id)
1626
                      WHERE url_user.access_url_id = $urlId";
1627
        } else {
1628
            $sql .= " WHERE 1=1 ";
1629
        }
1630
1631
        if (count($conditions) > 0) {
1632
            foreach ($conditions as $field => $value) {
1633
                $field = Database::escape_string($field);
1634
                $value = Database::escape_string($value);
1635
                $sql .= " AND $field = '$value'";
1636
            }
1637
        }
1638
1639
        if (count($order_by) > 0) {
1640
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1641
        }
1642
1643
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1644
            $limit_from = (int) $limit_from;
1645
            $limit_to = (int) $limit_to;
1646
            $sql .= " LIMIT $limit_from, $limit_to";
1647
        }
1648
        $sql_result = Database::query($sql);
1649
        while ($result = Database::fetch_array($sql_result)) {
1650
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1651
            $return_array[] = $result;
1652
        }
1653
1654
        return $return_array;
1655
    }
1656
1657
    public static function getUserListExtraConditions(
1658
        $conditions = [],
1659
        $order_by = [],
1660
        $limit_from = false,
1661
        $limit_to = false,
1662
        $idCampus = null,
1663
        $extraConditions = '',
1664
        $getCount = false
1665
    ) {
1666
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1667
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1668
        $return_array = [];
1669
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1670
1671
        if ($getCount) {
1672
            $sql = "SELECT count(user.id) count FROM $user_table user ";
1673
        }
1674
1675
        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

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

2848
            $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...
2849
                $session_id,
2850
                null,
2851
                $ignore_visibility_for_admins
2852
            );
2853
2854
            if (SESSION_VISIBLE != $visibility) {
2855
                // Course Coach session visibility.
2856
                $blockedCourseCount = 0;
2857
                $closedVisibilityList = [
2858
                    COURSE_VISIBILITY_CLOSED,
2859
                    COURSE_VISIBILITY_HIDDEN,
2860
                ];
2861
2862
                foreach ($courseList as $course) {
2863
                    // Checking session visibility
2864
                    $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

2864
                    $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...
2865
                        $session_id,
2866
                        $course['real_id'],
2867
                        $ignore_visibility_for_admins
2868
                    );
2869
2870
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
2871
                    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

2871
                    if (false === $courseIsVisible || /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $sessionCourseVisibility) {
Loading history...
2872
                        $blockedCourseCount++;
2873
                    }
2874
                }
2875
2876
                // If all courses are blocked then no show in the list.
2877
                if ($blockedCourseCount === count($courseList)) {
2878
                    $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

2878
                    $visibility = /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE;
Loading history...
2879
                } else {
2880
                    $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...
2881
                }
2882
            }
2883
2884
            switch ($visibility) {
2885
                case SESSION_VISIBLE_READ_ONLY:
2886
                case SESSION_VISIBLE:
2887
                case SESSION_AVAILABLE:
2888
                    break;
2889
                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

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

3067
                $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...
3068
                $session = api_get_session_entity($session_id);
3069
3070
                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

3070
                if (/** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $session_visibility) {
Loading history...
3071
                    continue;
3072
                }
3073
3074
                $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
3075
                    $user,
3076
                    $session,
3077
                    SessionEntity::GENERAL_COACH,
3078
                    $url
3079
                );
3080
                $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
3081
                    $user,
3082
                    $session,
3083
                    SessionEntity::COURSE_COACH,
3084
                    $url
3085
                );
3086
3087
                // This query is horribly slow when more than a few thousand
3088
                // users and just a few sessions to which they are subscribed
3089
                $coursesInSession = array_map(
3090
                    function (SessionRelCourse $courseInSession) {
3091
                        $course = $courseInSession->getCourse();
3092
3093
                        return [
3094
                            'code' => $course->getCode(),
3095
                            'i' => $course->getTitle(),
3096
                            'l' => $course->getCourseLanguage(),
3097
                            'sort' => 1,
3098
                        ];
3099
                    },
3100
                    array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
3101
                );
3102
3103
                foreach ($coursesInSession as $result_row) {
3104
                    $result_row['t'] = $formattedUserName;
3105
                    $result_row['email'] = $user->getEmail();
3106
                    $result_row['access_start_date'] = $session->getAccessStartDate()?->format('Y-m-d H:i:s');
3107
                    $result_row['access_end_date'] = $session->getAccessEndDate()?->format('Y-m-d H:i:s');
3108
                    $result_row['session_id'] = $session->getId();
3109
                    $result_row['session_name'] = $session->getTitle();
3110
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3111
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3112
                    $personal_course_list[$key] = $result_row;
3113
                }
3114
            }
3115
        }
3116
3117
        foreach ($sessions as $enreg) {
3118
            $session_id = $enreg['id'];
3119
            $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

3119
            $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...
3120
            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

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

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

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

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