UserManager   F
last analyzed

Complexity

Total Complexity 1038

Size/Duplication

Total Lines 8239
Duplicated Lines 0 %

Importance

Changes 12
Bugs 0 Features 2
Metric Value
wmc 1038
eloc 3917
c 12
b 0
f 2
dl 0
loc 8239
rs 0.8

163 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 2 1
A updateExpirationDate() 0 9 1
A encryptPassword() 0 7 1
A isPasswordValid() 0 5 1
A getRepository() 0 5 1
A getPasswordEncryption() 0 8 2
A setPasswordEncryption() 0 3 1
A getManager() 0 19 2
B detectPasswordEncryption() 0 24 10
A updatePassword() 0 9 1
A checkPassword() 0 21 4
B delete_users() 0 17 8
A update_openid() 0 18 4
F create_user() 0 542 71
A ensureCASUserExtraFieldExists() 0 26 6
A canDeleteUser() 0 30 5
A createCASAuthenticatedUserFromLDAP() 0 27 4
B casUserLoginName() 0 42 8
A updateUserFromLDAP() 0 36 4
F delete_user() 0 219 20
A activate_users() 0 21 4
A deactivate_users() 0 21 4
C updateCasUser() 0 61 17
A createCASAuthenticatedUserFromScratch() 0 27 3
B get_user_list_by_ids() 0 33 8
A is_user_id_valid() 0 16 2
A is_username_empty() 0 3 1
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 307 40
A enable() 0 8 2
F get_user_list() 0 99 16
A create_username() 0 27 5
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
A get_all_extra_field_by_type() 0 5 1
F getSubscribedSessionsByYear() 0 102 28
A is_user_followed_by_drh() 0 18 2
A requestUsersToHRManager() 0 8 1
A addUserCareer() 0 18 3
A get_extra_field_information_by_name() 0 5 1
B get_number_of_users() 0 53 9
A update_api_key() 0 26 6
A getFirstStudentBoss() 0 23 3
A getTimeSpentInCourses() 0 36 5
B get_all_user_tags() 0 73 8
B loginAsUser() 0 62 5
F getUserListLike() 0 69 14
A get_extra_user_data_by_field_variable() 0 16 3
A updateUserCareer() 0 15 2
A subscribeUsersToHRManager() 0 11 1
A get_extra_user_data_by_value() 0 28 4
A is_admin() 0 11 2
F updateCourseRelationTypeExLearner() 0 125 28
A isTeacherOfStudent() 0 12 2
A create_extra_field() 0 15 1
B getUserPathById() 0 36 8
C get_extra_user_data() 0 85 13
B get_api_keys() 0 31 8
A getCommonCoursesBetweenTeacherAndStudent() 0 25 4
B get_user_productions() 0 26 8
A removeAllBossFromStudent() 0 14 2
A is_extra_field_available() 0 6 2
A get_extra_field_information() 0 5 1
A get_user_tags() 0 24 3
A loginDelete() 0 6 1
A getUserCareers() 0 16 1
A get_all_administrators() 0 29 4
A remove_user_production() 0 11 2
A formatUserFullName() 0 11 3
F anonymize() 0 104 19
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
C get_user_upload_files_by_course() 0 49 16
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
D getUserPicture() 0 91 18
A get_extra_user_data_for_tags() 0 5 1
B get_user_tags_to_string() 0 38 7
A get_api_key_id() 0 22 5
A deleteUserPicture() 0 3 1
B anonymizeUserWithVerification() 0 36 8
A remove_user_rel_user() 0 71 4
B relate_users() 0 50 6
A cleanUserRequestsOfRemoval() 0 20 4
A update_all_user_languages() 0 10 3
A get_users_followed_by_drh() 0 26 1
B get_extra_user_data_by_field() 0 56 8
B deleteUserWithVerification() 0 34 8
A removeUserAdmin() 0 9 2
C get_extra_fields() 0 75 12
A get_top_tags() 0 24 3
F getUsersFollowedByUser() 0 248 26
A deleteUserFiles() 0 6 1
A blockIfMaxLoginAttempts() 0 47 5
A addUserAsAdmin() 0 12 3
A sendUserConfirmationMail() 0 31 3
A subscribeBossToUsers() 0 8 1
A update_extra_field_value() 0 10 1
A userHasCareer() 0 10 1
A getUserCareer() 0 10 1
B getUserSubscriptionTab() 0 46 7
A getExtraFilterableFields() 0 18 5
B subscribeUserToBossList() 0 69 9
F get_sessions_by_category() 0 279 51
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 3
B build_user_extra_file_list() 0 44 7
A remove_user_extra_file() 0 16 3
A getDrhListFromUser() 0 27 2
B update_user_extra_file() 0 40 8
B get_user_picture_path_by_id() 0 51 8
A getUsersFollowedByStudentBoss() 0 26 1
A get_extra_field_tags_information_by_name() 0 5 1
B build_production_list() 0 41 7
A logInAsFirstAdmin() 0 15 5
B get_info_gradebook_certificate() 0 59 7
A get_tags() 0 25 4
A get_extra_user_data_by_tags() 0 10 2
B getUserPicturePathById() 0 47 8
F update_user_picture() 0 108 21
F get_personal_session_course_list() 0 210 25
F getSessionsCalendarByYear() 0 52 15
A userIsBossOfStudent() 0 12 3
A show_menu() 0 14 1
B get_user_extra_files() 0 31 7
A get_user_id_of_course_admin_or_session_admin() 0 47 5
B subscribeUsersToUser() 0 78 9
A get_favicon_from_url() 0 17 4
C get_search_form_where_extra_fields() 0 43 14
B add_tag() 0 57 6
A createDataPrivacyExtraFields() 0 33 1
A delete_api_key() 0 25 6
F get_courses_list_by_session() 0 172 29
B get_search_form() 0 81 10
A get_tag_id_from_id() 0 14 2
A getScriptFunctionForActiveFilter() 0 34 1
A getEncoderFactory() 0 8 1
A getAllowedRolesAsTeacher() 0 12 2
B getUserStatusList() 0 31 7
A getGravatar() 0 23 4
A getUsersByMail() 0 17 2
A getExpirationDateByRole() 0 20 4
A getEncoder() 0 5 1
C redirectToResetPassword() 0 74 13
A generateUserHash() 0 10 2
B getActiveFilterForTable() 0 38 6
A change_active_state() 0 21 4
B countUsersWhoFinishedCoursesInSessions() 0 47 6
A isEmailingAllowed() 0 16 4
B countUsersWhoFinishedCourses() 0 42 6
A decryptUserHash() 0 10 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
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
6
use Chamilo\CoreBundle\Entity\Repository\AccessUrlRepository;
7
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
8
use Chamilo\CoreBundle\Entity\SkillRelUser;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, SkillRelUser. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
9
use Chamilo\CoreBundle\Entity\SkillRelUserComment;
10
use Chamilo\CoreBundle\Entity\TrackELoginAttempt;
11
use Chamilo\UserBundle\Entity\User;
12
use Chamilo\UserBundle\Repository\UserRepository;
13
use ChamiloSession as Session;
14
use Symfony\Component\Filesystem\Filesystem;
15
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
16
17
/**
18
 * Class UserManager.
19
 *
20
 * This library provides functions for user management.
21
 * Include/require it in your code to use its functionality.
22
 *
23
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
24
 */
25
class UserManager
26
{
27
    // This constants are deprecated use the constants located in ExtraField
28
    public const USER_FIELD_TYPE_TEXT = 1;
29
    public const USER_FIELD_TYPE_TEXTAREA = 2;
30
    public const USER_FIELD_TYPE_RADIO = 3;
31
    public const USER_FIELD_TYPE_SELECT = 4;
32
    public const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
33
    public const USER_FIELD_TYPE_DATE = 6;
34
    public const USER_FIELD_TYPE_DATETIME = 7;
35
    public const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
36
    public const USER_FIELD_TYPE_DIVIDER = 9;
37
    public const USER_FIELD_TYPE_TAG = 10;
38
    public const USER_FIELD_TYPE_TIMEZONE = 11;
39
    public const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
40
    public const USER_FIELD_TYPE_FILE = 13;
41
    public const USER_FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
42
43
    private static $encryptionMethod;
44
45
    /**
46
     * Constructor.
47
     *
48
     * @assert () === null
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
        /** @var UserRepository $userRepository */
62
63
        return Database::getManager()->getRepository('ChamiloUserBundle:User');
64
    }
65
66
    /**
67
     * Create/update/delete methods are available in the UserManager
68
     * (based in the Sonata\UserBundle\Entity\UserManager).
69
     *
70
     * @return Chamilo\UserBundle\Entity\Manager\UserManager
71
     */
72
    public static function getManager()
73
    {
74
        static $userManager;
75
76
        if (!isset($userManager)) {
77
            $encoderFactory = self::getEncoderFactory();
78
            $passwordUpdater = new \FOS\UserBundle\Util\PasswordUpdater($encoderFactory);
79
            $canonicalFieldUpdater = new \FOS\UserBundle\Util\CanonicalFieldsUpdater(
80
                new \FOS\UserBundle\Util\Canonicalizer(), new \FOS\UserBundle\Util\Canonicalizer()
81
            );
82
            $userManager = new Chamilo\UserBundle\Entity\Manager\UserManager(
83
                $passwordUpdater,
84
                $canonicalFieldUpdater,
85
                Database::getManager(),
86
                'Chamilo\\UserBundle\\Entity\\User'
87
            );
88
        }
89
90
        return $userManager;
91
    }
92
93
    /**
94
     * @param string $encryptionMethod
95
     */
96
    public static function setPasswordEncryption($encryptionMethod)
97
    {
98
        self::$encryptionMethod = $encryptionMethod;
99
    }
100
101
    /**
102
     * @return bool|mixed
103
     */
104
    public static function getPasswordEncryption()
105
    {
106
        $encryptionMethod = self::$encryptionMethod;
107
        if (empty($encryptionMethod)) {
108
            $encryptionMethod = api_get_configuration_value('password_encryption');
109
        }
110
111
        return $encryptionMethod;
112
    }
113
114
    /**
115
     * Validates the password.
116
     *
117
     * @param $encoded
118
     * @param $raw
119
     * @param $salt
120
     *
121
     * @return bool
122
     */
123
    public static function isPasswordValid($encoded, $raw, $salt)
124
    {
125
        $encoder = new \Chamilo\UserBundle\Security\Encoder(self::getPasswordEncryption());
126
127
        return $encoder->isPasswordValid($encoded, $raw, $salt);
128
    }
129
130
    /**
131
     * Detects and returns the type of encryption of the given encrypted
132
     * password.
133
     *
134
     * @param string $encoded The encrypted password
135
     * @param string $salt    The user salt, if any
136
     */
137
    public static function detectPasswordEncryption(string $encoded, string $salt): string
138
    {
139
        $encryption = false;
140
141
        $length = strlen($encoded);
142
143
        $pattern = '/^\$2y\$04\$[A-Za-z0-9\.\/]{53}$/';
144
145
        if ($length == 60 && preg_match($pattern, $encoded)) {
146
            $encryption = 'bcrypt';
147
        } elseif ($length == 32 && ctype_xdigit($encoded)) {
148
            $encryption = 'md5';
149
        } elseif ($length == 40 && ctype_xdigit($encoded)) {
150
            $encryption = 'sha1';
151
        } else {
152
            $start = strpos($encoded, '{');
153
            if ($start !== false && substr($encoded, -1, 1) == '}') {
154
                if (substr($encoded, $start + 1, -1) == $salt) {
155
                    $encryption = 'none';
156
                }
157
            }
158
        }
159
160
        return $encryption;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $encryption could return the type false which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
161
    }
162
163
    /**
164
     * Checks if the password is correct for this user.
165
     * If the password_conversion setting is true, also update the password
166
     * in the database to a new encryption method.
167
     *
168
     * @param string $encoded Encrypted password
169
     * @param string $raw     Clear password given through login form
170
     * @param string $salt    User salt, if any
171
     * @param int    $userId  The user's internal ID
172
     */
173
    public static function checkPassword(string $encoded, string $raw, string $salt, int $userId): bool
174
    {
175
        $result = false;
176
177
        if (true === api_get_configuration_value('password_conversion')) {
178
            $detectedEncryption = self::detectPasswordEncryption($encoded, $salt);
179
            if (self::getPasswordEncryption() != $detectedEncryption) {
180
                $encoder = new \Chamilo\UserBundle\Security\Encoder($detectedEncryption);
181
                $result = $encoder->isPasswordValid($encoded, $raw, $salt);
182
                if ($result) {
183
                    $raw = $encoder->encodePassword($encoded, $salt);
184
                    self::updatePassword($userId, $raw);
185
                }
186
            } else {
187
                $result = self::isPasswordValid($encoded, $raw, $salt);
188
            }
189
        } else {
190
            $result = self::isPasswordValid($encoded, $raw, $salt);
191
        }
192
193
        return $result;
194
    }
195
196
    /**
197
     * Encrypt the password using the current encoder.
198
     *
199
     * @param string $raw The clear password
200
     */
201
    public static function encryptPassword(string $raw, User $user): string
202
    {
203
        $encoder = self::getEncoder($user);
204
205
        return $encoder->encodePassword(
206
            $raw,
207
            $user->getSalt()
208
        );
209
    }
210
211
    /**
212
     * Update the password of the given user to the given (in-clear) password.
213
     *
214
     * @param int    $userId   Internal user ID
215
     * @param string $password Password in clear
216
     */
217
    public static function updatePassword(int $userId, string $password): void
218
    {
219
        $repository = self::getRepository();
220
        /** @var User $user */
221
        $user = $repository->find($userId);
222
        $userManager = self::getManager();
223
        $user->setPlainPassword($password);
224
        $userManager->updateUser($user, true);
225
        Event::addEvent(LOG_USER_PASSWORD_UPDATE, LOG_USER_ID, $userId);
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

225
        Event::/** @scrutinizer ignore-call */ 
226
               addEvent(LOG_USER_PASSWORD_UPDATE, LOG_USER_ID, $userId);

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...
226
    }
227
228
    /**
229
     * Updates user expiration date.
230
     *
231
     * @param int    $userId
232
     * @param string $expirationDate
233
     */
234
    public static function updateExpirationDate($userId, $expirationDate)
235
    {
236
        $repository = self::getRepository();
237
        /** @var User $user */
238
        $user = $repository->find($userId);
239
        $userManager = self::getManager();
240
        $expirationDate = api_get_utc_datetime($expirationDate, false, true);
241
        $user->setExpirationDate($expirationDate);
242
        $userManager->updateUser($user, true);
243
    }
244
245
    /**
246
     * Creates a new user for the platform.
247
     *
248
     * @author Hugues Peeters <[email protected]>,
249
     * @author Roan Embrechts <[email protected]>
250
     *
251
     * @param string        $firstName
252
     * @param string        $lastName
253
     * @param int           $status                  (1 for course tutor, 5 for student, 6 for anonymous)
254
     * @param string        $email
255
     * @param string        $loginName
256
     * @param string        $password
257
     * @param string        $official_code           Any official code (optional)
258
     * @param string        $language                User language    (optional)
259
     * @param string        $phone                   Phone number    (optional)
260
     * @param string        $picture_uri             Picture URI        (optional)
261
     * @param string        $authSource              Authentication source (defaults to 'platform', dependind on constant)
262
     * @param string        $expirationDate          Account expiration date (optional, defaults to null)
263
     * @param int           $active                  Whether the account is enabled or disabled by default
264
     * @param int           $hr_dept_id              The department of HR in which the user is registered (defaults to 0)
265
     * @param array         $extra                   Extra fields (prefix labels with "extra_")
266
     * @param string        $encrypt_method          Used if password is given encrypted. Set to an empty string by default
267
     * @param bool          $send_mail
268
     * @param bool          $isAdmin
269
     * @param string        $address
270
     * @param bool          $sendEmailToAllAdmins
271
     * @param FormValidator $form
272
     * @param int           $creatorId
273
     * @param array         $emailTemplate
274
     * @param string        $redirectToURLAfterLogin
275
     *
276
     * @return mixed new user id - if the new user creation succeeds, false otherwise
277
     * @desc The function tries to retrieve user id from the session.
278
     * If it exists, the current user id is the creator id. If a problem arises,
279
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
280
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
281
     */
282
    public static function create_user(
283
        $firstName,
284
        $lastName,
285
        $status,
286
        $email,
287
        $loginName,
288
        $password,
289
        $official_code = '',
290
        $language = '',
291
        $phone = '',
292
        $picture_uri = '',
293
        $authSource = PLATFORM_AUTH_SOURCE,
294
        $expirationDate = null,
295
        $active = 1,
296
        $hr_dept_id = 0,
297
        $extra = [],
298
        $encrypt_method = '',
299
        $send_mail = false,
300
        $isAdmin = false,
301
        $address = '',
302
        $sendEmailToAllAdmins = false,
303
        $form = null,
304
        $creatorId = 0,
305
        $emailTemplate = [],
306
        $redirectToURLAfterLogin = ''
307
    ) {
308
        $creatorId = empty($creatorId) ? api_get_user_id() : 0;
309
        $creatorInfo = api_get_user_info($creatorId);
310
        $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
311
312
        $hook = HookCreateUser::create();
313
        if (!empty($hook)) {
314
            $hook->notifyCreateUser(HOOK_EVENT_TYPE_PRE);
315
        }
316
317
        if ('true' === api_get_setting('registration', 'email')) {
318
            // Force email validation.
319
            if (false === api_valid_email($email)) {
320
                Display::addFlash(
321
                   Display::return_message(get_lang('PleaseEnterValidEmail').' - '.$email, 'warning')
322
               );
323
324
                return false;
325
            }
326
        } else {
327
            // Allow empty email. If email is set, check if is valid.
328
            if (!empty($email) && false === api_valid_email($email)) {
329
                Display::addFlash(
330
                    Display::return_message(get_lang('PleaseEnterValidEmail').' - '.$email, 'warning')
331
                );
332
333
                return false;
334
            }
335
        }
336
337
        if ('true' === api_get_setting('login_is_email')) {
338
            if (false === api_valid_email($loginName)) {
339
                Display::addFlash(
340
                    Display::return_message(get_lang('PleaseEnterValidEmail').' - '.$loginName, 'warning')
341
                );
342
343
                return false;
344
            }
345
        } else {
346
            if (false === self::is_username_valid($loginName)) {
347
                Display::addFlash(
348
                    Display::return_message(get_lang('UsernameWrong').' - '.$loginName, 'warning')
349
                );
350
351
                return false;
352
            }
353
        }
354
355
        // First check wether the login already exists
356
        if (!self::is_username_available($loginName)) {
357
            Display::addFlash(
358
                Display::return_message(get_lang('LoginAlreadyTaken').' - '.$loginName, 'warning')
359
            );
360
361
            return false;
362
        }
363
364
        global $_configuration;
365
        $original_password = $password;
366
367
        $access_url_id = 1;
368
        if (api_get_multiple_access_url()) {
369
            $access_url_id = api_get_current_access_url_id();
370
        } else {
371
            // In some cases, the first access_url ID might be different from 1
372
            // for example when using a DB cluster or hacking the DB manually.
373
            // In this case, we want the first row, not necessarily "1".
374
            $dbm = Database::getManager();
375
            /** @var AccessUrlRepository $accessUrlRepository */
376
            $accessUrlRepository = $dbm->getRepository('ChamiloCoreBundle:AccessUrl');
377
            $accessUrl = $accessUrlRepository->getFirstId();
378
            if (!empty($accessUrl[0]) && !empty($accessUrl[0][1])) {
379
                $access_url_id = $accessUrl[0][1];
380
            }
381
        }
382
383
        if (isset($_configuration[$access_url_id]) &&
384
            is_array($_configuration[$access_url_id]) &&
385
            isset($_configuration[$access_url_id]['hosting_limit_users']) &&
386
            $_configuration[$access_url_id]['hosting_limit_users'] > 0) {
387
            $num = self::get_number_of_users(null, $access_url_id);
388
            if ($num >= $_configuration[$access_url_id]['hosting_limit_users']) {
389
                api_warn_hosting_contact('hosting_limit_users');
390
                Display::addFlash(
391
                    Display::return_message(
392
                        get_lang('PortalUsersLimitReached'),
393
                        'warning'
394
                    )
395
                );
396
397
                return false;
398
            }
399
        }
400
401
        if ($status === 1 &&
402
            isset($_configuration[$access_url_id]) &&
403
            is_array($_configuration[$access_url_id]) &&
404
            isset($_configuration[$access_url_id]['hosting_limit_teachers']) &&
405
            $_configuration[$access_url_id]['hosting_limit_teachers'] > 0
406
        ) {
407
            $num = self::get_number_of_users(1, $access_url_id);
408
            if ($num >= $_configuration[$access_url_id]['hosting_limit_teachers']) {
409
                Display::addFlash(
410
                    Display::return_message(
411
                        get_lang('PortalTeachersLimitReached'),
412
                        'warning'
413
                    )
414
                );
415
                api_warn_hosting_contact('hosting_limit_teachers');
416
417
                return false;
418
            }
419
        }
420
421
        if (empty($password)) {
422
            if ($authSource === PLATFORM_AUTH_SOURCE) {
423
                Display::addFlash(
424
                    Display::return_message(
425
                        get_lang('ThisFieldIsRequired').': '.get_lang(
426
                            'Password'
427
                        ),
428
                        'warning'
429
                    )
430
                );
431
432
                return false;
433
            }
434
435
            // We use the authSource as password.
436
            // The real validation will be by processed by the auth
437
            // source not Chamilo
438
            $password = $authSource;
439
        }
440
441
        // database table definition
442
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
443
444
        // Checking the user language
445
        $languages = api_get_languages();
446
        $language = strtolower($language);
447
448
        if (isset($languages['folder'])) {
449
            if (!in_array($language, $languages['folder'])) {
450
                $language = api_get_setting('platformLanguage');
451
            }
452
        }
453
454
        $currentDate = api_get_utc_datetime();
455
        $now = new DateTime();
456
457
        if (empty($expirationDate) || $expirationDate == '0000-00-00 00:00:00') {
458
            // Default expiration date
459
            // if there is a default duration of a valid account then
460
            // we have to change the expiration_date accordingly
461
            // Accept 0000-00-00 00:00:00 as a null value to avoid issues with
462
            // third party code using this method with the previous (pre-1.10)
463
            // value of 0000...
464
            if (api_get_setting('account_valid_duration') != '') {
465
                $expirationDate = new DateTime($currentDate);
466
                $days = (int) api_get_setting('account_valid_duration');
467
                $expirationDate->modify('+'.$days.' day');
468
            }
469
        } else {
470
            $expirationDate = api_get_utc_datetime($expirationDate);
471
            $expirationDate = new \DateTime($expirationDate, new DateTimeZone('UTC'));
472
        }
473
474
        $userManager = self::getManager();
475
476
        /** @var User $user */
477
        $user = $userManager->createUser();
478
479
        $user
480
            ->setLastname($lastName)
481
            ->setFirstname($firstName)
482
            ->setUsername($loginName)
483
            ->setStatus($status)
484
            ->setPlainPassword($password)
485
            ->setEmail($email)
486
            ->setOfficialCode($official_code)
487
            ->setPictureUri($picture_uri)
488
            ->setCreatorId($creatorId)
489
            ->setAuthSource($authSource)
490
            ->setPhone($phone)
491
            ->setAddress($address)
492
            ->setLanguage($language)
493
            ->setRegistrationDate($now)
494
            ->setHrDeptId($hr_dept_id)
495
            ->setActive($active)
496
            ->setEnabled($active)
497
        ;
498
499
        if (!empty($expirationDate)) {
500
            $user->setExpirationDate($expirationDate);
501
        }
502
503
        $userManager->updateUser($user);
504
        $userId = $user->getId();
505
506
        if (!empty($userId)) {
507
            $return = $userId;
508
            $sql = "UPDATE $table_user SET user_id = $return WHERE id = $return";
509
            Database::query($sql);
510
511
            if ($isAdmin) {
512
                self::addUserAsAdmin($user);
513
            }
514
515
            if (api_get_multiple_access_url()) {
516
                UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
517
            } else {
518
                //we are adding by default the access_url_user table with access_url_id = 1
519
                UrlManager::add_user_to_url($userId, 1);
520
            }
521
522
            $extra['item_id'] = $userId;
523
524
            if (is_array($extra) && count($extra) > 0) {
525
                $userFieldValue = new ExtraFieldValue('user');
526
                // Force saving of extra fields (otherwise, if the current
527
                // user is not admin, fields not visible to the user - most
528
                // of them - are just ignored)
529
                $userFieldValue->saveFieldValues(
530
                    $extra,
531
                    true,
532
                    null,
533
                    null,
534
                    null,
535
                    true
536
                );
537
            } else {
538
                // Create notify settings by default
539
                self::update_extra_field_value(
540
                    $userId,
541
                    'mail_notify_invitation',
542
                    '1'
543
                );
544
                self::update_extra_field_value(
545
                    $userId,
546
                    'mail_notify_message',
547
                    '1'
548
                );
549
                self::update_extra_field_value(
550
                    $userId,
551
                    'mail_notify_group_message',
552
                    '1'
553
                );
554
            }
555
556
            self::update_extra_field_value(
557
                $userId,
558
                'already_logged_in',
559
                'false'
560
            );
561
562
            if (!empty($redirectToURLAfterLogin) && api_get_configuration_value('plugin_redirection_enabled')) {
563
                RedirectionPlugin::insert($userId, $redirectToURLAfterLogin);
564
            }
565
566
            if (!empty($email) && $send_mail) {
567
                $recipient_name = api_get_person_name(
568
                    $firstName,
569
                    $lastName,
570
                    null,
571
                    PERSON_NAME_EMAIL_ADDRESS
572
                );
573
                $tplSubject = new Template(
574
                    null,
575
                    false,
576
                    false,
577
                    false,
578
                    false,
579
                    false
580
		);
581
		// the complete_name is not used in the default Chamilo template but used in a specific template -refs BT#21334
582
		$tplSubject->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
583
                $layoutSubject = $tplSubject->get_template('mail/subject_registration_platform.tpl');
584
                $emailSubject = $tplSubject->fetch($layoutSubject);
585
                $sender_name = api_get_person_name(
586
                    api_get_setting('administratorName'),
587
                    api_get_setting('administratorSurname'),
588
                    null,
589
                    PERSON_NAME_EMAIL_ADDRESS
590
                );
591
                $email_admin = api_get_setting('emailAdministrator');
592
593
                $url = api_get_path(WEB_PATH);
594
                if (api_is_multiple_url_enabled()) {
595
                    $access_url_id = api_get_current_access_url_id();
596
                    if ($access_url_id != -1) {
597
                        $urlInfo = api_get_access_url($access_url_id);
598
                        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...
599
                            $url = $urlInfo['url'];
600
                        }
601
                    }
602
                }
603
604
                $tplContent = new Template(
605
                    null,
606
                    false,
607
                    false,
608
                    false,
609
                    false,
610
                    false
611
                );
612
                // variables for the default template
613
                $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
614
                $tplContent->assign('login_name', $loginName);
615
                $tplContent->assign('original_password', stripslashes($original_password));
616
                $tplContent->assign('mailWebPath', $url);
617
                $tplContent->assign('new_user', $user);
618
                // Adding this variable but not used in default template, used for task BT19518 with a customized template
619
                $tplContent->assign('status_type', $status);
620
621
                $layoutContent = $tplContent->get_template('mail/content_registration_platform.tpl');
622
                $emailBody = $tplContent->fetch($layoutContent);
623
624
                $userInfo = api_get_user_info($userId);
625
                $mailTemplateManager = new MailTemplateManager();
626
627
                /* MANAGE EVENT WITH MAIL */
628
                if (EventsMail::check_if_using_class('user_registration')) {
629
                    $values["about_user"] = $return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$values was never initialized. Although not strictly required by PHP, it is generally a good practice to add $values = array(); before regardless.
Loading history...
630
                    $values["password"] = $original_password;
631
                    $values["send_to"] = [$return];
632
                    $values["prior_lang"] = null;
633
                    EventsDispatcher::events('user_registration', $values);
634
                } else {
635
                    $phoneNumber = isset($extra['mobile_phone_number']) ? $extra['mobile_phone_number'] : null;
636
                    $additionalParameters = [
637
                        'smsType' => SmsPlugin::WELCOME_LOGIN_PASSWORD,
638
                        'userId' => $return,
639
                        'mobilePhoneNumber' => $phoneNumber,
640
                        'password' => $original_password,
641
                    ];
642
643
                    $emailBodyTemplate = '';
644
                    if (!empty($emailTemplate)) {
645
                        if (isset($emailTemplate['content_registration_platform.tpl']) &&
646
                            !empty($emailTemplate['content_registration_platform.tpl'])
647
                        ) {
648
                            $emailBodyTemplate = $mailTemplateManager->parseTemplate(
649
                                $emailTemplate['content_registration_platform.tpl'],
650
                                $userInfo
651
                            );
652
                        }
653
                    }
654
655
                    $twoEmail = api_get_configuration_value('send_two_inscription_confirmation_mail');
656
                    if ($twoEmail === true) {
657
                        $layoutContent = $tplContent->get_template('mail/new_user_first_email_confirmation.tpl');
658
                        $emailBody = $tplContent->fetch($layoutContent);
659
660
                        if (!empty($emailTemplate) &&
661
                            isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
662
                            !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
663
                        ) {
664
                            $emailBody = $mailTemplateManager->parseTemplate(
665
                                $emailTemplate['new_user_first_email_confirmation.tpl'],
666
                                $userInfo
667
                            );
668
                        }
669
670
                        api_mail_html(
671
                            $recipient_name,
672
                            $email,
673
                            $emailSubject,
674
                            $emailBody,
675
                            $sender_name,
676
                            $email_admin,
677
                            null,
678
                            null,
679
                            null,
680
                            $additionalParameters,
681
                            $creatorEmail
682
                        );
683
684
                        $layoutContent = $tplContent->get_template('mail/new_user_second_email_confirmation.tpl');
685
                        $emailBody = $tplContent->fetch($layoutContent);
686
687
                        if (!empty($emailTemplate) &&
688
                            isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
689
                            !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
690
                        ) {
691
                            $emailBody = $mailTemplateManager->parseTemplate(
692
                                $emailTemplate['new_user_second_email_confirmation.tpl'],
693
                                $userInfo
694
                            );
695
                        }
696
697
                        api_mail_html(
698
                            $recipient_name,
699
                            $email,
700
                            $emailSubject,
701
                            $emailBody,
702
                            $sender_name,
703
                            $email_admin,
704
                            null,
705
                            null,
706
                            null,
707
                            $additionalParameters,
708
                            $creatorEmail
709
                        );
710
                    } else {
711
                        if (!empty($emailBodyTemplate)) {
712
                            $emailBody = $emailBodyTemplate;
713
                        }
714
                        $sendToInbox = api_get_configuration_value('send_inscription_msg_to_inbox');
715
                        if ($sendToInbox) {
716
                            $adminList = self::get_all_administrators();
717
                            $senderId = 1;
718
                            if (!empty($adminList)) {
719
                                $adminInfo = current($adminList);
720
                                $senderId = $adminInfo['user_id'];
721
                            }
722
723
                            MessageManager::send_message_simple(
724
                                $userId,
725
                                $emailSubject,
726
                                $emailBody,
727
                                $senderId
728
                            );
729
                        } else {
730
                            api_mail_html(
731
                                $recipient_name,
732
                                $email,
733
                                $emailSubject,
734
                                $emailBody,
735
                                $sender_name,
736
                                $email_admin,
737
                                null,
738
                                null,
739
                                null,
740
                                $additionalParameters,
741
                                $creatorEmail
742
                            );
743
                        }
744
                    }
745
746
                    $notification = api_get_configuration_value('send_notification_when_user_added');
747
                    if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
748
                        foreach ($notification['admins'] as $adminId) {
749
                            $emailSubjectToAdmin = get_lang('UserAdded').': '.api_get_person_name($firstName, $lastName);
750
                            MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
751
                        }
752
                    }
753
                }
754
755
                if ($sendEmailToAllAdmins) {
756
                    $adminList = self::get_all_administrators();
757
758
                    $tplContent = new Template(
759
                        null,
760
                        false,
761
                        false,
762
                        false,
763
                        false,
764
                        false
765
                    );
766
                    // variables for the default template
767
                    $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
768
                    $tplContent->assign('user_added', $user);
769
                    $renderer = FormValidator::getDefaultRenderer();
770
                    // Form template
771
                    $elementTemplate = ' {label}: {element} <br />';
772
                    $renderer->setElementTemplate($elementTemplate);
773
                    /** @var FormValidator $form */
774
                    $form->freeze(null, $elementTemplate);
775
                    $form->removeElement('submit');
776
                    $formData = $form->returnForm();
777
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
778
                    $tplContent->assign('link', Display::url($url, $url));
779
                    $tplContent->assign('form', $formData);
780
781
                    $layoutContent = $tplContent->get_template('mail/content_registration_platform_to_admin.tpl');
782
                    $emailBody = $tplContent->fetch($layoutContent);
783
784
                    if (!empty($emailTemplate) &&
785
                        isset($emailTemplate['content_registration_platform_to_admin.tpl']) &&
786
                        !empty($emailTemplate['content_registration_platform_to_admin.tpl'])
787
                    ) {
788
                        $emailBody = $mailTemplateManager->parseTemplate(
789
                            $emailTemplate['content_registration_platform_to_admin.tpl'],
790
                            $userInfo
791
                        );
792
                    }
793
794
                    $subject = get_lang('UserAdded');
795
                    foreach ($adminList as $adminId => $data) {
796
                        MessageManager::send_message_simple(
797
                            $adminId,
798
                            $subject,
799
                            $emailBody,
800
                            $userId
801
                        );
802
                    }
803
                }
804
                /* ENDS MANAGE EVENT WITH MAIL */
805
            }
806
807
            if (!empty($hook)) {
808
                $hook->setEventData([
809
                    'return' => $userId,
810
                    'originalPassword' => $original_password,
811
                ]);
812
                $hook->notifyCreateUser(HOOK_EVENT_TYPE_POST);
813
            }
814
            Event::addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId);
815
        } else {
816
            Display::addFlash(
817
                Display::return_message(get_lang('ErrorContactPlatformAdmin'))
818
            );
819
820
            return false;
821
        }
822
823
        return $return;
824
    }
825
826
    /**
827
     * Ensure the CAS-authenticated user exists in the database.
828
     *
829
     * @param $casUser string the CAS user identifier
830
     *
831
     * @throws Exception if more than one user share the same CAS user identifier
832
     *
833
     * @return string|bool the recognised user login name or false if not found
834
     */
835
    public static function casUserLoginName($casUser)
836
    {
837
        $loginName = false;
838
839
        // look inside the casUser extra field
840
        if (UserManager::is_extra_field_available('cas_user')) {
841
            $valueModel = new ExtraFieldValue('user');
842
            $itemList = $valueModel->get_item_id_from_field_variable_and_field_value(
843
                'cas_user',
844
                $casUser,
845
                false,
846
                false,
847
                true
848
            );
849
            if (false !== $itemList) {
850
                // at least one user has $casUser in the 'cas_user' extra field
851
                // we attempt to load each candidate user because there might be deleted ones
852
                // (extra field values of a deleted user might remain)
853
                foreach ($itemList as $item) {
854
                    $userId = intval($item['item_id']);
855
                    $user = UserManager::getRepository()->find($userId);
856
                    if (!is_null($user)) {
857
                        if (false === $loginName) {
858
                            $loginName = $user->getUsername();
859
                        } else {
860
                            throw new Exception(get_lang('MoreThanOneUserMatched'));
861
                        }
862
                    }
863
                }
864
            }
865
        }
866
867
        if (false === $loginName) {
868
            // no matching 'cas_user' extra field value, or no such extra field
869
            // falling back to the old behaviour: $casUser must be the login name
870
            $userId = UserManager::get_user_id_from_username($casUser);
871
            if (false !== $userId) {
872
                $loginName = $casUser;
873
            }
874
        }
875
876
        return $loginName;
877
    }
878
879
    /**
880
     * Checks the availability of extra field 'cas_user'
881
     * and creates it if missing.
882
     *
883
     * @throws Exception on failure
884
     */
885
    public static function ensureCASUserExtraFieldExists()
886
    {
887
        if (!self::is_extra_field_available('cas_user')) {
888
            $extraField = new ExtraField('user');
889
            if (false === $extraField->save(
890
                    [
891
                        'variable' => 'cas_user',
892
                        'field_type' => ExtraField::FIELD_TYPE_TEXT,
893
                        'display_text' => get_lang('CAS User Identifier'),
894
                        'visible_to_self' => true,
895
                        'filter' => true,
896
                    ]
897
                )) {
898
                throw new Exception(get_lang('FailedToCreateExtraFieldCasUser'));
899
            }
900
901
            $rules = api_get_configuration_value('cas_user_map');
902
            if (!empty($rules) && isset($rules['extra'])) {
903
                foreach ($rules['extra'] as $extra) {
904
                    $extraField->save(
905
                        [
906
                            'variable' => $extra,
907
                            'field_type' => ExtraField::FIELD_TYPE_TEXT,
908
                            'display_text' => $extra,
909
                            'visible_to_self' => false,
910
                            'filter' => false,
911
                        ]
912
                    );
913
                }
914
            }
915
        }
916
    }
917
918
    /**
919
     * Create a CAS-authenticated user from scratch, from its CAS user identifier, with temporary default values.
920
     *
921
     * @param string $casUser the CAS user identifier
922
     *
923
     * @throws Exception on error
924
     *
925
     * @return string the login name of the new user
926
     */
927
    public static function createCASAuthenticatedUserFromScratch($casUser)
928
    {
929
        self::ensureCASUserExtraFieldExists();
930
931
        $loginName = 'cas_user_'.$casUser;
932
        $defaultValue = get_lang('EditInProfile');
933
        $defaultEmailValue = get_lang('EditInProfile');
934
        require_once __DIR__.'/../../auth/external_login/functions.inc.php';
935
        if ('true' === api_get_setting('login_is_email')) {
936
            $defaultEmailValue = $casUser;
937
        }
938
        $userId = external_add_user(
939
            [
940
                'username' => $loginName,
941
                'auth_source' => CAS_AUTH_SOURCE,
942
                'firstname' => $defaultValue,
943
                'lastname' => $defaultValue,
944
                'email' => $defaultEmailValue,
945
            ]
946
        );
947
        if (false === $userId) {
948
            throw new Exception(get_lang('FailedUserCreation'));
949
        }
950
        // Not checking function update_extra_field_value return value because not reliable
951
        self::update_extra_field_value($userId, 'cas_user', $casUser);
952
953
        return $loginName;
954
    }
955
956
    public static function updateCasUser($_user)
957
    {
958
        $rules = api_get_configuration_value('cas_user_map');
959
960
        if (empty($_user)) {
961
            return false;
962
        }
963
964
        if (!empty($rules)) {
965
            $userEntity = api_get_user_entity($_user['id']);
966
            $attributes = phpCAS::getAttributes();
967
            if (isset($rules['fields'])) {
968
                $isAdmin = false;
969
                foreach ($rules['fields'] as $field => $attributeName) {
970
                    if (!isset($attributes[$attributeName])) {
971
                        continue;
972
                    }
973
                    $value = $attributes[$attributeName];
974
                    // Check replace.
975
                    if (isset($rules['replace'][$attributeName])) {
976
                        $value = $rules['replace'][$attributeName][$value];
977
                    }
978
979
                    switch ($field) {
980
                        case 'email':
981
                            $userEntity->setEmail($value);
982
                            break;
983
                        case 'firstname':
984
                            $userEntity->setFirstname($value);
985
                            break;
986
                        case 'lastname':
987
                            $userEntity->setLastname($value);
988
                            break;
989
                        case 'active':
990
                            $userEntity->setActive('false' === $value);
991
                            break;
992
                        case 'status':
993
                            if (PLATFORM_ADMIN === (int) $value) {
994
                                $value = COURSEMANAGER;
995
                                $isAdmin = true;
996
                            }
997
                            $userEntity->setStatus($value);
998
                            break;
999
                    }
1000
1001
                    Database::getManager()->persist($userEntity);
1002
                    Database::getManager()->flush();
1003
1004
                    if ($isAdmin) {
1005
                        self::addUserAsAdmin($userEntity);
1006
                    }
1007
                }
1008
            }
1009
1010
            if (isset($rules['extra'])) {
1011
                foreach ($rules['extra'] as $variable) {
1012
                    if (isset($attributes[$variable])) {
1013
                        self::update_extra_field_value(
1014
                            $_user['id'],
1015
                            $variable,
1016
                            $attributes[$variable]
1017
                        );
1018
                    }
1019
                }
1020
            }
1021
        }
1022
    }
1023
1024
    /**
1025
     * Create a CAS-authenticated user from LDAP, from its CAS user identifier.
1026
     *
1027
     * @param $casUser
1028
     *
1029
     * @throws Exception
1030
     *
1031
     * @return string login name of the new user
1032
     */
1033
    public static function createCASAuthenticatedUserFromLDAP($casUser)
1034
    {
1035
        self::ensureCASUserExtraFieldExists();
1036
1037
        require_once __DIR__.'/../../auth/external_login/ldap.inc.php';
1038
        $login = extldapCasUserLogin($casUser);
1039
        if (false !== $login) {
1040
            $ldapUser = extldap_authenticate($login, 'nopass', true);
1041
            if (false !== $ldapUser) {
1042
                require_once __DIR__.'/../../auth/external_login/functions.inc.php';
1043
                $user = extldap_get_chamilo_user($ldapUser);
1044
                $user['username'] = $login;
1045
                $user['auth_source'] = CAS_AUTH_SOURCE;
1046
                $userId = external_add_user($user);
1047
                if (false !== $userId) {
1048
                    // Not checking function update_extra_field_value return value because not reliable
1049
                    self::update_extra_field_value($userId, 'cas_user', $casUser);
1050
1051
                    return $login;
1052
                } else {
1053
                    throw new Exception('Could not create the new user '.$login);
1054
                }
1055
            } else {
1056
                throw new Exception('Could not load the new user from LDAP using its login '.$login);
0 ignored issues
show
Bug introduced by
Are you sure $login of type string|true 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

1056
                throw new Exception('Could not load the new user from LDAP using its login './** @scrutinizer ignore-type */ $login);
Loading history...
1057
            }
1058
        } else {
1059
            throw new Exception('Could not find the new user from LDAP using its cas user identifier '.$casUser);
1060
        }
1061
    }
1062
1063
    /**
1064
     * updates user record in database from its LDAP record
1065
     * copies relevant LDAP attribute values : firstname, lastname and email.
1066
     *
1067
     * @param $login string the user login name
1068
     *
1069
     * @throws Exception when the user login name is not found in the LDAP or in the database
1070
     */
1071
    public static function updateUserFromLDAP($login)
1072
    {
1073
        require_once __DIR__.'/../../auth/external_login/ldap.inc.php';
1074
1075
        $ldapUser = extldap_authenticate($login, 'nopass', true);
1076
        if (false === $ldapUser) {
1077
            throw new Exception(get_lang('NoSuchUserInLDAP'));
1078
        }
1079
1080
        $user = extldap_get_chamilo_user($ldapUser);
1081
        $userInfo = api_get_user_info_from_username($login);
1082
        if (false === $userInfo) {
1083
            throw new Exception(get_lang('NoSuchUserInInternalDatabase'));
1084
        }
1085
1086
        $userId = UserManager::update_user(
1087
            $userInfo['user_id'],
1088
            $user["firstname"],
1089
            $user["lastname"],
1090
            $login,
1091
            null,
1092
            $userInfo['auth_source'],
1093
            $user["email"],
1094
            $userInfo['status'],
1095
            $userInfo['official_code'],
1096
            $userInfo['phone'],
1097
            $userInfo['picture_uri'],
1098
            $userInfo['expiration_date'],
1099
            $userInfo['active'],
1100
            $userInfo['creator_id'],
1101
            $userInfo['hr_dept_id'],
1102
            null,
1103
            $userInfo['language']
1104
        );
1105
        if (false === $userId) {
1106
            throw new Exception(get_lang('CouldNotUpdateUser'));
1107
        }
1108
    }
1109
1110
    /**
1111
     * Can user be deleted? This function checks whether there's a course
1112
     * in which the given user is the
1113
     * only course administrator. If that is the case, the user can't be
1114
     * deleted because the course would remain without a course admin.
1115
     *
1116
     * @param int $user_id The user id
1117
     *
1118
     * @return bool true if user can be deleted
1119
     *
1120
     * @assert (null) === false
1121
     * @assert (-1) === false
1122
     * @assert ('abc') === false
1123
     */
1124
    public static function canDeleteUser($user_id)
1125
    {
1126
        $deny = api_get_configuration_value('deny_delete_users');
1127
1128
        if ($deny) {
1129
            return false;
1130
        }
1131
1132
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1133
        $user_id = (int) $user_id;
1134
1135
        if (empty($user_id)) {
1136
            return false;
1137
        }
1138
1139
        $res = Database::query(
1140
            "SELECT c_id FROM $table_course_user WHERE status = 1 AND user_id = $user_id"
1141
        );
1142
        while ($course = Database::fetch_assoc($res)) {
1143
            $sql = Database::query(
1144
                "SELECT COUNT(id) number FROM $table_course_user WHERE status = 1 AND c_id = {$course['c_id']}"
1145
            );
1146
            $res2 = Database::fetch_assoc($sql);
1147
1148
            if ($res2['number'] == 1) {
1149
                return false;
1150
            }
1151
        }
1152
1153
        return true;
1154
    }
1155
1156
    /**
1157
     * Delete a user from the platform, and all its belongings. This is a
1158
     * very dangerous function that should only be accessible by
1159
     * super-admins. Other roles should only be able to disable a user,
1160
     * which removes access to the platform but doesn't delete anything.
1161
     *
1162
     * @param int The ID of th user to be deleted
1163
     *
1164
     * @throws Exception
1165
     *
1166
     * @return bool true if user is successfully deleted, false otherwise
1167
     * @assert (null) === false
1168
     * @assert ('abc') === false
1169
     */
1170
    public static function delete_user($user_id)
1171
    {
1172
        $user_id = (int) $user_id;
1173
1174
        if (empty($user_id)) {
1175
            return false;
1176
        }
1177
1178
        if (!self::canDeleteUser($user_id)) {
1179
            return false;
1180
        }
1181
1182
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1183
        $usergroup_rel_user = Database::get_main_table(TABLE_USERGROUP_REL_USER);
1184
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1185
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
1186
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
1187
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
1188
        $table_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
1189
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1190
        $table_group = Database::get_course_table(TABLE_GROUP_USER);
1191
        $table_work = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
1192
1193
        // Unsubscribe the user from all groups in all his courses
1194
        $sql = "SELECT c.id
1195
                FROM $table_course c
1196
                INNER JOIN $table_course_user cu
1197
                ON (c.id = cu.c_id)
1198
                WHERE
1199
                    cu.user_id = '".$user_id."' AND
1200
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
1201
                ";
1202
1203
        $res = Database::query($sql);
1204
        while ($course = Database::fetch_object($res)) {
1205
            $sql = "DELETE FROM $table_group
1206
                    WHERE c_id = {$course->id} AND user_id = $user_id";
1207
            Database::query($sql);
1208
        }
1209
1210
        // Unsubscribe user from usergroup_rel_user
1211
        $sql = "DELETE FROM $usergroup_rel_user WHERE user_id = '".$user_id."'";
1212
        Database::query($sql);
1213
1214
        // Unsubscribe user from all courses
1215
        $sql = "DELETE FROM $table_course_user WHERE user_id = '".$user_id."'";
1216
        Database::query($sql);
1217
1218
        // Unsubscribe user from all courses in sessions
1219
        $sql = "DELETE FROM $table_session_course_user WHERE user_id = '".$user_id."'";
1220
        Database::query($sql);
1221
1222
        // If the user was added as a id_coach then set the current admin as coach see BT#
1223
        $currentUserId = api_get_user_id();
1224
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
1225
                WHERE id_coach = '".$user_id."'";
1226
        Database::query($sql);
1227
1228
        $sql = "UPDATE $table_session SET session_admin_id = $currentUserId
1229
                WHERE session_admin_id = '".$user_id."'";
1230
        Database::query($sql);
1231
1232
        // Unsubscribe user from all sessions
1233
        $sql = "DELETE FROM $table_session_user
1234
                WHERE user_id = '".$user_id."'";
1235
        Database::query($sql);
1236
1237
        if (api_get_configuration_value('plugin_redirection_enabled')) {
1238
            RedirectionPlugin::deleteUserRedirection($user_id);
1239
        }
1240
1241
        $user_info = api_get_user_info($user_id);
1242
1243
        try {
1244
            self::deleteUserFiles($user_id);
1245
        } catch (Exception $exception) {
1246
            error_log('Delete user exception: '.$exception->getMessage());
1247
        }
1248
1249
        // Delete the personal course categories
1250
        $course_cat_table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
1251
        $sql = "DELETE FROM $course_cat_table WHERE user_id = '".$user_id."'";
1252
        Database::query($sql);
1253
1254
        // Delete user from the admin table
1255
        $sql = "DELETE FROM $table_admin WHERE user_id = '".$user_id."'";
1256
        Database::query($sql);
1257
1258
        // Delete the personal agenda-items from this user
1259
        $agenda_table = Database::get_main_table(TABLE_PERSONAL_AGENDA);
1260
        $sql = "DELETE FROM $agenda_table WHERE user = '".$user_id."'";
1261
        Database::query($sql);
1262
1263
        $gradebook_results_table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_RESULT);
1264
        $sql = 'DELETE FROM '.$gradebook_results_table.' WHERE user_id = '.$user_id;
1265
        Database::query($sql);
1266
1267
        $extraFieldValue = new ExtraFieldValue('user');
1268
        $extraFieldValue->deleteValuesByItem($user_id);
1269
1270
        UrlManager::deleteUserFromAllUrls($user_id);
1271
1272
        if (api_get_setting('allow_social_tool') === 'true') {
1273
            $userGroup = new UserGroup();
1274
            //Delete user from portal groups
1275
            $group_list = $userGroup->get_groups_by_user($user_id);
1276
            if (!empty($group_list)) {
1277
                foreach ($group_list as $group_id => $data) {
1278
                    $userGroup->delete_user_rel_group($user_id, $group_id);
1279
                }
1280
            }
1281
1282
            // Delete user from friend lists
1283
            SocialManager::remove_user_rel_user($user_id, true);
1284
        }
1285
1286
        // Removing survey invitation
1287
        SurveyManager::delete_all_survey_invitations_by_user($user_id);
1288
1289
        // Delete students works
1290
        $sql = "DELETE FROM $table_work WHERE user_id = $user_id AND c_id <> 0";
1291
        Database::query($sql);
1292
1293
        $sql = "UPDATE c_item_property SET to_user_id = NULL
1294
                WHERE to_user_id = '".$user_id."'";
1295
        Database::query($sql);
1296
1297
        $sql = "UPDATE c_item_property SET insert_user_id = NULL
1298
                WHERE insert_user_id = '".$user_id."'";
1299
        Database::query($sql);
1300
1301
        $sql = "UPDATE c_item_property SET lastedit_user_id = NULL
1302
                WHERE lastedit_user_id = '".$user_id."'";
1303
        Database::query($sql);
1304
1305
        // Skills
1306
        $em = Database::getManager();
1307
1308
        $criteria = ['user' => $user_id];
1309
        $skills = $em->getRepository('ChamiloCoreBundle:SkillRelUser')->findBy($criteria);
1310
        if ($skills) {
1311
            /** @var SkillRelUser $skill */
1312
            foreach ($skills as $skill) {
1313
                $comments = $skill->getComments();
1314
                if ($comments) {
1315
                    /** @var SkillRelUserComment $comment */
1316
                    foreach ($comments as $comment) {
1317
                        $em->remove($comment);
1318
                    }
1319
                }
1320
                $em->remove($skill);
1321
            }
1322
            $em->flush();
1323
        }
1324
1325
        // ExtraFieldSavedSearch
1326
        $criteria = ['user' => $user_id];
1327
        $searchList = $em->getRepository('ChamiloCoreBundle:ExtraFieldSavedSearch')->findBy($criteria);
1328
        if ($searchList) {
1329
            foreach ($searchList as $search) {
1330
                $em->remove($search);
1331
            }
1332
            $em->flush();
1333
        }
1334
1335
        $connection = Database::getManager()->getConnection();
1336
        $tableExists = $connection->getSchemaManager()->tablesExist(['plugin_bbb_room']);
1337
        if ($tableExists) {
1338
            // Delete user from database
1339
            $sql = "DELETE FROM plugin_bbb_room WHERE participant_id = $user_id";
1340
            Database::query($sql);
1341
        }
1342
1343
        // Delete user/ticket relationships :(
1344
        $tableExists = $connection->getSchemaManager()->tablesExist(['ticket_ticket']);
1345
        if ($tableExists) {
1346
            TicketManager::deleteUserFromTicketSystem($user_id);
1347
        }
1348
1349
        $tableExists = $connection->getSchemaManager()->tablesExist(['c_lp_category_user']);
1350
        if ($tableExists) {
1351
            $sql = "DELETE FROM c_lp_category_user WHERE user_id = $user_id";
1352
            Database::query($sql);
1353
        }
1354
1355
        $app_plugin = new AppPlugin();
1356
        $app_plugin->performActionsWhenDeletingItem('user', $user_id);
1357
1358
        // Delete user from database
1359
        $sql = "DELETE FROM $table_user WHERE id = '".$user_id."'";
1360
        Database::query($sql);
1361
1362
        // Add event to system log
1363
        $user_id_manager = api_get_user_id();
1364
1365
        Event::addEvent(
1366
            LOG_USER_DELETE,
1367
            LOG_USER_ID,
1368
            $user_id,
1369
            api_get_utc_datetime(),
1370
            $user_id_manager
1371
        );
1372
1373
        Event::addEvent(
1374
            LOG_USER_DELETE,
1375
            LOG_USER_OBJECT,
1376
            $user_info,
1377
            api_get_utc_datetime(),
1378
            $user_id_manager
1379
        );
1380
        $cacheAvailable = api_get_configuration_value('apc');
1381
        if ($cacheAvailable === true) {
1382
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1383
            if (apcu_exists($apcVar)) {
1384
                apcu_delete($apcVar);
1385
            }
1386
        }
1387
1388
        return true;
1389
    }
1390
1391
    /**
1392
     * Deletes users completely. Can be called either as:
1393
     * - UserManager::delete_users(1, 2, 3); or
1394
     * - UserManager::delete_users(array(1, 2, 3));.
1395
     *
1396
     * @param array|int $ids
1397
     *
1398
     * @return bool True if at least one user was successfuly deleted. False otherwise.
1399
     *
1400
     * @author Laurent Opprecht
1401
     *
1402
     * @uses \UserManager::delete_user() to actually delete each user
1403
     * @assert (null) === false
1404
     * @assert (-1) === false
1405
     * @assert (array(-1)) === false
1406
     */
1407
    public static function delete_users($ids = [])
1408
    {
1409
        $result = false;
1410
        $ids = is_array($ids) ? $ids : func_get_args();
1411
        if (!is_array($ids) || count($ids) == 0) {
1412
            return false;
1413
        }
1414
        $ids = array_map('intval', $ids);
1415
        foreach ($ids as $id) {
1416
            if (empty($id) || $id < 1) {
1417
                continue;
1418
            }
1419
            $deleted = self::delete_user($id);
1420
            $result = $deleted || $result;
1421
        }
1422
1423
        return $result;
1424
    }
1425
1426
    /**
1427
     * Disable users. Can be called either as:
1428
     * - UserManager::deactivate_users(1, 2, 3);
1429
     * - UserManager::deactivate_users(array(1, 2, 3));.
1430
     *
1431
     * @param array|int $ids
1432
     *
1433
     * @return bool
1434
     *
1435
     * @author Laurent Opprecht
1436
     * @assert (null) === false
1437
     * @assert (array(-1)) === false
1438
     */
1439
    public static function deactivate_users($ids = [])
1440
    {
1441
        if (empty($ids)) {
1442
            return false;
1443
        }
1444
1445
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1446
1447
        $ids = is_array($ids) ? $ids : func_get_args();
1448
        $ids = array_map('intval', $ids);
1449
        $ids = implode(',', $ids);
1450
1451
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
1452
        $r = Database::query($sql);
1453
        if ($r !== false) {
1454
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
1455
1456
            return true;
1457
        }
1458
1459
        return false;
1460
    }
1461
1462
    /**
1463
     * Enable users. Can be called either as:
1464
     * - UserManager::activate_users(1, 2, 3);
1465
     * - UserManager::activate_users(array(1, 2, 3));.
1466
     *
1467
     * @param array|int IDs of the users to enable
1468
     *
1469
     * @return bool
1470
     *
1471
     * @author Laurent Opprecht
1472
     * @assert (null) === false
1473
     * @assert (array(-1)) === false
1474
     */
1475
    public static function activate_users($ids = [])
1476
    {
1477
        if (empty($ids)) {
1478
            return false;
1479
        }
1480
1481
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1482
1483
        $ids = is_array($ids) ? $ids : func_get_args();
1484
        $ids = array_map('intval', $ids);
1485
        $ids = implode(',', $ids);
1486
1487
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
1488
        $r = Database::query($sql);
1489
        if ($r !== false) {
1490
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
1491
1492
            return true;
1493
        }
1494
1495
        return false;
1496
    }
1497
1498
    /**
1499
     * Update user information with new openid.
1500
     *
1501
     * @param int    $user_id
1502
     * @param string $openid
1503
     *
1504
     * @return bool true if the user information was updated
1505
     * @assert (false,'') === false
1506
     * @assert (-1,'') === false
1507
     */
1508
    public static function update_openid($user_id, $openid)
1509
    {
1510
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1511
        if ($user_id != strval(intval($user_id))) {
1512
            return false;
1513
        }
1514
        if ($user_id === false) {
1515
            return false;
1516
        }
1517
        $sql = "UPDATE $table_user SET
1518
                openid='".Database::escape_string($openid)."'";
1519
        $sql .= " WHERE id= $user_id";
1520
1521
        if (Database::query($sql) !== false) {
1522
            return true;
1523
        }
1524
1525
        return false;
1526
    }
1527
1528
    /**
1529
     * Update user information with all the parameters passed to this function.
1530
     *
1531
     * @param int    $user_id         The ID of the user to be updated
1532
     * @param string $firstname       The user's firstname
1533
     * @param string $lastname        The user's lastname
1534
     * @param string $username        The user's username (login)
1535
     * @param string $password        The user's password
1536
     * @param string $auth_source     The authentication source (default: "platform")
1537
     * @param string $email           The user's e-mail address
1538
     * @param int    $status          The user's status
1539
     * @param string $official_code   The user's official code (usually just an internal institutional code)
1540
     * @param string $phone           The user's phone number
1541
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
1542
     * @param string $expiration_date The date at which this user will be automatically disabled
1543
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
1544
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
1545
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
1546
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
1547
     * @param string $language        The language to which the user account will be set
1548
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
1549
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
1550
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
1551
     * @param string $address
1552
     * @param array  $emailTemplate
1553
     *
1554
     * @return bool|int False on error, or the user ID if the user information was updated
1555
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
1556
     */
1557
    public static function update_user(
1558
        $user_id,
1559
        $firstname,
1560
        $lastname,
1561
        $username,
1562
        $password,
1563
        $auth_source,
1564
        $email,
1565
        $status,
1566
        $official_code,
1567
        $phone,
1568
        $picture_uri,
1569
        $expiration_date,
1570
        $active,
1571
        $creator_id = null,
1572
        $hr_dept_id = 0,
1573
        $extra = null,
1574
        $language = 'english',
1575
        $encrypt_method = '',
1576
        $send_email = false,
1577
        $reset_password = 0,
1578
        $address = null,
1579
        $emailTemplate = []
1580
    ) {
1581
        $hook = HookUpdateUser::create();
1582
        if (!empty($hook)) {
1583
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_PRE);
1584
        }
1585
        $original_password = $password;
1586
        $user_id = (int) $user_id;
1587
        $creator_id = (int) $creator_id;
1588
1589
        if (empty($user_id)) {
1590
            return false;
1591
        }
1592
1593
        $userManager = self::getManager();
1594
        /** @var User $user */
1595
        $user = self::getRepository()->find($user_id);
1596
1597
        if (empty($user)) {
1598
            return false;
1599
        }
1600
1601
        if ($reset_password == 0) {
1602
            $password = null;
1603
            $auth_source = $user->getAuthSource();
1604
        } elseif ($reset_password == 1) {
1605
            $original_password = $password = api_generate_password();
1606
            $auth_source = PLATFORM_AUTH_SOURCE;
1607
        } elseif ($reset_password == 2) {
1608
            //$password = $password;
1609
            $auth_source = PLATFORM_AUTH_SOURCE;
1610
        } elseif ($reset_password == 3) {
1611
            //$password = $password;
1612
            //$auth_source = $auth_source;
1613
        }
1614
1615
        // Checking the user language
1616
        $languages = api_get_languages();
1617
        if (!in_array($language, $languages['folder'])) {
1618
            $language = api_get_setting('platformLanguage');
1619
        }
1620
1621
        $change_active = 0;
1622
        $isUserActive = $user->getActive();
1623
        if ($isUserActive != $active) {
1624
            $change_active = 1;
1625
        }
1626
1627
        $originalUsername = $user->getUsername();
1628
1629
        // If username is different from original then check if it exists.
1630
        if ($originalUsername !== $username) {
1631
            $available = self::is_username_available($username);
1632
            if ($available === false) {
1633
                return false;
1634
            }
1635
        }
1636
1637
        if (!empty($expiration_date)) {
1638
            $expiration_date = api_get_utc_datetime($expiration_date);
1639
            $expiration_date = new \DateTime(
1640
                $expiration_date,
1641
                new DateTimeZone('UTC')
1642
            );
1643
        }
1644
1645
        $user
1646
            ->setLastname($lastname)
1647
            ->setFirstname($firstname)
1648
            ->setUsername($username)
1649
            ->setStatus($status)
1650
            ->setAuthSource($auth_source)
1651
            ->setLanguage($language)
1652
            ->setEmail($email)
1653
            ->setOfficialCode($official_code)
1654
            ->setPhone($phone)
1655
            ->setAddress($address)
1656
            ->setPictureUri($picture_uri)
1657
            ->setExpirationDate($expiration_date)
1658
            ->setActive($active)
1659
            ->setEnabled($active)
1660
            ->setHrDeptId($hr_dept_id)
1661
        ;
1662
1663
        if (!is_null($password)) {
1664
            $user->setPlainPassword($password);
1665
            Event::addEvent(LOG_USER_PASSWORD_UPDATE, LOG_USER_ID, $user_id);
1666
            $date = api_get_local_time(
1667
                null,
1668
                null,
1669
                null,
1670
                null,
1671
                null,
1672
                null,
1673
                'Y-m-d'
1674
            );
1675
            $extraFieldValue = new ExtraFieldValue('user');
1676
            $extraFieldValue->save(
1677
                [
1678
                    'item_id' => $user->getId(),
1679
                    'variable' => 'password_updated_at',
1680
                    'value' => $date,
1681
                ]
1682
            );
1683
        }
1684
1685
        $userManager->updateUser($user, true);
1686
        Event::addEvent(LOG_USER_UPDATE, LOG_USER_ID, $user_id);
1687
1688
        if ($change_active == 1) {
1689
            if ($active == 1) {
1690
                $event_title = LOG_USER_ENABLE;
1691
            } else {
1692
                $event_title = LOG_USER_DISABLE;
1693
            }
1694
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
1695
        }
1696
1697
        if (is_array($extra) && count($extra) > 0) {
1698
            $res = true;
1699
            foreach ($extra as $fname => $fvalue) {
1700
                $res = $res && self::update_extra_field_value(
1701
                    $user_id,
1702
                    $fname,
1703
                    $fvalue
1704
                );
1705
            }
1706
        }
1707
1708
        if (!empty($email) && $send_email) {
1709
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
1710
            $sender_name = api_get_person_name(
1711
                api_get_setting('administratorName'),
1712
                api_get_setting('administratorSurname'),
1713
                null,
1714
                PERSON_NAME_EMAIL_ADDRESS
1715
            );
1716
            $email_admin = api_get_setting('emailAdministrator');
1717
            $url = api_get_path(WEB_PATH);
1718
            if (api_is_multiple_url_enabled()) {
1719
                $access_url_id = api_get_current_access_url_id();
1720
                if ($access_url_id != -1) {
1721
                    $url = api_get_access_url($access_url_id);
1722
                    $url = $url['url'];
1723
                }
1724
            }
1725
1726
            $tplContent = new Template(
1727
                null,
1728
                false,
1729
                false,
1730
                false,
1731
                false,
1732
                false
1733
            );
1734
            $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstname, $lastname)));
1735
            $tplContent->assign('login_name', $username);
1736
            $originalPassword = '';
1737
            if ($reset_password > 0) {
1738
                $originalPassword = stripslashes($original_password);
1739
            }
1740
            $tplContent->assign('original_password', $originalPassword);
1741
            // variables for the default template
1742
            $tplContent->assign('portal_url', $url);
1743
            // Adding this variable but not used in default template, used for task BT19518 with a customized template
1744
            $tplContent->assign('status_type', $status);
1745
            $creatorInfo = api_get_user_info($creator_id);
1746
            $creatorEmail = isset($creatorInfo['email']) ? $creatorInfo['email'] : '';
1747
            $tplSubject = new Template(
1748
                null,
1749
                false,
1750
                false,
1751
                false,
1752
                false,
1753
                false
1754
            );
1755
            // the complete_name is not used in the default Chamilo template but used in a specific template -refs BT#21334
1756
            $tplSubject->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $firstName does not exist. Did you maybe mean $firstname?
Loading history...
Comprehensibility Best Practice introduced by
The variable $lastName does not exist. Did you maybe mean $lastname?
Loading history...
1757
            $layoutSubject = $tplSubject->get_template('mail/subject_user_edit.tpl');
1758
            $emailSubject = $tplSubject->fetch($layoutSubject);
1759
1760
            if (!is_null($password) && api_get_configuration_value('send_two_inscription_confirmation_mail')) {
1761
                // The user has a new password *and* we need to tell him so,
1762
                // but the configuration is set to send 2 separate e-mails
1763
                // (one for username, one for password) when sending pass
1764
                $layoutContent = $tplContent->get_template('mail/new_user_first_email_confirmation.tpl');
1765
                $emailBody = $tplContent->fetch($layoutContent);
1766
                $mailTemplateManager = new MailTemplateManager();
1767
                if (!empty($emailTemplate) &&
1768
                    isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
1769
                    !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
1770
                ) {
1771
                    $userInfo = api_get_user_info($user_id);
1772
                    $emailBody = $mailTemplateManager->parseTemplate(
1773
                        $emailTemplate['new_user_first_email_confirmation.tpl'],
1774
                        $userInfo
1775
                    );
1776
                }
1777
1778
                api_mail_html(
1779
                    $recipient_name,
1780
                    $email,
1781
                    $emailSubject,
1782
                    $emailBody,
1783
                    $sender_name,
1784
                    $email_admin,
1785
                    null,
1786
                    null,
1787
                    null,
1788
                    null,
1789
                    $creatorEmail
1790
                );
1791
1792
                $layoutContent = $tplContent->get_template('mail/new_user_second_email_confirmation.tpl');
1793
                $emailBody = $tplContent->fetch($layoutContent);
1794
                $mailTemplateManager = new MailTemplateManager();
1795
                if (!empty($emailTemplate) &&
1796
                    isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
1797
                    !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
1798
                ) {
1799
                    $userInfo = api_get_user_info($user_id);
1800
                    $emailBody = $mailTemplateManager->parseTemplate(
1801
                        $emailTemplate['new_user_second_email_confirmation.tpl'],
1802
                        $userInfo
1803
                    );
1804
                }
1805
1806
                api_mail_html(
1807
                    $recipient_name,
1808
                    $email,
1809
                    $emailSubject,
1810
                    $emailBody,
1811
                    $sender_name,
1812
                    $email_admin,
1813
                    null,
1814
                    null,
1815
                    null,
1816
                    null,
1817
                    $creatorEmail
1818
                );
1819
            } else {
1820
                $layoutContent = $tplContent->get_template('mail/user_edit_content.tpl');
1821
                $emailBody = $tplContent->fetch($layoutContent);
1822
                $mailTemplateManager = new MailTemplateManager();
1823
                if (!empty($emailTemplate) &&
1824
                    isset($emailTemplate['user_edit_content.tpl']) &&
1825
                    !empty($emailTemplate['user_edit_content.tpl'])
1826
                ) {
1827
                    $userInfo = api_get_user_info($user_id);
1828
                    $emailBody = $mailTemplateManager->parseTemplate(
1829
                        $emailTemplate['user_edit_content.tpl'],
1830
                        $userInfo
1831
                    );
1832
                }
1833
1834
                api_mail_html(
1835
                    $recipient_name,
1836
                    $email,
1837
                    $emailSubject,
1838
                    $emailBody,
1839
                    $sender_name,
1840
                    $email_admin,
1841
                    null,
1842
                    null,
1843
                    null,
1844
                    null,
1845
                    $creatorEmail
1846
                );
1847
            }
1848
        }
1849
1850
        if (!empty($hook)) {
1851
            $hook->setEventData(['user' => $user]);
1852
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_POST);
1853
        }
1854
1855
        $cacheAvailable = api_get_configuration_value('apc');
1856
        if ($cacheAvailable === true) {
1857
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
1858
            if (apcu_exists($apcVar)) {
1859
                apcu_delete($apcVar);
1860
            }
1861
        }
1862
1863
        return $user->getId();
1864
    }
1865
1866
    /**
1867
     * Disables a user.
1868
     *
1869
     * @param int User id
1870
     *
1871
     * @return bool
1872
     *
1873
     * @uses \UserManager::change_active_state() to actually disable the user
1874
     * @assert (0) === false
1875
     */
1876
    public static function disable($user_id)
1877
    {
1878
        if (empty($user_id)) {
1879
            return false;
1880
        }
1881
        self::change_active_state($user_id, 0);
1882
1883
        return true;
1884
    }
1885
1886
    /**
1887
     * Enable a user.
1888
     *
1889
     * @param int User id
1890
     *
1891
     * @return bool
1892
     *
1893
     * @uses \UserManager::change_active_state() to actually disable the user
1894
     * @assert (0) === false
1895
     */
1896
    public static function enable($user_id)
1897
    {
1898
        if (empty($user_id)) {
1899
            return false;
1900
        }
1901
        self::change_active_state($user_id, 1);
1902
1903
        return true;
1904
    }
1905
1906
    /**
1907
     * Returns the user's id based on the original id and field name in
1908
     * the extra fields. Returns 0 if no user was found. This function is
1909
     * mostly useful in the context of a web services-based sinchronization.
1910
     *
1911
     * @param string Original user id
1912
     * @param string Original field name
1913
     *
1914
     * @return int User id
1915
     * @assert ('0','---') === 0
1916
     */
1917
    public static function get_user_id_from_original_id(
1918
        $original_user_id_value,
1919
        $original_user_id_name
1920
    ) {
1921
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1922
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1923
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1924
1925
        $original_user_id_name = Database::escape_string($original_user_id_name);
1926
        $original_user_id_value = Database::escape_string($original_user_id_value);
1927
1928
        $sql = "SELECT item_id as user_id
1929
                FROM $t_uf uf
1930
                INNER JOIN $t_ufv ufv
1931
                ON ufv.field_id = uf.id
1932
                WHERE
1933
                    variable = '$original_user_id_name' AND
1934
                    value = '$original_user_id_value' AND
1935
                    extra_field_type = $extraFieldType
1936
                ";
1937
        $res = Database::query($sql);
1938
        $row = Database::fetch_object($res);
1939
        if ($row) {
1940
            return $row->user_id;
1941
        }
1942
1943
        return 0;
1944
    }
1945
1946
    /**
1947
     * Check if a username is available.
1948
     *
1949
     * @param string $username the wanted username
1950
     *
1951
     * @return bool true if the wanted username is available
1952
     * @assert ('') === false
1953
     * @assert ('xyzxyzxyz') === true
1954
     */
1955
    public static function is_username_available($username)
1956
    {
1957
        if (empty($username)) {
1958
            return false;
1959
        }
1960
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1961
        $sql = "SELECT username FROM $table_user
1962
                WHERE username = '".Database::escape_string($username)."'";
1963
        $res = Database::query($sql);
1964
1965
        return Database::num_rows($res) == 0;
1966
    }
1967
1968
    /**
1969
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1970
     *
1971
     * @param string $firstname the first name of the user
1972
     * @param string $lastname  the last name of the user
1973
     *
1974
     * @return string suggests a username that contains only ASCII-letters and digits,
1975
     *                without check for uniqueness within the system
1976
     *
1977
     * @author Julio Montoya Armas
1978
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1979
     * @assert ('','') === false
1980
     * @assert ('a','b') === 'ab'
1981
     */
1982
    public static function create_username($firstname, $lastname)
1983
    {
1984
        if (empty($firstname) && empty($lastname)) {
1985
            return false;
1986
        }
1987
1988
        // The first letter only.
1989
        $firstname = api_substr(
1990
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1991
            0,
1992
            1
1993
        );
1994
        //Looking for a space in the lastname
1995
        $pos = api_strpos($lastname, ' ');
1996
        if ($pos !== false) {
1997
            $lastname = api_substr($lastname, 0, $pos);
1998
        }
1999
2000
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
2001
        $username = $firstname.$lastname;
2002
        if (empty($username)) {
2003
            $username = 'user';
2004
        }
2005
2006
        $username = URLify::transliterate($username);
2007
2008
        return strtolower(substr($username, 0, USERNAME_MAX_LENGTH - 3));
2009
    }
2010
2011
    /**
2012
     * Creates a unique username, using:
2013
     * 1. the first name and the last name of a user;
2014
     * 2. an already created username but not checked for uniqueness yet.
2015
     *
2016
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
2017
     *                          parameter is treated as username which is to be checked f
2018
     *                          or uniqueness and to be modified when it is necessary.
2019
     * @param string $lastname  the last name of the user
2020
     *
2021
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
2022
     *                Note: When the method is called several times with same parameters,
2023
     *                its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
2024
     *
2025
     * @author Ivan Tcholakov, 2009
2026
     */
2027
    public static function create_unique_username($firstname, $lastname = null)
2028
    {
2029
        if (is_null($lastname)) {
2030
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
2031
            // For making this method tolerant of mistakes,
2032
            // let us transliterate and purify the suggested input username anyway.
2033
            // So, instead of the sentence $username = $firstname; we place the following:
2034
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
2035
        } else {
2036
            $username = self::create_username($firstname, $lastname);
2037
        }
2038
        if (!self::is_username_available($username)) {
2039
            $i = 2;
2040
            $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
2041
            while (!self::is_username_available($temp_username)) {
2042
                $i++;
2043
                $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
2044
            }
2045
            $username = $temp_username;
2046
        }
2047
2048
        $username = URLify::transliterate($username);
2049
2050
        return $username;
2051
    }
2052
2053
    /**
2054
     * Modifies a given username accordingly to the specification for valid characters and length.
2055
     *
2056
     * @param $username string          The input username
2057
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
2058
     *                     otherwise compliance may be partial. The default value is FALSE.
2059
     *
2060
     * @return string the resulting purified username
2061
     */
2062
    public static function purify_username($username, $strict = false)
2063
    {
2064
        if ($strict) {
2065
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
2066
            // into ASCII letters in order they not to be totally removed.
2067
            // 2. Applying the strict purifier.
2068
            // 3. Length limitation.
2069
            if ('true' === api_get_setting('login_is_email')) {
2070
                $return = substr(preg_replace(USERNAME_PURIFIER_MAIL, '', $username), 0, USERNAME_MAX_LENGTH);
2071
            } else {
2072
                $return = substr(preg_replace(USERNAME_PURIFIER, '', $username), 0, USERNAME_MAX_LENGTH);
2073
            }
2074
2075
            return URLify::transliterate($return);
2076
        }
2077
2078
        // 1. Applying the shallow purifier.
2079
        // 2. Length limitation.
2080
        return substr(
2081
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
2082
            0,
2083
            USERNAME_MAX_LENGTH
2084
        );
2085
    }
2086
2087
    /**
2088
     * Checks whether the user id exists in the database.
2089
     *
2090
     * @param int $userId User id
2091
     *
2092
     * @return bool True if user id was found, false otherwise
2093
     */
2094
    public static function is_user_id_valid($userId)
2095
    {
2096
        $resultData = Database::select(
2097
            'COUNT(1) AS count',
2098
            Database::get_main_table(TABLE_MAIN_USER),
2099
            [
2100
                'where' => ['id = ?' => (int) $userId],
2101
            ],
2102
            'first'
2103
        );
2104
2105
        if ($resultData === false) {
2106
            return false;
2107
        }
2108
2109
        return $resultData['count'] > 0;
2110
    }
2111
2112
    /**
2113
     * Checks whether a given username matches to the specification strictly.
2114
     * The empty username is assumed here as invalid.
2115
     * Mostly this function is to be used in the user interface built-in validation routines
2116
     * for providing feedback while usernames are enterd manually.
2117
     *
2118
     * @param string $username the input username
2119
     *
2120
     * @return bool returns TRUE if the username is valid, FALSE otherwise
2121
     */
2122
    public static function is_username_valid($username)
2123
    {
2124
        return !empty($username) && $username == self::purify_username($username, true);
2125
    }
2126
2127
    /**
2128
     * Checks whether a username is empty. If the username contains whitespace characters,
2129
     * such as spaces, tabulators, newlines, etc.,
2130
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
2131
     *
2132
     * @param string $username the given username
2133
     *
2134
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
2135
     */
2136
    public static function is_username_empty($username)
2137
    {
2138
        return strlen(self::purify_username($username, false)) == 0;
2139
    }
2140
2141
    /**
2142
     * Checks whether a username is too long or not.
2143
     *
2144
     * @param string $username the given username, it should contain only ASCII-letters and digits
2145
     *
2146
     * @return bool returns TRUE if length of the username exceeds the limit, FALSE otherwise
2147
     */
2148
    public static function is_username_too_long($username)
2149
    {
2150
        return strlen($username) > USERNAME_MAX_LENGTH;
2151
    }
2152
2153
    /**
2154
     * Get the users by ID.
2155
     *
2156
     * @param array  $ids    student ids
2157
     * @param string $active
2158
     * @param string $order
2159
     * @param string $limit
2160
     *
2161
     * @return array $result student information
2162
     */
2163
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
2164
    {
2165
        if (empty($ids)) {
2166
            return [];
2167
        }
2168
2169
        $ids = is_array($ids) ? $ids : [$ids];
2170
        $ids = array_map('intval', $ids);
2171
        $ids = implode(',', $ids);
2172
2173
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2174
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
2175
        if (!is_null($active)) {
2176
            $sql .= ' AND active='.($active ? '1' : '0');
2177
        }
2178
2179
        if (!is_null($order)) {
2180
            $order = Database::escape_string($order);
2181
            $sql .= ' ORDER BY '.$order;
2182
        }
2183
2184
        if (!is_null($limit)) {
2185
            $limit = Database::escape_string($limit);
2186
            $sql .= ' LIMIT '.$limit;
2187
        }
2188
2189
        $rs = Database::query($sql);
2190
        $result = [];
2191
        while ($row = Database::fetch_array($rs)) {
2192
            $result[] = $row;
2193
        }
2194
2195
        return $result;
2196
    }
2197
2198
    /**
2199
     * Get a list of users of which the given conditions match with an = 'cond'.
2200
     *
2201
     * @param array $conditions a list of condition (example : status=>STUDENT)
2202
     * @param array $order_by   a list of fields on which sort
2203
     *
2204
     * @return array an array with all users of the platform
2205
     *
2206
     * @todo security filter order by
2207
     */
2208
    public static function get_user_list(
2209
        $conditions = [],
2210
        $order_by = [],
2211
        $limit_from = false,
2212
        $limit_to = false,
2213
        $idCampus = null,
2214
        $keyword = null,
2215
        $lastConnectionDate = null,
2216
        $getCount = false,
2217
        $filterUsers = null
2218
    ) {
2219
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
2220
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2221
        $return_array = [];
2222
2223
        if ($getCount) {
2224
            $sql = "SELECT count(user.id) as nbUsers FROM $user_table user ";
2225
        } else {
2226
            $sql = "SELECT user.* FROM $user_table user ";
2227
        }
2228
2229
        if (api_is_multiple_url_enabled()) {
2230
            if ($idCampus) {
2231
                $urlId = $idCampus;
2232
            } else {
2233
                $urlId = api_get_current_access_url_id();
2234
            }
2235
            $sql .= " INNER JOIN $userUrlTable url_user
2236
                      ON (user.user_id = url_user.user_id)
2237
                      WHERE url_user.access_url_id = $urlId";
2238
        } else {
2239
            $sql .= " WHERE 1=1 ";
2240
        }
2241
2242
        if (!empty($keyword)) {
2243
            $keyword = trim(Database::escape_string($keyword));
2244
            $keywordParts = array_filter(explode(' ', $keyword));
2245
            $extraKeyword = '';
2246
            if (!empty($keywordParts)) {
2247
                $keywordPartsFixed = Database::escape_string(implode('%', $keywordParts));
2248
                if (!empty($keywordPartsFixed)) {
2249
                    $extraKeyword .= " OR
2250
                        CONCAT(user.firstname, ' ', user.lastname) LIKE '%$keywordPartsFixed%' OR
2251
                        CONCAT(user.lastname, ' ', user.firstname) LIKE '%$keywordPartsFixed%' ";
2252
                }
2253
            }
2254
2255
            $sql .= " AND (
2256
                user.username LIKE '%$keyword%' OR
2257
                user.firstname LIKE '%$keyword%' OR
2258
                user.lastname LIKE '%$keyword%' OR
2259
                user.official_code LIKE '%$keyword%' OR
2260
                user.email LIKE '%$keyword%' OR
2261
                CONCAT(user.firstname, ' ', user.lastname) LIKE '%$keyword%' OR
2262
                CONCAT(user.lastname, ' ', user.firstname) LIKE '%$keyword%'
2263
                $extraKeyword
2264
            )";
2265
        }
2266
2267
        if (!empty($lastConnectionDate)) {
2268
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
2269
            $sql .= " AND user.last_login <= '$lastConnectionDate' ";
2270
        }
2271
2272
        if (count($conditions) > 0) {
2273
            foreach ($conditions as $field => $value) {
2274
                $field = Database::escape_string($field);
2275
                $value = Database::escape_string($value);
2276
                $sql .= " AND $field = '$value'";
2277
            }
2278
        }
2279
2280
        if (!empty($filterUsers)) {
2281
            $sql .= " AND user.id IN(".implode(',', $filterUsers).")";
2282
        }
2283
2284
        if (count($order_by) > 0) {
2285
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
2286
        }
2287
2288
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
2289
            $limit_from = (int) $limit_from;
2290
            $limit_to = (int) $limit_to;
2291
            $sql .= " LIMIT $limit_from, $limit_to";
2292
        }
2293
        $sql_result = Database::query($sql);
2294
2295
        if ($getCount) {
2296
            $result = Database::fetch_array($sql_result);
2297
2298
            return $result['nbUsers'];
2299
        }
2300
2301
        while ($result = Database::fetch_array($sql_result)) {
2302
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
2303
            $return_array[] = $result;
2304
        }
2305
2306
        return $return_array;
2307
    }
2308
2309
    public static function getUserListExtraConditions(
2310
        $conditions = [],
2311
        $order_by = [],
2312
        $limit_from = false,
2313
        $limit_to = false,
2314
        $idCampus = null,
2315
        $extraConditions = '',
2316
        $getCount = false
2317
    ) {
2318
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
2319
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2320
        $return_array = [];
2321
        $sql = "SELECT user.* FROM $user_table user ";
2322
2323
        if ($getCount) {
2324
            $sql = "SELECT count(user.id) count FROM $user_table user ";
2325
        }
2326
2327
        if (api_is_multiple_url_enabled()) {
2328
            if ($idCampus) {
2329
                $urlId = $idCampus;
2330
            } else {
2331
                $urlId = api_get_current_access_url_id();
2332
            }
2333
            $sql .= " INNER JOIN $userUrlTable url_user
2334
                      ON (user.user_id = url_user.user_id)
2335
                      WHERE url_user.access_url_id = $urlId";
2336
        } else {
2337
            $sql .= " WHERE 1=1 ";
2338
        }
2339
2340
        $sql .= " AND status <> ".ANONYMOUS." ";
2341
2342
        if (count($conditions) > 0) {
2343
            foreach ($conditions as $field => $value) {
2344
                $field = Database::escape_string($field);
2345
                $value = Database::escape_string($value);
2346
                $sql .= " AND $field = '$value'";
2347
            }
2348
        }
2349
2350
        $sql .= str_replace("\'", "'", Database::escape_string($extraConditions));
2351
2352
        if (!empty($order_by) && count($order_by) > 0) {
2353
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
2354
        }
2355
2356
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
2357
            $limit_from = (int) $limit_from;
2358
            $limit_to = (int) $limit_to;
2359
            $sql .= " LIMIT $limit_from, $limit_to";
2360
        }
2361
2362
        $sql_result = Database::query($sql);
2363
2364
        if ($getCount) {
2365
            $result = Database::fetch_array($sql_result);
2366
2367
            return $result['count'];
2368
        }
2369
2370
        while ($result = Database::fetch_array($sql_result)) {
2371
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
2372
            $return_array[] = $result;
2373
        }
2374
2375
        return $return_array;
2376
    }
2377
2378
    /**
2379
     * Get a list of users of which the given conditions match with a LIKE '%cond%'.
2380
     *
2381
     * @param array  $conditions       a list of condition (exemple : status=>STUDENT)
2382
     * @param array  $order_by         a list of fields on which sort
2383
     * @param bool   $simple_like      Whether we want a simple LIKE 'abc' or a LIKE '%abc%'
2384
     * @param string $condition        Whether we want the filters to be combined by AND or OR
2385
     * @param array  $onlyThisUserList
2386
     *
2387
     * @return array an array with all users of the platform
2388
     *
2389
     * @todo optional course code parameter, optional sorting parameters...
2390
     * @todo security filter order_by
2391
     */
2392
    public static function getUserListLike(
2393
        $conditions = [],
2394
        $order_by = [],
2395
        $simple_like = false,
2396
        $condition = 'AND',
2397
        $onlyThisUserList = []
2398
    ) {
2399
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
2400
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2401
        $return_array = [];
2402
        $sql_query = "SELECT user.id FROM $user_table user ";
2403
2404
        if (api_is_multiple_url_enabled()) {
2405
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
2406
        }
2407
2408
        $sql_query .= ' WHERE 1 = 1 ';
2409
        if (count($conditions) > 0) {
2410
2411
            $andActive = "";
2412
            if (isset($conditions['active'])) {
2413
                $andActive = " AND active = " . (int) $conditions['active'];
2414
                unset($conditions['active']);
2415
            }
2416
2417
            $temp_conditions = [];
2418
            foreach ($conditions as $field => $value) {
2419
                $field = Database::escape_string($field);
2420
                $value = Database::escape_string($value);
2421
                if ($simple_like) {
2422
                    $temp_conditions[] = $field." LIKE '$value%'";
2423
                } else {
2424
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
2425
                }
2426
            }
2427
            if (!empty($temp_conditions)) {
2428
                $sql_query .= ' AND ('.implode(' '.$condition.' ', $temp_conditions).') ';
2429
            }
2430
2431
            if (api_is_multiple_url_enabled()) {
2432
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
2433
            }
2434
            $sql_query .= $andActive;
2435
        } else {
2436
            if (api_is_multiple_url_enabled()) {
2437
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
2438
            }
2439
        }
2440
2441
        if (api_is_session_admin() && (api_get_setting('prevent_session_admins_to_manage_all_users') === 'true')) {
2442
            $sql_query .= ' AND user.creator_id = '.api_get_user_id();
2443
        }
2444
2445
        if (!empty($onlyThisUserList)) {
2446
            $onlyThisUserListToString = implode("','", $onlyThisUserList);
2447
            $sql_query .= " AND user.id IN ('$onlyThisUserListToString') ";
2448
        }
2449
2450
        if (count($order_by) > 0) {
2451
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by));
2452
        }
2453
2454
        $sql_result = Database::query($sql_query);
2455
        while ($result = Database::fetch_array($sql_result)) {
2456
            $userInfo = api_get_user_info($result['id']);
2457
            $return_array[] = $userInfo;
2458
        }
2459
2460
        return $return_array;
2461
    }
2462
2463
    /**
2464
     * Get user picture URL or path from user ID (returns an array).
2465
     * The return format is a complete path, enabling recovery of the directory
2466
     * with dirname() or the file with basename(). This also works for the
2467
     * functions dealing with the user's productions, as they are located in
2468
     * the same directory.
2469
     *
2470
     * @param int    $id       User ID
2471
     * @param string $type     Type of path to return (can be 'system', 'web')
2472
     * @param array  $userInfo user information to avoid query the DB
2473
     *                         returns the /main/img/unknown.jpg image set it at true
2474
     *
2475
     * @return array Array of 2 elements: 'dir' and 'file' which contain
2476
     *               the dir and file as the name implies if image does not exist it will
2477
     *               return the unknow image if anonymous parameter is true if not it returns an empty array
2478
     */
2479
    public static function get_user_picture_path_by_id(
2480
        $id,
2481
        $type = 'web',
2482
        $userInfo = []
2483
    ) {
2484
        switch ($type) {
2485
            case 'system': // Base: absolute system path.
2486
                $base = api_get_path(SYS_CODE_PATH);
2487
                break;
2488
            case 'web': // Base: absolute web path.
2489
            default:
2490
                $base = api_get_path(WEB_CODE_PATH);
2491
                break;
2492
        }
2493
2494
        $anonymousPath = [
2495
            'dir' => $base.'img/',
2496
            'file' => 'unknown.jpg',
2497
            'email' => '',
2498
        ];
2499
2500
        if (empty($id) || empty($type)) {
2501
            return $anonymousPath;
2502
        }
2503
2504
        $id = (int) $id;
2505
        if (empty($userInfo)) {
2506
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
2507
            $sql = "SELECT email, picture_uri FROM $user_table
2508
                    WHERE id = ".$id;
2509
            $res = Database::query($sql);
2510
2511
            if (!Database::num_rows($res)) {
2512
                return $anonymousPath;
2513
            }
2514
            $user = Database::fetch_array($res);
2515
            if (empty($user['picture_uri'])) {
2516
                return $anonymousPath;
2517
            }
2518
        } else {
2519
            $user = $userInfo;
2520
        }
2521
2522
        $pictureFilename = trim($user['picture_uri']);
2523
2524
        $dir = self::getUserPathById($id, $type);
2525
2526
        return [
2527
            'dir' => $dir,
2528
            'file' => $pictureFilename,
2529
            'email' => $user['email'],
2530
        ];
2531
    }
2532
2533
    /**
2534
     * *** READ BEFORE REVIEW THIS FUNCTION ***
2535
     * This function is a exact copy from get_user_picture_path_by_id() and it was create it to avoid
2536
     * a recursive calls for get_user_picture_path_by_id() in another functions when you update a user picture
2537
     * in same script, so you can find this function usage in update_user_picture() function.
2538
     *
2539
     * @param int    $id       User ID
2540
     * @param string $type     Type of path to return (can be 'system', 'web')
2541
     * @param array  $userInfo user information to avoid query the DB
2542
     *                         returns the /main/img/unknown.jpg image set it at true
2543
     *
2544
     * @return array Array of 2 elements: 'dir' and 'file' which contain
2545
     *               the dir and file as the name implies if image does not exist it will
2546
     *               return the unknown image if anonymous parameter is true if not it returns an empty array
2547
     */
2548
    public static function getUserPicturePathById($id, $type = 'web', $userInfo = [])
2549
    {
2550
        switch ($type) {
2551
            case 'system': // Base: absolute system path.
2552
                $base = api_get_path(SYS_CODE_PATH);
2553
                break;
2554
            case 'web': // Base: absolute web path.
2555
            default:
2556
                $base = api_get_path(WEB_CODE_PATH);
2557
                break;
2558
        }
2559
2560
        $anonymousPath = [
2561
            'dir' => $base.'img/',
2562
            'file' => 'unknown.jpg',
2563
            'email' => '',
2564
        ];
2565
2566
        if (empty($id) || empty($type)) {
2567
            return $anonymousPath;
2568
        }
2569
2570
        $id = (int) $id;
2571
        if (empty($userInfo)) {
2572
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
2573
            $sql = "SELECT email, picture_uri FROM $user_table WHERE id = $id";
2574
            $res = Database::query($sql);
2575
2576
            if (!Database::num_rows($res)) {
2577
                return $anonymousPath;
2578
            }
2579
            $user = Database::fetch_array($res);
2580
2581
            if (empty($user['picture_uri'])) {
2582
                return $anonymousPath;
2583
            }
2584
        } else {
2585
            $user = $userInfo;
2586
        }
2587
2588
        $pictureFilename = trim($user['picture_uri']);
2589
        $dir = self::getUserPathById($id, $type);
2590
2591
        return [
2592
            'dir' => $dir,
2593
            'file' => $pictureFilename,
2594
            'email' => $user['email'],
2595
        ];
2596
    }
2597
2598
    /**
2599
     * Get user path from user ID (returns an array).
2600
     * The return format is a complete path to a folder ending with "/"
2601
     * In case the first level of subdirectory of users/ does not exist, the
2602
     * function will attempt to create it. Probably not the right place to do it
2603
     * but at least it avoids headaches in many other places.
2604
     *
2605
     * @param int    $id   User ID
2606
     * @param string $type Type of path to return (can be 'system', 'web', 'last')
2607
     *
2608
     * @return string User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
2609
     */
2610
    public static function getUserPathById($id, $type)
2611
    {
2612
        $id = (int) $id;
2613
        if (!$id) {
2614
            return null;
2615
        }
2616
2617
        $userPath = "users/$id/";
2618
        if (api_get_setting('split_users_upload_directory') === 'true') {
2619
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
2620
            // In exceptional cases, on some portals, the intermediate base user
2621
            // directory might not have been created. Make sure it is before
2622
            // going further.
2623
2624
            $rootPath = api_get_path(SYS_UPLOAD_PATH).'users/'.substr((string) $id, 0, 1);
2625
            if (!is_dir($rootPath)) {
2626
                $perm = api_get_permissions_for_new_directories();
2627
                try {
2628
                    mkdir($rootPath, $perm);
2629
                } catch (Exception $e) {
2630
                    error_log($e->getMessage());
2631
                }
2632
            }
2633
        }
2634
        switch ($type) {
2635
            case 'system': // Base: absolute system path.
2636
                $userPath = api_get_path(SYS_UPLOAD_PATH).$userPath;
2637
                break;
2638
            case 'web': // Base: absolute web path.
2639
                $userPath = api_get_path(WEB_UPLOAD_PATH).$userPath;
2640
                break;
2641
            case 'last': // Only the last part starting with users/
2642
                break;
2643
        }
2644
2645
        return $userPath;
2646
    }
2647
2648
    /**
2649
     * Gets the current user image.
2650
     *
2651
     * @param string $user_id
2652
     * @param int    $size        it can be USER_IMAGE_SIZE_SMALL,
2653
     *                            USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
2654
     * @param bool   $addRandomId
2655
     * @param array  $userInfo    to avoid query the DB
2656
     *
2657
     * @return string
2658
     */
2659
    public static function getUserPicture(
2660
        $user_id,
2661
        $size = USER_IMAGE_SIZE_MEDIUM,
2662
        $addRandomId = true,
2663
        $userInfo = []
2664
    ) {
2665
        // Make sure userInfo is defined. Otherwise, define it!
2666
        if (empty($userInfo) || !is_array($userInfo) || count($userInfo) == 0) {
2667
            if (empty($user_id)) {
2668
                return '';
2669
            } else {
2670
                $userInfo = api_get_user_info($user_id);
2671
            }
2672
        }
2673
2674
        $imageWebPath = self::get_user_picture_path_by_id(
2675
            $user_id,
2676
            'web',
2677
            $userInfo
2678
        );
2679
        $pictureWebFile = $imageWebPath['file'];
2680
        $pictureWebDir = $imageWebPath['dir'];
2681
2682
        $pictureAnonymousSize = '128';
2683
        $gravatarSize = 22;
2684
        $realSizeName = 'small_';
2685
2686
        switch ($size) {
2687
            case USER_IMAGE_SIZE_SMALL:
2688
                $pictureAnonymousSize = '32';
2689
                $realSizeName = 'small_';
2690
                $gravatarSize = 32;
2691
                break;
2692
            case USER_IMAGE_SIZE_MEDIUM:
2693
                $pictureAnonymousSize = '64';
2694
                $realSizeName = 'medium_';
2695
                $gravatarSize = 64;
2696
                break;
2697
            case USER_IMAGE_SIZE_ORIGINAL:
2698
                $pictureAnonymousSize = '128';
2699
                $realSizeName = '';
2700
                $gravatarSize = 128;
2701
                break;
2702
            case USER_IMAGE_SIZE_BIG:
2703
                $pictureAnonymousSize = '128';
2704
                $realSizeName = 'big_';
2705
                $gravatarSize = 128;
2706
                break;
2707
        }
2708
2709
        $gravatarEnabled = api_get_setting('gravatar_enabled');
2710
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
2711
        if ($pictureWebFile == 'unknown.jpg' || empty($pictureWebFile)) {
2712
            if ($gravatarEnabled === 'true') {
2713
                $file = self::getGravatar(
2714
                    $imageWebPath['email'],
2715
                    $gravatarSize,
2716
                    api_get_setting('gravatar_type')
2717
                );
2718
2719
                if ($addRandomId) {
2720
                    $file .= '&rand='.uniqid();
2721
                }
2722
2723
                return $file;
2724
            }
2725
2726
            return $anonymousPath;
2727
        }
2728
2729
        $pictureSysPath = self::get_user_picture_path_by_id($user_id, 'system');
2730
        $file = $pictureSysPath['dir'].$realSizeName.$pictureWebFile;
2731
        $picture = '';
2732
        if (file_exists($file)) {
2733
            $picture = $pictureWebDir.$realSizeName.$pictureWebFile;
2734
        } else {
2735
            $file = $pictureSysPath['dir'].$pictureWebFile;
2736
            if (file_exists($file) && !is_dir($file)) {
2737
                $picture = $pictureWebFile['dir'].$pictureWebFile;
2738
            }
2739
        }
2740
2741
        if (empty($picture)) {
2742
            return $anonymousPath;
2743
        }
2744
2745
        if ($addRandomId) {
2746
            $picture .= '?rand='.uniqid();
2747
        }
2748
2749
        return $picture;
2750
    }
2751
2752
    /**
2753
     * Creates new user photos in various sizes of a user, or deletes user photos.
2754
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2755
     *
2756
     * @param int    $user_id the user internal identification number
2757
     * @param string $file    The common file name for the newly created photos.
2758
     *                        It will be checked and modified for compatibility with the file system.
2759
     *                        If full name is provided, path component is ignored.
2760
     *                        If an empty name is provided, then old user photos are deleted only,
2761
     *
2762
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
2763
     *
2764
     * @param string $source_file    the full system name of the image from which user photos will be created
2765
     * @param string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
2766
     *
2767
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
2768
     *              When deletion is requested returns empty string.
2769
     *              In case of internal error or negative validation returns FALSE.
2770
     */
2771
    public static function update_user_picture(
2772
        $user_id,
2773
        $file = null,
2774
        $source_file = null,
2775
        $cropParameters = ''
2776
    ) {
2777
        if (empty($user_id)) {
2778
            return false;
2779
        }
2780
        $delete = empty($file);
2781
        if (empty($source_file)) {
2782
            $source_file = $file;
2783
        }
2784
2785
        // User-reserved directory where photos have to be placed.
2786
        $path_info = self::getUserPicturePathById($user_id, 'system');
2787
        $path = $path_info['dir'];
2788
2789
        // If this directory does not exist - we create it.
2790
        if (!file_exists($path)) {
2791
            mkdir($path, api_get_permissions_for_new_directories(), true);
2792
        }
2793
2794
        // The old photos (if any).
2795
        $old_file = $path_info['file'];
2796
2797
        // Let us delete them.
2798
        if ($old_file != 'unknown.jpg') {
2799
            if (KEEP_THE_OLD_IMAGE_AFTER_CHANGE) {
0 ignored issues
show
Bug introduced by
The constant KEEP_THE_OLD_IMAGE_AFTER_CHANGE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2800
                $prefix = 'saved_'.date('Y_m_d_H_i_s').'_'.uniqid('').'_';
2801
                @rename($path.'small_'.$old_file, $path.$prefix.'small_'.$old_file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

2801
                /** @scrutinizer ignore-unhandled */ @rename($path.'small_'.$old_file, $path.$prefix.'small_'.$old_file);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2802
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
2803
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
2804
                @rename($path.$old_file, $path.$prefix.$old_file);
2805
            } else {
2806
                @unlink($path.'small_'.$old_file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

2806
                /** @scrutinizer ignore-unhandled */ @unlink($path.'small_'.$old_file);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2807
                @unlink($path.'medium_'.$old_file);
2808
                @unlink($path.'big_'.$old_file);
2809
                @unlink($path.$old_file);
2810
            }
2811
        }
2812
2813
        // Exit if only deletion has been requested. Return an empty picture name.
2814
        if ($delete) {
2815
            return '';
2816
        }
2817
2818
        // Validation 2.
2819
        $allowed_types = api_get_supported_image_extensions();
2820
        $file = str_replace('\\', '/', $file);
2821
        $filename = (($pos = strrpos($file, '/')) !== false) ? substr($file, $pos + 1) : $file;
2822
        $extension = strtolower(substr(strrchr($filename, '.'), 1));
2823
        if (!in_array($extension, $allowed_types)) {
2824
            return false;
2825
        }
2826
2827
        // This is the common name for the new photos.
2828
        if (KEEP_THE_NAME_WHEN_CHANGE_IMAGE && $old_file != 'unknown.jpg') {
0 ignored issues
show
Bug introduced by
The constant KEEP_THE_NAME_WHEN_CHANGE_IMAGE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2829
            $old_extension = strtolower(substr(strrchr($old_file, '.'), 1));
2830
            $filename = in_array($old_extension, $allowed_types) ? substr($old_file, 0, -strlen($old_extension)) : $old_file;
2831
            $filename = (substr($filename, -1) == '.') ? $filename.$extension : $filename.'.'.$extension;
2832
        } else {
2833
            $filename = api_replace_dangerous_char($filename);
2834
            if (PREFIX_IMAGE_FILENAME_WITH_UID) {
0 ignored issues
show
Bug introduced by
The constant PREFIX_IMAGE_FILENAME_WITH_UID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2835
                $filename = uniqid('').'_'.$filename;
2836
            }
2837
            // We always prefix user photos with user ids, so on setting
2838
            // api_get_setting('split_users_upload_directory') === 'true'
2839
            // the correspondent directories to be found successfully.
2840
            $filename = $user_id.'_'.$filename;
2841
        }
2842
2843
        if (!file_exists($source_file)) {
2844
            return false;
2845
        }
2846
2847
        $mimeContentType = mime_content_type($source_file);
2848
        if (false === strpos($mimeContentType, 'image')) {
2849
            return false;
2850
        }
2851
2852
        //Crop the image to adjust 1:1 ratio
2853
        $image = new Image($source_file);
2854
        $image->crop($cropParameters);
2855
2856
        // Storing the new photos in 4 versions with various sizes.
2857
        $userPath = self::getUserPathById($user_id, 'system');
2858
2859
        // If this path does not exist - we create it.
2860
        if (!file_exists($userPath)) {
2861
            mkdir($userPath, api_get_permissions_for_new_directories(), true);
2862
        }
2863
        $small = new Image($source_file);
2864
        $small->resize(32);
2865
        $small->send_image($userPath.'small_'.$filename);
2866
        $medium = new Image($source_file);
2867
        $medium->resize(85);
2868
        $medium->send_image($userPath.'medium_'.$filename);
2869
        $normal = new Image($source_file);
2870
        $normal->resize(200);
2871
        $normal->send_image($userPath.$filename);
2872
2873
        $big = new Image($source_file); // This is the original picture.
2874
        $big->send_image($userPath.'big_'.$filename);
2875
2876
        $result = $small && $medium && $normal && $big;
0 ignored issues
show
introduced by
$medium is of type Image, thus it always evaluated to true.
Loading history...
introduced by
$big is of type Image, thus it always evaluated to true.
Loading history...
introduced by
$normal is of type Image, thus it always evaluated to true.
Loading history...
2877
2878
        return $result ? $filename : false;
2879
    }
2880
2881
    /**
2882
     * Update User extra field file type into {user_folder}/{$extra_field}.
2883
     *
2884
     * @param int    $user_id     The user internal identification number
2885
     * @param string $extra_field The $extra_field The extra field name
2886
     * @param null   $file        The filename
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $file is correct as it would always require null to be passed?
Loading history...
2887
     * @param null   $source_file The temporal filename
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $source_file is correct as it would always require null to be passed?
Loading history...
2888
     *
2889
     * @return bool|null return filename if success, but false
2890
     */
2891
    public static function update_user_extra_file(
2892
        $user_id,
2893
        $extra_field = '',
2894
        $file = null,
2895
        $source_file = null
2896
    ) {
2897
        // Add Filter
2898
        $source_file = Security::filter_filename($source_file);
2899
        $file = Security::filter_filename($file);
2900
2901
        if (empty($user_id)) {
2902
            return false;
2903
        }
2904
2905
        if (empty($source_file)) {
2906
            $source_file = $file;
2907
        }
2908
2909
        // User-reserved directory where extra file have to be placed.
2910
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2911
        $path = $path_info['dir'];
2912
        if (!empty($extra_field)) {
2913
            $path .= $extra_field.'/';
2914
        }
2915
        // If this directory does not exist - we create it.
2916
        if (!file_exists($path)) {
2917
            @mkdir($path, api_get_permissions_for_new_directories(), true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

2917
            /** @scrutinizer ignore-unhandled */ @mkdir($path, api_get_permissions_for_new_directories(), true);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2918
        }
2919
2920
        if (filter_extension($file)) {
2921
            if (@move_uploaded_file($source_file, $path.$file)) {
2922
                if ($extra_field) {
2923
                    return $extra_field.'/'.$file;
2924
                } else {
2925
                    return $file;
2926
                }
2927
            }
2928
        }
2929
2930
        return false; // this should be returned if anything went wrong with the upload
2931
    }
2932
2933
    /**
2934
     * Deletes user photos.
2935
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2936
     *
2937
     * @param int $userId the user internal identification number
2938
     *
2939
     * @return mixed returns empty string on success, FALSE on error
2940
     */
2941
    public static function deleteUserPicture($userId)
2942
    {
2943
        return self::update_user_picture($userId);
2944
    }
2945
2946
    /**
2947
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
2948
     * doesn't have any.
2949
     *
2950
     * If there has been a request to remove a production, the function will return
2951
     * without building the list unless forced to do so by the optional second
2952
     * parameter. This increases performance by avoiding to read through the
2953
     * productions on the filesystem before the removal request has been carried
2954
     * out because they'll have to be re-read afterwards anyway.
2955
     *
2956
     * @param int  $user_id    User id
2957
     * @param bool $force      Optional parameter to force building after a removal request
2958
     * @param bool $showDelete
2959
     *
2960
     * @return string A string containing the XHTML code to display the production list, or FALSE
2961
     */
2962
    public static function build_production_list($user_id, $force = false, $showDelete = false)
2963
    {
2964
        if (!$force && !empty($_POST['remove_production'])) {
2965
            return true; // postpone reading from the filesystem
2966
        }
2967
2968
        $productions = self::get_user_productions($user_id);
2969
2970
        if (empty($productions)) {
2971
            return false;
2972
        }
2973
2974
        $production_dir = self::getUserPathById($user_id, 'web');
2975
        $del_image = Display::returnIconPath('delete.png');
2976
        $add_image = Display::returnIconPath('archive.png');
2977
        $del_text = get_lang('Delete');
2978
        $production_list = '';
2979
        if (count($productions) > 0) {
2980
            $production_list = '<div class="files-production"><ul id="productions">';
2981
            foreach ($productions as $file) {
2982
                $production_list .= '<li>
2983
                    <img src="'.$add_image.'" />
2984
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2985
                        '.htmlentities($file).'
2986
                    </a>';
2987
                if ($showDelete) {
2988
                    $production_list .= '&nbsp;&nbsp;
2989
                        <input
2990
                            style="width:16px;"
2991
                            type="image"
2992
                            name="remove_production['.urlencode($file).']"
2993
                            src="'.$del_image.'"
2994
                            alt="'.$del_text.'"
2995
                            title="'.$del_text.' '.htmlentities($file).'"
2996
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2997
                }
2998
            }
2999
            $production_list .= '</ul></div>';
3000
        }
3001
3002
        return $production_list;
3003
    }
3004
3005
    /**
3006
     * Returns an array with the user's productions.
3007
     *
3008
     * @param int $user_id User id
3009
     *
3010
     * @return array An array containing the user's productions
3011
     */
3012
    public static function get_user_productions($user_id)
3013
    {
3014
        $production_repository = self::getUserPathById($user_id, 'system');
3015
        $productions = [];
3016
3017
        if (is_dir($production_repository)) {
3018
            $handle = opendir($production_repository);
3019
            while ($file = readdir($handle)) {
3020
                if ($file == '.' ||
3021
                    $file == '..' ||
3022
                    $file == '.htaccess' ||
3023
                    is_dir($production_repository.$file)
3024
                ) {
3025
                    // skip current/parent directory and .htaccess
3026
                    continue;
3027
                }
3028
3029
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
3030
                    // User's photos should not be listed as productions.
3031
                    continue;
3032
                }
3033
                $productions[] = $file;
3034
            }
3035
        }
3036
3037
        return $productions;
3038
    }
3039
3040
    /**
3041
     * Remove a user production.
3042
     *
3043
     * @param int    $user_id    User id
3044
     * @param string $production The production to remove
3045
     *
3046
     * @return bool
3047
     */
3048
    public static function remove_user_production($user_id, $production)
3049
    {
3050
        $production_path = self::get_user_picture_path_by_id($user_id, 'system');
3051
        $production_file = $production_path['dir'].$production;
3052
        if (is_file($production_file)) {
3053
            unlink($production_file);
3054
3055
            return true;
3056
        }
3057
3058
        return false;
3059
    }
3060
3061
    /**
3062
     * Update an extra field value for a given user.
3063
     *
3064
     * @param int    $userId   User ID
3065
     * @param string $variable Field variable name
3066
     * @param string $value    Field value
3067
     *
3068
     * @return bool true if field updated, false otherwise
3069
     */
3070
    public static function update_extra_field_value($userId, $variable, $value = '')
3071
    {
3072
        $extraFieldValue = new ExtraFieldValue('user');
3073
        $params = [
3074
            'item_id' => $userId,
3075
            'variable' => $variable,
3076
            'value' => $value,
3077
        ];
3078
3079
        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...
3080
    }
3081
3082
    /**
3083
     * Get an array of extra fields with field details (type, default value and options).
3084
     *
3085
     * @param    int    Offset (from which row)
3086
     * @param    int    Number of items
3087
     * @param    int    Column on which sorting is made
3088
     * @param    string    Sorting direction
3089
     * @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...
3090
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
3091
     *
3092
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
3093
     */
3094
    public static function get_extra_fields(
3095
        $from = 0,
3096
        $number_of_items = 0,
3097
        $column = 5,
3098
        $direction = 'ASC',
3099
        $all_visibility = true,
3100
        $field_filter = null
3101
    ) {
3102
        $fields = [];
3103
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
3104
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
3105
        $columns = [
3106
            'id',
3107
            'variable',
3108
            'field_type',
3109
            'display_text',
3110
            'default_value',
3111
            'field_order',
3112
            'filter',
3113
        ];
3114
        $column = (int) $column;
3115
        $sort_direction = '';
3116
        if (!empty($direction)) {
3117
            if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
3118
                $sort_direction = strtoupper($direction);
3119
            }
3120
        }
3121
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
3122
        $sqlf = "SELECT * FROM $t_uf WHERE extra_field_type = $extraFieldType ";
3123
        if (!$all_visibility) {
3124
            $sqlf .= " AND visible_to_self = 1 ";
3125
        }
3126
        if (!is_null($field_filter)) {
3127
            $field_filter = (int) $field_filter;
3128
            $sqlf .= " AND filter = $field_filter ";
3129
        }
3130
        $sqlf .= " ORDER BY `".$columns[$column]."` $sort_direction ";
3131
        if ($number_of_items != 0) {
3132
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
3133
        }
3134
        $resf = Database::query($sqlf);
3135
        if (Database::num_rows($resf) > 0) {
3136
            while ($rowf = Database::fetch_array($resf)) {
3137
                $fields[$rowf['id']] = [
3138
                    0 => $rowf['id'],
3139
                    1 => $rowf['variable'],
3140
                    2 => $rowf['field_type'],
3141
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
3142
                    4 => $rowf['default_value'],
3143
                    5 => $rowf['field_order'],
3144
                    6 => $rowf['visible_to_self'],
3145
                    7 => $rowf['changeable'],
3146
                    8 => $rowf['filter'],
3147
                    9 => [],
3148
                    10 => '<a name="'.$rowf['id'].'"></a>',
3149
                ];
3150
3151
                $sqlo = "SELECT * FROM $t_ufo
3152
                         WHERE field_id = ".$rowf['id']."
3153
                         ORDER BY option_order ASC";
3154
                $reso = Database::query($sqlo);
3155
                if (Database::num_rows($reso) > 0) {
3156
                    while ($rowo = Database::fetch_array($reso)) {
3157
                        $fields[$rowf['id']][9][$rowo['id']] = [
3158
                            0 => $rowo['id'],
3159
                            1 => $rowo['option_value'],
3160
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
3161
                            3 => $rowo['option_order'],
3162
                        ];
3163
                    }
3164
                }
3165
            }
3166
        }
3167
3168
        return $fields;
3169
    }
3170
3171
    /**
3172
     * Build a list of extra file already uploaded in $user_folder/{$extra_field}/.
3173
     *
3174
     * @param $user_id
3175
     * @param $extra_field
3176
     * @param bool $force
3177
     * @param bool $showDelete
3178
     *
3179
     * @return bool|string
3180
     */
3181
    public static function build_user_extra_file_list(
3182
        $user_id,
3183
        $extra_field,
3184
        $force = false,
3185
        $showDelete = false
3186
    ) {
3187
        if (!$force && !empty($_POST['remove_'.$extra_field])) {
3188
            return true; // postpone reading from the filesystem
3189
        }
3190
3191
        $extra_files = self::get_user_extra_files($user_id, $extra_field);
3192
        if (empty($extra_files)) {
3193
            return false;
3194
        }
3195
3196
        $path_info = self::get_user_picture_path_by_id($user_id, 'web');
3197
        $path = $path_info['dir'];
3198
        $del_image = Display::returnIconPath('delete.png');
3199
3200
        $del_text = get_lang('Delete');
3201
        $extra_file_list = '';
3202
        if (count($extra_files) > 0) {
3203
            $extra_file_list = '<div class="files-production"><ul id="productions">';
3204
            foreach ($extra_files as $file) {
3205
                $filename = substr($file, strlen($extra_field) + 1);
3206
                $extra_file_list .= '<li>'.Display::return_icon('archive.png').
3207
                    '<a href="'.$path.$extra_field.'/'.urlencode($filename).'" target="_blank">
3208
                        '.htmlentities($filename).
3209
                    '</a> ';
3210
                if ($showDelete) {
3211
                    $extra_file_list .= '<input
3212
                        style="width:16px;"
3213
                        type="image"
3214
                        name="remove_extra_'.$extra_field.'['.urlencode($file).']"
3215
                        src="'.$del_image.'"
3216
                        alt="'.$del_text.'"
3217
                        title="'.$del_text.' '.htmlentities($filename).'"
3218
                        onclick="javascript: return confirmation(\''.htmlentities($filename).'\');" /></li>';
3219
                }
3220
            }
3221
            $extra_file_list .= '</ul></div>';
3222
        }
3223
3224
        return $extra_file_list;
3225
    }
3226
3227
    /**
3228
     * Get valid filenames in $user_folder/{$extra_field}/.
3229
     *
3230
     * @param $user_id
3231
     * @param $extra_field
3232
     * @param bool $full_path
3233
     *
3234
     * @return array
3235
     */
3236
    public static function get_user_extra_files($user_id, $extra_field, $full_path = false)
3237
    {
3238
        if (!$full_path) {
3239
            // Nothing to do
3240
        } else {
3241
            $path_info = self::get_user_picture_path_by_id($user_id, 'system');
3242
            $path = $path_info['dir'];
3243
        }
3244
        $extra_data = self::get_extra_user_data_by_field($user_id, $extra_field);
3245
        $extra_files = $extra_data[$extra_field];
3246
3247
        $files = [];
3248
        if (is_array($extra_files)) {
3249
            foreach ($extra_files as $key => $value) {
3250
                if (!$full_path) {
3251
                    // Relative path from user folder
3252
                    $files[] = $value;
3253
                } else {
3254
                    $files[] = $path.$value;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $path does not seem to be defined for all execution paths leading up to this point.
Loading history...
3255
                }
3256
            }
3257
        } elseif (!empty($extra_files)) {
3258
            if (!$full_path) {
3259
                // Relative path from user folder
3260
                $files[] = $extra_files;
3261
            } else {
3262
                $files[] = $path.$extra_files;
3263
            }
3264
        }
3265
3266
        return $files; // can be an empty array
3267
    }
3268
3269
    /**
3270
     * Remove an {$extra_file} from the user folder $user_folder/{$extra_field}/.
3271
     *
3272
     * @param int    $user_id
3273
     * @param string $extra_field
3274
     * @param string $extra_file
3275
     *
3276
     * @return bool
3277
     */
3278
    public static function remove_user_extra_file($user_id, $extra_field, $extra_file)
3279
    {
3280
        $extra_file = Security::filter_filename($extra_file);
3281
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
3282
        if (strpos($extra_file, $extra_field) !== false) {
3283
            $path_extra_file = $path_info['dir'].$extra_file;
3284
        } else {
3285
            $path_extra_file = $path_info['dir'].$extra_field.'/'.$extra_file;
3286
        }
3287
        if (is_file($path_extra_file)) {
3288
            unlink($path_extra_file);
3289
3290
            return true;
3291
        }
3292
3293
        return false;
3294
    }
3295
3296
    /**
3297
     * Creates a new extra field.
3298
     *
3299
     * @param string $variable    Field's internal variable name
3300
     * @param int    $fieldType   Field's type
3301
     * @param string $displayText Field's language var name
3302
     * @param string $default     Field's default value
3303
     *
3304
     * @return int
3305
     */
3306
    public static function create_extra_field(
3307
        $variable,
3308
        $fieldType,
3309
        $displayText,
3310
        $default
3311
    ) {
3312
        $extraField = new ExtraField('user');
3313
        $params = [
3314
            'variable' => $variable,
3315
            'field_type' => $fieldType,
3316
            'display_text' => $displayText,
3317
            'default_value' => $default,
3318
        ];
3319
3320
        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...
3321
    }
3322
3323
    /**
3324
     * Check if a field is available.
3325
     *
3326
     * @param string $variable
3327
     *
3328
     * @return bool
3329
     */
3330
    public static function is_extra_field_available($variable)
3331
    {
3332
        $extraField = new ExtraField('user');
3333
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
3334
3335
        return !empty($data) ? true : false;
3336
    }
3337
3338
    /**
3339
     * Gets user extra fields data.
3340
     *
3341
     * @param    int    User ID
3342
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
3343
     * @param    bool    Whether to return invisible fields as well
3344
     * @param    bool    Whether to split multiple-selection fields or not
3345
     * @param    mixed   Whether to filter on the value of filter
3346
     *
3347
     * @return array Array of fields => value for the given user
3348
     */
3349
    public static function get_extra_user_data(
3350
        $user_id,
3351
        $prefix = false,
3352
        $allVisibility = true,
3353
        $splitMultiple = false,
3354
        $fieldFilter = null
3355
    ) {
3356
        $user_id = (int) $user_id;
3357
3358
        if (empty($user_id)) {
3359
            return [];
3360
        }
3361
3362
        $extra_data = [];
3363
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
3364
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3365
        $user_id = (int) $user_id;
3366
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
3367
                FROM $t_uf f
3368
                WHERE
3369
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
3370
                ";
3371
        $filter_cond = '';
3372
3373
        if (!$allVisibility) {
3374
            if (isset($fieldFilter)) {
3375
                $fieldFilter = (int) $fieldFilter;
3376
                $filter_cond .= " AND filter = $fieldFilter ";
3377
            }
3378
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
3379
        } else {
3380
            if (isset($fieldFilter)) {
3381
                $fieldFilter = (int) $fieldFilter;
3382
                $sql .= " AND filter = $fieldFilter ";
3383
            }
3384
        }
3385
3386
        $sql .= ' ORDER BY f.field_order';
3387
3388
        $res = Database::query($sql);
3389
        if (Database::num_rows($res) > 0) {
3390
            while ($row = Database::fetch_array($res)) {
3391
                if ($row['type'] == self::USER_FIELD_TYPE_TAG) {
3392
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
3393
                    $extra_data['extra_'.$row['fvar']] = $tags;
3394
                } else {
3395
                    $sqlu = "SELECT value as fval
3396
                            FROM $t_ufv
3397
                            WHERE field_id = ".$row['id']." AND item_id = ".$user_id;
3398
                    $resu = Database::query($sqlu);
3399
3400
                    if (Database::num_rows($resu) > 0) {
3401
                        $rowu = Database::fetch_array($resu);
3402
                        $fval = $rowu['fval'];
3403
                        if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
3404
                            $fval = explode(';', $rowu['fval']);
3405
                        }
3406
                    } else {
3407
                        // get default value
3408
                        $sql_df = "SELECT default_value as fval_df FROM $t_uf
3409
                               WHERE id = ".$row['id'];
3410
                        $res_df = Database::query($sql_df);
3411
                        $row_df = Database::fetch_array($res_df);
3412
                        $fval = $row_df['fval_df'];
3413
                    }
3414
                    // We get here (and fill the $extra_data array) even if there
3415
                    // is no user with data (we fill it with default values)
3416
                    if ($prefix) {
3417
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
3418
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
3419
                        } else {
3420
                            $extra_data['extra_'.$row['fvar']] = $fval;
3421
                        }
3422
                    } else {
3423
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
3424
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
3425
                        } else {
3426
                            $extra_data[$row['fvar']] = $fval;
3427
                        }
3428
                    }
3429
                }
3430
            }
3431
        }
3432
3433
        return $extra_data;
3434
    }
3435
3436
    /**
3437
     * Get extra user data by field.
3438
     *
3439
     * @param int    user ID
3440
     * @param string the internal variable name of the field
3441
     *
3442
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
3443
     */
3444
    public static function get_extra_user_data_by_field(
3445
        $user_id,
3446
        $field_variable,
3447
        $prefix = false,
3448
        $all_visibility = true,
3449
        $splitmultiple = false
3450
    ) {
3451
        $user_id = (int) $user_id;
3452
3453
        if (empty($user_id)) {
3454
            return [];
3455
        }
3456
3457
        $extra_data = [];
3458
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
3459
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3460
3461
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
3462
                FROM $t_uf f
3463
                WHERE f.variable = '$field_variable' ";
3464
3465
        if (!$all_visibility) {
3466
            $sql .= " AND f.visible_to_self = 1 ";
3467
        }
3468
3469
        $sql .= " AND extra_field_type = ".EntityExtraField::USER_FIELD_TYPE;
3470
        $sql .= " ORDER BY f.field_order ";
3471
3472
        $res = Database::query($sql);
3473
        if (Database::num_rows($res) > 0) {
3474
            while ($row = Database::fetch_array($res)) {
3475
                $sqlu = "SELECT value as fval FROM $t_ufv v
3476
                         INNER JOIN $t_uf f
3477
                         ON (v.field_id = f.id)
3478
                         WHERE
3479
                            extra_field_type = ".EntityExtraField::USER_FIELD_TYPE." AND
3480
                            field_id = ".$row['id']." AND
3481
                            item_id = ".$user_id;
3482
                $resu = Database::query($sqlu);
3483
                $fval = '';
3484
                if (Database::num_rows($resu) > 0) {
3485
                    $rowu = Database::fetch_array($resu);
3486
                    $fval = $rowu['fval'];
3487
                    if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
3488
                        $fval = explode(';', $rowu['fval']);
3489
                    }
3490
                }
3491
                if ($prefix) {
3492
                    $extra_data['extra_'.$row['fvar']] = $fval;
3493
                } else {
3494
                    $extra_data[$row['fvar']] = $fval;
3495
                }
3496
            }
3497
        }
3498
3499
        return $extra_data;
3500
    }
3501
3502
    /**
3503
     * Get the extra field information for a certain field (the options as well).
3504
     *
3505
     * @param string $variable The name of the field we want to know everything about
3506
     *
3507
     * @return array Array containing all the information about the extra profile field
3508
     *               (first level of array contains field details, then 'options' sub-array contains options details,
3509
     *               as returned by the database)
3510
     *
3511
     * @author Julio Montoya
3512
     *
3513
     * @since v1.8.6
3514
     */
3515
    public static function get_extra_field_information_by_name($variable)
3516
    {
3517
        $extraField = new ExtraField('user');
3518
3519
        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...
3520
    }
3521
3522
    /**
3523
     * Get the extra field information for user tag (the options as well).
3524
     *
3525
     * @param int $variable The name of the field we want to know everything about
3526
     *
3527
     * @return array Array containing all the information about the extra profile field
3528
     *               (first level of array contains field details, then 'options' sub-array contains options details,
3529
     *               as returned by the database)
3530
     *
3531
     * @author José Loguercio
3532
     *
3533
     * @since v1.11.0
3534
     */
3535
    public static function get_extra_field_tags_information_by_name($variable)
3536
    {
3537
        $extraField = new ExtraField('user');
3538
3539
        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...
3540
    }
3541
3542
    /**
3543
     * @param string $type
3544
     *
3545
     * @return array
3546
     */
3547
    public static function get_all_extra_field_by_type($type)
3548
    {
3549
        $extraField = new ExtraField('user');
3550
3551
        return $extraField->get_all_extra_field_by_type($type);
3552
    }
3553
3554
    /**
3555
     * Get all the extra field information of a certain field (also the options).
3556
     *
3557
     * @param int $fieldId the ID of the field we want to know everything of
3558
     *
3559
     * @return array $return containing all th information about the extra profile field
3560
     *
3561
     * @author Julio Montoya
3562
     *
3563
     * @deprecated
3564
     * @since v1.8.6
3565
     */
3566
    public static function get_extra_field_information($fieldId)
3567
    {
3568
        $extraField = new ExtraField('user');
3569
3570
        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...
3571
    }
3572
3573
    /**
3574
     * Get extra user data by value.
3575
     *
3576
     * @param string $variable the internal variable name of the field
3577
     * @param string $value    the internal value of the field
3578
     * @param bool   $useLike
3579
     *
3580
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
3581
     */
3582
    public static function get_extra_user_data_by_value($variable, $value, $useLike = false)
3583
    {
3584
        $extraFieldValue = new ExtraFieldValue('user');
3585
        $extraField = new ExtraField('user');
3586
3587
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
3588
3589
        if (false === $info) {
3590
            return [];
3591
        }
3592
3593
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
3594
            $variable,
3595
            $value,
3596
            false,
3597
            false,
3598
            true,
3599
            $useLike
3600
        );
3601
3602
        $result = [];
3603
        if (!empty($data)) {
3604
            foreach ($data as $item) {
3605
                $result[] = $item['item_id'];
3606
            }
3607
        }
3608
3609
        return $result;
3610
    }
3611
3612
    /**
3613
     * Get extra user data by tags value.
3614
     *
3615
     * @param int    $fieldId the ID of the field we want to know everything of
3616
     * @param string $tag     the tag name for search
3617
     *
3618
     * @return array with extra data info of a user
3619
     *
3620
     * @author José Loguercio
3621
     *
3622
     * @since v1.11.0
3623
     */
3624
    public static function get_extra_user_data_by_tags($fieldId, $tag)
3625
    {
3626
        $extraField = new ExtraField('user');
3627
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
3628
        $array = [];
3629
        foreach ($result as $index => $user) {
3630
            $array[] = $user['user_id'];
3631
        }
3632
3633
        return $array;
3634
    }
3635
3636
    /**
3637
     * Get extra user data by field variable.
3638
     *
3639
     * @param string $variable field variable
3640
     *
3641
     * @return array data
3642
     */
3643
    public static function get_extra_user_data_by_field_variable($variable)
3644
    {
3645
        $extraInfo = self::get_extra_field_information_by_name($variable);
3646
        $field_id = (int) $extraInfo['id'];
3647
3648
        $extraField = new ExtraFieldValue('user');
3649
        $data = $extraField->getValuesByFieldId($field_id);
3650
3651
        if (!empty($data)) {
3652
            foreach ($data as $row) {
3653
                $user_id = $row['item_id'];
3654
                $data[$user_id] = $row;
3655
            }
3656
        }
3657
3658
        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...
3659
    }
3660
3661
    /**
3662
     * Get extra user data tags by field variable.
3663
     *
3664
     * @param string $variable field variable
3665
     *
3666
     * @return array
3667
     */
3668
    public static function get_extra_user_data_for_tags($variable)
3669
    {
3670
        $data = self::get_extra_field_tags_information_by_name($variable);
3671
3672
        return $data;
3673
    }
3674
3675
    /**
3676
     * Gives a list of [session_category][session_id] for the current user.
3677
     *
3678
     * @param int  $user_id
3679
     * @param bool $is_time_over                 whether to fill the first element or not
3680
     *                                           (to give space for courses out of categories)
3681
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
3682
     * @param bool $ignoreTimeLimit              ignore time start/end
3683
     * @param bool $getCount
3684
     *
3685
     * @return array list of statuses [session_category][session_id]
3686
     *
3687
     * @todo ensure multiple access urls are managed correctly
3688
     */
3689
    public static function get_sessions_by_category(
3690
        $user_id,
3691
        $is_time_over = true,
3692
        $ignore_visibility_for_admins = false,
3693
        $ignoreTimeLimit = false,
3694
        $getCount = false
3695
    ) {
3696
        $user_id = (int) $user_id;
3697
3698
        if (empty($user_id)) {
3699
            return [];
3700
        }
3701
3702
        $allowOrder = api_get_configuration_value('session_list_order');
3703
        $position = '';
3704
        if ($allowOrder) {
3705
            $position = ', s.position AS position ';
3706
        }
3707
3708
        // Get the list of sessions per user
3709
        $now = new DateTime('now', new DateTimeZone('UTC'));
3710
3711
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
3712
        // join would not catch session-courses where the user is general
3713
        // session coach but which do not have students nor coaches registered
3714
        $dqlSelect = ' COUNT(DISTINCT s.id) ';
3715
3716
        if (!$getCount) {
3717
            $dqlSelect = " DISTINCT
3718
                s.id,
3719
                s.name,
3720
                s.accessStartDate AS access_start_date,
3721
                s.accessEndDate AS access_end_date,
3722
                s.duration,
3723
                sc.id AS session_category_id,
3724
                sc.name AS session_category_name,
3725
                sc.dateStart AS session_category_date_start,
3726
                sc.dateEnd AS session_category_date_end,
3727
                s.coachAccessStartDate AS coach_access_start_date,
3728
                s.coachAccessEndDate AS coach_access_end_date,
3729
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
3730
                $position
3731
            ";
3732
        }
3733
3734
        $dql = "SELECT $dqlSelect
3735
                FROM ChamiloCoreBundle:Session AS s
3736
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
3737
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.sessionId = s.id
3738
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc ";
3739
3740
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
3741
        // is awfully inefficient for large sets of data (1m25s for 58K
3742
        // sessions, BT#14115) but executing a similar query twice and grouping
3743
        // the results afterwards in PHP takes about 1/1000th of the time
3744
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
3745
        $dqlStudent = $dql.' WHERE scu.user = :user AND url.accessUrlId = :url ';
3746
        $dqlCoach = $dql.' WHERE s.generalCoach = :user AND url.accessUrlId = :url ';
3747
3748
        // Default order
3749
        $order = 'ORDER BY sc.name, s.name';
3750
3751
        // Order by date if showing all sessions
3752
        $showAllSessions = api_get_configuration_value('show_all_sessions_on_my_course_page') === true;
3753
        if ($showAllSessions) {
3754
            $order = 'ORDER BY s.accessStartDate';
3755
        }
3756
3757
        // Order by position
3758
        if ($allowOrder) {
3759
            $order = 'ORDER BY s.position';
3760
        }
3761
3762
        // Order by dates according to settings
3763
        $orderBySettings = api_get_configuration_value('my_courses_session_order');
3764
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
3765
            $field = $orderBySettings['field'];
3766
            $orderSetting = $orderBySettings['order'];
3767
            switch ($field) {
3768
                case 'start_date':
3769
                    $order = " ORDER BY s.accessStartDate $orderSetting";
3770
                    break;
3771
                case 'end_date':
3772
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
3773
                    if ($orderSetting === 'asc') {
3774
                        // Put null values at the end
3775
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
3776
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
3777
                    }
3778
                    break;
3779
                case 'name':
3780
                    $order = " ORDER BY s.name $orderSetting ";
3781
                    break;
3782
            }
3783
        }
3784
3785
        $dqlStudent .= $order;
3786
        $dqlCoach .= $order;
3787
3788
        $accessUrlId = api_get_current_access_url_id();
3789
        $dqlStudent = Database::getManager()
3790
            ->createQuery($dqlStudent)
3791
            ->setParameters(
3792
                ['user' => $user_id, 'url' => $accessUrlId]
3793
            )
3794
        ;
3795
        $dqlCoach = Database::getManager()
3796
            ->createQuery($dqlCoach)
3797
            ->setParameters(
3798
                ['user' => $user_id, 'url' => $accessUrlId]
3799
            )
3800
        ;
3801
3802
        if ($getCount) {
3803
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
3804
        }
3805
3806
        $sessionDataStudent = $dqlStudent->getResult();
3807
        $sessionDataCoach = $dqlCoach->getResult();
3808
3809
        $sessionData = [];
3810
        // First fill $sessionData with student sessions
3811
        if (!empty($sessionDataStudent)) {
3812
            foreach ($sessionDataStudent as $row) {
3813
                $sessionData[$row['id']] = $row;
3814
            }
3815
        }
3816
3817
        // Overwrite session data of the user as a student with session data
3818
        // of the user as a coach.
3819
        // There shouldn't be such duplicate rows, but just in case...
3820
        if (!empty($sessionDataCoach)) {
3821
            foreach ($sessionDataCoach as $row) {
3822
                $sessionData[$row['id']] = $row;
3823
            }
3824
        }
3825
3826
        $collapsable = api_get_configuration_value('allow_user_session_collapsable');
3827
        $extraField = new ExtraFieldValue('session');
3828
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
3829
3830
        if (empty($sessionData)) {
3831
            return [];
3832
        }
3833
3834
        $categories = [];
3835
        foreach ($sessionData as $row) {
3836
            $session_id = $row['id'];
3837
            $coachList = SessionManager::getCoachesBySession($session_id);
3838
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
3839
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
3840
            $courseList = self::get_courses_list_by_session($user_id, $session_id);
3841
3842
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
3843
3844
            // User portal filters:
3845
            if (false === $ignoreTimeLimit) {
3846
                if ($is_time_over) {
3847
                    // History
3848
                    if ($row['duration']) {
3849
                        if ($daysLeft >= 0) {
3850
                            continue;
3851
                        }
3852
                    } else {
3853
                        if (empty($row['access_end_date'])) {
3854
                            continue;
3855
                        } else {
3856
                            if ($row['access_end_date'] > $now) {
3857
                                continue;
3858
                            }
3859
                        }
3860
                    }
3861
                } else {
3862
                    // Current user portal
3863
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
3864
                    $isCoachOfCourse = in_array($user_id, $coachList);
3865
3866
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
3867
                        // Teachers can access the session depending in the access_coach date
3868
                    } else {
3869
                        if ($row['duration']) {
3870
                            if ($daysLeft <= 0) {
3871
                                continue;
3872
                            }
3873
                        } else {
3874
                            if (isset($row['access_end_date']) &&
3875
                                !empty($row['access_end_date'])
3876
                            ) {
3877
                                if ($row['access_end_date'] <= $now) {
3878
                                    continue;
3879
                                }
3880
                            }
3881
                        }
3882
                    }
3883
                }
3884
            }
3885
3886
            $categories[$row['session_category_id']]['session_category'] = [
3887
                'id' => $row['session_category_id'],
3888
                'name' => $row['session_category_name'],
3889
                'date_start' => $categoryStart,
3890
                'date_end' => $categoryEnd,
3891
            ];
3892
3893
            $visibility = api_get_session_visibility(
3894
                $session_id,
3895
                null,
3896
                $ignore_visibility_for_admins
3897
            );
3898
3899
            if ($visibility != SESSION_VISIBLE) {
3900
                // Course Coach session visibility.
3901
                $blockedCourseCount = 0;
3902
                $closedVisibilityList = [
3903
                    COURSE_VISIBILITY_CLOSED,
3904
                    COURSE_VISIBILITY_HIDDEN,
3905
                ];
3906
3907
                foreach ($courseList as $course) {
3908
                    // Checking session visibility
3909
                    $sessionCourseVisibility = api_get_session_visibility(
3910
                        $session_id,
3911
                        $course['real_id'],
3912
                        $ignore_visibility_for_admins
3913
                    );
3914
3915
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
3916
                    if ($courseIsVisible === false || $sessionCourseVisibility == SESSION_INVISIBLE) {
3917
                        $blockedCourseCount++;
3918
                    }
3919
                }
3920
3921
                // If all courses are blocked then no show in the list.
3922
                if ($blockedCourseCount === count($courseList)) {
3923
                    $visibility = SESSION_INVISIBLE;
3924
                } else {
3925
                    $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...
3926
                }
3927
            }
3928
3929
            switch ($visibility) {
3930
                case SESSION_VISIBLE_READ_ONLY:
3931
                case SESSION_VISIBLE:
3932
                case SESSION_AVAILABLE:
3933
                    break;
3934
                case SESSION_INVISIBLE:
3935
                    if ($ignore_visibility_for_admins === false) {
3936
                        continue 2;
3937
                    }
3938
            }
3939
3940
            $collapsed = '';
3941
            $collapsedAction = '';
3942
            if ($collapsable) {
3943
                $collapsableData = SessionManager::getCollapsableData(
3944
                    $user_id,
3945
                    $session_id,
3946
                    $extraField,
3947
                    $collapsableLink
3948
                );
3949
                $collapsed = $collapsableData['collapsed'];
3950
                $collapsedAction = $collapsableData['collapsable_link'];
3951
            }
3952
3953
            $categories[$row['session_category_id']]['sessions'][] = [
3954
                'session_name' => $row['name'],
3955
                'session_id' => $row['id'],
3956
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
3957
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
3958
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
3959
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
3960
                'courses' => $courseList,
3961
                'collapsed' => $collapsed,
3962
                'collapsable_link' => $collapsedAction,
3963
                'duration' => $row['duration'],
3964
            ];
3965
        }
3966
3967
        return $categories;
3968
    }
3969
3970
    /**
3971
     * Gives a list of [session_id-course_code] => [status] for the current user.
3972
     *
3973
     * @param int $user_id
3974
     * @param int $sessionLimit
3975
     *
3976
     * @return array list of statuses (session_id-course_code => status)
3977
     */
3978
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
3979
    {
3980
        // Database Table Definitions
3981
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3982
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
3983
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3984
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3985
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3986
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3987
3988
        $user_id = (int) $user_id;
3989
3990
        if (empty($user_id)) {
3991
            return [];
3992
        }
3993
3994
        // We filter the courses from the URL
3995
        $join_access_url = $where_access_url = '';
3996
        if (api_get_multiple_access_url()) {
3997
            $access_url_id = api_get_current_access_url_id();
3998
            if ($access_url_id != -1) {
3999
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4000
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
4001
                $where_access_url = " AND access_url_id = $access_url_id ";
4002
            }
4003
        }
4004
4005
        // Courses in which we subscribed out of any session
4006
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
4007
4008
        $sql = "SELECT
4009
                    course.code,
4010
                    course_rel_user.status course_rel_status,
4011
                    course_rel_user.sort sort,
4012
                    course_rel_user.user_course_cat user_course_cat
4013
                 FROM $tbl_course_user course_rel_user
4014
                 LEFT JOIN $tbl_course course
4015
                 ON course.id = course_rel_user.c_id
4016
                 LEFT JOIN $tbl_user_course_category user_course_category
4017
                 ON course_rel_user.user_course_cat = user_course_category.id
4018
                 $join_access_url
4019
                 WHERE
4020
                    course_rel_user.user_id = '".$user_id."' AND
4021
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
4022
                    $where_access_url
4023
                 ORDER BY user_course_category.sort, course_rel_user.sort, course.title ASC";
4024
4025
        $course_list_sql_result = Database::query($sql);
4026
4027
        $personal_course_list = [];
4028
        if (Database::num_rows($course_list_sql_result) > 0) {
4029
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
4030
                $course_info = api_get_course_info($result_row['code']);
4031
                $result_row['course_info'] = $course_info;
4032
                $personal_course_list[] = $result_row;
4033
            }
4034
        }
4035
4036
        $coachCourseConditions = '';
4037
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
4038
        if (api_is_allowed_to_create_course()) {
4039
            $sessionListFromCourseCoach = [];
4040
            $sql = " SELECT DISTINCT session_id
4041
                    FROM $tbl_session_course_user
4042
                    WHERE user_id = $user_id AND status = 2 ";
4043
4044
            $result = Database::query($sql);
4045
            if (Database::num_rows($result)) {
4046
                $result = Database::store_result($result);
4047
                foreach ($result as $session) {
4048
                    $sessionListFromCourseCoach[] = $session['session_id'];
4049
                }
4050
            }
4051
            if (!empty($sessionListFromCourseCoach)) {
4052
                $condition = implode("','", $sessionListFromCourseCoach);
4053
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
4054
            }
4055
        }
4056
4057
        // Get the list of sessions where the user is subscribed
4058
        // This is divided into two different queries
4059
        $sessions = [];
4060
        $sessionLimitRestriction = '';
4061
        if (!empty($sessionLimit)) {
4062
            $sessionLimit = (int) $sessionLimit;
4063
            $sessionLimitRestriction = "LIMIT $sessionLimit";
4064
        }
4065
4066
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
4067
                FROM $tbl_session_user su INNER JOIN $tbl_session s
4068
                ON (s.id = su.session_id)
4069
                WHERE (
4070
                    su.user_id = $user_id AND
4071
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
4072
                )
4073
                $coachCourseConditions
4074
                ORDER BY access_start_date, access_end_date, name
4075
                $sessionLimitRestriction
4076
        ";
4077
4078
        $result = Database::query($sql);
4079
        if (Database::num_rows($result) > 0) {
4080
            while ($row = Database::fetch_assoc($result)) {
4081
                $sessions[$row['id']] = $row;
4082
            }
4083
        }
4084
4085
        $sql = "SELECT DISTINCT
4086
                id, name, access_start_date, access_end_date
4087
                FROM $tbl_session s
4088
                WHERE (
4089
                    id_coach = $user_id
4090
                )
4091
                $coachCourseConditions
4092
                ORDER BY access_start_date, access_end_date, name";
4093
4094
        $result = Database::query($sql);
4095
        if (Database::num_rows($result) > 0) {
4096
            while ($row = Database::fetch_assoc($result)) {
4097
                if (empty($sessions[$row['id']])) {
4098
                    $sessions[$row['id']] = $row;
4099
                }
4100
            }
4101
        }
4102
4103
        if (api_is_allowed_to_create_course()) {
4104
            foreach ($sessions as $enreg) {
4105
                $session_id = $enreg['id'];
4106
                $session_visibility = api_get_session_visibility($session_id);
4107
4108
                if ($session_visibility == SESSION_INVISIBLE) {
4109
                    continue;
4110
                }
4111
4112
                // This query is horribly slow when more than a few thousand
4113
                // users and just a few sessions to which they are subscribed
4114
                $sql = "SELECT DISTINCT
4115
                        course.code code,
4116
                        course.title i,
4117
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
4118
                        email, course.course_language l,
4119
                        1 sort,
4120
                        category_code user_course_cat,
4121
                        access_start_date,
4122
                        access_end_date,
4123
                        session.id as session_id,
4124
                        session.name as session_name
4125
                    FROM $tbl_session_course_user as session_course_user
4126
                    INNER JOIN $tbl_course AS course
4127
                        ON course.id = session_course_user.c_id
4128
                    INNER JOIN $tbl_session as session
4129
                        ON session.id = session_course_user.session_id
4130
                    LEFT JOIN $tbl_user as user
4131
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
4132
                    WHERE
4133
                        session_course_user.session_id = $session_id AND (
4134
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
4135
                            OR session.id_coach = $user_id
4136
                        )
4137
                    ORDER BY i";
4138
                $course_list_sql_result = Database::query($sql);
4139
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
4140
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
4141
                    $key = $result_row['session_id'].' - '.$result_row['code'];
4142
                    $personal_course_list[$key] = $result_row;
4143
                }
4144
            }
4145
        }
4146
4147
        foreach ($sessions as $enreg) {
4148
            $session_id = $enreg['id'];
4149
            $session_visibility = api_get_session_visibility($session_id);
4150
            if ($session_visibility == SESSION_INVISIBLE) {
4151
                continue;
4152
            }
4153
4154
            /* This query is very similar to the above query,
4155
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
4156
            $sql = "SELECT DISTINCT
4157
                course.code code,
4158
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
4159
                email,
4160
                course.course_language l,
4161
                1 sort,
4162
                category_code user_course_cat,
4163
                access_start_date,
4164
                access_end_date,
4165
                session.id as session_id,
4166
                session.name as session_name,
4167
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
4168
            FROM $tbl_session_course_user as session_course_user
4169
            INNER JOIN $tbl_course AS course
4170
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
4171
            INNER JOIN $tbl_session as session
4172
            ON session_course_user.session_id = session.id
4173
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
4174
            WHERE session_course_user.user_id = $user_id
4175
            ORDER BY i";
4176
4177
            $course_list_sql_result = Database::query($sql);
4178
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
4179
                $result_row['course_info'] = api_get_course_info($result_row['code']);
4180
                $key = $result_row['session_id'].' - '.$result_row['code'];
4181
                if (!isset($personal_course_list[$key])) {
4182
                    $personal_course_list[$key] = $result_row;
4183
                }
4184
            }
4185
        }
4186
4187
        return $personal_course_list;
4188
    }
4189
4190
    /**
4191
     * Gives a list of courses for the given user in the given session.
4192
     *
4193
     * @param int $user_id
4194
     * @param int $session_id
4195
     *
4196
     * @return array list of statuses (session_id-course_code => status)
4197
     */
4198
    public static function get_courses_list_by_session($user_id, $session_id)
4199
    {
4200
        // Database Table Definitions
4201
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4202
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
4203
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4204
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4205
4206
        $user_id = (int) $user_id;
4207
        $session_id = (int) $session_id;
4208
        // We filter the courses from the URL
4209
        $join_access_url = $where_access_url = '';
4210
        if (api_get_multiple_access_url()) {
4211
            $urlId = api_get_current_access_url_id();
4212
            if (-1 != $urlId) {
4213
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4214
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
4215
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
4216
            }
4217
        }
4218
4219
        $exlearnerCondition = "";
4220
        if (false !== api_get_configuration_value('user_edition_extra_field_to_check')) {
4221
            $exlearnerCondition = " AND scu.status NOT IN(".COURSE_EXLEARNER.")";
4222
        }
4223
4224
        /* This query is very similar to the query below, but it will check the
4225
        session_rel_course_user table if there are courses registered
4226
        to our user or not */
4227
        $sql = "SELECT DISTINCT
4228
                    c.title,
4229
                    c.visibility,
4230
                    c.id as real_id,
4231
                    c.code as course_code,
4232
                    c.course_language,
4233
                    sc.position,
4234
                    c.unsubscribe
4235
                FROM $tbl_session_course_user as scu
4236
                INNER JOIN $tbl_session_course sc
4237
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
4238
                INNER JOIN $tableCourse as c
4239
                ON (scu.c_id = c.id)
4240
                $join_access_url
4241
                WHERE
4242
                    scu.user_id = $user_id AND
4243
                    scu.session_id = $session_id
4244
                    $where_access_url
4245
                    $exlearnerCondition
4246
                ORDER BY sc.position ASC";
4247
4248
        $myCourseList = [];
4249
        $courses = [];
4250
        $result = Database::query($sql);
4251
        if (Database::num_rows($result) > 0) {
4252
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
4253
                $result_row['status'] = 5;
4254
                if (!in_array($result_row['real_id'], $courses)) {
4255
                    $position = $result_row['position'];
4256
                    if (!isset($myCourseList[$position])) {
4257
                        $myCourseList[$position] = $result_row;
4258
                    } else {
4259
                        $myCourseList[] = $result_row;
4260
                    }
4261
                    $courses[] = $result_row['real_id'];
4262
                }
4263
            }
4264
        }
4265
4266
        if (api_is_allowed_to_create_course()) {
4267
            $sql = "SELECT DISTINCT
4268
                        c.title,
4269
                        c.visibility,
4270
                        c.id as real_id,
4271
                        c.code as course_code,
4272
                        c.course_language,
4273
                        sc.position,
4274
                        c.unsubscribe
4275
                    FROM $tbl_session_course_user as scu
4276
                    INNER JOIN $tbl_session as s
4277
                    ON (scu.session_id = s.id)
4278
                    INNER JOIN $tbl_session_course sc
4279
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
4280
                    INNER JOIN $tableCourse as c
4281
                    ON (scu.c_id = c.id)
4282
                    $join_access_url
4283
                    WHERE
4284
                      s.id = $session_id AND
4285
                      (
4286
                        (scu.user_id = $user_id AND scu.status = 2) OR
4287
                        s.id_coach = $user_id
4288
                      )
4289
                    $where_access_url
4290
                    $exlearnerCondition
4291
                    ORDER BY sc.position ASC";
4292
            $result = Database::query($sql);
4293
4294
            if (Database::num_rows($result) > 0) {
4295
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
4296
                    $result_row['status'] = 2;
4297
                    if (!in_array($result_row['real_id'], $courses)) {
4298
                        $position = $result_row['position'];
4299
                        if (!isset($myCourseList[$position])) {
4300
                            $myCourseList[$position] = $result_row;
4301
                        } else {
4302
                            $myCourseList[] = $result_row;
4303
                        }
4304
                        $courses[] = $result_row['real_id'];
4305
                    }
4306
                }
4307
            }
4308
        }
4309
4310
        if (api_is_drh()) {
4311
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
4312
            $sessionList = array_keys($sessionList);
4313
            if (in_array($session_id, $sessionList)) {
4314
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
4315
                if (!empty($courseList)) {
4316
                    foreach ($courseList as $course) {
4317
                        if (!in_array($course['id'], $courses)) {
4318
                            $position = $course['position'];
4319
                            if (!isset($myCourseList[$position])) {
4320
                                $myCourseList[$position] = $course;
4321
                            } else {
4322
                                $myCourseList[] = $course;
4323
                            }
4324
                        }
4325
                    }
4326
                }
4327
            }
4328
        } else {
4329
            //check if user is general coach for this session
4330
            $sessionInfo = api_get_session_info($session_id);
4331
            if ($sessionInfo['id_coach'] == $user_id) {
4332
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
4333
                if (!empty($courseList)) {
4334
                    foreach ($courseList as $course) {
4335
                        if (!in_array($course['id'], $courses)) {
4336
                            $position = $course['position'];
4337
                            if (!isset($myCourseList[$position])) {
4338
                                $myCourseList[$position] = $course;
4339
                            } else {
4340
                                $myCourseList[] = $course;
4341
                            }
4342
                        }
4343
                    }
4344
                }
4345
            }
4346
        }
4347
4348
        if (!empty($myCourseList)) {
4349
            ksort($myCourseList);
4350
            $checkPosition = array_filter(array_column($myCourseList, 'position'));
4351
            if (empty($checkPosition)) {
4352
                // The session course list doesn't have any position,
4353
                // then order the course list by course code.
4354
                $orderByCode = array_column($myCourseList, 'course_code');
4355
                sort($orderByCode, SORT_NATURAL);
4356
                $newCourseList = [];
4357
                foreach ($orderByCode as $code) {
4358
                    foreach ($myCourseList as $course) {
4359
                        if ($code === $course['course_code']) {
4360
                            $newCourseList[] = $course;
4361
                            break;
4362
                        }
4363
                    }
4364
                }
4365
                $myCourseList = $newCourseList;
4366
            }
4367
        }
4368
4369
        return $myCourseList;
4370
    }
4371
4372
    /**
4373
     * Get user id from a username.
4374
     *
4375
     * @param string $username
4376
     *
4377
     * @return int User ID (or false if not found)
4378
     */
4379
    public static function get_user_id_from_username($username)
4380
    {
4381
        if (empty($username)) {
4382
            return false;
4383
        }
4384
        $username = trim($username);
4385
        $username = Database::escape_string($username);
4386
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
4387
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
4388
        $res = Database::query($sql);
4389
4390
        if ($res === false) {
4391
            return false;
4392
        }
4393
        if (Database::num_rows($res) !== 1) {
4394
            return false;
4395
        }
4396
        $row = Database::fetch_array($res);
4397
4398
        return $row['id'];
4399
    }
4400
4401
    /**
4402
     * Get the users files upload from his share_folder.
4403
     *
4404
     * @param string $user_id      User ID
4405
     * @param string $course       course directory
4406
     * @param string $resourceType resource type: images, all
4407
     *
4408
     * @return string
4409
     */
4410
    public static function get_user_upload_files_by_course(
4411
        $user_id,
4412
        $course,
4413
        $resourceType = 'all'
4414
    ) {
4415
        $return = '';
4416
        $user_id = (int) $user_id;
4417
4418
        if (!empty($user_id) && !empty($course)) {
4419
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
4420
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
4421
            $file_list = [];
4422
4423
            if (is_dir($path)) {
4424
                $handle = opendir($path);
4425
                while ($file = readdir($handle)) {
4426
                    if ($file == '.' || $file == '..' || $file == '.htaccess' || is_dir($path.$file)) {
4427
                        continue; // skip current/parent directory and .htaccess
4428
                    }
4429
                    $file_list[] = $file;
4430
                }
4431
                if (count($file_list) > 0) {
4432
                    $return = "<h4>$course</h4>";
4433
                    $return .= '<ul class="thumbnails">';
4434
                }
4435
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
4436
                foreach ($file_list as $file) {
4437
                    if ($resourceType == 'all') {
4438
                        $return .= '<li>
4439
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
4440
                    } elseif ($resourceType == 'images') {
4441
                        //get extension
4442
                        $ext = explode('.', $file);
4443
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
4444
                            $return .= '<li class="span2">
4445
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
4446
                                                <img src="'.$web_path.urlencode($file).'" >
4447
                                            </a>
4448
                                        </li>';
4449
                        }
4450
                    }
4451
                }
4452
                if (count($file_list) > 0) {
4453
                    $return .= '</ul>';
4454
                }
4455
            }
4456
        }
4457
4458
        return $return;
4459
    }
4460
4461
    /**
4462
     * Gets the API key (or keys) and return them into an array.
4463
     *
4464
     * @param int     Optional user id (defaults to the result of api_get_user_id())
4465
     * @param string $api_service
4466
     *
4467
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
4468
     */
4469
    public static function get_api_keys($user_id = null, $api_service = 'dokeos')
4470
    {
4471
        if ($user_id != strval(intval($user_id))) {
4472
            return false;
4473
        }
4474
        if (empty($user_id)) {
4475
            $user_id = api_get_user_id();
4476
        }
4477
        if ($user_id === false) {
4478
            return false;
4479
        }
4480
        $service_name = Database::escape_string($api_service);
4481
        if (is_string($service_name) === false) {
4482
            return false;
4483
        }
4484
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4485
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
4486
        $res = Database::query($sql);
4487
        if ($res === false) {
4488
            return false;
4489
        } //error during query
4490
        $num = Database::num_rows($res);
4491
        if ($num == 0) {
4492
            return false;
4493
        }
4494
        $list = [];
4495
        while ($row = Database::fetch_array($res)) {
4496
            $list[$row['id']] = $row['api_key'];
4497
        }
4498
4499
        return $list;
4500
    }
4501
4502
    /**
4503
     * Adds a new API key to the users' account.
4504
     *
4505
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
4506
     * @param string $api_service
4507
     *
4508
     * @return bool True on success, false on failure
4509
     */
4510
    public static function add_api_key($user_id = null, $api_service = 'dokeos')
4511
    {
4512
        if ($user_id != strval(intval($user_id))) {
4513
            return false;
4514
        }
4515
        if (empty($user_id)) {
4516
            $user_id = api_get_user_id();
4517
        }
4518
        if ($user_id === false) {
4519
            return false;
4520
        }
4521
        $service_name = Database::escape_string($api_service);
4522
        if (is_string($service_name) === false) {
4523
            return false;
4524
        }
4525
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4526
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
4527
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
4528
        $res = Database::query($sql);
4529
        if ($res === false) {
4530
            return false;
4531
        } //error during query
4532
        $num = Database::insert_id();
4533
4534
        return $num == 0 ? false : $num;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $num == 0 ? false : $num also could return the type string which is incompatible with the documented return type boolean.
Loading history...
4535
    }
4536
4537
    /**
4538
     * Deletes an API key from the user's account.
4539
     *
4540
     * @param   int     API key's internal ID
4541
     *
4542
     * @return bool True on success, false on failure
4543
     */
4544
    public static function delete_api_key($key_id)
4545
    {
4546
        if ($key_id != strval(intval($key_id))) {
4547
            return false;
4548
        }
4549
        if ($key_id === false) {
4550
            return false;
4551
        }
4552
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4553
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
4554
        $res = Database::query($sql);
4555
        if ($res === false) {
4556
            return false;
4557
        } //error during query
4558
        $num = Database::num_rows($res);
4559
        if ($num !== 1) {
4560
            return false;
4561
        }
4562
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
4563
        $res = Database::query($sql);
4564
        if ($res === false) {
4565
            return false;
4566
        } //error during query
4567
4568
        return true;
4569
    }
4570
4571
    /**
4572
     * Regenerate an API key from the user's account.
4573
     *
4574
     * @param   int     user ID (defaults to the results of api_get_user_id())
4575
     * @param   string  API key's internal ID
4576
     *
4577
     * @return int num
4578
     */
4579
    public static function update_api_key($user_id, $api_service)
4580
    {
4581
        if ($user_id != strval(intval($user_id))) {
4582
            return false;
4583
        }
4584
        if ($user_id === false) {
4585
            return false;
4586
        }
4587
        $service_name = Database::escape_string($api_service);
4588
        if (is_string($service_name) === false) {
4589
            return false;
4590
        }
4591
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4592
        $sql = "SELECT id FROM $t_api
4593
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
4594
        $res = Database::query($sql);
4595
        $num = Database::num_rows($res);
4596
        if ($num == 1) {
4597
            $id_key = Database::fetch_array($res, 'ASSOC');
4598
            self::delete_api_key($id_key['id']);
4599
            $num = self::add_api_key($user_id, $api_service);
4600
        } elseif ($num == 0) {
4601
            $num = self::add_api_key($user_id, $api_service);
4602
        }
4603
4604
        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...
4605
    }
4606
4607
    /**
4608
     * @param   int     user ID (defaults to the results of api_get_user_id())
4609
     * @param   string    API key's internal ID
4610
     *
4611
     * @return int row ID, or return false if not found
4612
     */
4613
    public static function get_api_key_id($user_id, $api_service)
4614
    {
4615
        if ($user_id != strval(intval($user_id))) {
4616
            return false;
4617
        }
4618
        if ($user_id === false) {
4619
            return false;
4620
        }
4621
        if (empty($api_service)) {
4622
            return false;
4623
        }
4624
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
4625
        $api_service = Database::escape_string($api_service);
4626
        $sql = "SELECT id FROM $t_api
4627
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
4628
        $res = Database::query($sql);
4629
        if (Database::num_rows($res) < 1) {
4630
            return false;
4631
        }
4632
        $row = Database::fetch_array($res, 'ASSOC');
4633
4634
        return $row['id'];
4635
    }
4636
4637
    /**
4638
     * Checks if a user_id is platform admin.
4639
     *
4640
     * @param   int user ID
4641
     *
4642
     * @return bool True if is admin, false otherwise
4643
     *
4644
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
4645
     */
4646
    public static function is_admin($user_id)
4647
    {
4648
        $user_id = (int) $user_id;
4649
        if (empty($user_id)) {
4650
            return false;
4651
        }
4652
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
4653
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
4654
        $res = Database::query($sql);
4655
4656
        return Database::num_rows($res) === 1;
4657
    }
4658
4659
    /**
4660
     * Get the total count of users.
4661
     *
4662
     * @param int|null $status        Status of users to be counted
4663
     * @param int|null $access_url_id Access URL ID (optional)
4664
     *
4665
     * @return mixed Number of users or false on error
4666
     */
4667
    public static function get_number_of_users(
4668
        int $status = null,
4669
        int $access_url_id = null,
4670
        int $active = null,
4671
        string $dateFrom = null,
4672
        string $dateUntil = null
4673
    ) {
4674
        $tableUser = Database::get_main_table(TABLE_MAIN_USER);
4675
        $tableAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4676
4677
        if (empty($access_url_id)) {
4678
            $access_url_id = api_get_current_access_url_id();
4679
        }
4680
4681
        if (api_is_multiple_url_enabled()) {
4682
            $sql = "SELECT count(u.id)
4683
                    FROM $tableUser u
4684
                    INNER JOIN $tableAccessUrlRelUser url_user
4685
                    ON (u.id = url_user.user_id)
4686
                    WHERE url_user.access_url_id = $access_url_id
4687
            ";
4688
        } else {
4689
            $sql = "SELECT count(u.id)
4690
                    FROM $tableUser u
4691
                    WHERE 1 = 1 ";
4692
        }
4693
4694
        if (is_int($status) && $status > 0) {
4695
            $status = (int) $status;
4696
            $sql .= " AND u.status = $status ";
4697
        }
4698
4699
        if (isset($active)) {
4700
            $active = (int) $active;
4701
            $sql .= " AND u.active = $active ";
4702
        }
4703
4704
        if (!empty($dateFrom)) {
4705
            $dateFrom = api_get_utc_datetime("$dateFrom 00:00:00");
4706
            $sql .= " AND u.registration_date >= '$dateFrom' ";
4707
        }
4708
4709
        if (!empty($dateUntil)) {
4710
            $dateUntil = api_get_utc_datetime("$dateUntil 23:59:59");
4711
            $sql .= " AND u.registration_date <= '$dateUntil' ";
4712
        }
4713
4714
        $res = Database::query($sql);
4715
        if (Database::num_rows($res) === 1) {
4716
            return (int) Database::result($res, 0, 0);
4717
        }
4718
4719
        return false;
4720
    }
4721
4722
    /**
4723
     * Gets the tags of a specific field_id
4724
     * USER TAGS.
4725
     *
4726
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
4727
     *
4728
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
4729
     *    Called it "books" for example.
4730
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
4731
     * 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
4732
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
4733
     * 5. Test and enjoy.
4734
     *
4735
     * @param string $tag
4736
     * @param int    $field_id      field_id
4737
     * @param string $return_format how we are going to result value in array or in a string (json)
4738
     * @param $limit
4739
     *
4740
     * @return mixed
4741
     */
4742
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
4743
    {
4744
        // database table definition
4745
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4746
        $field_id = (int) $field_id;
4747
        $limit = (int) $limit;
4748
        $tag = trim(Database::escape_string($tag));
4749
4750
        // all the information of the field
4751
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
4752
                WHERE field_id = $field_id AND tag LIKE '$tag%'
4753
                ORDER BY tag
4754
                LIMIT $limit";
4755
        $result = Database::query($sql);
4756
        $return = [];
4757
        if (Database::num_rows($result) > 0) {
4758
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4759
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
4760
            }
4761
        }
4762
        if ($return_format === 'json') {
4763
            $return = json_encode($return);
4764
        }
4765
4766
        return $return;
4767
    }
4768
4769
    /**
4770
     * @param int $field_id
4771
     * @param int $limit
4772
     *
4773
     * @return array
4774
     */
4775
    public static function get_top_tags($field_id, $limit = 100)
4776
    {
4777
        // database table definition
4778
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4779
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4780
        $field_id = (int) $field_id;
4781
        $limit = (int) $limit;
4782
        // all the information of the field
4783
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
4784
                INNER JOIN $table_user_tag ut
4785
                ON (ut.id = uv.tag_id)
4786
                WHERE field_id = $field_id
4787
                GROUP BY tag_id
4788
                ORDER BY count DESC
4789
                LIMIT $limit";
4790
        $result = Database::query($sql);
4791
        $return = [];
4792
        if (Database::num_rows($result) > 0) {
4793
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4794
                $return[] = $row;
4795
            }
4796
        }
4797
4798
        return $return;
4799
    }
4800
4801
    /**
4802
     * Get user's tags.
4803
     *
4804
     * @param int $user_id
4805
     * @param int $field_id
4806
     *
4807
     * @return array
4808
     */
4809
    public static function get_user_tags($user_id, $field_id)
4810
    {
4811
        // database table definition
4812
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4813
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4814
        $field_id = (int) $field_id;
4815
        $user_id = (int) $user_id;
4816
4817
        // all the information of the field
4818
        $sql = "SELECT ut.id, tag, count
4819
                FROM $table_user_tag ut
4820
                INNER JOIN $table_user_tag_values uv
4821
                ON (uv.tag_id=ut.ID)
4822
                WHERE field_id = $field_id AND user_id = $user_id
4823
                ORDER BY tag";
4824
        $result = Database::query($sql);
4825
        $return = [];
4826
        if (Database::num_rows($result) > 0) {
4827
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4828
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
4829
            }
4830
        }
4831
4832
        return $return;
4833
    }
4834
4835
    /**
4836
     * Get user's tags.
4837
     *
4838
     * @param int  $user_id
4839
     * @param int  $field_id
4840
     * @param bool $show_links show links or not
4841
     *
4842
     * @return string
4843
     */
4844
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
4845
    {
4846
        // database table definition
4847
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4848
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4849
        $field_id = (int) $field_id;
4850
        $user_id = (int) $user_id;
4851
4852
        // all the information of the field
4853
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
4854
                INNER JOIN $table_user_tag_values uv
4855
                ON (uv.tag_id = ut.id)
4856
                WHERE field_id = $field_id AND user_id = $user_id
4857
                ORDER BY tag";
4858
4859
        $result = Database::query($sql);
4860
        $return = [];
4861
        if (Database::num_rows($result) > 0) {
4862
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4863
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
4864
            }
4865
        }
4866
        $user_tags = $return;
4867
        $tag_tmp = [];
4868
        foreach ($user_tags as $tag) {
4869
            if ($show_links) {
4870
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
4871
                    $tag['tag'].
4872
                '</a>';
4873
            } else {
4874
                $tag_tmp[] = $tag['tag'];
4875
            }
4876
        }
4877
4878
        if (is_array($user_tags) && count($user_tags) > 0) {
4879
            return implode(', ', $tag_tmp);
4880
        } else {
4881
            return '';
4882
        }
4883
    }
4884
4885
    /**
4886
     * Get the tag id.
4887
     *
4888
     * @param int $tag
4889
     * @param int $field_id
4890
     *
4891
     * @return int returns 0 if fails otherwise the tag id
4892
     */
4893
    public static function get_tag_id($tag, $field_id)
4894
    {
4895
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4896
        $tag = Database::escape_string($tag);
4897
        $field_id = (int) $field_id;
4898
        //with COLLATE latin1_bin to select query in a case sensitive mode
4899
        $sql = "SELECT id FROM $table_user_tag
4900
                WHERE tag LIKE '$tag' AND field_id = $field_id";
4901
        $result = Database::query($sql);
4902
        if (Database::num_rows($result) > 0) {
4903
            $row = Database::fetch_array($result, 'ASSOC');
4904
4905
            return $row['id'];
4906
        } else {
4907
            return 0;
4908
        }
4909
    }
4910
4911
    /**
4912
     * Get the tag id.
4913
     *
4914
     * @param int $tag_id
4915
     * @param int $field_id
4916
     *
4917
     * @return int 0 if fails otherwise the tag id
4918
     */
4919
    public static function get_tag_id_from_id($tag_id, $field_id)
4920
    {
4921
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4922
        $tag_id = (int) $tag_id;
4923
        $field_id = (int) $field_id;
4924
        $sql = "SELECT id FROM $table_user_tag
4925
                WHERE id = '$tag_id' AND field_id = $field_id";
4926
        $result = Database::query($sql);
4927
        if (Database::num_rows($result) > 0) {
4928
            $row = Database::fetch_array($result, 'ASSOC');
4929
4930
            return $row['id'];
4931
        } else {
4932
            return false;
4933
        }
4934
    }
4935
4936
    /**
4937
     * Adds a user-tag value.
4938
     *
4939
     * @param mixed $tag
4940
     * @param int   $user_id
4941
     * @param int   $field_id field id of the tag
4942
     *
4943
     * @return bool True if the tag was inserted or updated. False otherwise.
4944
     *              The return value doesn't take into account *values* added to the tag.
4945
     *              Only the creation/update of the tag field itself.
4946
     */
4947
    public static function add_tag($tag, $user_id, $field_id)
4948
    {
4949
        // database table definition
4950
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4951
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4952
        $tag = trim(Database::escape_string($tag));
4953
        $user_id = (int) $user_id;
4954
        $field_id = (int) $field_id;
4955
4956
        $tag_id = self::get_tag_id($tag, $field_id);
4957
4958
        /* IMPORTANT
4959
         *  @todo we don't create tags with numbers
4960
         *
4961
         */
4962
        if (is_numeric($tag)) {
4963
            //the form is sending an id this means that the user select it from the list so it MUST exists
4964
            /* $new_tag_id = self::get_tag_id_from_id($tag,$field_id);
4965
              if ($new_tag_id !== false) {
4966
              $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $new_tag_id";
4967
              $result = Database::query($sql);
4968
              $last_insert_id = $new_tag_id;
4969
              } else {
4970
              $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
4971
              $result = Database::query($sql);
4972
              $last_insert_id = Database::insert_id();
4973
              } */
4974
        }
4975
4976
        //this is a new tag
4977
        if ($tag_id == 0) {
4978
            //the tag doesn't exist
4979
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
4980
            Database::query($sql);
4981
            $last_insert_id = Database::insert_id();
4982
        } else {
4983
            //the tag exists we update it
4984
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
4985
            Database::query($sql);
4986
            $last_insert_id = $tag_id;
4987
        }
4988
4989
        if (!empty($last_insert_id) && ($last_insert_id != 0)) {
4990
            //we insert the relationship user-tag
4991
            $sql = "SELECT tag_id FROM $table_user_tag_values
4992
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
4993
            $result = Database::query($sql);
4994
            //if the relationship does not exist we create it
4995
            if (Database::num_rows($result) == 0) {
4996
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
4997
                Database::query($sql);
4998
            }
4999
5000
            return true;
5001
        }
5002
5003
        return false;
5004
    }
5005
5006
    /**
5007
     * Deletes an user tag.
5008
     *
5009
     * @param int $user_id
5010
     * @param int $field_id
5011
     */
5012
    public static function delete_user_tags($user_id, $field_id)
5013
    {
5014
        // database table definition
5015
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
5016
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
5017
        $user_id = (int) $user_id;
5018
5019
        $tags = self::get_user_tags($user_id, $field_id);
5020
        if (is_array($tags) && count($tags) > 0) {
5021
            foreach ($tags as $key => $tag) {
5022
                if ($tag['count'] > '0') {
5023
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
5024
                    Database::query($sql);
5025
                }
5026
                $sql = "DELETE FROM $table_user_tag_values
5027
                        WHERE user_id = $user_id AND tag_id = $key";
5028
                Database::query($sql);
5029
            }
5030
        }
5031
    }
5032
5033
    /**
5034
     * Process the tag list comes from the UserManager::update_extra_field_value() function.
5035
     *
5036
     * @param array $tags     the tag list that will be added
5037
     * @param int   $user_id
5038
     * @param int   $field_id
5039
     *
5040
     * @return bool
5041
     */
5042
    public static function process_tags($tags, $user_id, $field_id)
5043
    {
5044
        // We loop the tags and add it to the DB
5045
        if (is_array($tags)) {
5046
            foreach ($tags as $tag) {
5047
                self::add_tag($tag, $user_id, $field_id);
5048
            }
5049
        } else {
5050
            self::add_tag($tags, $user_id, $field_id);
5051
        }
5052
5053
        return true;
5054
    }
5055
5056
    /**
5057
     * Returns a list of all administrators.
5058
     *
5059
     * @return array
5060
     */
5061
    public static function get_all_administrators()
5062
    {
5063
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5064
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
5065
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5066
        $access_url_id = api_get_current_access_url_id();
5067
        if (api_get_multiple_access_url()) {
5068
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
5069
                    FROM $tbl_url_rel_user as url
5070
                    INNER JOIN $table_admin as admin
5071
                    ON (admin.user_id=url.user_id)
5072
                    INNER JOIN $table_user u
5073
                    ON (u.id=admin.user_id)
5074
                    WHERE access_url_id ='".$access_url_id."'";
5075
        } else {
5076
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
5077
                    FROM $table_admin as admin
5078
                    INNER JOIN $table_user u
5079
                    ON (u.id=admin.user_id)";
5080
        }
5081
        $result = Database::query($sql);
5082
        $return = [];
5083
        if (Database::num_rows($result) > 0) {
5084
            while ($row = Database::fetch_array($result, 'ASSOC')) {
5085
                $return[$row['user_id']] = $row;
5086
            }
5087
        }
5088
5089
        return $return;
5090
    }
5091
5092
    /**
5093
     * Search an user (tags, first name, last name and email ).
5094
     *
5095
     * @param string $tag
5096
     * @param int    $field_id        field id of the tag
5097
     * @param int    $from            where to start in the query
5098
     * @param int    $number_of_items
5099
     * @param bool   $getCount        get count or not
5100
     *
5101
     * @return array
5102
     */
5103
    public static function get_all_user_tags(
5104
        $tag,
5105
        $field_id = 0,
5106
        $from = 0,
5107
        $number_of_items = 10,
5108
        $getCount = false
5109
    ) {
5110
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
5111
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
5112
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
5113
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5114
5115
        $field_id = intval($field_id);
5116
        $from = intval($from);
5117
        $number_of_items = intval($number_of_items);
5118
5119
        $where_field = "";
5120
        $where_extra_fields = self::get_search_form_where_extra_fields();
5121
        if ($field_id != 0) {
5122
            $where_field = " field_id = $field_id AND ";
5123
        }
5124
5125
        // all the information of the field
5126
        if ($getCount) {
5127
            $select = "SELECT count(DISTINCT u.id) count";
5128
        } else {
5129
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, picture_uri";
5130
        }
5131
5132
        $sql = " $select
5133
                FROM $user_table u
5134
                INNER JOIN $access_url_rel_user_table url_rel_user
5135
                ON (u.id = url_rel_user.user_id)
5136
                LEFT JOIN $table_user_tag_values uv
5137
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
5138
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
5139
                WHERE
5140
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
5141
                    (
5142
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
5143
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
5144
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
5145
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
5146
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
5147
                     )
5148
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
5149
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
5150
5151
        $keyword_active = true;
5152
        // only active users
5153
        if ($keyword_active) {
5154
            $sql .= " AND u.active='1'";
5155
        }
5156
        // avoid anonymous
5157
        $sql .= " AND u.status <> 6 ";
5158
        $sql .= " ORDER BY username";
5159
        $sql .= " LIMIT $from , $number_of_items";
5160
5161
        $result = Database::query($sql);
5162
        $return = [];
5163
5164
        if (Database::num_rows($result) > 0) {
5165
            if ($getCount) {
5166
                $row = Database::fetch_array($result, 'ASSOC');
5167
5168
                return $row['count'];
5169
            }
5170
            while ($row = Database::fetch_array($result, 'ASSOC')) {
5171
                $return[$row['id']] = $row;
5172
            }
5173
        }
5174
5175
        return $return;
5176
    }
5177
5178
    /**
5179
     * Get extra filterable user fields (only type select).
5180
     *
5181
     * @return array Array of extra fields as [int => ['name' => ..., 'variable' => ..., 'data' => ...]] (
5182
     *               or empty array if no extra field)
5183
     */
5184
    public static function getExtraFilterableFields()
5185
    {
5186
        $extraFieldList = self::get_extra_fields();
5187
        $fields = [];
5188
        if (is_array($extraFieldList)) {
5189
            foreach ($extraFieldList as $extraField) {
5190
                // If is enabled to filter and is a "<select>" field type
5191
                if ($extraField[8] == 1 && $extraField[2] == 4) {
5192
                    $fields[] = [
5193
                        'name' => $extraField[3],
5194
                        'variable' => $extraField[1],
5195
                        'data' => $extraField[9],
5196
                    ];
5197
                }
5198
            }
5199
        }
5200
5201
        return $fields;
5202
    }
5203
5204
    /**
5205
     * Get extra where clauses for finding users based on extra filterable user fields (type select).
5206
     *
5207
     * @return string With AND clauses based on user's ID which have the values to search in extra user fields
5208
     *                (or empty if no extra field exists)
5209
     */
5210
    public static function get_search_form_where_extra_fields()
5211
    {
5212
        $useExtraFields = false;
5213
        $extraFields = self::getExtraFilterableFields();
5214
        $extraFieldResult = [];
5215
        if (is_array($extraFields) && count($extraFields) > 0) {
5216
            foreach ($extraFields as $extraField) {
5217
                $varName = 'field_'.$extraField['variable'];
5218
                if (self::is_extra_field_available($extraField['variable'])) {
5219
                    if (isset($_GET[$varName]) && $_GET[$varName] != '0') {
5220
                        $useExtraFields = true;
5221
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
5222
                            $extraField['variable'],
5223
                            $_GET[$varName]
5224
                        );
5225
                    }
5226
                }
5227
            }
5228
        }
5229
5230
        if ($useExtraFields) {
5231
            $finalResult = [];
5232
            if (count($extraFieldResult) > 1) {
5233
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
5234
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
5235
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
5236
                    }
5237
                }
5238
            } else {
5239
                $finalResult = $extraFieldResult[0];
5240
            }
5241
5242
            if (is_array($finalResult) && count($finalResult) > 0) {
5243
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
5244
            } else {
5245
                //no results
5246
                $whereFilter = " AND u.id  = -1 ";
5247
            }
5248
5249
            return $whereFilter;
5250
        }
5251
5252
        return '';
5253
    }
5254
5255
    /**
5256
     * Show the search form.
5257
     *
5258
     * @param string $query the value of the search box
5259
     *
5260
     * @throws Exception
5261
     *
5262
     * @return string HTML form
5263
     */
5264
    public static function get_search_form($query, $defaultParams = [])
5265
    {
5266
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
5267
        $form = new FormValidator(
5268
            'search_user',
5269
            'get',
5270
            api_get_path(WEB_PATH).'main/social/search.php',
5271
            '',
5272
            [],
5273
            FormValidator::LAYOUT_HORIZONTAL
5274
        );
5275
5276
        $query = Security::remove_XSS($query);
5277
5278
        if (!empty($query)) {
5279
            $form->addHeader(get_lang('Results').' "'.$query.'"');
5280
        }
5281
5282
        $form->addText(
5283
            'q',
5284
            get_lang('UsersGroups'),
5285
            false,
5286
            [
5287
                'id' => 'q',
5288
            ]
5289
        );
5290
        $options = [
5291
            0 => get_lang('Select'),
5292
            1 => get_lang('User'),
5293
            2 => get_lang('Group'),
5294
        ];
5295
        $form->addSelect(
5296
            'search_type',
5297
            get_lang('Type'),
5298
            $options,
5299
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
5300
        );
5301
5302
        // Extra fields
5303
        $extraFields = self::getExtraFilterableFields();
5304
        $defaults = [];
5305
        if (is_array($extraFields) && count($extraFields) > 0) {
5306
            foreach ($extraFields as $extraField) {
5307
                $varName = 'field_'.$extraField['variable'];
5308
                $options = [
5309
                    0 => get_lang('Select'),
5310
                ];
5311
                foreach ($extraField['data'] as $option) {
5312
                    if (isset($_GET[$varName])) {
5313
                        if ($_GET[$varName] == $option[1]) {
5314
                            $defaults[$option[1]] = true;
5315
                        }
5316
                    }
5317
5318
                    $options[$option[1]] = $option[1];
5319
                }
5320
                $form->addSelect($varName, $extraField['name'], $options);
5321
            }
5322
        }
5323
5324
        $defaults['search_type'] = (int) $searchType;
5325
        $defaults['q'] = $query;
5326
5327
        if (!empty($defaultParams)) {
5328
            $defaults = array_merge($defaults, $defaultParams);
5329
        }
5330
        $form->setDefaults($defaults);
5331
        $form->addButtonSearch(get_lang('Search'));
5332
5333
        $js = '<script>
5334
        extra_field_toogle();
5335
        function extra_field_toogle() {
5336
            if (jQuery("select[name=search_type]").val() != "1") {
5337
                jQuery(".extra_field").hide();
5338
            } else {
5339
                jQuery(".extra_field").show();
5340
            }
5341
        }
5342
        </script>';
5343
5344
        return $js.$form->returnForm();
5345
    }
5346
5347
    /**
5348
     * Shows the user menu.
5349
     */
5350
    public static function show_menu()
5351
    {
5352
        echo '<div class="actions">';
5353
        echo '<a href="/main/auth/profile.php">'.
5354
            Display::return_icon('profile.png').' '.get_lang('PersonalData').'</a>';
5355
        echo '<a href="/main/messages/inbox.php">'.
5356
            Display::return_icon('inbox.png').' '.get_lang('Inbox').'</a>';
5357
        echo '<a href="/main/messages/outbox.php">'.
5358
            Display::return_icon('outbox.png').' '.get_lang('Outbox').'</a>';
5359
        echo '<span style="float:right; padding-top:7px;">'.
5360
        '<a href="/main/auth/profile.php?show=1">'.
5361
            Display::return_icon('edit.gif').' '.get_lang('Configuration').'</a>';
5362
        echo '</span>';
5363
        echo '</div>';
5364
    }
5365
5366
    /**
5367
     * Allow to register contact to social network.
5368
     *
5369
     * @param int $friend_id     user friend id
5370
     * @param int $my_user_id    user id
5371
     * @param int $relation_type relation between users see constants definition
5372
     *
5373
     * @return bool
5374
     */
5375
    public static function relate_users($friend_id, $my_user_id, $relation_type)
5376
    {
5377
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5378
5379
        $friend_id = (int) $friend_id;
5380
        $my_user_id = (int) $my_user_id;
5381
        $relation_type = (int) $relation_type;
5382
5383
        $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
5384
                WHERE
5385
                    friend_user_id='.$friend_id.' AND
5386
                    user_id='.$my_user_id.' AND
5387
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
5388
        $result = Database::query($sql);
5389
        $row = Database::fetch_array($result, 'ASSOC');
5390
        $current_date = api_get_utc_datetime();
5391
5392
        if ($row['count'] == 0) {
5393
            $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
5394
                    VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
5395
            Database::query($sql);
5396
5397
            return true;
5398
        }
5399
5400
        $sql = 'SELECT COUNT(*) as count, relation_type  FROM '.$tbl_my_friend.'
5401
                WHERE
5402
                    friend_user_id='.$friend_id.' AND
5403
                    user_id='.$my_user_id.' AND
5404
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
5405
        $result = Database::query($sql);
5406
        $row = Database::fetch_array($result, 'ASSOC');
5407
5408
        if ($row['count'] == 1) {
5409
            //only for the case of a RRHH or a Student BOSS
5410
            if ($row['relation_type'] != $relation_type &&
5411
                ($relation_type == USER_RELATION_TYPE_RRHH || $relation_type == USER_RELATION_TYPE_BOSS)
5412
            ) {
5413
                $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
5414
                        VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
5415
            } else {
5416
                $sql = 'UPDATE '.$tbl_my_friend.' SET relation_type='.$relation_type.'
5417
                        WHERE friend_user_id='.$friend_id.' AND user_id='.$my_user_id;
5418
            }
5419
            Database::query($sql);
5420
5421
            return true;
5422
        }
5423
5424
        return false;
5425
    }
5426
5427
    /**
5428
     * Deletes a contact.
5429
     *
5430
     * @param bool   $friend_id
5431
     * @param bool   $real_removed          true will delete ALL friends relationship
5432
     * @param string $with_status_condition
5433
     *
5434
     * @author isaac flores paz <[email protected]>
5435
     * @author Julio Montoya <[email protected]> Cleaning code
5436
     */
5437
    public static function remove_user_rel_user(
5438
        $friend_id,
5439
        $real_removed = false,
5440
        $with_status_condition = ''
5441
    ) {
5442
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5443
        $tbl_my_message = Database::get_main_table(TABLE_MESSAGE);
5444
        $friend_id = (int) $friend_id;
5445
        $user_id = api_get_user_id();
5446
5447
        if ($real_removed) {
5448
            $extra_condition = '';
5449
            if ($with_status_condition != '') {
5450
                $extra_condition = ' AND relation_type = '.intval($with_status_condition);
5451
            }
5452
            $sql = 'DELETE FROM '.$tbl_my_friend.'
5453
                    WHERE
5454
                        relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
5455
                        friend_user_id='.$friend_id.' '.$extra_condition;
5456
            Database::query($sql);
5457
            $sql = 'DELETE FROM '.$tbl_my_friend.'
5458
                   WHERE
5459
                    relation_type <> '.USER_RELATION_TYPE_RRHH.' AND
5460
                    user_id='.$friend_id.' '.$extra_condition;
5461
            Database::query($sql);
5462
        } else {
5463
            $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
5464
                    WHERE
5465
                        user_id='.$user_id.' AND
5466
                        relation_type NOT IN('.USER_RELATION_TYPE_DELETED.', '.USER_RELATION_TYPE_RRHH.') AND
5467
                        friend_user_id='.$friend_id;
5468
            $result = Database::query($sql);
5469
            $row = Database::fetch_array($result, 'ASSOC');
5470
            if ($row['count'] == 1) {
5471
                //Delete user rel user
5472
                $sql_i = 'UPDATE '.$tbl_my_friend.' SET relation_type='.USER_RELATION_TYPE_DELETED.'
5473
                          WHERE user_id='.$user_id.' AND friend_user_id='.$friend_id;
5474
5475
                $sql_j = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
5476
                          WHERE
5477
                                user_receiver_id='.$user_id.' AND
5478
                                user_sender_id='.$friend_id.' AND update_date="0000-00-00 00:00:00" ';
5479
                // Delete user
5480
                $sql_ij = 'UPDATE '.$tbl_my_friend.'  SET relation_type='.USER_RELATION_TYPE_DELETED.'
5481
                           WHERE user_id='.$friend_id.' AND friend_user_id='.$user_id;
5482
                $sql_ji = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
5483
                           WHERE
5484
                                user_receiver_id='.$friend_id.' AND
5485
                                user_sender_id='.$user_id.' AND
5486
                                update_date="0000-00-00 00:00:00" ';
5487
                Database::query($sql_i);
5488
                Database::query($sql_j);
5489
                Database::query($sql_ij);
5490
                Database::query($sql_ji);
5491
            }
5492
        }
5493
5494
        // Delete accepted invitations
5495
        $sql = "DELETE FROM $tbl_my_message
5496
                WHERE
5497
                    msg_status = ".MESSAGE_STATUS_INVITATION_ACCEPTED." AND
5498
                    (
5499
                        user_receiver_id = $user_id AND
5500
                        user_sender_id = $friend_id
5501
                    ) OR
5502
                    (
5503
                        user_sender_id = $user_id AND
5504
                        user_receiver_id = $friend_id
5505
                    )
5506
        ";
5507
        Database::query($sql);
5508
    }
5509
5510
    /**
5511
     * @param int $userId
5512
     *
5513
     * @return array
5514
     */
5515
    public static function getDrhListFromUser($userId)
5516
    {
5517
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
5518
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5519
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5520
        $userId = (int) $userId;
5521
5522
        $orderBy = null;
5523
        if (api_is_western_name_order()) {
5524
            $orderBy .= ' ORDER BY firstname, lastname ';
5525
        } else {
5526
            $orderBy .= ' ORDER BY lastname, firstname ';
5527
        }
5528
5529
        $sql = "SELECT u.id, username, u.firstname, u.lastname
5530
                FROM $tblUser u
5531
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
5532
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
5533
                WHERE
5534
                    access_url_id = ".api_get_current_access_url_id()." AND
5535
                    uru.user_id = '$userId' AND
5536
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
5537
                $orderBy
5538
                ";
5539
        $result = Database::query($sql);
5540
5541
        return Database::store_result($result);
5542
    }
5543
5544
    /**
5545
     * get users followed by human resource manager.
5546
     *
5547
     * @param int    $userId
5548
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
5549
     * @param bool   $getOnlyUserId
5550
     * @param bool   $getSql
5551
     * @param bool   $getCount
5552
     * @param int    $from
5553
     * @param int    $numberItems
5554
     * @param int    $column
5555
     * @param string $direction
5556
     * @param int    $active
5557
     * @param string $lastConnectionDate
5558
     *
5559
     * @return array users
5560
     */
5561
    public static function get_users_followed_by_drh(
5562
        $userId,
5563
        $userStatus = 0,
5564
        $getOnlyUserId = false,
5565
        $getSql = false,
5566
        $getCount = false,
5567
        $from = null,
5568
        $numberItems = null,
5569
        $column = null,
5570
        $direction = null,
5571
        $active = null,
5572
        $lastConnectionDate = null
5573
    ) {
5574
        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...
5575
            $userId,
5576
            $userStatus,
5577
            $getOnlyUserId,
5578
            $getSql,
5579
            $getCount,
5580
            $from,
5581
            $numberItems,
5582
            $column,
5583
            $direction,
5584
            $active,
5585
            $lastConnectionDate,
5586
            DRH
5587
        );
5588
    }
5589
5590
    /**
5591
     * Get users followed by human resource manager.
5592
     *
5593
     * @param int    $userId
5594
     * @param int    $userStatus             Filter users by status (STUDENT, COURSEMANAGER, etc)
5595
     * @param bool   $getOnlyUserId
5596
     * @param bool   $getSql
5597
     * @param bool   $getCount
5598
     * @param int    $from
5599
     * @param int    $numberItems
5600
     * @param int    $column
5601
     * @param string $direction
5602
     * @param int    $active
5603
     * @param string $lastConnectionDate
5604
     * @param int    $status                 the function is called by who? COURSEMANAGER, DRH?
5605
     * @param string $keyword
5606
     * @param bool   $checkSessionVisibility
5607
     *
5608
     * @return mixed Users list (array) or the SQL query if $getSQL was set to true
5609
     */
5610
    public static function getUsersFollowedByUser(
5611
        $userId,
5612
        $userStatus = null,
5613
        $getOnlyUserId = false,
5614
        $getSql = false,
5615
        $getCount = false,
5616
        $from = null,
5617
        $numberItems = null,
5618
        $column = null,
5619
        $direction = null,
5620
        $active = null,
5621
        $lastConnectionDate = null,
5622
        $status = null,
5623
        $keyword = null,
5624
        $checkSessionVisibility = false,
5625
        $filterUsers = null
5626
    ) {
5627
        // Database Table Definitions
5628
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
5629
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5630
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5631
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
5632
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5633
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5634
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
5635
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
5636
5637
        $userId = (int) $userId;
5638
        $limitCondition = '';
5639
5640
        if (isset($from) && isset($numberItems)) {
5641
            $from = (int) $from;
5642
            $numberItems = (int) $numberItems;
5643
            $limitCondition = "LIMIT $from, $numberItems";
5644
        }
5645
5646
        $column = Database::escape_string($column);
5647
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
5648
5649
        $userConditions = '';
5650
        if (!empty($userStatus)) {
5651
            $userConditions .= ' AND u.status = '.intval($userStatus);
5652
        }
5653
5654
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
5655
        if ($getOnlyUserId) {
5656
            $select = " SELECT DISTINCT u.id user_id";
5657
        }
5658
5659
        $masterSelect = "SELECT DISTINCT * FROM ";
5660
5661
        if ($getCount) {
5662
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
5663
            $select = " SELECT DISTINCT(u.id) user_id";
5664
        }
5665
5666
        if (!is_null($active)) {
5667
            $active = intval($active);
5668
            $userConditions .= " AND u.active = $active ";
5669
        }
5670
5671
        if (!empty($keyword)) {
5672
            $keyword = trim(Database::escape_string($keyword));
5673
            $keywordParts = array_filter(explode(' ', $keyword));
5674
            $extraKeyword = '';
5675
            if (!empty($keywordParts)) {
5676
                $keywordPartsFixed = Database::escape_string(implode('%', $keywordParts));
5677
                if (!empty($keywordPartsFixed)) {
5678
                    $extraKeyword .= " OR
5679
                        CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keywordPartsFixed%' OR
5680
                        CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keywordPartsFixed%' ";
5681
                }
5682
            }
5683
5684
            $userConditions .= " AND (
5685
                u.username LIKE '%$keyword%' OR
5686
                u.firstname LIKE '%$keyword%' OR
5687
                u.lastname LIKE '%$keyword%' OR
5688
                u.official_code LIKE '%$keyword%' OR
5689
                u.email LIKE '%$keyword%' OR
5690
                CONCAT(u.firstname, ' ', u.lastname) LIKE '%$keyword%' OR
5691
                CONCAT(u.lastname, ' ', u.firstname) LIKE '%$keyword%'
5692
                $extraKeyword
5693
            )";
5694
        }
5695
5696
        if (!empty($lastConnectionDate)) {
5697
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
5698
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
5699
        }
5700
5701
        if (!empty($filterUsers)) {
5702
            $userConditions .= " AND u.id IN(".implode(',', $filterUsers).")";
5703
        }
5704
5705
        $sessionConditionsCoach = null;
5706
        $dateCondition = '';
5707
        $drhConditions = null;
5708
        $teacherSelect = null;
5709
5710
        $urlId = api_get_current_access_url_id();
5711
5712
        switch ($status) {
5713
            case DRH:
5714
                $drhConditions .= " AND
5715
                    friend_user_id = '$userId' AND
5716
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
5717
                ";
5718
                break;
5719
            case COURSEMANAGER:
5720
                $drhConditions .= " AND
5721
                    friend_user_id = '$userId' AND
5722
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
5723
                ";
5724
5725
                $sessionConditionsCoach .= " AND
5726
                    (s.id_coach = '$userId')
5727
                ";
5728
5729
                $sessionConditionsTeacher = " AND
5730
                    (scu.status = 2 AND scu.user_id = '$userId')
5731
                ";
5732
5733
                if ($checkSessionVisibility) {
5734
                    $today = api_strtotime('now', 'UTC');
5735
                    $today = date('Y-m-d', $today);
5736
                    $dateCondition = "
5737
                        AND
5738
                        (
5739
                            (s.access_start_date <= '$today' AND '$today' <= s.access_end_date) OR
5740
                            (s.access_start_date IS NULL AND s.access_end_date IS NULL) OR
5741
                            (s.access_start_date <= '$today' AND s.access_end_date IS NULL) OR
5742
                            ('$today' <= s.access_end_date AND s.access_start_date IS NULL)
5743
                        )
5744
					";
5745
                }
5746
5747
                // Use $tbl_session_rel_course_rel_user instead of $tbl_session_rel_user
5748
                /*
5749
                INNER JOIN $tbl_session_rel_user sru
5750
                ON (sru.user_id = u.id)
5751
                INNER JOIN $tbl_session_rel_course_rel_user scu
5752
                ON (scu.user_id = u.id AND scu.c_id IS NOT NULL AND visibility = 1)*/
5753
                $teacherSelect =
5754
                "UNION ALL (
5755
                        $select
5756
                        FROM $tbl_user u
5757
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
5758
                        WHERE
5759
                            (
5760
                                sru.session_id IN (
5761
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
5762
                                    $tbl_session_rel_access_url session_rel_access_rel_user
5763
                                    ON session_rel_access_rel_user.session_id = s.id
5764
                                    WHERE access_url_id = ".$urlId."
5765
                                    $sessionConditionsCoach
5766
                                ) OR sru.session_id IN (
5767
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
5768
                                    INNER JOIN $tbl_session_rel_access_url url
5769
                                    ON (url.session_id = s.id)
5770
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
5771
                                    ON (scu.session_id = s.id)
5772
                                    WHERE access_url_id = ".$urlId."
5773
                                    $sessionConditionsTeacher
5774
                                    $dateCondition
5775
                                )
5776
                            )
5777
                            $userConditions
5778
                    )
5779
                    UNION ALL(
5780
                        $select
5781
                        FROM $tbl_user u
5782
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
5783
                        WHERE cu.c_id IN (
5784
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
5785
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
5786
                        )
5787
                        $userConditions
5788
                    )"
5789
                ;
5790
                break;
5791
            case STUDENT_BOSS:
5792
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
5793
                break;
5794
            case HRM_REQUEST:
5795
                $drhConditions .= " AND
5796
                    friend_user_id = '$userId' AND
5797
                    relation_type = '".USER_RELATION_TYPE_HRM_REQUEST."'
5798
                ";
5799
                break;
5800
        }
5801
5802
        $join = null;
5803
        $sql = " $masterSelect
5804
                (
5805
                    (
5806
                        $select
5807
                        FROM $tbl_user u
5808
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
5809
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
5810
                        $join
5811
                        WHERE
5812
                            access_url_id = ".$urlId."
5813
                            $drhConditions
5814
                            $userConditions
5815
                    )
5816
                    $teacherSelect
5817
                ) as t1";
5818
5819
        if ($getSql) {
5820
            return $sql;
5821
        }
5822
5823
        if ($getCount) {
5824
            $result = Database::query($sql);
5825
            $row = Database::fetch_array($result);
5826
5827
            return $row['count'];
5828
        }
5829
5830
        $orderBy = null;
5831
        if ($getOnlyUserId == false) {
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...
5832
            if (api_is_western_name_order()) {
5833
                $orderBy .= " ORDER BY firstname, lastname ";
5834
            } else {
5835
                $orderBy .= " ORDER BY lastname, firstname ";
5836
            }
5837
5838
            if (!empty($column) && !empty($direction)) {
5839
                // Fixing order due the UNIONs
5840
                $column = str_replace('u.', '', $column);
5841
                $column = trim($column);
5842
                $orderBy = " ORDER BY `$column` $direction ";
5843
            }
5844
        }
5845
5846
        $sql .= $orderBy;
5847
        $sql .= $limitCondition;
5848
5849
        $result = Database::query($sql);
5850
        $users = [];
5851
        if (Database::num_rows($result) > 0) {
5852
            while ($row = Database::fetch_array($result)) {
5853
                $users[$row['user_id']] = $row;
5854
            }
5855
        }
5856
5857
        return $users;
5858
    }
5859
5860
    /**
5861
     * Subscribes users to human resource manager (Dashboard feature).
5862
     *
5863
     * @param int   $hr_dept_id
5864
     * @param array $users_id
5865
     * @param bool  $deleteOtherAssignedUsers
5866
     *
5867
     * @return int
5868
     */
5869
    public static function subscribeUsersToHRManager(
5870
        $hr_dept_id,
5871
        $users_id,
5872
        $deleteOtherAssignedUsers = true
5873
    ) {
5874
        return self::subscribeUsersToUser(
5875
            $hr_dept_id,
5876
            $users_id,
5877
            USER_RELATION_TYPE_RRHH,
5878
            false,
5879
            $deleteOtherAssignedUsers
5880
        );
5881
    }
5882
5883
    /**
5884
     * Register request to assign users to HRM.
5885
     *
5886
     * @param int   $hrmId   The HRM ID
5887
     * @param array $usersId The users IDs
5888
     *
5889
     * @return int
5890
     */
5891
    public static function requestUsersToHRManager($hrmId, $usersId)
5892
    {
5893
        return self::subscribeUsersToUser(
5894
            $hrmId,
5895
            $usersId,
5896
            USER_RELATION_TYPE_HRM_REQUEST,
5897
            false,
5898
            false
5899
        );
5900
    }
5901
5902
    /**
5903
     * Remove the requests for assign a user to a HRM.
5904
     *
5905
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
5906
     */
5907
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
5908
    {
5909
        $users = implode(', ', $usersId);
5910
        Database::getManager()
5911
            ->createQuery('
5912
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
5913
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
5914
            ')
5915
            ->execute(['hrm_id' => $hrmId, 'relation_type' => USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
5916
    }
5917
5918
    /**
5919
     * Add subscribed users to a user by relation type.
5920
     *
5921
     * @param int    $userId                   The user id
5922
     * @param array  $subscribedUsersId        The id of subscribed users
5923
     * @param string $relationType             The relation type
5924
     * @param bool   $deleteUsersBeforeInsert
5925
     * @param bool   $deleteOtherAssignedUsers
5926
     *
5927
     * @return int
5928
     */
5929
    public static function subscribeUsersToUser(
5930
        $userId,
5931
        $subscribedUsersId,
5932
        $relationType,
5933
        $deleteUsersBeforeInsert = false,
5934
        $deleteOtherAssignedUsers = true
5935
    ) {
5936
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5937
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5938
5939
        $userId = (int) $userId;
5940
        $relationType = (int) $relationType;
5941
        $affectedRows = 0;
5942
5943
        if ($deleteOtherAssignedUsers) {
5944
            if (api_get_multiple_access_url()) {
5945
                // Deleting assigned users to hrm_id
5946
                $sql = "SELECT s.user_id
5947
                        FROM $userRelUserTable s
5948
                        INNER JOIN $userRelAccessUrlTable a
5949
                        ON (a.user_id = s.user_id)
5950
                        WHERE
5951
                            friend_user_id = $userId AND
5952
                            relation_type = $relationType AND
5953
                            access_url_id = ".api_get_current_access_url_id();
5954
            } else {
5955
                $sql = "SELECT user_id
5956
                        FROM $userRelUserTable
5957
                        WHERE
5958
                            friend_user_id = $userId AND
5959
                            relation_type = $relationType";
5960
            }
5961
            $result = Database::query($sql);
5962
5963
            if (Database::num_rows($result) > 0) {
5964
                while ($row = Database::fetch_array($result)) {
5965
                    $sql = "DELETE FROM $userRelUserTable
5966
                            WHERE
5967
                                user_id = {$row['user_id']} AND
5968
                                friend_user_id = $userId AND
5969
                                relation_type = $relationType";
5970
                    Database::query($sql);
5971
                }
5972
            }
5973
        }
5974
5975
        if ($deleteUsersBeforeInsert) {
5976
            $sql = "DELETE FROM $userRelUserTable
5977
                    WHERE
5978
                        user_id = $userId AND
5979
                        relation_type = $relationType";
5980
            Database::query($sql);
5981
        }
5982
5983
        // Inserting new user list
5984
        if (is_array($subscribedUsersId)) {
5985
            foreach ($subscribedUsersId as $subscribedUserId) {
5986
                $subscribedUserId = (int) $subscribedUserId;
5987
                $sql = "SELECT id
5988
                        FROM $userRelUserTable
5989
                        WHERE
5990
                            user_id = $subscribedUserId AND
5991
                            friend_user_id = $userId AND
5992
                            relation_type = $relationType";
5993
5994
                $result = Database::query($sql);
5995
                $num = Database::num_rows($result);
5996
                if ($num === 0) {
5997
                    $date = api_get_utc_datetime();
5998
                    $sql = "INSERT INTO $userRelUserTable (user_id, friend_user_id, relation_type, last_edit)
5999
                            VALUES ($subscribedUserId, $userId, $relationType, '$date')";
6000
                    $result = Database::query($sql);
6001
                    $affectedRows += Database::affected_rows($result);
6002
                }
6003
            }
6004
        }
6005
6006
        return $affectedRows;
6007
    }
6008
6009
    /**
6010
     * This function check if an user is followed by human resources manager.
6011
     *
6012
     * @param int $user_id
6013
     * @param int $hr_dept_id Human resources manager
6014
     *
6015
     * @return bool
6016
     */
6017
    public static function is_user_followed_by_drh($user_id, $hr_dept_id)
6018
    {
6019
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6020
        $user_id = (int) $user_id;
6021
        $hr_dept_id = (int) $hr_dept_id;
6022
        $result = false;
6023
6024
        $sql = "SELECT user_id FROM $tbl_user_rel_user
6025
                WHERE
6026
                    user_id = $user_id AND
6027
                    friend_user_id = $hr_dept_id AND
6028
                    relation_type = ".USER_RELATION_TYPE_RRHH;
6029
        $rs = Database::query($sql);
6030
        if (Database::num_rows($rs) > 0) {
6031
            $result = true;
6032
        }
6033
6034
        return $result;
6035
    }
6036
6037
    /**
6038
     * Return the user id of teacher or session administrator.
6039
     *
6040
     * @return int|bool The user id, or false if the session ID was negative
6041
     */
6042
    public static function get_user_id_of_course_admin_or_session_admin(array $courseInfo)
6043
    {
6044
        $session = api_get_session_id();
6045
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6046
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
6047
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
6048
6049
        if (empty($courseInfo)) {
6050
            return false;
6051
        }
6052
6053
        $courseId = $courseInfo['real_id'];
6054
6055
        if (empty($session)) {
6056
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
6057
                    INNER JOIN '.$table_course_user.' ru
6058
                    ON ru.user_id = u.id
6059
                    WHERE
6060
                        ru.status = '.COURSEMANAGER.' AND
6061
                        ru.c_id = "'.$courseId.'" ';
6062
        } else {
6063
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
6064
                    INNER JOIN '.$table_session_course_user.' sru
6065
                    ON sru.user_id=u.id
6066
                    WHERE
6067
                        sru.c_id="'.$courseId.'" AND
6068
                        sru.session_id="'.$session.'" AND
6069
                        sru.status = '.SessionEntity::COACH;
6070
        }
6071
6072
        $rs = Database::query($sql);
6073
        $num_rows = Database::num_rows($rs);
6074
6075
        if (0 === $num_rows) {
6076
            return false;
6077
        }
6078
6079
        if (1 === $num_rows) {
6080
            $row = Database::fetch_array($rs);
6081
6082
            return (int) $row['uid'];
6083
        }
6084
6085
        $my_num_rows = $num_rows;
6086
        $my_user_id = Database::result($rs, $my_num_rows - 1, 'uid');
6087
6088
        return (int) $my_user_id;
6089
    }
6090
6091
    /**
6092
     * Determines if a user is a gradebook certified.
6093
     *
6094
     * @param int $cat_id  The category id of gradebook
6095
     * @param int $user_id The user id
6096
     *
6097
     * @return bool
6098
     */
6099
    public static function is_user_certified($cat_id, $user_id)
6100
    {
6101
        $cat_id = (int) $cat_id;
6102
        $user_id = (int) $user_id;
6103
6104
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
6105
        $sql = 'SELECT path_certificate
6106
                FROM '.$table.'
6107
                WHERE
6108
                    cat_id = "'.$cat_id.'" AND
6109
                    user_id = "'.$user_id.'"';
6110
        $rs = Database::query($sql);
6111
        $row = Database::fetch_array($rs);
6112
6113
        if ($row['path_certificate'] == '' || is_null($row['path_certificate'])) {
6114
            return false;
6115
        }
6116
6117
        return true;
6118
    }
6119
6120
    /**
6121
     * Gets the info about a gradebook certificate for a user by course.
6122
     *
6123
     * @param string $course_code The course code
6124
     * @param int    $session_id
6125
     * @param int    $user_id     The user id
6126
     * @param string $startDate   date string
6127
     * @param string $endDate     date string
6128
     *
6129
     * @return array if there is not information return false
6130
     */
6131
    public static function get_info_gradebook_certificate(
6132
        $course_code,
6133
        $session_id,
6134
        $user_id,
6135
        $startDate = null,
6136
        $endDate = null
6137
    ) {
6138
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
6139
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
6140
        $session_id = (int) $session_id;
6141
        $user_id = (int) $user_id;
6142
6143
        if (empty($session_id)) {
6144
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
6145
        } else {
6146
            $session_condition = " AND session_id = $session_id";
6147
        }
6148
6149
        $dateConditions = "";
6150
        if (!empty($startDate)) {
6151
            $startDate = api_get_utc_datetime($startDate, false, true);
6152
            $dateConditions .= " AND created_at >= '".$startDate->format('Y-m-d 00:00:00')."' ";
6153
        }
6154
        if (!empty($endDate)) {
6155
            $endDate = api_get_utc_datetime($endDate, false, true);
6156
            $dateConditions .= " AND created_at <= '".$endDate->format('Y-m-d 23:59:59')."' ";
6157
        }
6158
6159
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.'
6160
                WHERE cat_id = (
6161
                    SELECT id FROM '.$tbl_grade_category.'
6162
                    WHERE
6163
                        course_code = "'.Database::escape_string($course_code).'" '.$session_condition.' '.$dateConditions.'
6164
                    LIMIT 1
6165
                ) AND user_id='.$user_id;
6166
6167
        $rs = Database::query($sql);
6168
        if (Database::num_rows($rs) > 0) {
6169
            $row = Database::fetch_array($rs, 'ASSOC');
6170
            $score = $row['score_certificate'];
6171
            $category_id = $row['cat_id'];
6172
            $cat = Category::load($category_id);
6173
            $displayscore = ScoreDisplay::instance();
6174
            if (isset($cat) && $displayscore->is_custom()) {
6175
                $grade = $displayscore->display_score(
6176
                    [$score, $cat[0]->get_weight()],
6177
                    SCORE_DIV_PERCENT_WITH_CUSTOM
6178
                );
6179
            } else {
6180
                $grade = $displayscore->display_score(
6181
                    [$score, $cat[0]->get_weight()]
6182
                );
6183
            }
6184
            $row['grade'] = $grade;
6185
6186
            return $row;
6187
        }
6188
6189
        return false;
6190
    }
6191
6192
    /**
6193
     * This function check if the user is a coach inside session course.
6194
     *
6195
     * @param int $user_id    User id
6196
     * @param int $courseId
6197
     * @param int $session_id
6198
     *
6199
     * @return bool True if the user is a coach
6200
     */
6201
    public static function is_session_course_coach($user_id, $courseId, $session_id)
6202
    {
6203
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
6204
        // Protect data
6205
        $user_id = intval($user_id);
6206
        $courseId = intval($courseId);
6207
        $session_id = intval($session_id);
6208
        $result = false;
6209
6210
        $sql = "SELECT session_id FROM $table
6211
                WHERE
6212
                  session_id = $session_id AND
6213
                  c_id = $courseId AND
6214
                  user_id = $user_id AND
6215
                  status = 2 ";
6216
        $res = Database::query($sql);
6217
6218
        if (Database::num_rows($res) > 0) {
6219
            $result = true;
6220
        }
6221
6222
        return $result;
6223
    }
6224
6225
    /**
6226
     * This function returns an icon path that represents the favicon of the website of which the url given.
6227
     * Defaults to the current Chamilo favicon.
6228
     *
6229
     * @param string $url1 URL of website where to look for favicon.ico
6230
     * @param string $url2 Optional second URL of website where to look for favicon.ico
6231
     *
6232
     * @return string Path of icon to load
6233
     */
6234
    public static function get_favicon_from_url($url1, $url2 = null)
6235
    {
6236
        $icon_link = '';
6237
        $url = $url1;
6238
        if (empty($url1)) {
6239
            $url = $url2;
6240
            if (empty($url)) {
6241
                $url = api_get_access_url(api_get_current_access_url_id());
6242
                $url = $url[0];
6243
            }
6244
        }
6245
        if (!empty($url)) {
6246
            $pieces = parse_url($url);
6247
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
6248
        }
6249
6250
        return $icon_link;
6251
    }
6252
6253
    public static function addUserAsAdmin(User $user)
6254
    {
6255
        if ($user) {
0 ignored issues
show
introduced by
$user is of type Chamilo\UserBundle\Entity\User, thus it always evaluated to true.
Loading history...
6256
            $userId = $user->getId();
6257
            if (!self::is_admin($userId)) {
6258
                $table = Database::get_main_table(TABLE_MAIN_ADMIN);
6259
                $sql = "INSERT INTO $table SET user_id = $userId";
6260
                Database::query($sql);
6261
            }
6262
6263
            $user->addRole('ROLE_SUPER_ADMIN');
6264
            self::getManager()->updateUser($user, true);
6265
        }
6266
    }
6267
6268
    public static function removeUserAdmin(User $user)
6269
    {
6270
        $userId = (int) $user->getId();
6271
        if (self::is_admin($userId)) {
6272
            $table = Database::get_main_table(TABLE_MAIN_ADMIN);
6273
            $sql = "DELETE FROM $table WHERE user_id = $userId";
6274
            Database::query($sql);
6275
            $user->removeRole('ROLE_SUPER_ADMIN');
6276
            self::getManager()->updateUser($user, true);
6277
        }
6278
    }
6279
6280
    /**
6281
     * @param string $from
6282
     * @param string $to
6283
     */
6284
    public static function update_all_user_languages($from, $to)
6285
    {
6286
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6287
        $from = Database::escape_string($from);
6288
        $to = Database::escape_string($to);
6289
6290
        if (!empty($to) && !empty($from)) {
6291
            $sql = "UPDATE $table_user SET language = '$to'
6292
                    WHERE language = '$from'";
6293
            Database::query($sql);
6294
        }
6295
    }
6296
6297
    /**
6298
     * Subscribe boss to students.
6299
     *
6300
     * @param int   $bossId                   The boss id
6301
     * @param array $usersId                  The users array
6302
     * @param bool  $deleteOtherAssignedUsers
6303
     *
6304
     * @return int Affected rows
6305
     */
6306
    public static function subscribeBossToUsers($bossId, $usersId, $deleteOtherAssignedUsers = true)
6307
    {
6308
        return self::subscribeUsersToUser(
6309
            $bossId,
6310
            $usersId,
6311
            USER_RELATION_TYPE_BOSS,
6312
            false,
6313
            $deleteOtherAssignedUsers
6314
        );
6315
    }
6316
6317
    /**
6318
     * @param int $userId
6319
     *
6320
     * @return bool
6321
     */
6322
    public static function removeAllBossFromStudent($userId)
6323
    {
6324
        $userId = (int) $userId;
6325
6326
        if (empty($userId)) {
6327
            return false;
6328
        }
6329
6330
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6331
        $sql = "DELETE FROM $userRelUserTable
6332
                WHERE user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
6333
        Database::query($sql);
6334
6335
        return true;
6336
    }
6337
6338
    /**
6339
     * It updates course relation type as EX-LEARNER if project name (extra field from user_edition_extra_field_to_check) is changed.
6340
     *
6341
     * @param $userId
6342
     * @param $extraValue
6343
     *
6344
     * @throws \Doctrine\DBAL\Exception
6345
     */
6346
    public static function updateCourseRelationTypeExLearner($userId, $extraValue)
6347
    {
6348
        if (false !== api_get_configuration_value('user_edition_extra_field_to_check')) {
6349
            $extraToCheck = api_get_configuration_value('user_edition_extra_field_to_check');
6350
6351
            // Get the old user extra value to check
6352
            $userExtra = UserManager::get_extra_user_data_by_field($userId, $extraToCheck);
6353
            if (isset($userExtra[$extraToCheck]) && $userExtra[$extraToCheck] != $extraValue) {
6354
                // it searchs the courses with the user old extravalue
6355
                $extraFieldValues = new ExtraFieldValue('course');
6356
                $extraItems = $extraFieldValues->get_item_id_from_field_variable_and_field_value($extraToCheck, $userExtra[$extraToCheck], false, false, true);
6357
                $coursesTocheck = [];
6358
                if (!empty($extraItems)) {
6359
                    foreach ($extraItems as $items) {
6360
                        $coursesTocheck[] = $items['item_id'];
6361
                    }
6362
                }
6363
6364
                $tblUserGroupRelUser = Database::get_main_table(TABLE_USERGROUP_REL_USER);
6365
                $tblUserGroupRelCourse = Database::get_main_table(TABLE_USERGROUP_REL_COURSE);
6366
                $tblUserGroupRelSession = Database::get_main_table(TABLE_USERGROUP_REL_SESSION);
6367
                $tblSessionUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
6368
                $tblCourseUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
6369
6370
                // To check in main course
6371
                if (!empty($coursesTocheck)) {
6372
                    foreach ($coursesTocheck as $courseId) {
6373
                        $sql = "SELECT id FROM $tblCourseUser
6374
                                WHERE user_id = $userId AND c_id = $courseId";
6375
                        $rs = Database::query($sql);
6376
                        if (Database::num_rows($rs) > 0) {
6377
                            $id = Database::result($rs, 0, 0);
6378
                            $sql = "UPDATE $tblCourseUser SET relation_type = ".COURSE_EXLEARNER."
6379
                                    WHERE id = $id";
6380
                            Database::query($sql);
6381
                        }
6382
                    }
6383
                }
6384
6385
                // To check in sessions
6386
                if (!empty($coursesTocheck)) {
6387
                    $tblSessionCourseUser = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
6388
                    $sessionsToCheck = [];
6389
                    foreach ($coursesTocheck as $courseId) {
6390
                        $sql = "SELECT id, session_id FROM $tblSessionCourseUser
6391
                                WHERE user_id = $userId AND c_id = $courseId";
6392
                        $rs = Database::query($sql);
6393
                        if (Database::num_rows($rs) > 0) {
6394
                            $row = Database::fetch_array($rs);
6395
                            $id = $row['id'];
6396
                            $sessionId = $row['session_id'];
6397
                            $sql = "UPDATE $tblSessionCourseUser SET status = ".COURSE_EXLEARNER."
6398
                                    WHERE id = $id";
6399
                            Database::query($sql);
6400
                            $sessionsToCheck[] = $sessionId;
6401
                        }
6402
                    }
6403
                    // It checks if user is ex-learner in all courses in the session to update the session relation type
6404
                    if (!empty($sessionsToCheck)) {
6405
                        $sessionsToCheck = array_unique($sessionsToCheck);
6406
                        foreach ($sessionsToCheck as $sessionId) {
6407
                            $checkAll = Database::query("SELECT count(id) FROM $tblSessionCourseUser WHERE user_id = $userId AND session_id = $sessionId");
6408
                            $countAll = Database::result($checkAll, 0, 0);
6409
                            $checkExLearner = Database::query("SELECT count(id) FROM $tblSessionCourseUser WHERE status = ".COURSE_EXLEARNER." AND user_id = $userId AND session_id = $sessionId");
6410
                            $countExLearner = Database::result($checkExLearner, 0, 0);
6411
                            if ($countAll > 0 && $countAll == $countExLearner) {
6412
                                $sql = "UPDATE $tblSessionUser SET relation_type = ".COURSE_EXLEARNER."
6413
                                    WHERE user_id = $userId AND session_id = $sessionId";
6414
                                Database::query($sql);
6415
                            }
6416
                        }
6417
                    }
6418
                }
6419
                // To check users inside a class
6420
                $rsUser = Database::query("SELECT usergroup_id FROM $tblUserGroupRelUser WHERE user_id = $userId");
6421
                if (Database::num_rows($rsUser) > 0) {
6422
                    while ($rowUser = Database::fetch_array($rsUser)) {
6423
                        $usergroupId = $rowUser['usergroup_id'];
6424
6425
                        // Count courses with exlearners
6426
                        $sqlC1 = "SELECT count(id) FROM $tblUserGroupRelCourse WHERE usergroup_id = $usergroupId";
6427
                        $rsCourses = Database::query($sqlC1);
6428
                        $countGroupCourses = Database::result($rsCourses, 0, 0);
6429
6430
                        $sqlC2 = "SELECT count(cu.id)
6431
                                FROM $tblCourseUser cu
6432
                                INNER JOIN $tblUserGroupRelCourse gc
6433
                                    ON gc.course_id = cu.c_id
6434
                                WHERE
6435
                                    cu.user_id = $userId AND
6436
                                    usergroup_id = $usergroupId AND
6437
                                    relation_type = ".COURSE_EXLEARNER;
6438
                        $rsExCourses = Database::query($sqlC2);
6439
                        $countExCourses = Database::result($rsExCourses, 0, 0);
6440
                        $checkedExCourses = $countGroupCourses > 0 && ($countExCourses == $countGroupCourses);
6441
6442
                        // Count sessions with exlearners
6443
                        $sqlS1 = "SELECT count(id) FROM $tblUserGroupRelSession WHERE usergroup_id = $usergroupId";
6444
                        $rsSessions = Database::query($sqlS1);
6445
                        $countGroupSessions = Database::result($rsSessions, 0, 0);
6446
6447
                        $sqlS2 = "SELECT count(su.id)
6448
                                FROM $tblSessionUser su
6449
                                INNER JOIN $tblUserGroupRelSession gs
6450
                                    ON gs.session_id = su.session_id
6451
                                WHERE
6452
                                    su.user_id = $userId AND
6453
                                    usergroup_id = $usergroupId AND
6454
                                    relation_type = ".COURSE_EXLEARNER;
6455
                        $rsExSessions = Database::query($sqlS2);
6456
                        $countExSessions = Database::result($rsExSessions, 0, 0);
6457
                        $checkedExSessions = $countGroupSessions > 0 && ($countExSessions == $countGroupSessions);
6458
6459
                        // it checks if usergroup user should be set to EXLEARNER
6460
                        $checkedExClassLearner = false;
6461
                        if ($countGroupCourses > 0 && $countGroupSessions == 0) {
6462
                            $checkedExClassLearner = $checkedExCourses;
6463
                        } elseif ($countGroupCourses == 0 && $countGroupSessions > 0) {
6464
                            $checkedExClassLearner = $checkedExSessions;
6465
                        } elseif ($countGroupCourses > 0 && $countGroupSessions > 0) {
6466
                            $checkedExClassLearner = ($checkedExCourses && $checkedExSessions);
6467
                        }
6468
6469
                        if ($checkedExClassLearner) {
6470
                            Database::query("UPDATE $tblUserGroupRelUser SET relation_type = ".COURSE_EXLEARNER." WHERE user_id = $userId AND usergroup_id = $usergroupId");
6471
                        }
6472
                    }
6473
                }
6474
            }
6475
        }
6476
    }
6477
6478
    /**
6479
     * Subscribe boss to students, if $bossList is empty then the boss list will be empty too.
6480
     *
6481
     * @param int   $studentId
6482
     * @param array $bossList
6483
     * @param bool  $sendNotification
6484
     *
6485
     * @return mixed Affected rows or false on failure
6486
     */
6487
    public static function subscribeUserToBossList(
6488
        $studentId,
6489
        $bossList,
6490
        $sendNotification = false
6491
    ) {
6492
        $inserted = 0;
6493
        if (!empty($bossList)) {
6494
            sort($bossList);
6495
            $studentId = (int) $studentId;
6496
            $studentInfo = api_get_user_info($studentId);
6497
6498
            if (empty($studentInfo)) {
6499
                return false;
6500
            }
6501
6502
            $previousBossList = self::getStudentBossList($studentId);
6503
            $previousBossList = !empty($previousBossList) ? array_column($previousBossList, 'boss_id') : [];
6504
            sort($previousBossList);
6505
6506
            // Boss list is the same, nothing changed.
6507
            if ($bossList == $previousBossList) {
6508
                return false;
6509
            }
6510
6511
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6512
            self::removeAllBossFromStudent($studentId);
6513
6514
            foreach ($bossList as $bossId) {
6515
                $bossId = (int) $bossId;
6516
                $bossInfo = api_get_user_info($bossId);
6517
6518
                if (empty($bossInfo)) {
6519
                    continue;
6520
                }
6521
6522
                $bossLanguage = $bossInfo['language'];
6523
6524
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
6525
                        VALUES ($studentId, $bossId, ".USER_RELATION_TYPE_BOSS.")";
6526
                $insertId = Database::query($sql);
6527
6528
                if ($insertId) {
6529
                    if ($sendNotification) {
6530
                        $name = $studentInfo['complete_name'];
6531
                        $url = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?student='.$studentId;
6532
                        $url = Display::url($url, $url);
6533
                        $subject = sprintf(
6534
                            get_lang('UserXHasBeenAssignedToBoss', false, $bossLanguage),
6535
                            $name
6536
                        );
6537
                        $message = sprintf(
6538
                            get_lang('UserXHasBeenAssignedToBossWithUrlX', false, $bossLanguage),
6539
                            $name,
6540
                            $url
6541
                        );
6542
                        MessageManager::send_message_simple(
6543
                            $bossId,
6544
                            $subject,
6545
                            $message
6546
                        );
6547
                    }
6548
                    $inserted++;
6549
                }
6550
            }
6551
        } else {
6552
            self::removeAllBossFromStudent($studentId);
6553
        }
6554
6555
        return $inserted;
6556
    }
6557
6558
    /**
6559
     * Get users followed by student boss.
6560
     *
6561
     * @param int    $userId
6562
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
6563
     * @param bool   $getOnlyUserId
6564
     * @param bool   $getSql
6565
     * @param bool   $getCount
6566
     * @param int    $from
6567
     * @param int    $numberItems
6568
     * @param int    $column
6569
     * @param string $direction
6570
     * @param int    $active
6571
     * @param string $lastConnectionDate
6572
     *
6573
     * @return array users
6574
     */
6575
    public static function getUsersFollowedByStudentBoss(
6576
        $userId,
6577
        $userStatus = 0,
6578
        $getOnlyUserId = false,
6579
        $getSql = false,
6580
        $getCount = false,
6581
        $from = null,
6582
        $numberItems = null,
6583
        $column = null,
6584
        $direction = null,
6585
        $active = null,
6586
        $lastConnectionDate = null
6587
    ) {
6588
        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...
6589
            $userId,
6590
            $userStatus,
6591
            $getOnlyUserId,
6592
            $getSql,
6593
            $getCount,
6594
            $from,
6595
            $numberItems,
6596
            $column,
6597
            $direction,
6598
            $active,
6599
            $lastConnectionDate,
6600
            STUDENT_BOSS
6601
        );
6602
    }
6603
6604
    /**
6605
     * @return array
6606
     */
6607
    public static function getOfficialCodeGrouped()
6608
    {
6609
        $user = Database::get_main_table(TABLE_MAIN_USER);
6610
        $sql = "SELECT DISTINCT official_code
6611
                FROM $user
6612
                GROUP BY official_code";
6613
        $result = Database::query($sql);
6614
        $values = Database::store_result($result, 'ASSOC');
6615
        $result = [];
6616
        foreach ($values as $value) {
6617
            $result[$value['official_code']] = $value['official_code'];
6618
        }
6619
6620
        return $result;
6621
    }
6622
6623
    /**
6624
     * @param string $officialCode
6625
     *
6626
     * @return array
6627
     */
6628
    public static function getUsersByOfficialCode($officialCode)
6629
    {
6630
        $user = Database::get_main_table(TABLE_MAIN_USER);
6631
        $officialCode = Database::escape_string($officialCode);
6632
6633
        $sql = "SELECT DISTINCT id
6634
                FROM $user
6635
                WHERE official_code = '$officialCode'
6636
                ";
6637
        $result = Database::query($sql);
6638
6639
        $users = [];
6640
        while ($row = Database::fetch_array($result)) {
6641
            $users[] = $row['id'];
6642
        }
6643
6644
        return $users;
6645
    }
6646
6647
    /**
6648
     * Calc the expended time (in seconds) by a user in a course.
6649
     *
6650
     * @param int    $userId    The user id
6651
     * @param int    $courseId  The course id
6652
     * @param int    $sessionId Optional. The session id
6653
     * @param string $from      Optional. From date
6654
     * @param string $until     Optional. Until date
6655
     *
6656
     * @return int The time
6657
     */
6658
    public static function getTimeSpentInCourses(
6659
        $userId,
6660
        $courseId,
6661
        $sessionId = 0,
6662
        $from = '',
6663
        $until = ''
6664
    ) {
6665
        $userId = (int) $userId;
6666
        $sessionId = (int) $sessionId;
6667
6668
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6669
        $whereConditions = [
6670
            'user_id = ? ' => $userId,
6671
            'AND c_id = ? ' => $courseId,
6672
            'AND session_id = ? ' => $sessionId,
6673
        ];
6674
6675
        if (!empty($from) && !empty($until)) {
6676
            $whereConditions["AND (login_course_date >= '?' "] = $from;
6677
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
6678
        }
6679
6680
        $trackResult = Database::select(
6681
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
6682
            $trackCourseAccessTable,
6683
            [
6684
                'where' => $whereConditions,
6685
            ],
6686
            'first'
6687
        );
6688
6689
        if ($trackResult != false) {
6690
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
6691
        }
6692
6693
        return 0;
6694
    }
6695
6696
    /**
6697
     * Get the boss user ID from a followed user id.
6698
     *
6699
     * @param $userId
6700
     *
6701
     * @return bool
6702
     */
6703
    public static function getFirstStudentBoss($userId)
6704
    {
6705
        $userId = (int) $userId;
6706
        if ($userId > 0) {
6707
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6708
            $row = Database::select(
6709
                'DISTINCT friend_user_id AS boss_id',
6710
                $userRelTable,
6711
                [
6712
                    'where' => [
6713
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
6714
                            $userId,
6715
                            USER_RELATION_TYPE_BOSS,
6716
                        ],
6717
                    ],
6718
                ]
6719
            );
6720
            if (!empty($row)) {
6721
                return $row[0]['boss_id'];
6722
            }
6723
        }
6724
6725
        return false;
6726
    }
6727
6728
    /**
6729
     * Get the boss user ID from a followed user id.
6730
     *
6731
     * @param int $userId student id
6732
     *
6733
     * @return array
6734
     */
6735
    public static function getStudentBossList($userId)
6736
    {
6737
        $userId = (int) $userId;
6738
6739
        if ($userId > 0) {
6740
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
6741
6742
            return Database::select(
6743
                'DISTINCT friend_user_id AS boss_id',
6744
                $userRelTable,
6745
                [
6746
                    'where' => [
6747
                        'user_id = ? AND relation_type = ? ' => [
6748
                            $userId,
6749
                            USER_RELATION_TYPE_BOSS,
6750
                        ],
6751
                    ],
6752
                ]
6753
            );
6754
        }
6755
6756
        return [];
6757
    }
6758
6759
    /**
6760
     * @param int $bossId
6761
     * @param int $studentId
6762
     *
6763
     * @return bool
6764
     */
6765
    public static function userIsBossOfStudent($bossId, $studentId)
6766
    {
6767
        $result = false;
6768
        $bossList = self::getStudentBossList($studentId);
6769
        if (!empty($bossList)) {
6770
            $bossList = array_column($bossList, 'boss_id');
6771
            if (in_array($bossId, $bossList)) {
6772
                $result = true;
6773
            }
6774
        }
6775
6776
        return $result;
6777
    }
6778
6779
    /**
6780
     * Displays the name of the user and makes the link to the user profile.
6781
     *
6782
     * @param array $userInfo
6783
     *
6784
     * @return string
6785
     */
6786
    public static function getUserProfileLink($userInfo)
6787
    {
6788
        if (isset($userInfo) && isset($userInfo['user_id'])) {
6789
            return Display::url(
6790
                $userInfo['complete_name_with_username'],
6791
                $userInfo['profile_url']
6792
            );
6793
        }
6794
6795
        return get_lang('Anonymous');
6796
    }
6797
6798
    /**
6799
     * Get users whose name matches $firstname and $lastname.
6800
     *
6801
     * @param string $firstname Firstname to search
6802
     * @param string $lastname  Lastname to search
6803
     *
6804
     * @return array The user list
6805
     */
6806
    public static function getUsersByName($firstname, $lastname)
6807
    {
6808
        $firstname = Database::escape_string($firstname);
6809
        $lastname = Database::escape_string($lastname);
6810
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
6811
6812
        $sql = <<<SQL
6813
            SELECT id, username, lastname, firstname
6814
            FROM $userTable
6815
            WHERE
6816
                firstname LIKE '$firstname%' AND
6817
                lastname LIKE '$lastname%'
6818
SQL;
6819
        $result = Database::query($sql);
6820
        $users = [];
6821
        while ($resultData = Database::fetch_object($result)) {
6822
            $users[] = $resultData;
6823
        }
6824
6825
        return $users;
6826
    }
6827
6828
    /**
6829
     * @param int $optionSelected
6830
     *
6831
     * @return string
6832
     */
6833
    public static function getUserSubscriptionTab($optionSelected = 1)
6834
    {
6835
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
6836
        if (($allowAdmin === 'true' && api_is_allowed_to_edit()) ||
6837
            api_is_platform_admin()
6838
        ) {
6839
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
6840
6841
            $headers = [
6842
                [
6843
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
6844
                    'content' => get_lang('Students'),
6845
                ],
6846
                [
6847
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
6848
                    'content' => get_lang('Teachers'),
6849
                ],
6850
                /*[
6851
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
6852
                    'content' => get_lang('Students'),
6853
                ],
6854
                [
6855
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
6856
                    'content' => get_lang('Teachers'),
6857
                ],*/
6858
                [
6859
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
6860
                    'content' => get_lang('Groups'),
6861
                ],
6862
                'classes' => [
6863
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
6864
                    'content' => get_lang('Classes'),
6865
                ],
6866
            ];
6867
6868
            if (api_get_configuration_value('session_classes_tab_disable')
6869
                && !api_is_platform_admin()
6870
                && api_get_session_id()
6871
            ) {
6872
                unset($headers['classes']);
6873
            }
6874
6875
            return Display::tabsOnlyLink($headers, $optionSelected);
6876
        }
6877
6878
        return '';
6879
    }
6880
6881
    /**
6882
     * Make sure this function is protected because it does NOT check password!
6883
     *
6884
     * This function defines globals.
6885
     *
6886
     * @param int  $userId
6887
     * @param bool $checkIfUserCanLoginAs
6888
     *
6889
     * @return bool
6890
     *
6891
     * @author Evie Embrechts
6892
     * @author Yannick Warnier <[email protected]>
6893
     */
6894
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
6895
    {
6896
        $userId = (int) $userId;
6897
        $userInfo = api_get_user_info($userId);
6898
6899
        // Check if the user is allowed to 'login_as'
6900
        $canLoginAs = true;
6901
        if ($checkIfUserCanLoginAs) {
6902
            $canLoginAs = api_can_login_as($userId);
6903
        }
6904
6905
        if (!$canLoginAs || empty($userInfo)) {
6906
            return false;
6907
        }
6908
6909
        if ($userId) {
6910
            $logInfo = [
6911
                'tool' => 'logout',
6912
                'tool_id' => 0,
6913
                'tool_id_detail' => 0,
6914
                'action' => '',
6915
                'info' => 'Change user (login as)',
6916
            ];
6917
            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

6917
            Event::/** @scrutinizer ignore-call */ 
6918
                   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...
6918
6919
            // Logout the current user
6920
            self::loginDelete(api_get_user_id());
6921
6922
            Session::erase('_user');
6923
            Session::erase('is_platformAdmin');
6924
            Session::erase('is_allowedCreateCourse');
6925
            Session::erase('_uid');
6926
6927
            // Cleaning session variables
6928
            $_user['firstName'] = $userInfo['firstname'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$_user was never initialized. Although not strictly required by PHP, it is generally a good practice to add $_user = array(); before regardless.
Loading history...
6929
            $_user['lastName'] = $userInfo['lastname'];
6930
            $_user['mail'] = $userInfo['email'];
6931
            $_user['official_code'] = $userInfo['official_code'];
6932
            $_user['picture_uri'] = $userInfo['picture_uri'];
6933
            $_user['user_id'] = $userId;
6934
            $_user['id'] = $userId;
6935
            $_user['status'] = $userInfo['status'];
6936
6937
            // Filling session variables with new data
6938
            Session::write('_uid', $userId);
6939
            Session::write('_user', $userInfo);
6940
            Session::write('is_platformAdmin', (bool) self::is_admin($userId));
6941
            Session::write('is_allowedCreateCourse', $userInfo['status'] == 1);
6942
            // will be useful later to know if the user is actually an admin or not (example reporting)
6943
            Session::write('login_as', true);
6944
            $logInfo = [
6945
                'tool' => 'login',
6946
                'tool_id' => 0,
6947
                'tool_id_detail' => 0,
6948
                'info' => $userId,
6949
            ];
6950
            Event::registerLog($logInfo);
6951
6952
            return true;
6953
        }
6954
6955
        return false;
6956
    }
6957
6958
    /**
6959
     * Remove all login records from the track_e_online stats table,
6960
     * for the given user ID.
6961
     *
6962
     * @param int $userId User ID
6963
     */
6964
    public static function loginDelete($userId)
6965
    {
6966
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6967
        $userId = (int) $userId;
6968
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
6969
        Database::query($query);
6970
    }
6971
6972
    /**
6973
     * Login as first admin user registered in the platform.
6974
     *
6975
     * @return array
6976
     */
6977
    public static function logInAsFirstAdmin()
6978
    {
6979
        $adminList = self::get_all_administrators();
6980
6981
        if (!empty($adminList)) {
6982
            $userInfo = current($adminList);
6983
            if (!empty($userInfo)) {
6984
                $result = self::loginAsUser($userInfo['user_id'], false);
6985
                if ($result && api_is_platform_admin()) {
6986
                    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...
6987
                }
6988
            }
6989
        }
6990
6991
        return [];
6992
    }
6993
6994
    public static function blockIfMaxLoginAttempts(array $userInfo)
6995
    {
6996
        if (false === (bool) $userInfo['active'] || null === $userInfo['last_login']) {
6997
            return;
6998
        }
6999
7000
        $maxAllowed = (int) api_get_configuration_value('login_max_attempt_before_blocking_account');
7001
7002
        if ($maxAllowed <= 0) {
7003
            return;
7004
        }
7005
7006
        $em = Database::getManager();
7007
7008
        $countFailedAttempts = $em
7009
            ->getRepository(TrackELoginAttempt::class)
7010
            ->createQueryBuilder('la')
7011
            ->select('COUNT(la)')
7012
            ->where('la.username = :username')
7013
            ->andWhere('la.loginDate >= :last_login')
7014
            ->andWhere('la.success <> TRUE')
7015
            ->setParameters(
7016
                [
7017
                    'username' => $userInfo['username'],
7018
                    'last_login' => $userInfo['last_login'],
7019
                ]
7020
            )
7021
            ->getQuery()
7022
            ->getSingleScalarResult()
7023
        ;
7024
7025
        if ($countFailedAttempts >= $maxAllowed) {
7026
            Database::update(
7027
                Database::get_main_table(TABLE_MAIN_USER),
7028
                ['active' => false],
7029
                ['username = ?' => $userInfo['username']]
7030
            );
7031
7032
            Display::addFlash(
7033
                Display::return_message(
7034
                    sprintf(
7035
                        get_lang('XAccountDisabledByYAttempts'),
7036
                        $userInfo['username'],
7037
                        $countFailedAttempts
7038
                    ),
7039
                    'error',
7040
                    false
7041
                )
7042
            );
7043
        }
7044
    }
7045
7046
    /**
7047
     * Check if user is teacher of a student based in their courses.
7048
     *
7049
     * @param $teacherId
7050
     * @param $studentId
7051
     *
7052
     * @return array
7053
     */
7054
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
7055
    {
7056
        $courses = CourseManager::getCoursesFollowedByUser(
7057
            $teacherId,
7058
            COURSEMANAGER
7059
        );
7060
        if (empty($courses)) {
7061
            return false;
7062
        }
7063
7064
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
7065
        if (empty($coursesFromUser)) {
7066
            return false;
7067
        }
7068
7069
        $coursesCodeList = array_column($courses, 'code');
7070
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
7071
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
7072
        $commonCourses = array_filter($commonCourses);
7073
7074
        if (!empty($commonCourses)) {
7075
            return $commonCourses;
7076
        }
7077
7078
        return [];
7079
    }
7080
7081
    /**
7082
     * @param int $teacherId
7083
     * @param int $studentId
7084
     *
7085
     * @return bool
7086
     */
7087
    public static function isTeacherOfStudent($teacherId, $studentId)
7088
    {
7089
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
7090
            $teacherId,
7091
            $studentId
7092
        );
7093
7094
        if (!empty($courses)) {
7095
            return true;
7096
        }
7097
7098
        return false;
7099
    }
7100
7101
    /**
7102
     * Send user confirmation mail.
7103
     *
7104
     * @throws Exception
7105
     */
7106
    public static function sendUserConfirmationMail(User $user)
7107
    {
7108
        $uniqueId = api_get_unique_id();
7109
        $user->setConfirmationToken($uniqueId);
7110
7111
        Database::getManager()->persist($user);
7112
        Database::getManager()->flush();
7113
7114
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
7115
7116
        // Check if the user was originally set for an automated subscription to a course or session
7117
        $courseCodeToRedirect = Session::read('course_redirect');
7118
        $sessionToRedirect = Session::read('session_redirect');
7119
        if (!empty($courseCodeToRedirect)) {
7120
            $url .= '&c='.$courseCodeToRedirect;
7121
        }
7122
        if (!empty($sessionToRedirect)) {
7123
            $url .= '&s='.$sessionToRedirect;
7124
        }
7125
        $mailSubject = get_lang('RegistrationConfirmation');
7126
        $mailBody = get_lang('RegistrationConfirmationEmailMessage')
7127
            .PHP_EOL
7128
            .Display::url($url, $url);
7129
7130
        api_mail_html(
7131
            self::formatUserFullName($user),
7132
            $user->getEmail(),
7133
            $mailSubject,
7134
            $mailBody
7135
        );
7136
        Display::addFlash(Display::return_message(get_lang('CheckYourEmailAndFollowInstructions')));
7137
    }
7138
7139
    /**
7140
     * Anonymize a user. Replace personal info by anonymous info.
7141
     *
7142
     * @param int  $userId   User id
7143
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
7144
     *
7145
     * @throws \Exception
7146
     *
7147
     * @return bool
7148
     * @assert (0) === false
7149
     */
7150
    public static function anonymize($userId, $deleteIP = true)
7151
    {
7152
        global $debug;
7153
7154
        $userId = (int) $userId;
7155
7156
        if (empty($userId)) {
7157
            return false;
7158
        }
7159
7160
        $em = Database::getManager();
7161
        $user = api_get_user_entity($userId);
7162
        $uniqueId = uniqid('anon', true);
7163
        $user
7164
            ->setFirstname($uniqueId)
7165
            ->setLastname($uniqueId)
7166
            ->setBiography('')
7167
            ->setAddress('')
7168
            ->setCurriculumItems(null)
7169
            ->setDateOfBirth(null)
7170
            ->setCompetences('')
7171
            ->setDiplomas('')
7172
            ->setOpenarea('')
7173
            ->setTeach('')
7174
            ->setProductions(null)
7175
            ->setOpenid('')
7176
            ->setEmailCanonical($uniqueId.'@example.com')
7177
            ->setEmail($uniqueId.'@example.com')
7178
            ->setUsername($uniqueId)
7179
            ->setUsernameCanonical($uniqueId)
7180
            ->setPhone('')
7181
            ->setOfficialCode('')
7182
        ;
7183
7184
        self::deleteUserPicture($userId);
7185
        self::cleanUserRequestsOfRemoval($userId);
7186
7187
        // The IP address is a border-case personal data, as it does
7188
        // not directly allow for personal identification (it is not
7189
        // a completely safe value in most countries - the IP could
7190
        // be used by neighbours and crackers)
7191
        if ($deleteIP) {
7192
            $substitute = '127.0.0.1';
7193
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
7194
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
7195
            $res = Database::query($sql);
7196
            if ($res === false && $debug > 0) {
7197
                error_log("Could not anonymize IP address for user $userId ($sql)");
7198
            }
7199
7200
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
7201
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
7202
            $res = Database::query($sql);
7203
            if ($res === false && $debug > 0) {
7204
                error_log("Could not anonymize IP address for user $userId ($sql)");
7205
            }
7206
7207
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
7208
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE exe_user_id = $userId";
7209
            $res = Database::query($sql);
7210
            if ($res === false && $debug > 0) {
7211
                error_log("Could not anonymize IP address for user $userId ($sql)");
7212
            }
7213
7214
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
7215
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
7216
            $res = Database::query($sql);
7217
            if ($res === false && $debug > 0) {
7218
                error_log("Could not anonymize IP address for user $userId ($sql)");
7219
            }
7220
7221
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
7222
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
7223
            $res = Database::query($sql);
7224
            if ($res === false && $debug > 0) {
7225
                error_log("Could not anonymize IP address for user $userId ($sql)");
7226
            }
7227
7228
            $table = Database::get_course_table(TABLE_WIKI);
7229
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
7230
            $res = Database::query($sql);
7231
            if ($res === false && $debug > 0) {
7232
                error_log("Could not anonymize IP address for user $userId ($sql)");
7233
            }
7234
7235
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
7236
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
7237
            $res = Database::query($sql);
7238
            if ($res === false && $debug > 0) {
7239
                error_log("Could not anonymize IP address for user $userId ($sql)");
7240
            }
7241
7242
            $table = Database::get_course_table(TABLE_WIKI);
7243
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
7244
            $res = Database::query($sql);
7245
            if ($res === false && $debug > 0) {
7246
                error_log("Could not anonymize IP address for user $userId ($sql)");
7247
            }
7248
        }
7249
        $em->persist($user);
7250
        $em->flush($user);
7251
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
7252
7253
        return true;
7254
    }
7255
7256
    /**
7257
     * @param int $userId
7258
     *
7259
     * @throws Exception
7260
     *
7261
     * @return string
7262
     */
7263
    public static function anonymizeUserWithVerification($userId)
7264
    {
7265
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
7266
7267
        $message = '';
7268
        if (api_is_platform_admin() ||
7269
            ($allowDelete && api_is_session_admin())
7270
        ) {
7271
            $userToUpdateInfo = api_get_user_info($userId);
7272
            $currentUserId = api_get_user_id();
7273
7274
            if ($userToUpdateInfo &&
7275
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
7276
            ) {
7277
                if ($userId != $currentUserId &&
7278
                    self::anonymize($userId)
7279
                ) {
7280
                    $message = Display::return_message(
7281
                        sprintf(get_lang('UserXAnonymized'), $userToUpdateInfo['complete_name_with_username']),
7282
                        'confirmation'
7283
                    );
7284
                } else {
7285
                    $message = Display::return_message(
7286
                        sprintf(get_lang('CannotAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
7287
                        'error'
7288
                    );
7289
                }
7290
            } else {
7291
                $message = Display::return_message(
7292
                    sprintf(get_lang('NoPermissionToAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
7293
                    'error'
7294
                );
7295
            }
7296
        }
7297
7298
        return $message;
7299
    }
7300
7301
    /**
7302
     * @param int $userId
7303
     *
7304
     * @throws Exception
7305
     *
7306
     * @return string
7307
     */
7308
    public static function deleteUserWithVerification($userId)
7309
    {
7310
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
7311
        $message = Display::return_message(get_lang('CannotDeleteUser'), 'error');
7312
        $userToUpdateInfo = api_get_user_info($userId);
7313
7314
        // User must exist.
7315
        if (empty($userToUpdateInfo)) {
7316
            return $message;
7317
        }
7318
7319
        $currentUserId = api_get_user_id();
7320
7321
        // Cannot delete myself.
7322
        if ($userId == $currentUserId) {
7323
            return $message;
7324
        }
7325
7326
        if (api_is_platform_admin() ||
7327
            ($allowDelete && api_is_session_admin())
7328
        ) {
7329
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
7330
                if (self::delete_user($userId)) {
7331
                    $message = Display::return_message(
7332
                        get_lang('UserDeleted').': '.$userToUpdateInfo['complete_name_with_username'],
7333
                        'confirmation'
7334
                    );
7335
                } else {
7336
                    $message = Display::return_message(get_lang('CannotDeleteUserBecauseOwnsCourse'), 'error');
7337
                }
7338
            }
7339
        }
7340
7341
        return $message;
7342
    }
7343
7344
    /**
7345
     * @return array
7346
     */
7347
    public static function createDataPrivacyExtraFields()
7348
    {
7349
        self::create_extra_field(
7350
            'request_for_legal_agreement_consent_removal_justification',
7351
            1, //text
7352
            'Request for legal agreement consent removal justification	',
7353
            ''
7354
        );
7355
7356
        self::create_extra_field(
7357
            'request_for_delete_account_justification',
7358
            1, //text
7359
            'Request for delete account justification',
7360
            ''
7361
        );
7362
7363
        $extraFieldId = self::create_extra_field(
7364
            'request_for_legal_agreement_consent_removal',
7365
            1, //text
7366
            'Request for legal agreement consent removal',
7367
            ''
7368
        );
7369
7370
        $extraFieldIdDeleteAccount = self::create_extra_field(
7371
            'request_for_delete_account',
7372
            1, //text
7373
            'Request for delete user account',
7374
            ''
7375
        );
7376
7377
        return [
7378
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
7379
            'delete_legal' => $extraFieldId,
7380
        ];
7381
    }
7382
7383
    /**
7384
     * @param int $userId
7385
     */
7386
    public static function cleanUserRequestsOfRemoval($userId)
7387
    {
7388
        $userId = (int) $userId;
7389
7390
        $extraFieldValue = new ExtraFieldValue('user');
7391
        $extraFieldsToDelete = [
7392
            'legal_accept',
7393
            'request_for_legal_agreement_consent_removal',
7394
            'request_for_legal_agreement_consent_removal_justification',
7395
            'request_for_delete_account_justification', // just in case delete also this
7396
            'request_for_delete_account',
7397
        ];
7398
7399
        foreach ($extraFieldsToDelete as $variable) {
7400
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
7401
                $userId,
7402
                $variable
7403
            );
7404
            if ($value && isset($value['id'])) {
7405
                $extraFieldValue->delete($value['id']);
7406
            }
7407
        }
7408
    }
7409
7410
    /**
7411
     * @param int $searchYear
7412
     *
7413
     * @throws Exception
7414
     *
7415
     * @return array
7416
     */
7417
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
7418
    {
7419
        $timezone = new DateTimeZone(api_get_timezone());
7420
7421
        $sessions = [];
7422
        if (DRH == $userInfo['status']) {
7423
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
7424
        } elseif (api_is_platform_admin(true)) {
7425
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
7426
        } else {
7427
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
7428
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
7429
7430
            foreach ($sessionsByCategory as $sessionsInCategory) {
7431
                $sessions = array_merge($sessions, $sessionsInCategory);
7432
            }
7433
        }
7434
7435
        $sessions = array_map(
7436
            function ($sessionInfo) {
7437
                if (!isset($sessionInfo['session_id'])) {
7438
                    $sessionInfo['session_id'] = $sessionInfo['id'];
7439
                }
7440
                if (!isset($sessionInfo['session_name'])) {
7441
                    $sessionInfo['session_name'] = $sessionInfo['name'];
7442
                }
7443
7444
                return $sessionInfo;
7445
            },
7446
            $sessions
7447
        );
7448
7449
        $calendarSessions = [];
7450
7451
        foreach ($sessions as $sessionInfo) {
7452
            if (!empty($sessionInfo['duration'])) {
7453
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
7454
                    $sessionInfo['session_id'],
7455
                    $userInfo['id']
7456
                );
7457
7458
                if (empty($courseAccess)) {
7459
                    continue;
7460
                }
7461
7462
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
7463
                $lastAccessDate = clone $firstAcessDate;
7464
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
7465
7466
                $firstAccessYear = (int) $firstAcessDate->format('Y');
7467
                $lastAccessYear = (int) $lastAccessDate->format('Y');
7468
7469
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
7470
                    $calendarSessions[$sessionInfo['session_id']] = [
7471
                        'name' => $sessionInfo['session_name'],
7472
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
7473
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
7474
                    ];
7475
                }
7476
7477
                continue;
7478
            }
7479
7480
            $accessStartDate = !empty($sessionInfo['access_start_date'])
7481
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
7482
                : null;
7483
            $accessEndDate = !empty($sessionInfo['access_end_date'])
7484
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
7485
                : null;
7486
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
7487
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
7488
7489
            $isValid = false;
7490
7491
            if ($accessStartYear && $accessEndYear) {
7492
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
7493
                    $isValid = true;
7494
                }
7495
            }
7496
7497
            if ($accessStartYear && !$accessEndYear) {
7498
                if ($accessStartYear == $searchYear) {
7499
                    $isValid = true;
7500
                }
7501
            }
7502
7503
            if (!$accessStartYear && $accessEndYear) {
7504
                if ($accessEndYear == $searchYear) {
7505
                    $isValid = true;
7506
                }
7507
            }
7508
7509
            if ($isValid) {
7510
                $calendarSessions[$sessionInfo['session_id']] = [
7511
                    'name' => $sessionInfo['session_name'],
7512
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
7513
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
7514
                ];
7515
            }
7516
        }
7517
7518
        return $calendarSessions;
7519
    }
7520
7521
    /**
7522
     * Get sessions info for planification calendar.
7523
     *
7524
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
7525
     * @param int   $searchYear
7526
     *
7527
     * @throws Exception
7528
     *
7529
     * @return array
7530
     */
7531
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
7532
    {
7533
        $timezone = new DateTimeZone(api_get_timezone());
7534
        $calendar = [];
7535
7536
        foreach ($sessionsList as $sessionId => $sessionInfo) {
7537
            $startDate = $sessionInfo['access_start_date']
7538
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
7539
                : null;
7540
            $endDate = $sessionInfo['access_end_date']
7541
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
7542
                : null;
7543
7544
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
7545
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
7546
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
7547
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
7548
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
7549
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
7550
7551
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
7552
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
7553
7554
            $calendar[] = [
7555
                'id' => $sessionId,
7556
                'name' => $sessionInfo['name'],
7557
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
7558
                'start_in_last_year' => $startYear < $searchYear,
7559
                'end_in_next_year' => $endYear > $searchYear,
7560
                'no_start' => !$startWeek,
7561
                'no_end' => !$endWeek,
7562
                'start' => $start,
7563
                'duration' => $duration > 0 ? $duration : 1,
7564
            ];
7565
        }
7566
7567
        usort(
7568
            $calendar,
7569
            function ($sA, $sB) {
7570
                if ($sA['start'] == $sB['start']) {
7571
                    return 0;
7572
                }
7573
7574
                if ($sA['start'] < $sB['start']) {
7575
                    return -1;
7576
                }
7577
7578
                return 1;
7579
            }
7580
        );
7581
7582
        return $calendar;
7583
    }
7584
7585
    /**
7586
     * Return the user's full name. Optionally with the username.
7587
     *
7588
     * @param bool $includeUsername Optional. By default username is not included.
7589
     *
7590
     * @return string
7591
     */
7592
    public static function formatUserFullName(User $user, $includeUsername = false)
7593
    {
7594
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
7595
7596
        if ($includeUsername && api_get_configuration_value('hide_username_with_complete_name') !== true) {
7597
            $username = $user->getUsername();
7598
7599
            return "$fullName ($username)";
7600
        }
7601
7602
        return $fullName;
7603
    }
7604
7605
    /**
7606
     * @param int $userId
7607
     *
7608
     * @return array
7609
     */
7610
    public static function getUserCareers($userId)
7611
    {
7612
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7613
        $tableCareer = Database::get_main_table(TABLE_CAREER);
7614
        $userId = (int) $userId;
7615
7616
        $sql = "SELECT c.id, c.name
7617
                FROM $table uc
7618
                INNER JOIN $tableCareer c
7619
                ON uc.career_id = c.id
7620
                WHERE user_id = $userId
7621
                ORDER BY uc.created_at
7622
                ";
7623
        $result = Database::query($sql);
7624
7625
        return Database::store_result($result, 'ASSOC');
7626
    }
7627
7628
    /**
7629
     * @param int $userId
7630
     * @param int $careerId
7631
     */
7632
    public static function addUserCareer($userId, $careerId)
7633
    {
7634
        if (!api_get_configuration_value('allow_career_users')) {
7635
            return false;
7636
        }
7637
7638
        if (self::userHasCareer($userId, $careerId) === false) {
7639
            $params = [
7640
                'user_id' => $userId,
7641
                'career_id' => $careerId,
7642
                'created_at' => api_get_utc_datetime(),
7643
                'updated_at' => api_get_utc_datetime(),
7644
            ];
7645
            $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7646
            Database::insert($table, $params);
7647
        }
7648
7649
        return true;
7650
    }
7651
7652
    /**
7653
     * @param int   $userCareerId
7654
     * @param array $data
7655
     *
7656
     * @return bool
7657
     */
7658
    public static function updateUserCareer($userCareerId, $data)
7659
    {
7660
        if (!api_get_configuration_value('allow_career_users')) {
7661
            return false;
7662
        }
7663
7664
        $params = ['extra_data' => $data, 'updated_at' => api_get_utc_datetime()];
7665
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7666
        Database::update(
7667
            $table,
7668
            $params,
7669
            ['id = ?' => (int) $userCareerId]
7670
        );
7671
7672
        return true;
7673
    }
7674
7675
    /**
7676
     * @param int $userId
7677
     * @param int $careerId
7678
     *
7679
     * @return array
7680
     */
7681
    public static function getUserCareer($userId, $careerId)
7682
    {
7683
        $userId = (int) $userId;
7684
        $careerId = (int) $careerId;
7685
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7686
7687
        $sql = "SELECT * FROM $table WHERE user_id = $userId AND career_id = $careerId";
7688
        $result = Database::query($sql);
7689
7690
        return Database::fetch_array($result, 'ASSOC');
7691
    }
7692
7693
    /**
7694
     * @param int $userId
7695
     * @param int $careerId
7696
     *
7697
     * @return bool
7698
     */
7699
    public static function userHasCareer($userId, $careerId)
7700
    {
7701
        $userId = (int) $userId;
7702
        $careerId = (int) $careerId;
7703
        $table = Database::get_main_table(TABLE_MAIN_USER_CAREER);
7704
7705
        $sql = "SELECT id FROM $table WHERE user_id = $userId AND career_id = $careerId";
7706
        $result = Database::query($sql);
7707
7708
        return Database::num_rows($result) > 0;
7709
    }
7710
7711
    /**
7712
     * @param int $userId
7713
     *
7714
     * @throws Exception
7715
     */
7716
    public static function deleteUserFiles($userId)
7717
    {
7718
        $path = self::getUserPathById($userId, 'system');
7719
7720
        $fs = new Filesystem();
7721
        $fs->remove($path);
7722
    }
7723
7724
    public static function redirectToResetPassword($userId)
7725
    {
7726
        $forceRenew = api_get_configuration_value('force_renew_password_at_first_login');
7727
7728
        if ($forceRenew) {
7729
            $askPassword = self::get_extra_user_data_by_field(
7730
                $userId,
7731
                'ask_new_password'
7732
            );
7733
7734
            if (!empty($askPassword) && isset($askPassword['ask_new_password']) &&
7735
                1 === (int) $askPassword['ask_new_password']
7736
            ) {
7737
                $uniqueId = api_get_unique_id();
7738
                $userObj = api_get_user_entity($userId);
7739
7740
                $userObj->setConfirmationToken($uniqueId);
7741
                $userObj->setPasswordRequestedAt(new \DateTime());
7742
7743
                Database::getManager()->persist($userObj);
7744
                Database::getManager()->flush();
7745
7746
                $url = api_get_path(WEB_CODE_PATH).'auth/reset.php?token='.$uniqueId;
7747
                api_location($url);
7748
            }
7749
        }
7750
7751
        $forceRotateDays = api_get_configuration_value('security_password_rotate_days');
7752
        $forceRotate = false;
7753
7754
        if ($forceRotateDays > 0) {
7755
            // get the date of the last password update recorded
7756
            $lastUpdate = self::get_extra_user_data_by_field(
7757
                $userId,
7758
                'password_updated_at'
7759
            );
7760
7761
            if (empty($lastUpdate) or empty($lastUpdate['password_updated_at'])) {
7762
                $userObj = api_get_user_entity($userId);
7763
                $registrationDate = $userObj->getRegistrationDate();
7764
                $now = new \DateTime(null, new DateTimeZone('UTC'));
7765
                $interval = $now->diff($registrationDate);
7766
                $daysSince = $interval->format('%a');
7767
                if ($daysSince > $forceRotateDays) {
7768
                    $forceRotate = true;
7769
                }
7770
            } else {
7771
                $now = new \DateTime(null, new DateTimeZone('UTC'));
7772
                // In some cases, old records might contain an incomplete Y-m-d H:i:s format
7773
                if (strlen($lastUpdate['password_updated_at']) == 10) {
7774
                    $lastUpdate['password_updated_at'] .= ' 00:00:00';
7775
                }
7776
                if (strlen($lastUpdate['password_updated_at']) == 16) {
7777
                    $lastUpdate['password_updated_at'] .= ':00';
7778
                }
7779
                $date = \DateTime::createFromFormat('Y-m-d H:i:s', $lastUpdate['password_updated_at'], new DateTimeZone('UTC'));
7780
                $interval = $now->diff($date);
7781
                $daysSince = $interval->format('%a');
7782
                if ($daysSince > $forceRotateDays) {
7783
                    $forceRotate = true;
7784
                }
7785
            }
7786
            if ($forceRotate) {
7787
                $uniqueId = api_get_unique_id();
7788
                $userObj = api_get_user_entity($userId);
7789
7790
                $userObj->setConfirmationToken($uniqueId);
7791
                $userObj->setPasswordRequestedAt(new \DateTime());
7792
7793
                Database::getManager()->persist($userObj);
7794
                Database::getManager()->flush();
7795
7796
                $url = api_get_path(WEB_CODE_PATH).'auth/reset.php?token='.$uniqueId.'&rotate=1';
7797
                api_location($url);
7798
            }
7799
        }
7800
    }
7801
7802
    /**
7803
     * It returns the list of user status available.
7804
     *
7805
     * @return array
7806
     */
7807
    public static function getUserStatusList()
7808
    {
7809
        $userStatusConfig = [];
7810
        // it gets the roles to show in creation/edition user
7811
        if (true === api_get_configuration_value('user_status_show_options_enabled')) {
7812
            $userStatusConfig = api_get_configuration_value('user_status_show_option');
7813
        }
7814
        // it gets the roles to show in creation/edition user (only for admins)
7815
        if (true === api_get_configuration_value('user_status_option_only_for_admin_enabled') && api_is_platform_admin()) {
7816
            $userStatusConfig = api_get_configuration_value('user_status_option_show_only_for_admin');
7817
        }
7818
7819
        $status = [];
7820
        if (!empty($userStatusConfig)) {
7821
            $statusLang = api_get_status_langvars();
7822
            foreach ($userStatusConfig as $role => $enabled) {
7823
                if ($enabled) {
7824
                    $constStatus = constant($role);
7825
                    $status[$constStatus] = $statusLang[$constStatus];
7826
                }
7827
            }
7828
        } else {
7829
            $status[COURSEMANAGER] = get_lang('Teacher');
7830
            $status[STUDENT] = get_lang('Learner');
7831
            $status[DRH] = get_lang('Drh');
7832
            $status[SESSIONADMIN] = get_lang('SessionsAdmin');
7833
            $status[STUDENT_BOSS] = get_lang('RoleStudentBoss');
7834
            $status[INVITEE] = get_lang('Invitee');
7835
        }
7836
7837
        return $status;
7838
    }
7839
7840
    /**
7841
     * Get the expiration date by user status from configuration value.
7842
     *
7843
     * @param $status
7844
     *
7845
     * @throws Exception
7846
     *
7847
     * @return array
7848
     */
7849
    public static function getExpirationDateByRole($status)
7850
    {
7851
        $status = (int) $status;
7852
        $nbDaysByRole = api_get_configuration_value('user_number_of_days_for_default_expiration_date_per_role');
7853
        $dates = [];
7854
        if (!empty($nbDaysByRole)) {
7855
            $date = new DateTime();
7856
            foreach ($nbDaysByRole as $strVariable => $nDays) {
7857
                $constStatus = constant($strVariable);
7858
                if ($status == $constStatus) {
7859
                    $duration = "P{$nDays}D";
7860
                    $date->add(new DateInterval($duration));
7861
                    $newExpirationDate = $date->format('Y-m-d H:i');
7862
                    $formatted = api_format_date($newExpirationDate, DATE_TIME_FORMAT_LONG_24H);
7863
                    $dates = ['formatted' => $formatted, 'date' => $newExpirationDate];
7864
                }
7865
            }
7866
        }
7867
7868
        return $dates;
7869
    }
7870
7871
    public static function getAllowedRolesAsTeacher(): array
7872
    {
7873
        if (api_get_configuration_value('course_allow_student_role_to_be_teacher')) {
7874
            return [
7875
                STUDENT,
7876
                COURSEMANAGER,
7877
                SESSIONADMIN,
7878
            ];
7879
        } else {
7880
            return [
7881
                COURSEMANAGER,
7882
                SESSIONADMIN,
7883
            ];
7884
        }
7885
    }
7886
7887
    /**
7888
     * Count users in courses and if they have certificate.
7889
     * This function is resource intensive.
7890
     *
7891
     * @return array
7892
     */
7893
    public static function countUsersWhoFinishedCourses()
7894
    {
7895
        $courses = [];
7896
        $currentAccessUrlId = api_get_current_access_url_id();
7897
        $sql = "SELECT course.code, cru.user_id
7898
                FROM course_rel_user cru
7899
                    JOIN course ON cru.c_id = course.id
7900
                    JOIN access_url_rel_user auru on cru.user_id = auru.user_id
7901
                    JOIN access_url_rel_course ON course.id = access_url_rel_course.c_id
7902
                WHERE access_url_rel_course.access_url_id = $currentAccessUrlId
7903
                ORDER BY course.code
7904
        ";
7905
7906
        $res = Database::query($sql);
7907
        if (Database::num_rows($res) > 0) {
7908
            while ($row = Database::fetch_array($res)) {
7909
                if (!isset($courses[$row['code']])) {
7910
                    $courses[$row['code']] = [
7911
                        'subscribed' => 0,
7912
                        'finished' => 0,
7913
                    ];
7914
                }
7915
7916
                $courses[$row['code']]['subscribed']++;
7917
                $entityManager = Database::getManager();
7918
                $repository = $entityManager->getRepository('ChamiloCoreBundle:GradebookCategory');
7919
                //todo check when have more than 1 gradebook
7920
                /** @var \Chamilo\CoreBundle\Entity\GradebookCategory $gradebook */
7921
                $gradebook = $repository->findOneBy(['courseCode' => $row['code']]);
7922
7923
                if (!empty($gradebook)) {
7924
                    $finished = 0;
7925
                    $gb = Category::createCategoryObjectFromEntity($gradebook);
7926
                    $finished = $gb->is_certificate_available($row['user_id']);
7927
                    if (!empty($finished)) {
7928
                        $courses[$row['code']]['finished']++;
7929
                    }
7930
                }
7931
            }
7932
        }
7933
7934
        return $courses;
7935
    }
7936
7937
    /**
7938
     * Count users in sessions and if they have certificate.
7939
     * This function is resource intensive.
7940
     *
7941
     * @return array
7942
     */
7943
    public static function countUsersWhoFinishedCoursesInSessions()
7944
    {
7945
        $coursesInSessions = [];
7946
        $currentAccessUrlId = api_get_current_access_url_id();
7947
        $sql = "SELECT course.code, srcru.session_id, srcru.user_id, session.name
7948
                FROM session_rel_course_rel_user srcru
7949
                    JOIN course ON srcru.c_id = course.id
7950
                    JOIN access_url_rel_session aurs on srcru.session_id = aurs.session_id
7951
                    JOIN session ON srcru.session_id = session.id
7952
                WHERE aurs.access_url_id = $currentAccessUrlId
7953
                ORDER BY course.code, session.name
7954
        ";
7955
7956
        $res = Database::query($sql);
7957
        if (Database::num_rows($res) > 0) {
7958
            while ($row = Database::fetch_array($res)) {
7959
                $index = $row['code'].' ('.$row['name'].')';
7960
                if (!isset($coursesInSessions[$index])) {
7961
                    $coursesInSessions[$index] = [
7962
                        'subscribed' => 0,
7963
                        'finished' => 0,
7964
                    ];
7965
                }
7966
7967
                $coursesInSessions[$index]['subscribed']++;
7968
                $entityManager = Database::getManager();
7969
                $repository = $entityManager->getRepository('ChamiloCoreBundle:GradebookCategory');
7970
                /** @var \Chamilo\CoreBundle\Entity\GradebookCategory $gradebook */
7971
                $gradebook = $repository->findOneBy(
7972
                    [
7973
                        'courseCode' => $row['code'],
7974
                        'sessionId' => $row['session_id'],
7975
                    ]
7976
                );
7977
7978
                if (!empty($gradebook)) {
7979
                    $finished = 0;
7980
                    $gb = Category::createCategoryObjectFromEntity($gradebook);
7981
                    $finished = $gb->is_certificate_available($row['user_id']);
7982
                    if (!empty($finished)) {
7983
                        $coursesInSessions[$index]['finished']++;
7984
                    }
7985
                }
7986
            }
7987
        }
7988
7989
        return $coursesInSessions;
7990
    }
7991
7992
    /**
7993
     * Build the active-column of the table to lock or unlock a certain user
7994
     * lock = the user can no longer use this account.
7995
     *
7996
     * @author Patrick Cool <[email protected]>, Ghent University
7997
     *
7998
     * @return string Some HTML-code with the lock/unlock button
7999
     */
8000
    public static function getActiveFilterForTable(string $active, string $params, array $row): string
8001
    {
8002
        if ('1' == $active) {
8003
            $action = 'Lock';
8004
            $image = 'accept';
8005
        } elseif ('-1' == $active) {
8006
            $action = 'edit';
8007
            $image = 'warning';
8008
        } elseif ('0' == $active) {
8009
            $action = 'Unlock';
8010
            $image = 'error';
8011
        }
8012
8013
        if ('edit' === $action) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $action does not seem to be defined for all execution paths leading up to this point.
Loading history...
8014
            $langAccountExpired = get_lang('AccountExpired');
8015
8016
            return Display::return_icon(
8017
                $image.'.png',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $image does not seem to be defined for all execution paths leading up to this point.
Loading history...
8018
                    $langAccountExpired,
8019
                [],
8020
                ICON_SIZE_TINY
8021
            ).'<span class="sr-only" aria-hidden="true">'.$langAccountExpired.'</span>';
8022
        }
8023
8024
        if ($row['0'] != api_get_user_id()) {
8025
            $langAction = get_lang(ucfirst($action));
8026
            // you cannot lock yourself out otherwise you could disable all the
8027
            // accounts including your own => everybody is locked out and nobody
8028
            // can change it anymore.
8029
            return Display::return_icon(
8030
                $image.'.png',
8031
                $langAction,
8032
                ['onclick' => 'active_user(this);', 'id' => 'img_'.$row['0'], 'style' => 'cursor: pointer;'],
8033
                ICON_SIZE_TINY
8034
                ).'<span class="sr-only" aria-hidden="true">'.$langAction.'</span>';
8035
        }
8036
8037
        return '';
8038
    }
8039
8040
    public static function getScriptFunctionForActiveFilter(): string
8041
    {
8042
        return 'function active_user(element_div) {
8043
            id_image = $(element_div).attr("id");
8044
            image_clicked = $(element_div).attr("src");
8045
            image_clicked_info = image_clicked.split("/");
8046
            image_real_clicked = image_clicked_info[image_clicked_info.length-1];
8047
            var status = 1;
8048
            if (image_real_clicked == "accept.png") {
8049
                status = 0;
8050
            }
8051
            user_id = id_image.split("_");
8052
            ident = "#img_"+user_id[1];
8053
            if (confirm("'.get_lang('AreYouSureToEditTheUserStatus', '').'")) {
8054
                 $.ajax({
8055
                    contentType: "application/x-www-form-urlencoded",
8056
                    beforeSend: function(myObject) {
8057
                        $(ident).attr("src","'.Display::returnIconPath('loading1.gif').'"); //candy eye stuff
8058
                    },
8059
                    type: "GET",
8060
                    url: _p.web_ajax + "user_manager.ajax.php?a=active_user",
8061
                    data: "user_id=" + user_id[1] + "&status=" + status,
8062
                    success: function(data) {
8063
                        if (data == 1) {
8064
                            $(ident).attr("src", "'.Display::returnIconPath('accept.png', ICON_SIZE_TINY).'");
8065
                            $(ident).attr("title","'.get_lang('Lock').'");
8066
                        }
8067
                        if (data == 0) {
8068
                            $(ident).attr("src","'.Display::returnIconPath('error.png').'");
8069
                            $(ident).attr("title","'.get_lang('Unlock').'");
8070
                        }
8071
                        if (data == -1) {
8072
                            $(ident).attr("src", "'.Display::returnIconPath('warning.png').'");
8073
                            $(ident).attr("title","'.get_lang('ActionNotAllowed').'");
8074
                        }
8075
                    }
8076
                });
8077
            }
8078
        }';
8079
    }
8080
8081
    /**
8082
     * Get a list of users with the given e-mail address + their "active" field value (0 or 1).
8083
     *
8084
     * @param string $mail User id
8085
     *
8086
     * @return array List of users e-mails + active field
8087
     */
8088
    public static function getUsersByMail(string $mail): array
8089
    {
8090
        $resultData = Database::select(
8091
            'id, active',
8092
            Database::get_main_table(TABLE_MAIN_USER),
8093
            [
8094
                'where' => ['email = ?' => $mail],
8095
            ],
8096
            'all',
8097
            null
8098
        );
8099
8100
        if ($resultData === false) {
8101
            return [];
8102
        }
8103
8104
        return $resultData;
8105
    }
8106
8107
    /**
8108
     * Get whether we can send an e-mail or not.
8109
     * If the e-mail is not in the database, send the mail.
8110
     * If the e-mail is in the database but none of its occurences is active, don't send.
8111
     *
8112
     * @param string $mail The e-mail address to check
8113
     *
8114
     * @return bool Whether we can send an e-mail or not
8115
     */
8116
    public function isEmailingAllowed(string $mail): bool
8117
    {
8118
        $list = self::getUsersByMail($mail);
8119
        if (empty($list)) {
8120
            // No e-mail matches, send the mail
8121
            return true;
8122
        }
8123
        $send = false;
8124
        foreach ($list as $id => $user) {
8125
            if ($user['active'] == 1) {
8126
                // as soon as we find at least one active user, send the mail
8127
                return true;
8128
            }
8129
        }
8130
8131
        return false;
8132
    }
8133
8134
    /**
8135
     * @return EncoderFactory
8136
     */
8137
    private static function getEncoderFactory()
8138
    {
8139
        $encryption = self::getPasswordEncryption();
8140
        $encoders = [
8141
            'Chamilo\\UserBundle\\Entity\\User' => new \Chamilo\UserBundle\Security\Encoder($encryption),
8142
        ];
8143
8144
        return new EncoderFactory($encoders);
8145
    }
8146
8147
    /**
8148
     * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface
8149
     */
8150
    private static function getEncoder(User $user)
8151
    {
8152
        $encoderFactory = self::getEncoderFactory();
8153
8154
        return $encoderFactory->getEncoder($user);
8155
    }
8156
8157
    /**
8158
     * Disables or enables a user.
8159
     *
8160
     * @param int $user_id
8161
     * @param int $active  Enable or disable
8162
     *
8163
     * @return bool True on success, false on failure
8164
     * @assert (-1,0) === false
8165
     * @assert (1,1) === true
8166
     */
8167
    private static function change_active_state($user_id, $active)
8168
    {
8169
        $user_id = (int) $user_id;
8170
        $active = (int) $active;
8171
8172
        if (empty($user_id)) {
8173
            return false;
8174
        }
8175
8176
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
8177
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
8178
        $r = Database::query($sql);
8179
        $ev = LOG_USER_DISABLE;
8180
        if ($active == 1) {
8181
            $ev = LOG_USER_ENABLE;
8182
        }
8183
        if ($r !== false) {
8184
            Event::addEvent($ev, LOG_USER_ID, $user_id);
8185
        }
8186
8187
        return $r;
8188
    }
8189
8190
    /**
8191
     * Get either a Gravatar URL or complete image tag for a specified email address.
8192
     *
8193
     * @param string $email The email address
8194
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
8195
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
8196
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
8197
     * @param bool   $img   True to return a complete IMG tag False for just the URL
8198
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
8199
     *
8200
     * @return string containing either just a URL or a complete image tag
8201
     * @source http://gravatar.com/site/implement/images/php/
8202
     */
8203
    private static function getGravatar(
8204
        $email,
8205
        $s = 80,
8206
        $d = 'mm',
8207
        $r = 'g',
8208
        $img = false,
8209
        $atts = []
8210
    ) {
8211
        $url = 'http://www.gravatar.com/avatar/';
8212
        if (!empty($_SERVER['HTTPS'])) {
8213
            $url = 'https://secure.gravatar.com/avatar/';
8214
        }
8215
        $url .= md5(strtolower(trim($email)));
8216
        $url .= "?s=$s&d=$d&r=$r";
8217
        if ($img) {
8218
            $url = '<img src="'.$url.'"';
8219
            foreach ($atts as $key => $val) {
8220
                $url .= ' '.$key.'="'.$val.'"';
8221
            }
8222
            $url .= ' />';
8223
        }
8224
8225
        return $url;
8226
    }
8227
8228
   /**
8229
    * return user hash based on user_id and loggedin user's salt
8230
    *
8231
    * @param int user_id id of the user for whom we need the hash
8232
    *
8233
    * @return string containing the hash
8234
    */
8235
    public static function generateUserHash(int $user_id): string
8236
    {
8237
        $currentUserId = api_get_user_id();
8238
        $userManager = self::getManager();
8239
        /** @var User $user */
8240
        $user = self::getRepository()->find($currentUserId);
8241
        if (empty($user)) {
8242
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the type-hinted return string.
Loading history...
8243
        }
8244
        return rawurlencode(api_encrypt_hash($user_id, $user->getSalt()));
8245
    }
8246
8247
   /**
8248
    * return decrypted hash or false
8249
    *
8250
    * @param string hash    hash that is to be decrypted
8251
    *
8252
    * @return string
8253
    */
8254
    public static function decryptUserHash(string $hash): string
8255
    {
8256
        $currentUserId = api_get_user_id();
8257
        $userManager = self::getManager();
8258
        /** @var User $user */
8259
        $user = self::getRepository()->find($currentUserId);
8260
        if (empty($user)) {
8261
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the type-hinted return string.
Loading history...
8262
        }
8263
        return api_decrypt_hash(rawurldecode($hash), $user->getSalt());
8264
    }
8265
}
8266