UserManager   F
last analyzed

Complexity

Total Complexity 740

Size/Duplication

Total Lines 6221
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 2799
dl 0
loc 6221
rs 0.8
c 2
b 0
f 0
wmc 740

124 Methods

Rating   Name   Duplication   Size   Complexity  
B get_all_user_tags() 0 73 8
A get_users_followed_by_drh() 0 26 1
A getExtraFilterableFields() 0 18 5
A getDrhListFromUser() 0 27 2
C get_search_form_where_extra_fields() 0 43 14
B get_search_form() 0 81 10
A isPasswordValid() 0 8 1
A getRepository() 0 3 1
A updatePassword() 0 6 1
A __construct() 0 2 1
B delete_users() 0 17 8
B get_user_list_by_ids() 0 33 8
A is_user_id_valid() 0 16 2
A canDeleteUser() 0 28 5
A is_username_empty() 0 3 1
A delete_user() 0 42 5
A is_username_valid() 0 3 2
A disable() 0 8 2
A create_unique_username() 0 24 4
A is_username_too_long() 0 3 1
F update_user() 0 212 28
A enable() 0 8 2
B get_user_list() 0 49 9
A activate_users() 0 21 4
A create_username() 0 27 5
A deactivate_users() 0 21 4
A purify_username() 0 22 3
A get_user_id_from_original_id() 0 27 2
C getUserListExtraConditions() 0 67 12
A is_username_available() 0 11 2
F create_user() 0 485 54
A get_extra_field_information_by_name() 0 5 1
D getUserListLike() 0 70 14
A get_extra_user_data_by_field_variable() 0 16 3
A get_extra_user_data_by_value() 0 27 4
A create_extra_field() 0 15 1
B getUserPathById() 0 36 8
C get_extra_user_data() 0 84 13
B get_user_productions() 0 28 8
A is_extra_field_available() 0 6 2
A get_extra_field_information() 0 5 1
A remove_user_production() 0 3 1
B getUserPicture() 0 41 8
A get_extra_user_data_for_tags() 0 5 1
A deleteUserPicture() 0 7 2
B get_extra_user_data_by_field() 0 56 8
C get_extra_fields() 0 73 11
A update_extra_field_value() 0 10 1
F get_sessions_by_category() 0 278 51
A get_extra_field_tags_information_by_name() 0 5 1
B build_production_list() 0 43 7
A get_extra_user_data_by_tags() 0 10 2
A update_user_picture() 0 11 4
F getSubscribedSessionsByYear() 0 102 28
A is_user_followed_by_drh() 0 16 2
A requestUsersToHRManager() 0 8 1
A addUserCareer() 0 13 3
B get_number_of_users() 0 48 8
A getGravatar() 0 23 4
A update_api_key() 0 26 6
A getFirstStudentBoss() 0 23 3
A getTimeSpentInCourses() 0 36 5
B loginAsUser() 0 64 5
A updateUserCareer() 0 15 2
A subscribeUsersToHRManager() 0 11 1
A is_admin() 0 11 2
A isTeacherOfStudent() 0 12 2
B get_api_keys() 0 31 8
A getCommonCoursesBetweenTeacherAndStudent() 0 25 4
A removeAllBossFromStudent() 0 14 2
A get_user_tags() 0 24 3
A loginDelete() 0 6 1
A getUserCareers() 0 16 1
A get_all_administrators() 0 30 5
A formatUserFullName() 0 11 3
F anonymize() 0 123 21
A get_tag_id() 0 15 2
A getUserProfileLink() 0 10 3
A getUsersByName() 0 20 2
A clearHrmRequestsForUser() 0 9 1
A getUsersByOfficialCode() 0 17 2
A getStudentBossList() 0 22 2
B add_api_key() 0 25 7
A delete_user_tags() 0 17 5
A process_tags() 0 12 3
A get_api_key_id() 0 22 5
B get_user_tags_to_string() 0 38 7
A redirectToResetPassword() 0 20 5
B anonymizeUserWithVerification() 0 36 8
A cleanUserRequestsOfRemoval() 0 20 4
A update_all_user_languages() 0 10 3
B deleteUserWithVerification() 0 34 8
A removeUserAdmin() 0 9 2
A get_top_tags() 0 24 3
F getUsersFollowedByUser() 0 238 25
A addUserAsAdmin() 0 12 2
A sendUserConfirmationMail() 0 31 3
A subscribeBossToUsers() 0 8 1
A userHasCareer() 0 10 1
A getUserCareer() 0 10 1
A getUserSubscriptionTab() 0 39 4
B subscribeUserToBossList() 0 61 9
A get_user_id_from_username() 0 20 4
A getOfficialCodeGrouped() 0 14 2
A is_session_course_coach() 0 22 2
A is_user_certified() 0 19 4
A getUsersFollowedByStudentBoss() 0 26 1
A logInAsFirstAdmin() 0 15 5
A change_active_state() 0 21 4
A get_info_gradebook_certificate() 0 45 5
A get_tags() 0 23 4
B countUsersWhoFinishedCoursesInSessions() 0 44 6
F get_personal_session_course_list() 0 222 24
F getSessionsCalendarByYear() 0 52 15
A userIsBossOfStudent() 0 12 3
B countUsersWhoFinishedCourses() 0 39 6
B get_user_id_of_course_admin_or_session_admin() 0 47 6
B subscribeUsersToUser() 0 75 9
A get_favicon_from_url() 0 17 4
A add_tag() 0 44 5
A createDataPrivacyExtraFields() 0 33 1
A delete_api_key() 0 25 6
F get_courses_list_by_session() 0 161 25
A get_tag_id_from_id() 0 14 2

How to fix   Complexity   

Complex Class

Complex classes like UserManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use UserManager, and based on these observations, apply Extract Interface, too.

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

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

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

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

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

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

986
            $emailsubject = '['./** @scrutinizer ignore-type */ api_get_setting('siteName').'] '.get_lang('Your registration on').' '.api_get_setting('siteName');
Loading history...
987
            $sender_name = api_get_person_name(
988
                api_get_setting('administratorName'),
989
                api_get_setting('administratorSurname'),
990
                null,
991
                PERSON_NAME_EMAIL_ADDRESS
992
            );
993
            $email_admin = api_get_setting('emailAdministrator');
994
            $url = api_get_path(WEB_PATH);
995
            if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

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

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

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

1414
        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...
1415
            if ($idCampus) {
1416
                $urlId = $idCampus;
1417
            } else {
1418
                $urlId = api_get_current_access_url_id();
1419
            }
1420
            $sql .= " INNER JOIN $userUrlTable url_user
1421
                      ON (user.id = url_user.user_id)
1422
                      WHERE url_user.access_url_id = $urlId";
1423
        } else {
1424
            $sql .= " WHERE 1=1 ";
1425
        }
1426
1427
        if (count($conditions) > 0) {
1428
            foreach ($conditions as $field => $value) {
1429
                $field = Database::escape_string($field);
1430
                $value = Database::escape_string($value);
1431
                $sql .= " AND $field = '$value'";
1432
            }
1433
        }
1434
1435
        if (count($order_by) > 0) {
1436
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
1437
        }
1438
1439
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1440
            $limit_from = (int) $limit_from;
1441
            $limit_to = (int) $limit_to;
1442
            $sql .= " LIMIT $limit_from, $limit_to";
1443
        }
1444
        $sql_result = Database::query($sql);
1445
        while ($result = Database::fetch_array($sql_result)) {
1446
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1447
            $return_array[] = $result;
1448
        }
1449
1450
        return $return_array;
1451
    }
1452
1453
    public static function getUserListExtraConditions(
1454
        $conditions = [],
1455
        $order_by = [],
1456
        $limit_from = false,
1457
        $limit_to = false,
1458
        $idCampus = null,
1459
        $extraConditions = '',
1460
        $getCount = false
1461
    ) {
1462
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1463
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1464
        $return_array = [];
1465
        $sql = "SELECT user.*, user.id as user_id FROM $user_table user ";
1466
1467
        if ($getCount) {
1468
            $sql = "SELECT count(user.id) count FROM $user_table user ";
1469
        }
1470
1471
        if (api_is_multiple_url_enabled()) {
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlHelper::isMultiple ( Ignorable by Annotation )

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

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

2644
            $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...
2645
                $session_id,
2646
                null,
2647
                $ignore_visibility_for_admins
2648
            );
2649
2650
            if (SESSION_VISIBLE != $visibility) {
2651
                // Course Coach session visibility.
2652
                $blockedCourseCount = 0;
2653
                $closedVisibilityList = [
2654
                    COURSE_VISIBILITY_CLOSED,
2655
                    COURSE_VISIBILITY_HIDDEN,
2656
                ];
2657
2658
                foreach ($courseList as $course) {
2659
                    // Checking session visibility
2660
                    $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

2660
                    $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...
2661
                        $session_id,
2662
                        $course['real_id'],
2663
                        $ignore_visibility_for_admins
2664
                    );
2665
2666
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
2667
                    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

2667
                    if (false === $courseIsVisible || /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $sessionCourseVisibility) {
Loading history...
2668
                        $blockedCourseCount++;
2669
                    }
2670
                }
2671
2672
                // If all courses are blocked then no show in the list.
2673
                if ($blockedCourseCount === count($courseList)) {
2674
                    $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

2674
                    $visibility = /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE;
Loading history...
2675
                } else {
2676
                    $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...
2677
                }
2678
            }
2679
2680
            switch ($visibility) {
2681
                case SESSION_VISIBLE_READ_ONLY:
2682
                case SESSION_VISIBLE:
2683
                case SESSION_AVAILABLE:
2684
                    break;
2685
                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

2685
                case /** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE:
Loading history...
2686
                    if (false === $ignore_visibility_for_admins) {
2687
                        continue 2;
2688
                    }
2689
            }
2690
2691
            $collapsed = '';
2692
            $collapsedAction = '';
2693
            if ($collapsable) {
2694
                $collapsableData = SessionManager::getCollapsableData(
2695
                    $user_id,
2696
                    $session_id,
2697
                    $extraField,
2698
                    $collapsableLink
2699
                );
2700
                $collapsed = $collapsableData['collapsed'];
2701
                $collapsedAction = $collapsableData['collapsable_link'];
2702
            }
2703
2704
            $categories[$row['session_category_id']]['sessions'][] = [
2705
                'session_name' => $row['title'],
2706
                'session_id' => $row['id'],
2707
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
2708
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
2709
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
2710
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
2711
                'courses' => $courseList,
2712
                'collapsed' => $collapsed,
2713
                'collapsable_link' => $collapsedAction,
2714
                'duration' => $row['duration'],
2715
            ];
2716
        }
2717
2718
        return $categories;
2719
    }
2720
2721
    /**
2722
     * Gives a list of [session_id-course_code] => [status] for the current user.
2723
     *
2724
     * @param  int  $user_id
2725
     * @param  int  $sessionLimit
2726
     *
2727
     * @return array list of statuses (session_id-course_code => status)
2728
     *
2729
     * @throws Exception
2730
     */
2731
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
2732
    {
2733
        // Database Table Definitions
2734
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2735
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2736
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2737
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
2738
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2739
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2740
2741
        $user_id = (int) $user_id;
2742
2743
        if (empty($user_id)) {
2744
            return [];
2745
        }
2746
2747
        $sessionRepo = Container::getSessionRepository();
2748
2749
        $user = api_get_user_entity($user_id);
2750
        $url = null;
2751
        $formattedUserName = Container::$container->get(NameConvention::class)->getPersonName($user);
2752
2753
        // We filter the courses from the URL
2754
        $join_access_url = $where_access_url = '';
2755
        if (api_get_multiple_access_url()) {
2756
            $access_url_id = api_get_current_access_url_id();
2757
            if (-1 != $access_url_id) {
2758
                $url = api_get_url_entity($access_url_id);
2759
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
2760
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
2761
                $where_access_url = " AND access_url_id = $access_url_id ";
2762
            }
2763
        }
2764
2765
        // Courses in which we subscribed out of any session
2766
2767
        $sql = "SELECT
2768
                    course.code,
2769
                    course_rel_user.status course_rel_status,
2770
                    course_rel_user.sort sort,
2771
                    course_rel_user.user_course_cat user_course_cat
2772
                 FROM $tbl_course_user course_rel_user
2773
                 LEFT JOIN $tbl_course course
2774
                 ON course.id = course_rel_user.c_id
2775
                 $join_access_url
2776
                 WHERE
2777
                    course_rel_user.user_id = '".$user_id."' AND
2778
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
2779
                    $where_access_url
2780
                 ORDER BY course_rel_user.sort, course.title ASC";
2781
2782
        $course_list_sql_result = Database::query($sql);
2783
        $personal_course_list = [];
2784
        if (Database::num_rows($course_list_sql_result) > 0) {
2785
            while ($result_row = Database::fetch_assoc($course_list_sql_result)) {
2786
                $course_info = api_get_course_info($result_row['code']);
2787
                $result_row['course_info'] = $course_info;
2788
                $personal_course_list[] = $result_row;
2789
            }
2790
        }
2791
2792
        $coachCourseConditions = '';
2793
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
2794
        if (api_is_allowed_to_create_course()) {
2795
            $sessionListFromCourseCoach = [];
2796
            $sql = " SELECT DISTINCT session_id
2797
                    FROM $tbl_session_course_user
2798
                    WHERE user_id = $user_id AND status = ".SessionEntity::COURSE_COACH;
2799
2800
            $result = Database::query($sql);
2801
            if (Database::num_rows($result)) {
2802
                $result = Database::store_result($result);
2803
                foreach ($result as $session) {
2804
                    $sessionListFromCourseCoach[] = $session['session_id'];
2805
                }
2806
            }
2807
            if (!empty($sessionListFromCourseCoach)) {
2808
                $condition = implode("','", $sessionListFromCourseCoach);
2809
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
2810
            }
2811
        }
2812
2813
        // Get the list of sessions where the user is subscribed
2814
        // This is divided into two different queries
2815
        $sessions = [];
2816
        $sessionLimitRestriction = '';
2817
        if (!empty($sessionLimit)) {
2818
            $sessionLimit = (int) $sessionLimit;
2819
            $sessionLimitRestriction = "LIMIT $sessionLimit";
2820
        }
2821
2822
        $sql = "SELECT DISTINCT s.id, s.title, access_start_date, access_end_date
2823
                FROM $tbl_session_user su INNER JOIN $tbl_session s
2824
                ON (s.id = su.session_id)
2825
                WHERE (
2826
                    su.user_id = $user_id AND
2827
                    su.relation_type = ".SessionEntity::STUDENT."
2828
                )
2829
                $coachCourseConditions
2830
                ORDER BY access_start_date, access_end_date, s.title
2831
                $sessionLimitRestriction
2832
        ";
2833
2834
        $result = Database::query($sql);
2835
        if (Database::num_rows($result) > 0) {
2836
            while ($row = Database::fetch_assoc($result)) {
2837
                $sessions[$row['id']] = $row;
2838
            }
2839
        }
2840
2841
        $sql = "SELECT DISTINCT
2842
                s.id, s.title, s.access_start_date, s.access_end_date
2843
                FROM $tbl_session s
2844
                INNER JOIN $tbl_session_user sru ON sru.session_id = s.id
2845
                WHERE (
2846
                    sru.user_id = $user_id AND sru.relation_type = ".SessionEntity::GENERAL_COACH."
2847
                )
2848
                $coachCourseConditions
2849
                ORDER BY s.access_start_date, s.access_end_date, s.title";
2850
2851
        $result = Database::query($sql);
2852
        if (Database::num_rows($result) > 0) {
2853
            while ($row = Database::fetch_assoc($result)) {
2854
                if (empty($sessions[$row['id']])) {
2855
                    $sessions[$row['id']] = $row;
2856
                }
2857
            }
2858
        }
2859
2860
        if (api_is_allowed_to_create_course()) {
2861
            foreach ($sessions as $enreg) {
2862
                $session_id = $enreg['id'];
2863
                $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

2863
                $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...
2864
                $session = api_get_session_entity($session_id);
2865
2866
                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

2866
                if (/** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $session_visibility) {
Loading history...
2867
                    continue;
2868
                }
2869
2870
                $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
2871
                    $user,
2872
                    $session,
2873
                    SessionEntity::GENERAL_COACH,
2874
                    $url
2875
                );
2876
                $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
2877
                    $user,
2878
                    $session,
2879
                    SessionEntity::COURSE_COACH,
2880
                    $url
2881
                );
2882
2883
                // This query is horribly slow when more than a few thousand
2884
                // users and just a few sessions to which they are subscribed
2885
                $coursesInSession = array_map(
2886
                    function (SessionRelCourse $courseInSession) {
2887
                        $course = $courseInSession->getCourse();
2888
2889
                        return [
2890
                            'code' => $course->getCode(),
2891
                            'i' => $course->getTitle(),
2892
                            'l' => $course->getCourseLanguage(),
2893
                            'sort' => 1,
2894
                        ];
2895
                    },
2896
                    array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
2897
                );
2898
2899
                foreach ($coursesInSession as $result_row) {
2900
                    $result_row['t'] = $formattedUserName;
2901
                    $result_row['email'] = $user->getEmail();
2902
                    $result_row['access_start_date'] = $session->getAccessStartDate()?->format('Y-m-d H:i:s');
2903
                    $result_row['access_end_date'] = $session->getAccessEndDate()?->format('Y-m-d H:i:s');
2904
                    $result_row['session_id'] = $session->getId();
2905
                    $result_row['session_name'] = $session->getTitle();
2906
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
2907
                    $key = $result_row['session_id'].' - '.$result_row['code'];
2908
                    $personal_course_list[$key] = $result_row;
2909
                }
2910
            }
2911
        }
2912
2913
        foreach ($sessions as $enreg) {
2914
            $session_id = $enreg['id'];
2915
            $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

2915
            $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...
2916
            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

2916
            if (/** @scrutinizer ignore-deprecated */ SESSION_INVISIBLE == $session_visibility) {
Loading history...
2917
                continue;
2918
            }
2919
2920
            /* This query is very similar to the above query,
2921
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
2922
            $sql = "SELECT DISTINCT
2923
                course.code code,
2924
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
2925
                email,
2926
                course.course_language l,
2927
                1 sort,
2928
                access_start_date,
2929
                access_end_date,
2930
                session.id as session_id,
2931
                session.title as session_name,
2932
                IF((session_course_user.user_id = 3 AND session_course_user.status = ".SessionEntity::COURSE_COACH."),'2', '5')
2933
            FROM $tbl_session_course_user as session_course_user
2934
            INNER JOIN $tbl_course AS course
2935
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
2936
            INNER JOIN $tbl_session as session
2937
            ON session_course_user.session_id = session.id
2938
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
2939
            WHERE session_course_user.user_id = $user_id
2940
            ORDER BY i";
2941
2942
            $course_list_sql_result = Database::query($sql);
2943
            while ($result_row = Database::fetch_assoc($course_list_sql_result)) {
2944
                $result_row['course_info'] = api_get_course_info($result_row['code']);
2945
                $key = $result_row['session_id'].' - '.$result_row['code'];
2946
                if (!isset($personal_course_list[$key])) {
2947
                    $personal_course_list[$key] = $result_row;
2948
                }
2949
            }
2950
        }
2951
2952
        return $personal_course_list;
2953
    }
2954
2955
    /**
2956
     * Gives a list of courses for the given user in the given session.
2957
     *
2958
     * @param int $user_id
2959
     * @param int $session_id
2960
     *
2961
     * @return array list of statuses (session_id-course_code => status)
2962
     */
2963
    public static function get_courses_list_by_session($user_id, $session_id)
2964
    {
2965
        // Database Table Definitions
2966
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
2967
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2968
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
2969
2970
        $user_id = (int) $user_id;
2971
        $session_id = (int) $session_id;
2972
2973
        $sessionRepo = Container::getSessionRepository();
2974
2975
        $user = api_get_user_entity($user_id);
2976
        $session = api_get_session_entity($session_id);
2977
        $url = null;
2978
2979
        // We filter the courses from the URL
2980
        $join_access_url = $where_access_url = '';
2981
        if (api_get_multiple_access_url()) {
2982
            $urlId = api_get_current_access_url_id();
2983
            if (-1 != $urlId) {
2984
                $url = api_get_url_entity($urlId);
2985
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
2986
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
2987
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
2988
            }
2989
        }
2990
2991
        /* This query is very similar to the query below, but it will check the
2992
        session_rel_course_user table if there are courses registered
2993
        to our user or not */
2994
        $sql = "SELECT DISTINCT
2995
                    c.title,
2996
                    c.visibility,
2997
                    c.id as real_id,
2998
                    c.code as course_code,
2999
                    sc.position,
3000
                    c.unsubscribe
3001
                FROM $tbl_session_course_user as scu
3002
                INNER JOIN $tbl_session_course sc
3003
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3004
                INNER JOIN $tableCourse as c
3005
                ON (scu.c_id = c.id)
3006
                $join_access_url
3007
                WHERE
3008
                    scu.user_id = $user_id AND
3009
                    scu.session_id = $session_id
3010
                    $where_access_url
3011
                ORDER BY sc.position ASC";
3012
3013
        $myCourseList = [];
3014
        $courses = [];
3015
        $result = Database::query($sql);
3016
        if (Database::num_rows($result) > 0) {
3017
            while ($result_row = Database::fetch_assoc($result)) {
3018
                $result_row['status'] = 5;
3019
                if (!in_array($result_row['real_id'], $courses)) {
3020
                    $position = $result_row['position'];
3021
                    if (!isset($myCourseList[$position])) {
3022
                        $myCourseList[$position] = $result_row;
3023
                    } else {
3024
                        $myCourseList[] = $result_row;
3025
                    }
3026
                    $courses[] = $result_row['real_id'];
3027
                }
3028
            }
3029
        }
3030
3031
        if (api_is_allowed_to_create_course()) {
3032
            $coursesAsGeneralCoach = $sessionRepo->getSessionCoursesByStatusInUserSubscription(
3033
                $user,
3034
                $session,
3035
                SessionEntity::GENERAL_COACH,
3036
                $url
3037
            );
3038
            $coursesAsCourseCoach = $sessionRepo->getSessionCoursesByStatusInCourseSubscription(
3039
                $user,
3040
                $session,
3041
                SessionEntity::COURSE_COACH,
3042
                $url
3043
            );
3044
3045
            $coursesInSession = array_map(
3046
                function (SessionRelCourse $courseInSession) {
3047
                    $course = $courseInSession->getCourse();
3048
3049
                    return [
3050
                        'title' => $course->getTitle(),
3051
                        'visibility' => $course->getVisibility(),
3052
                        'real_id' => $course->getId(),
3053
                        'course_code' => $course->getCode(),
3054
                        'position' => $courseInSession->getPosition(),
3055
                        'unsubscribe' => $course->getUnsubscribe(),
3056
                    ];
3057
                },
3058
                array_merge($coursesAsGeneralCoach, $coursesAsCourseCoach)
3059
            );
3060
3061
            foreach ($coursesInSession as $result_row) {
3062
                $result_row['status'] = 2;
3063
                if (!in_array($result_row['real_id'], $courses)) {
3064
                    $position = $result_row['position'];
3065
                    if (!isset($myCourseList[$position])) {
3066
                        $myCourseList[$position] = $result_row;
3067
                    } else {
3068
                        $myCourseList[] = $result_row;
3069
                    }
3070
                    $courses[] = $result_row['real_id'];
3071
                }
3072
            }
3073
        }
3074
3075
        if (api_is_drh()) {
3076
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3077
            $sessionList = array_keys($sessionList);
3078
            if (in_array($session_id, $sessionList)) {
3079
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3080
                if (!empty($courseList)) {
3081
                    foreach ($courseList as $course) {
3082
                        if (!in_array($course['id'], $courses)) {
3083
                            $position = $course['position'];
3084
                            if (!isset($myCourseList[$position])) {
3085
                                $myCourseList[$position] = $course;
3086
                            } else {
3087
                                $myCourseList[] = $course;
3088
                            }
3089
                        }
3090
                    }
3091
                }
3092
            }
3093
        } else {
3094
            //check if user is general coach for this session
3095
            if ($session && $session->hasUserAsGeneralCoach($user)) {
3096
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3097
                if (!empty($courseList)) {
3098
                    foreach ($courseList as $course) {
3099
                        if (!in_array($course['id'], $courses)) {
3100
                            $position = $course['position'];
3101
                            if (!isset($myCourseList[$position])) {
3102
                                $myCourseList[$position] = $course;
3103
                            } else {
3104
                                $myCourseList[] = $course;
3105
                            }
3106
                        }
3107
                    }
3108
                }
3109
            }
3110
        }
3111
3112
        if (!empty($myCourseList)) {
3113
            ksort($myCourseList);
3114
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
3115
            if (empty($checkPosition)) {
3116
                // The session course list doesn't have any position,
3117
                // then order the course list by course code
3118
                $list = array_column($myCourseList, 'course_code');
3119
                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

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

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

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

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

5305
            Event::/** @scrutinizer ignore-call */ 
5306
                   registerLog($logInfo);

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

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

Loading history...
5306
5307
            // Logout the current user
5308
            self::loginDelete(api_get_user_id());
5309
5310
            return true;
5311
5312
            Session::erase('_user');
0 ignored issues
show
Unused Code introduced by
ChamiloSession::erase('_user') is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

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