UserManager::sendUserConfirmationMail()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 31
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

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

1202
            $emailsubject = '['./** @scrutinizer ignore-type */ api_get_setting('site_name').'] '.sprintf(get_lang('Your registration on %s'), api_get_setting('site_name'));
Loading history...
1203
            $sender_name = api_get_person_name(
1204
                api_get_setting('administratorName'),
1205
                api_get_setting('administratorSurname'),
1206
                null,
1207
                PERSON_NAME_EMAIL_ADDRESS
1208
            );
1209
            $email_admin = api_get_setting('emailAdministrator');
1210
            $url = api_get_path(WEB_PATH);
1211
            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

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

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

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

2838
            $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...
2839
                $session_id,
2840
                null,
2841
                $ignore_visibility_for_admins
2842
            );
2843
2844
            if (SESSION_VISIBLE != $visibility) {
2845
                // Course Coach session visibility.
2846
                $blockedCourseCount = 0;
2847
                $closedVisibilityList = [
2848
                    COURSE_VISIBILITY_CLOSED,
2849
                    COURSE_VISIBILITY_HIDDEN,
2850
                ];
2851
2852
                foreach ($courseList as $course) {
2853
                    // Checking session visibility
2854
                    $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

2854
                    $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...
2855
                        $session_id,
2856
                        $course['real_id'],
2857
                        $ignore_visibility_for_admins
2858
                    );
2859
2860
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
2861
                    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

2861
                    if (false === $courseIsVisible || /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $sessionCourseVisibility) {
Loading history...
2862
                        $blockedCourseCount++;
2863
                    }
2864
                }
2865
2866
                // If all courses are blocked then no show in the list.
2867
                if ($blockedCourseCount === count($courseList)) {
2868
                    $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

2868
                    $visibility = /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE;
Loading history...
2869
                } else {
2870
                    $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...
2871
                }
2872
            }
2873
2874
            switch ($visibility) {
2875
                case SESSION_VISIBLE_READ_ONLY:
2876
                case SESSION_VISIBLE:
2877
                case SESSION_AVAILABLE:
2878
                    break;
2879
                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

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

3057
                $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...
3058
                $session = api_get_session_entity($session_id);
3059
3060
                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

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

3109
            $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...
3110
            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

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

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

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

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