Completed
Push — master ( 27e209...a08afa )
by Julito
186:04 queued 150:53
created

UserManager::get_user_path_certificate()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 18
nc 6
nop 1
dl 0
loc 26
rs 8.439
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\UserBundle\Entity\Repository\UserRepository;
5
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
6
use Chamilo\UserBundle\Entity\User;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, User. 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...
7
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...
8
use Chamilo\CoreBundle\Entity\SkillRelUserComment;
9
use Chamilo\CoreBundle\Entity\Repository\AccessUrlRepository;
10
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
11
use ChamiloSession as Session;
12
13
/**
14
 *
15
 * Class UserManager
16
 *
17
 * This library provides functions for user management.
18
 * Include/require it in your code to use its functionality.
19
 * @package chamilo.library
20
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
21
 *
22
 */
23
class UserManager
24
{
25
    // This constants are deprecated use the constants located in ExtraField
26
    const USER_FIELD_TYPE_TEXT = 1;
27
    const USER_FIELD_TYPE_TEXTAREA = 2;
28
    const USER_FIELD_TYPE_RADIO = 3;
29
    const USER_FIELD_TYPE_SELECT = 4;
30
    const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
31
    const USER_FIELD_TYPE_DATE = 6;
32
    const USER_FIELD_TYPE_DATETIME = 7;
33
    const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
34
    const USER_FIELD_TYPE_DIVIDER = 9;
35
    const USER_FIELD_TYPE_TAG = 10;
36
    const USER_FIELD_TYPE_TIMEZONE = 11;
37
    const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
38
    const USER_FIELD_TYPE_FILE = 13;
39
    const USER_FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
40
41
    private static $encryptionMethod;
42
43
    /**
44
     * Constructor
45
     * @assert () === null
46
     */
47
    public function __construct()
48
    {
49
    }
50
51
    /**
52
     * Repository is use to query the DB, selects, etc
53
     * @return UserRepository
54
     */
55
    public static function getRepository()
56
    {
57
        return Database::getManager()->getRepository('ChamiloUserBundle:User');
58
    }
59
60
    /**
61
     * Create/update/delete methods are available in the UserManager
62
     * (based in the Sonata\UserBundle\Entity\UserManager)
63
     *
64
     * @return Chamilo\UserBundle\Entity\Manager\UserManager
65
     */
66
    public static function getManager()
67
    {
68
        static $userManager;
69
70
        if (!isset($userManager)) {
71
            $encoderFactory = self::getEncoderFactory();
72
            $passwordUpdater = new FOS\UserBundle\Util\PasswordUpdater($encoderFactory);
73
            $canonicalUpdater = new FOS\UserBundle\Util\CanonicalFieldsUpdater(
74
                new \FOS\UserBundle\Util\Canonicalizer(),
75
                new \FOS\UserBundle\Util\Canonicalizer()
76
            );
77
78
            $userManager = new Chamilo\UserBundle\Entity\Manager\UserManager(
0 ignored issues
show
Bug introduced by
The call to Chamilo\UserBundle\Entit...rManager::__construct() has too few arguments starting with class. ( Ignorable by Annotation )

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

78
            $userManager = /** @scrutinizer ignore-call */ new Chamilo\UserBundle\Entity\Manager\UserManager(

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
79
                $passwordUpdater,
80
                $canonicalUpdater,
81
                Database::getManager(),
82
                User::class
83
            );
84
        }
85
86
        return $userManager;
87
    }
88
89
    /**
90
     * @param string $encryptionMethod
91
     */
92
    public static function setPasswordEncryption($encryptionMethod)
93
    {
94
        self::$encryptionMethod = $encryptionMethod;
95
    }
96
97
    /**
98
     * @return bool|mixed
99
     */
100
    public static function getPasswordEncryption()
101
    {
102
        $encryptionMethod = self::$encryptionMethod;
103
        if (empty($encryptionMethod)) {
104
            $encryptionMethod = api_get_configuration_value('password_encryption');
105
        }
106
107
        return $encryptionMethod;
108
    }
109
110
    /**
111
     * @return EncoderFactory
112
     */
113
    private static function getEncoderFactory()
114
    {
115
        $encryption = self::getPasswordEncryption();
116
        $encoders = [
117
            'Chamilo\\UserBundle\\Entity\\User' => new \Chamilo\UserBundle\Security\Encoder($encryption)
118
        ];
119
120
        $encoderFactory = new EncoderFactory($encoders);
121
122
        return $encoderFactory;
123
    }
124
125
    /**
126
     * @param User $user
127
     *
128
     * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface
129
     */
130
    private static function getEncoder(User $user)
131
    {
132
        $encoderFactory = self::getEncoderFactory();
133
134
        return $encoderFactory->getEncoder($user);
135
    }
136
137
    /**
138
     * Validates the password
139
     *
140
     * @param $encoded
141
     * @param $raw
142
     * @param $salt
143
     * @return bool
144
     */
145
    public static function isPasswordValid($encoded, $raw, $salt)
146
    {
147
        $encoder = new \Chamilo\UserBundle\Security\Encoder(self::getPasswordEncryption());
148
        $validPassword = $encoder->isPasswordValid($encoded, $raw, $salt);
149
150
        return $validPassword;
151
    }
152
153
    /**
154
     * @param string $raw
155
     * @param User   $user
156
     *
157
     * @return string
158
     */
159
    public static function encryptPassword($raw, User $user)
160
    {
161
        $encoder = self::getEncoder($user);
162
        $encodedPassword = $encoder->encodePassword(
163
            $raw,
164
            $user->getSalt()
165
        );
166
167
        return $encodedPassword;
168
    }
169
170
    /**
171
     * @param int $userId
172
     * @param string $password
173
     *
174
     */
175
    public static function updatePassword($userId, $password)
176
    {
177
        $repository = self::getRepository();
178
        /** @var User $user */
179
        $user = $repository->find($userId);
180
        $userManager = self::getManager();
181
        $user->setPlainPassword($password);
182
        $userManager->updateUser($user, true);
183
    }
184
185
    /**
186
     * Creates a new user for the platform
187
     * @author Hugues Peeters <[email protected]>,
188
     * @author Roan Embrechts <[email protected]>
189
     * @param  string $firstName
190
     * @param  string $lastName
191
     * @param  int    $status (1 for course tutor, 5 for student, 6 for anonymous)
192
     * @param  string $email
193
     * @param  string $loginName
194
     * @param  string $password
195
     * @param  string $official_code Any official code (optional)
196
     * @param  string $language User language    (optional)
197
     * @param  string $phone Phone number    (optional)
198
     * @param  string $picture_uri Picture URI        (optional)
199
     * @param  string $authSource Authentication source    (optional, defaults to 'platform', dependind on constant)
200
     * @param  string $expirationDate Account expiration date (optional, defaults to null)
201
     * @param  int    $active Whether the account is enabled or disabled by default
202
     * @param  int    $hr_dept_id The department of HR in which the user is registered (optional, defaults to 0)
203
     * @param  array  $extra    Extra fields
204
     * @param  string $encrypt_method Encrypt method used if password is given encrypted. Set to an empty string by default
205
     * @param  bool $send_mail
206
     * @param  bool $isAdmin
207
     * @param  string $address
208
     * @param  bool $sendEmailToAllAdmins
209
     * @param FormValidator $form
210
     * @param int $creatorId
211
     *
212
     * @return mixed   new user id - if the new user creation succeeds, false otherwise
213
     * @desc The function tries to retrieve user id from the session.
214
     * If it exists, the current user id is the creator id. If a problem arises,
215
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
216
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
217
     */
218
    public static function create_user(
219
        $firstName,
220
        $lastName,
221
        $status,
222
        $email,
223
        $loginName,
224
        $password,
225
        $official_code = '',
226
        $language = '',
227
        $phone = '',
228
        $picture_uri = '',
229
        $authSource = PLATFORM_AUTH_SOURCE,
230
        $expirationDate = null,
231
        $active = 1,
232
        $hr_dept_id = 0,
233
        $extra = [],
234
        $encrypt_method = '',
0 ignored issues
show
Unused Code introduced by
The parameter $encrypt_method is not used and could be removed. ( Ignorable by Annotation )

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

234
        /** @scrutinizer ignore-unused */ $encrypt_method = '',

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
235
        $send_mail = false,
236
        $isAdmin = false,
237
        $address = '',
238
        $sendEmailToAllAdmins = false,
239
        $form = null,
240
        $creatorId = 0
241
    ) {
242
        $creatorId = empty($creatorId) ? api_get_user_id() : 0;
243
        $hook = HookCreateUser::create();
244
        if (!empty($hook)) {
245
            $hook->notifyCreateUser(HOOK_EVENT_TYPE_PRE);
246
        }
247
248
        // First check wether the login already exists
249
        if (!self::is_username_available($loginName)) {
250
            Display::addFlash(
251
                Display::return_message(get_lang('LoginAlreadyTaken'))
252
            );
253
254
            return false;
255
        }
256
257
        global $_configuration;
258
        $original_password = $password;
259
260
        $access_url_id = 1;
261
        if (api_get_multiple_access_url()) {
262
            $access_url_id = api_get_current_access_url_id();
263
        } else {
264
            // In some cases, the first access_url ID might be different from 1
265
            // for example when using a DB cluster or hacking the DB manually.
266
            // In this case, we want the first row, not necessarily "1".
267
            $dbm = Database::getManager();
268
            /** @var AccessUrlRepository $accessUrlRepository */
269
            $accessUrlRepository = $dbm->getRepository('ChamiloCoreBundle:AccessUrl');
270
            $accessUrl = $accessUrlRepository->getFirstId();
271
            if (!empty($accessUrl[0]) && !empty($accessUrl[0][1])) {
272
                $access_url_id = $accessUrl[0][1];
273
            }
274
        }
275
276
        if (isset($_configuration[$access_url_id]) &&
277
            is_array($_configuration[$access_url_id]) &&
278
            isset($_configuration[$access_url_id]['hosting_limit_users']) &&
279
            $_configuration[$access_url_id]['hosting_limit_users'] > 0) {
280
            $num = self::get_number_of_users();
281
            if ($num >= $_configuration[$access_url_id]['hosting_limit_users']) {
282
                api_warn_hosting_contact('hosting_limit_users');
283
                Display::addFlash(
284
                    Display::return_message(
285
                        get_lang('PortalUsersLimitReached'),
286
                        'warning'
287
                    )
288
                );
289
290
                return false;
291
            }
292
        }
293
294
        if ($status === 1 &&
295
            isset($_configuration[$access_url_id]) &&
296
            is_array($_configuration[$access_url_id]) &&
297
            isset($_configuration[$access_url_id]['hosting_limit_teachers']) &&
298
            $_configuration[$access_url_id]['hosting_limit_teachers'] > 0
299
        ) {
300
            $num = self::get_number_of_users(1);
301
            if ($num >= $_configuration[$access_url_id]['hosting_limit_teachers']) {
302
                Display::addFlash(
303
                    Display::return_message(
304
                        get_lang('PortalTeachersLimitReached'),
305
                        'warning'
306
                    )
307
                );
308
                api_warn_hosting_contact('hosting_limit_teachers');
309
310
                return false;
311
            }
312
        }
313
314
        if (empty($password)) {
315
            if ($authSource === PLATFORM_AUTH_SOURCE) {
316
                Display::addFlash(
317
                    Display::return_message(
318
                        get_lang('ThisFieldIsRequired').': '.get_lang(
319
                            'Password'
320
                        ),
321
                        'warning'
322
                    )
323
                );
324
325
                return false;
326
            }
327
328
            // We use the authSource as password.
329
            // The real validation will be by processed by the auth
330
            // source not Chamilo
331
            $password = $authSource;
332
        }
333
334
        // database table definition
335
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
336
337
        // Checking the user language
338
        $languages = api_get_languages();
339
        $language = strtolower($language);
340
341
        if (isset($languages['folder'])) {
342
            if (!in_array($language, $languages['folder'])) {
343
                $language = api_get_setting('platformLanguage');
344
            }
345
        }
346
347
        $currentDate = api_get_utc_datetime();
348
        $now = new DateTime();
349
350
        if (empty($expirationDate) || $expirationDate == '0000-00-00 00:00:00') {
351
            // Default expiration date
352
            // if there is a default duration of a valid account then
353
            // we have to change the expiration_date accordingly
354
            // Accept 0000-00-00 00:00:00 as a null value to avoid issues with
355
            // third party code using this method with the previous (pre-1.10)
356
            // value of 0000...
357
            if (api_get_setting('account_valid_duration') != '') {
358
                $expirationDate = new DateTime($currentDate);
359
                $days = intval(api_get_setting('account_valid_duration'));
360
                $expirationDate->modify('+'.$days.' day');
361
            }
362
        } else {
363
            $expirationDate = api_get_utc_datetime($expirationDate);
364
            $expirationDate = new \DateTime($expirationDate, new DateTimeZone('UTC'));
365
        }
366
367
        $userManager = self::getManager();
368
369
        /** @var User $user */
370
        $user = $userManager->createUser();
371
372
        $user
373
            ->setLastname($lastName)
374
            ->setFirstname($firstName)
375
            ->setUsername($loginName)
376
            ->setStatus($status)
377
            ->setPlainPassword($password)
378
            ->setEmail($email)
379
            ->setOfficialCode($official_code)
380
            ->setPictureUri($picture_uri)
381
            ->setCreatorId($creatorId)
382
            ->setAuthSource($authSource)
383
            ->setPhone($phone)
384
            ->setAddress($address)
385
            ->setLanguage($language)
386
            ->setRegistrationDate($now)
387
            ->setHrDeptId($hr_dept_id)
388
            ->setActive($active)
389
            ->setEnabled($active)
390
        ;
391
392
        if (!empty($expirationDate)) {
393
            $user->setExpirationDate($expirationDate);
0 ignored issues
show
Bug introduced by
It seems like $expirationDate can also be of type string; however, parameter $expirationDate of Chamilo\UserBundle\Entit...er::setExpirationDate() does only seem to accept DateTime, maybe add an additional type check? ( Ignorable by Annotation )

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

393
            $user->setExpirationDate(/** @scrutinizer ignore-type */ $expirationDate);
Loading history...
394
        }
395
396
        $userManager->updateUser($user);
397
        $userId = $user->getId();
398
399
        if (!empty($userId)) {
400
            $return = $userId;
401
            $sql = "UPDATE $table_user SET user_id = $return WHERE id = $return";
402
            Database::query($sql);
403
404
            if ($isAdmin) {
405
                self::add_user_as_admin($user);
406
            }
407
408
            if (api_get_multiple_access_url()) {
409
                UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
410
            } else {
411
                //we are adding by default the access_url_user table with access_url_id = 1
412
                UrlManager::add_user_to_url($userId, 1);
413
            }
414
415
            $extra['item_id'] = $userId;
416
417
            if (is_array($extra) && count($extra) > 0) {
418
                $courseFieldValue = new ExtraFieldValue('user');
419
                $courseFieldValue->saveFieldValues($extra);
420
            } else {
421
                // Create notify settings by default
422
                self::update_extra_field_value(
423
                    $userId,
424
                    'mail_notify_invitation',
425
                    '1'
426
                );
427
                self::update_extra_field_value(
428
                    $userId,
429
                    'mail_notify_message',
430
                    '1'
431
                );
432
                self::update_extra_field_value(
433
                    $userId,
434
                    'mail_notify_group_message',
435
                    '1'
436
                );
437
            }
438
439
            self::update_extra_field_value(
440
                $userId,
441
                'already_logged_in',
442
                'false'
443
            );
444
445
            if (!empty($email) && $send_mail) {
446
                $recipient_name = api_get_person_name(
447
                    $firstName,
448
                    $lastName,
449
                    null,
450
                    PERSON_NAME_EMAIL_ADDRESS
451
                );
452
                $tplSubject = new Template(
453
                    null,
454
                    false,
455
                    false,
456
                    false,
457
                    false,
458
                    false
459
                );
460
                $layoutSubject = $tplSubject->get_template(
461
                    'mail/subject_registration_platform.tpl'
462
                );
463
                $emailSubject = $tplSubject->fetch($layoutSubject);
464
                $sender_name = api_get_person_name(
465
                    api_get_setting('administratorName'),
466
                    api_get_setting('administratorSurname'),
467
                    null,
468
                    PERSON_NAME_EMAIL_ADDRESS
469
                );
470
                $email_admin = api_get_setting('emailAdministrator');
471
472
                $url = api_get_path(WEB_PATH);
473
                if (api_is_multiple_url_enabled()) {
474
                    $access_url_id = api_get_current_access_url_id();
475
                    if ($access_url_id != -1) {
476
                        $urlInfo = api_get_access_url($access_url_id);
477
                        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...
478
                            $url = $urlInfo['url'];
479
                        }
480
                    }
481
                }
482
483
                $tplContent = new Template(
484
                    null,
485
                    false,
486
                    false,
487
                    false,
488
                    false,
489
                    false
490
                );
491
                // variables for the default template
492
                $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
493
                $tplContent->assign('login_name', $loginName);
494
                $tplContent->assign('original_password', stripslashes($original_password));
495
                $tplContent->assign('mailWebPath', $url);
496
                $tplContent->assign('new_user', $user);
497
498
                $layoutContent = $tplContent->get_template('mail/content_registration_platform.tpl');
499
                $emailBody = $tplContent->fetch($layoutContent);
500
501
                /* MANAGE EVENT WITH MAIL */
502
                if (EventsMail::check_if_using_class('user_registration')) {
503
                    $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...
504
                    $values["password"] = $original_password;
505
                    $values["send_to"] = [$return];
506
                    $values["prior_lang"] = null;
507
                    EventsDispatcher::events('user_registration', $values);
508
                } else {
509
                    $phoneNumber = isset($extra['mobile_phone_number']) ? $extra['mobile_phone_number'] : null;
510
511
                    $additionalParameters = [
512
                        'smsType' => SmsPlugin::WELCOME_LOGIN_PASSWORD,
513
                        'userId' => $return,
514
                        'mobilePhoneNumber' => $phoneNumber,
515
                        'password' => $original_password
516
                    ];
517
518
                    api_mail_html(
519
                        $recipient_name,
520
                        $email,
521
                        $emailSubject,
522
                        $emailBody,
523
                        $sender_name,
524
                        $email_admin,
525
                        null,
526
                        null,
527
                        null,
528
                        $additionalParameters
529
                    );
530
531
                    $notification = api_get_configuration_value('send_notification_when_user_added');
532
                    if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
533
                        foreach ($notification['admins'] as $adminId) {
534
                            $emailSubjectToAdmin = get_lang('UserAdded').': '.api_get_person_name($firstName, $lastName);
535
                            MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
536
                        }
537
                    }
538
                }
539
540
                if ($sendEmailToAllAdmins) {
541
                    $adminList = self::get_all_administrators();
542
543
                    $tplContent = new Template(
544
                        null,
545
                        false,
546
                        false,
547
                        false,
548
                        false,
549
                        false
550
                    );
551
                    // variables for the default template
552
                    $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
553
                    $tplContent->assign('user_added', $user);
554
555
                    $renderer = FormValidator::getDefaultRenderer();
556
557
                    // Form template
558
                    $elementTemplate = ' {label}: {element} <br />';
559
                    $renderer->setElementTemplate($elementTemplate);
560
                    /** @var FormValidator $form */
561
                    $form->freeze(null, $elementTemplate);
562
                    $form->removeElement('submit');
563
                    $formData = $form->returnForm();
564
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
565
                    $tplContent->assign('link', Display::url($url, $url));
566
                    $tplContent->assign('form', $formData);
567
568
                    $layoutContent = $tplContent->get_template('mail/content_registration_platform_to_admin.tpl');
569
                    $emailBody = $tplContent->fetch($layoutContent);
570
                    $subject = get_lang('UserAdded');
571
572
                    foreach ($adminList as $adminId => $data) {
573
                        MessageManager::send_message_simple(
574
                            $adminId,
575
                            $subject,
576
                            $emailBody,
577
                            $userId
578
                        );
579
                    }
580
                }
581
                /* ENDS MANAGE EVENT WITH MAIL */
582
            }
583
584
            if (!empty($hook)) {
585
                $hook->setEventData([
586
                    'return' => $userId,
587
                    'originalPassword' => $original_password
588
                ]);
589
                $hook->notifyCreateUser(HOOK_EVENT_TYPE_POST);
590
            }
591
            Event::addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId);
592
        } else {
593
            Display::addFlash(
594
                Display::return_message(get_lang('ErrorContactPlatformAdmin'))
595
            );
596
597
            return false;
598
        }
599
600
        return $return;
601
    }
602
603
    /**
604
     * Can user be deleted? This function checks whether there's a course
605
     * in which the given user is the
606
     * only course administrator. If that is the case, the user can't be
607
     * deleted because the course would remain without a course admin.
608
     * @param int $user_id The user id
609
     * @return boolean true if user can be deleted
610
     * @assert (null) === false
611
     * @assert (-1) === false
612
     * @assert ('abc') === false
613
     */
614
    public static function can_delete_user($user_id)
615
    {
616
        $deny = api_get_configuration_value('deny_delete_users');
617
618
        if ($deny) {
619
            return false;
620
        }
621
622
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
623
        if ($user_id != strval(intval($user_id))) {
624
            return false;
625
        }
626
        if ($user_id === false) {
627
            return false;
628
        }
629
        $sql = "SELECT * FROM $table_course_user
630
                WHERE status = 1 AND user_id = ".$user_id;
631
        $res = Database::query($sql);
632
        while ($course = Database::fetch_object($res)) {
633
            $sql = "SELECT id FROM $table_course_user
634
                    WHERE status=1 AND c_id = ".intval($course->c_id);
635
            $res2 = Database::query($sql);
636
            if (Database::num_rows($res2) == 1) {
637
                return false;
638
            }
639
        }
640
641
        return true;
642
    }
643
644
    /**
645
     * Delete a user from the platform, and all its belongings. This is a
646
     * very dangerous function that should only be accessible by
647
     * super-admins. Other roles should only be able to disable a user,
648
     * which removes access to the platform but doesn't delete anything.
649
     * @param int The ID of th user to be deleted
650
     * @return boolean true if user is successfully deleted, false otherwise
651
     * @assert (null) === false
652
     * @assert ('abc') === false
653
     */
654
    public static function delete_user($user_id)
655
    {
656
        if ($user_id != strval(intval($user_id))) {
657
            return false;
658
        }
659
660
        if ($user_id === false) {
661
            return false;
662
        }
663
664
        if (!self::can_delete_user($user_id)) {
665
            return false;
666
        }
667
668
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
669
        $usergroup_rel_user = Database::get_main_table(TABLE_USERGROUP_REL_USER);
670
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
671
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
672
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
673
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
674
        $table_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
675
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
676
        $table_group = Database::get_course_table(TABLE_GROUP_USER);
677
        $table_work = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
678
679
        // Unsubscribe the user from all groups in all his courses
680
        $sql = "SELECT c.id 
681
                FROM $table_course c 
682
                INNER JOIN $table_course_user cu
683
                ON (c.id = cu.c_id)
684
                WHERE
685
                    cu.user_id = '".$user_id."' AND
686
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
687
                ";
688
689
        $res = Database::query($sql);
690
        while ($course = Database::fetch_object($res)) {
691
            $sql = "DELETE FROM $table_group
692
                    WHERE c_id = {$course->id} AND user_id = $user_id";
693
            Database::query($sql);
694
        }
695
696
        // Unsubscribe user from usergroup_rel_user
697
        $sql = "DELETE FROM $usergroup_rel_user WHERE user_id = '".$user_id."'";
698
        Database::query($sql);
699
700
        // Unsubscribe user from all courses
701
        $sql = "DELETE FROM $table_course_user WHERE user_id = '".$user_id."'";
702
        Database::query($sql);
703
704
        // Unsubscribe user from all courses in sessions
705
        $sql = "DELETE FROM $table_session_course_user WHERE user_id = '".$user_id."'";
706
        Database::query($sql);
707
708
        // If the user was added as a id_coach then set the current admin as coach see BT#
709
        $currentUserId = api_get_user_id();
710
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
711
                WHERE id_coach = '".$user_id."'";
712
        Database::query($sql);
713
714
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
715
                WHERE session_admin_id = '".$user_id."'";
716
        Database::query($sql);
717
718
        // Unsubscribe user from all sessions
719
        $sql = "DELETE FROM $table_session_user
720
                WHERE user_id = '".$user_id."'";
721
        Database::query($sql);
722
723
        // Delete user picture
724
        /* TODO: Logic about api_get_setting('split_users_upload_directory') == 'true'
725
        a user has 4 different sized photos to be deleted. */
726
        $user_info = api_get_user_info($user_id);
727
728
        if (strlen($user_info['picture_uri']) > 0) {
729
            $path = self::getUserPathById($user_id, 'system');
730
            $img_path = $path.$user_info['picture_uri'];
731
            if (file_exists($img_path)) {
732
                unlink($img_path);
733
            }
734
        }
735
736
        // Delete the personal course categories
737
        $course_cat_table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
738
        $sql = "DELETE FROM $course_cat_table WHERE user_id = '".$user_id."'";
739
        Database::query($sql);
740
741
        // Delete user from the admin table
742
        $sql = "DELETE FROM $table_admin WHERE user_id = '".$user_id."'";
743
        Database::query($sql);
744
745
        // Delete the personal agenda-items from this user
746
        $agenda_table = Database::get_main_table(TABLE_PERSONAL_AGENDA);
747
        $sql = "DELETE FROM $agenda_table WHERE user = '".$user_id."'";
748
        Database::query($sql);
749
750
        $gradebook_results_table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_RESULT);
751
        $sql = 'DELETE FROM '.$gradebook_results_table.' WHERE user_id = '.$user_id;
752
        Database::query($sql);
753
754
        $extraFieldValue = new ExtraFieldValue('user');
755
        $extraFieldValue->deleteValuesByItem($user_id);
756
757
        UrlManager::deleteUserFromAllUrls($user_id);
758
759
        if (api_get_setting('allow_social_tool') == 'true') {
760
            $userGroup = new UserGroup();
761
            //Delete user from portal groups
762
            $group_list = $userGroup->get_groups_by_user($user_id);
763
            if (!empty($group_list)) {
764
                foreach ($group_list as $group_id => $data) {
765
                    $userGroup->delete_user_rel_group($user_id, $group_id);
766
                }
767
            }
768
769
            // Delete user from friend lists
770
            SocialManager::remove_user_rel_user($user_id, true);
771
        }
772
773
        // Removing survey invitation
774
        SurveyManager::delete_all_survey_invitations_by_user($user_id);
775
776
        // Delete students works
777
        $sql = "DELETE FROM $table_work WHERE user_id = $user_id AND c_id <> 0";
778
        Database::query($sql);
779
780
        $sql = "UPDATE c_item_property SET to_user_id = NULL
781
                WHERE to_user_id = '".$user_id."'";
782
        Database::query($sql);
783
784
        $sql = "UPDATE c_item_property SET insert_user_id = NULL
785
                WHERE insert_user_id = '".$user_id."'";
786
        Database::query($sql);
787
788
        $sql = "UPDATE c_item_property SET lastedit_user_id = NULL
789
                WHERE lastedit_user_id = '".$user_id."'";
790
        Database::query($sql);
791
792
        // Skills
793
        $em = Database::getManager();
794
795
        $criteria = ['user' => $user_id];
796
        $skills = $em->getRepository('ChamiloCoreBundle:SkillRelUser')->findBy($criteria);
797
        if ($skills) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $skills 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...
798
            /** @var SkillRelUser $skill */
799
            foreach ($skills as $skill) {
800
                $comments = $skill->getComments();
801
                if ($comments) {
802
                    /** @var SkillRelUserComment $comment */
803
                    foreach ($comments as $comment) {
804
                        $em->remove($comment);
805
                    }
806
                }
807
                $em->remove($skill);
808
            }
809
            $em->flush();
810
        }
811
812
        // ExtraFieldSavedSearch
813
        $criteria = ['user' => $user_id];
814
        $searchList = $em->getRepository('ChamiloCoreBundle:ExtraFieldSavedSearch')->findBy($criteria);
815
        if ($searchList) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $searchList 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...
816
            foreach ($searchList as $search) {
817
                $em->remove($search);
818
            }
819
            $em->flush();
820
        }
821
822
        $connection = Database::getManager()->getConnection();
823
        $tableExists = $connection->getSchemaManager()->tablesExist(['plugin_bbb_room']);
824
        if ($tableExists) {
825
            // Delete user from database
826
            $sql = "DELETE FROM plugin_bbb_room WHERE participant_id = $user_id";
827
            Database::query($sql);
828
        }
829
830
        // Delete user/ticket relationships :(
831
        $tableExists = $connection->getSchemaManager()->tablesExist(['ticket_ticket']);
832
        if ($tableExists) {
833
            TicketManager::deleteUserFromTicketSystem($user_id);
834
        }
835
836
        $tableExists = $connection->getSchemaManager()->tablesExist(['c_lp_category_user']);
837
        if ($tableExists) {
838
            $sql = "DELETE FROM c_lp_category_user WHERE user_id = $user_id";
839
            Database::query($sql);
840
        }
841
842
        // Delete user from database
843
        $sql = "DELETE FROM $table_user WHERE id = '".$user_id."'";
844
        Database::query($sql);
845
846
        // Add event to system log
847
        $user_id_manager = api_get_user_id();
848
849
        Event::addEvent(
850
            LOG_USER_DELETE,
851
            LOG_USER_ID,
852
            $user_id,
853
            api_get_utc_datetime(),
854
            $user_id_manager
855
        );
856
857
        Event::addEvent(
858
            LOG_USER_DELETE,
859
            LOG_USER_OBJECT,
860
            $user_info,
861
            api_get_utc_datetime(),
862
            $user_id_manager
863
        );
864
        $cacheAvailable = api_get_configuration_value('apc');
865
        if ($cacheAvailable === true) {
866
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
0 ignored issues
show
Bug introduced by
Are you sure api_get_configuration_value('apc_prefix') of type mixed|false 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

866
            $apcVar = /** @scrutinizer ignore-type */ api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
Loading history...
867
            if (apcu_exists($apcVar)) {
868
                apcu_delete($apcVar);
869
            }
870
        }
871
872
        return true;
873
    }
874
875
    /**
876
     * Deletes users completely. Can be called either as:
877
     * - UserManager::delete_users(1, 2, 3); or
878
     * - UserManager::delete_users(array(1, 2, 3));
879
     * @param array|int $ids
880
     * @return boolean  True if at least one user was successfuly deleted. False otherwise.
881
     * @author Laurent Opprecht
882
     * @uses UserManager::delete_user() to actually delete each user
883
     * @assert (null) === false
884
     * @assert (-1) === false
885
     * @assert (array(-1)) === false
886
     */
887
    public static function delete_users($ids = [])
888
    {
889
        $result = false;
890
        $ids = is_array($ids) ? $ids : func_get_args();
891
        if (!is_array($ids) || count($ids) == 0) {
892
            return false;
893
        }
894
        $ids = array_map('intval', $ids);
895
        foreach ($ids as $id) {
896
            if (empty($id) || $id < 1) {
897
                continue;
898
            }
899
            $deleted = self::delete_user($id);
900
            $result = $deleted || $result;
901
        }
902
903
        return $result;
904
    }
905
906
    /**
907
     * Disable users. Can be called either as:
908
     * - UserManager::deactivate_users(1, 2, 3);
909
     * - UserManager::deactivate_users(array(1, 2, 3));
910
     * @param array|int $ids
911
     * @return boolean
912
     * @author Laurent Opprecht
913
     * @assert (null) === false
914
     * @assert (array(-1)) === false
915
     */
916
    public static function deactivate_users($ids = [])
917
    {
918
        if (empty($ids)) {
919
            return false;
920
        }
921
922
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
923
924
        $ids = is_array($ids) ? $ids : func_get_args();
925
        $ids = array_map('intval', $ids);
926
        $ids = implode(',', $ids);
927
928
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
929
        $r = Database::query($sql);
930
        if ($r !== false) {
931
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
932
        }
933
        return $r;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $r returns the type Doctrine\DBAL\Driver\Statement which is incompatible with the documented return type boolean.
Loading history...
934
    }
935
936
    /**
937
     * Enable users. Can be called either as:
938
     * - UserManager::activate_users(1, 2, 3);
939
     * - UserManager::activate_users(array(1, 2, 3));
940
     * @param array|int IDs of the users to enable
941
     * @return boolean
942
     * @author Laurent Opprecht
943
     * @assert (null) === false
944
     * @assert (array(-1)) === false
945
     */
946
    public static function activate_users($ids = [])
947
    {
948
        if (empty($ids)) {
949
            return false;
950
        }
951
952
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
953
954
        $ids = is_array($ids) ? $ids : func_get_args();
955
        $ids = array_map('intval', $ids);
956
        $ids = implode(',', $ids);
957
958
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
959
        $r = Database::query($sql);
960
        if ($r !== false) {
961
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
962
        }
963
        return $r;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $r returns the type Doctrine\DBAL\Driver\Statement which is incompatible with the documented return type boolean.
Loading history...
964
    }
965
966
    /**
967
     * Update user information with new openid
968
     * @param int $user_id
969
     * @param string $openid
970
     * @return boolean true if the user information was updated
971
     * @assert (false,'') === false
972
     * @assert (-1,'') === false
973
     */
974
    public static function update_openid($user_id, $openid)
975
    {
976
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
977
        if ($user_id != strval(intval($user_id))) {
978
            return false;
979
        }
980
        if ($user_id === false) {
981
            return false;
982
        }
983
        $sql = "UPDATE $table_user SET
984
                openid='".Database::escape_string($openid)."'";
985
        $sql .= " WHERE id= $user_id";
986
987
        return Database::query($sql);
0 ignored issues
show
Bug Best Practice introduced by
The expression return Database::query($sql) returns the type Doctrine\DBAL\Driver\Statement which is incompatible with the documented return type boolean.
Loading history...
988
    }
989
990
    /**
991
     * Update user information with all the parameters passed to this function
992
     * @param int The ID of the user to be updated
993
     * @param string The user's firstname
994
     * @param string The user's lastname
995
     * @param string The user's username (login)
996
     * @param string The user's password
997
     * @param string The authentication source (default: "platform")
998
     * @param string The user's e-mail address
999
     * @param int The user's status
1000
     * @param string The user's official code (usually just an internal institutional code)
1001
     * @param string The user's phone number
1002
     * @param string The user's picture URL (internal to the Chamilo directory)
1003
     * @param int The user ID of the person who registered this user (optional, defaults to null)
1004
     * @param int The department of HR in which the user is registered (optional, defaults to 0)
1005
     * @param array A series of additional fields to add to this user as extra fields (optional, defaults to null)
1006
     * @return boolean|integer False on error, or the user ID if the user information was updated
1007
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
1008
     */
1009
    public static function update_user(
1010
        $user_id,
1011
        $firstname,
1012
        $lastname,
1013
        $username,
1014
        $password = null,
1015
        $auth_source = null,
1016
        $email,
1017
        $status,
1018
        $official_code,
1019
        $phone,
1020
        $picture_uri,
1021
        $expiration_date,
1022
        $active,
1023
        $creator_id = null,
0 ignored issues
show
Unused Code introduced by
The parameter $creator_id is not used and could be removed. ( Ignorable by Annotation )

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

1023
        /** @scrutinizer ignore-unused */ $creator_id = null,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1024
        $hr_dept_id = 0,
1025
        $extra = null,
1026
        $language = 'english',
1027
        $encrypt_method = '',
0 ignored issues
show
Unused Code introduced by
The parameter $encrypt_method is not used and could be removed. ( Ignorable by Annotation )

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

1027
        /** @scrutinizer ignore-unused */ $encrypt_method = '',

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1028
        $send_email = false,
1029
        $reset_password = 0,
1030
        $address = null
1031
    ) {
1032
        $hook = HookUpdateUser::create();
1033
        if (!empty($hook)) {
1034
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_PRE);
1035
        }
1036
        $original_password = $password;
1037
1038
        if ($user_id != strval(intval($user_id))) {
1039
            return false;
1040
        }
1041
1042
        if (empty($user_id)) {
1043
            return false;
1044
        }
1045
1046
        $userManager = self::getManager();
1047
        /** @var Chamilo\UserBundle\Entity\User $user */
1048
        $user = self::getRepository()->find($user_id);
1049
1050
        if (empty($user)) {
1051
            return false;
1052
        }
1053
1054
        if ($reset_password == 0) {
1055
            $password = null;
1056
            $auth_source = $user->getAuthSource();
1057
        } elseif ($reset_password == 1) {
1058
            $original_password = $password = api_generate_password();
1059
            $auth_source = PLATFORM_AUTH_SOURCE;
1060
        } elseif ($reset_password == 2) {
1061
            $password = $password;
1062
            $auth_source = PLATFORM_AUTH_SOURCE;
1063
        } elseif ($reset_password == 3) {
1064
            $password = $password;
1065
            $auth_source = $auth_source;
1066
        }
1067
1068
        // Checking the user language
1069
        $languages = api_get_languages();
1070
        if (!in_array($language, $languages['folder'])) {
1071
            $language = api_get_setting('platformLanguage');
1072
        }
1073
1074
        $change_active = 0;
1075
        $isUserActive = $user->getActive();
1076
        if ($isUserActive != $active) {
1077
            $change_active = 1;
1078
        }
1079
1080
        $originalUsername = $user->getUsername();
1081
1082
        // If username is different from original then check if it exists.
1083
        if ($originalUsername !== $username) {
1084
            $available = self::is_username_available($username);
1085
            if ($available === false) {
1086
                return false;
1087
            }
1088
        }
1089
1090
        if (!empty($expiration_date)) {
1091
            $expiration_date = api_get_utc_datetime($expiration_date);
1092
            $expiration_date = new \DateTime(
1093
                $expiration_date,
1094
                new DateTimeZone('UTC')
1095
            );
1096
        }
1097
1098
        $user
1099
            ->setLastname($lastname)
1100
            ->setFirstname($firstname)
1101
            ->setUsername($username)
1102
            ->setStatus($status)
1103
            ->setAuthSource($auth_source)
1104
            ->setLanguage($language)
1105
            ->setEmail($email)
1106
            ->setOfficialCode($official_code)
1107
            ->setPhone($phone)
1108
            ->setAddress($address)
1109
            ->setPictureUri($picture_uri)
1110
            ->setExpirationDate($expiration_date)
1111
            ->setActive($active)
1112
            ->setEnabled($active)
1113
            ->setHrDeptId($hr_dept_id)
1114
        ;
1115
1116
        if (!is_null($password)) {
1117
            $user->setPlainPassword($password);
1118
        }
1119
1120
        $userManager->updateUser($user, true);
1121
1122
        if ($change_active == 1) {
1123
            if ($active == 1) {
1124
                $event_title = LOG_USER_ENABLE;
1125
            } else {
1126
                $event_title = LOG_USER_DISABLE;
1127
            }
1128
            Event::addEvent($event_title, LOG_USER_ID, $user_id);
1129
        }
1130
1131
        if (is_array($extra) && count($extra) > 0) {
1132
            $res = true;
1133
            foreach ($extra as $fname => $fvalue) {
1134
                $res = $res && self::update_extra_field_value(
1135
                    $user_id,
1136
                    $fname,
1137
                    $fvalue
1138
                );
1139
            }
1140
        }
1141
1142
        if (!empty($email) && $send_email) {
1143
            $recipient_name = api_get_person_name($firstname, $lastname, null, PERSON_NAME_EMAIL_ADDRESS);
1144
            $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('YourReg').' '.api_get_setting('siteName');
1145
            $sender_name = api_get_person_name(
1146
                api_get_setting('administratorName'),
1147
                api_get_setting('administratorSurname'),
1148
                null,
1149
                PERSON_NAME_EMAIL_ADDRESS
1150
            );
1151
            $email_admin = api_get_setting('emailAdministrator');
1152
1153
            if (api_is_multiple_url_enabled()) {
1154
                $access_url_id = api_get_current_access_url_id();
1155
                if ($access_url_id != -1) {
1156
                    $url = api_get_access_url($access_url_id);
1157
                    $emailbody = get_lang('Dear')." ".stripslashes(api_get_person_name($firstname, $lastname)).",\n\n".
1158
                        get_lang('YouAreReg')." ".api_get_setting('siteName')." ".get_lang('WithTheFollowingSettings')."\n\n".
1159
                        get_lang('Username')." : ".$username.(($reset_password > 0) ? "\n".
1160
                        get_lang('Pass')." : ".stripslashes($original_password) : "")."\n\n".
1161
                        get_lang('Address')." ".api_get_setting('siteName')." ".get_lang('Is')." : ".$url['url']."\n\n".
1162
                        get_lang('Problem')."\n\n".
1163
                        get_lang('SignatureFormula').",\n\n".
1164
                        api_get_person_name(api_get_setting('administratorName'), api_get_setting('administratorSurname'))."\n".
1165
                        get_lang('Manager')." ".api_get_setting('siteName')."\nT. ".api_get_setting('administratorTelephone')."\n".
1166
                        get_lang('Email')." : ".api_get_setting('emailAdministrator');
1167
                }
1168
            } else {
1169
                $emailbody = get_lang('Dear')." ".stripslashes(api_get_person_name($firstname, $lastname)).",\n\n".
1170
                    get_lang('YouAreReg')." ".api_get_setting('siteName')." ".get_lang('WithTheFollowingSettings')."\n\n".
1171
                    get_lang('Username')." : ".$username.(($reset_password > 0) ? "\n".
1172
                    get_lang('Pass')." : ".stripslashes($original_password) : "")."\n\n".
1173
                    get_lang('Address')." ".api_get_setting('siteName')." ".get_lang('Is')." : ".api_get_path(WEB_PATH)."\n\n".
1174
                    get_lang('Problem')."\n\n".
1175
                    get_lang('SignatureFormula').",\n\n".
1176
                    api_get_person_name(api_get_setting('administratorName'), api_get_setting('administratorSurname'))."\n".
1177
                    get_lang('Manager')." ".api_get_setting('siteName')."\nT. ".api_get_setting('administratorTelephone')."\n".
1178
                    get_lang('Email')." : ".api_get_setting('emailAdministrator');
1179
            }
1180
1181
            $emailbody = nl2br($emailbody);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $emailbody does not seem to be defined for all execution paths leading up to this point.
Loading history...
1182
            api_mail_html(
1183
                $recipient_name,
1184
                $email,
1185
                $emailsubject,
1186
                $emailbody,
1187
                $sender_name,
1188
                $email_admin
1189
            );
1190
        }
1191
1192
        if (!empty($hook)) {
1193
            $hook->notifyUpdateUser(HOOK_EVENT_TYPE_POST);
1194
        }
1195
1196
        $cacheAvailable = api_get_configuration_value('apc');
1197
        if ($cacheAvailable === true) {
1198
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
0 ignored issues
show
Bug introduced by
Are you sure api_get_configuration_value('apc_prefix') of type mixed|false 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

1198
            $apcVar = /** @scrutinizer ignore-type */ api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
Loading history...
1199
            if (apcu_exists($apcVar)) {
1200
                apcu_delete($apcVar);
1201
            }
1202
        }
1203
1204
        return $user->getId();
1205
    }
1206
1207
    /**
1208
     * Disables or enables a user
1209
     * @param int $user_id
1210
     * @param int $active Enable or disable
1211
     * @return void
1212
     * @assert (-1,0) === false
1213
     * @assert (1,1) === true
1214
     */
1215
    private static function change_active_state($user_id, $active)
1216
    {
1217
        if (strval(intval($user_id)) != $user_id) {
1218
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type void.
Loading history...
1219
        }
1220
        if ($user_id < 1) {
1221
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type void.
Loading history...
1222
        }
1223
        $user_id = intval($user_id);
1224
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1225
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
1226
        $r = Database::query($sql);
1227
        $ev = LOG_USER_DISABLE;
1228
        if ($active == 1) {
1229
            $ev = LOG_USER_ENABLE;
1230
        }
1231
        if ($r !== false) {
1232
            Event::addEvent($ev, LOG_USER_ID, $user_id);
1233
        }
1234
1235
        return $r;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $r returns the type Doctrine\DBAL\Driver\Statement which is incompatible with the documented return type void.
Loading history...
1236
    }
1237
1238
    /**
1239
     * Disables a user
1240
     * @param int User id
1241
     * @return bool
1242
     * @uses UserManager::change_active_state() to actually disable the user
1243
     * @assert (0) === false
1244
     */
1245
    public static function disable($user_id)
1246
    {
1247
        if (empty($user_id)) {
1248
            return false;
1249
        }
1250
        self::change_active_state($user_id, 0);
1251
        return true;
1252
    }
1253
1254
    /**
1255
     * Enable a user
1256
     * @param int User id
1257
     * @return bool
1258
     * @uses UserManager::change_active_state() to actually disable the user
1259
     * @assert (0) === false
1260
     */
1261
    public static function enable($user_id)
1262
    {
1263
        if (empty($user_id)) {
1264
            return false;
1265
        }
1266
        self::change_active_state($user_id, 1);
1267
        return true;
1268
    }
1269
1270
    /**
1271
     * Returns the user's id based on the original id and field name in
1272
     * the extra fields. Returns 0 if no user was found. This function is
1273
     * mostly useful in the context of a web services-based sinchronization
1274
     * @param string Original user id
1275
     * @param string Original field name
1276
     * @return int User id
1277
     * @assert ('0','---') === 0
1278
     */
1279
    public static function get_user_id_from_original_id(
1280
        $original_user_id_value,
1281
        $original_user_id_name
1282
    ) {
1283
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
1284
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1285
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
1286
        $sql = "SELECT item_id as user_id
1287
                FROM $t_uf uf
1288
                INNER JOIN $t_ufv ufv
1289
                ON ufv.field_id=uf.id
1290
                WHERE
1291
                    variable='$original_user_id_name' AND
1292
                    value='$original_user_id_value' AND
1293
                    extra_field_type = $extraFieldType
1294
                ";
1295
        $res = Database::query($sql);
1296
        $row = Database::fetch_object($res);
1297
        if ($row) {
1298
            return $row->user_id;
1299
        } else {
1300
            return 0;
1301
        }
1302
    }
1303
1304
    /**
1305
     * Check if a username is available
1306
     * @param string $username the wanted username
1307
     * @return boolean true if the wanted username is available
1308
     * @assert ('') === false
1309
     * @assert ('xyzxyzxyz') === true
1310
     */
1311
    public static function is_username_available($username)
1312
    {
1313
        if (empty($username)) {
1314
            return false;
1315
        }
1316
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1317
        $sql = "SELECT username FROM $table_user
1318
                WHERE username = '".Database::escape_string($username)."'";
1319
        $res = Database::query($sql);
1320
1321
        return Database::num_rows($res) == 0;
1322
    }
1323
1324
    /**
1325
     * Creates a username using person's names, i.e. creates jmontoya from Julio Montoya.
1326
     * @param string $firstname The first name of the user.
1327
     * @param string $lastname The last name of the user.
1328
     * @return string Suggests a username that contains only ASCII-letters and digits,
1329
     * without check for uniqueness within the system.
1330
     * @author Julio Montoya Armas
1331
     * @author Ivan Tcholakov, 2009 - rework about internationalization.
1332
     * @assert ('','') === false
1333
     * @assert ('a','b') === 'ab'
1334
     */
1335
    public static function create_username($firstname, $lastname)
1336
    {
1337
        if (empty($firstname) && empty($lastname)) {
1338
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
1339
        }
1340
1341
        // The first letter only.
1342
        $firstname = api_substr(
1343
            preg_replace(USERNAME_PURIFIER, '', $firstname),
1344
            0,
1345
            1
1346
        );
1347
        //Looking for a space in the lastname
1348
        $pos = api_strpos($lastname, ' ');
1349
        if ($pos !== false) {
1350
            $lastname = api_substr($lastname, 0, $pos);
1351
        }
1352
1353
        $lastname = preg_replace(USERNAME_PURIFIER, '', $lastname);
1354
        $username = $firstname.$lastname;
1355
        if (empty($username)) {
1356
            $username = 'user';
1357
        }
1358
1359
        $username = URLify::transliterate($username);
1360
1361
        return strtolower(substr($username, 0, USERNAME_MAX_LENGTH - 3));
1362
    }
1363
1364
    /**
1365
     * Creates a unique username, using:
1366
     * 1. the first name and the last name of a user;
1367
     * 2. an already created username but not checked for uniqueness yet.
1368
     * @param string $firstname The first name of a given user. If the second parameter $lastname is NULL, then this
1369
     * parameter is treated as username which is to be checked for uniqueness and to be modified when it is necessary.
1370
     * @param string $lastname                The last name of the user.
1371
     * @return string Returns a username that contains only ASCII-letters and digits and that is unique in the system.
1372
     * Note: When the method is called several times with same parameters,
1373
     * its results look like the following sequence: ivan, ivan2, ivan3, ivan4, ...
1374
     * @author Ivan Tcholakov, 2009
1375
     */
1376
    public static function create_unique_username($firstname, $lastname = null)
1377
    {
1378
        if (is_null($lastname)) {
1379
            // In this case the actual input parameter $firstname should contain ASCII-letters and digits only.
1380
            // For making this method tolerant of mistakes,
1381
            // let us transliterate and purify the suggested input username anyway.
1382
            // So, instead of the sentence $username = $firstname; we place the following:
1383
            $username = strtolower(preg_replace(USERNAME_PURIFIER, '', $firstname));
1384
        } else {
1385
            $username = self::create_username($firstname, $lastname);
1386
        }
1387
        if (!self::is_username_available($username)) {
1388
            $i = 2;
1389
            $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1390
            while (!self::is_username_available($temp_username)) {
1391
                $i++;
1392
                $temp_username = substr($username, 0, USERNAME_MAX_LENGTH - strlen((string) $i)).$i;
1393
            }
1394
            $username = $temp_username;
1395
        }
1396
1397
        $username = URLify::transliterate($username);
1398
1399
        return $username;
1400
    }
1401
1402
    /**
1403
     * Modifies a given username accordingly to the specification for valid characters and length.
1404
     * @param $username string          The input username.
1405
     * @param bool $strict (optional)   When this flag is TRUE, the result is guaranteed for full compliance,
1406
     * otherwise compliance may be partial. The default value is FALSE.
1407
     * @return string                   The resulting purified username.
1408
     */
1409
    public static function purify_username($username, $strict = false)
1410
    {
1411
        if ($strict) {
1412
            // 1. Conversion of unacceptable letters (latinian letters with accents for example)
1413
            // into ASCII letters in order they not to be totally removed.
1414
            // 2. Applying the strict purifier.
1415
            // 3. Length limitation.
1416
            $return = api_get_setting('login_is_email') === 'true' ? substr(preg_replace(USERNAME_PURIFIER_MAIL, '', $username), 0, USERNAME_MAX_LENGTH) : substr(preg_replace(USERNAME_PURIFIER, '', $username), 0, USERNAME_MAX_LENGTH);
1417
            $return = URLify::transliterate($return);
1418
1419
            return $return;
1420
        }
1421
1422
        // 1. Applying the shallow purifier.
1423
        // 2. Length limitation.
1424
        return substr(
1425
            preg_replace(USERNAME_PURIFIER_SHALLOW, '', $username),
1426
            0,
1427
            USERNAME_MAX_LENGTH
1428
        );
1429
    }
1430
1431
    /**
1432
     * Checks whether the user id exists in the database
1433
     *
1434
     * @param int $userId User id
1435
     * @return bool True if user id was found, false otherwise
1436
     */
1437
    public static function is_user_id_valid($userId)
1438
    {
1439
        $resultData = Database::select(
1440
            'COUNT(1) AS count',
1441
            Database::get_main_table(TABLE_MAIN_USER),
1442
            [
1443
                'where' => ['id = ?' => intval($userId)]
1444
            ],
1445
            'first'
1446
        );
1447
1448
        if ($resultData === false) {
1449
            return false;
1450
        }
1451
1452
        return $resultData['count'] > 0;
1453
    }
1454
1455
    /**
1456
     * Checks whether a given username matches to the specification strictly.
1457
     * The empty username is assumed here as invalid.
1458
     * Mostly this function is to be used in the user interface built-in validation routines
1459
     * for providing feedback while usernames are enterd manually.
1460
     * @param string $username The input username.
1461
     * @return bool Returns TRUE if the username is valid, FALSE otherwise.
1462
     */
1463
    public static function is_username_valid($username)
1464
    {
1465
        return !empty($username) && $username == self::purify_username($username, true);
1466
    }
1467
1468
    /**
1469
     * Checks whether a username is empty. If the username contains whitespace characters,
1470
     * such as spaces, tabulators, newlines, etc.,
1471
     * it is assumed as empty too. This function is safe for validation unpurified data (during importing).
1472
     * @param string $username The given username.
1473
     * @return bool  Returns TRUE if length of the username exceeds the limit, FALSE otherwise.
1474
     */
1475
    public static function is_username_empty($username)
1476
    {
1477
        return (strlen(self::purify_username($username, false)) == 0);
1478
    }
1479
1480
    /**
1481
     * Checks whether a username is too long or not.
1482
     * @param string $username The given username, it should contain only ASCII-letters and digits.
1483
     * @return bool Returns TRUE if length of the username exceeds the limit, FALSE otherwise.
1484
     */
1485
    public static function is_username_too_long($username)
1486
    {
1487
        return (strlen($username) > USERNAME_MAX_LENGTH);
1488
    }
1489
1490
    /**
1491
    * Get the users by ID
1492
    * @param array $ids student ids
1493
    * @param string $active
1494
    * @param string $order
1495
    * @param string $limit
1496
    * @return array $result student information
1497
    */
1498
    public static function get_user_list_by_ids($ids = [], $active = null, $order = null, $limit = null)
1499
    {
1500
        if (empty($ids)) {
1501
            return [];
1502
        }
1503
1504
        $ids = is_array($ids) ? $ids : [$ids];
1505
        $ids = array_map('intval', $ids);
1506
        $ids = implode(',', $ids);
1507
1508
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
1509
        $sql = "SELECT * FROM $tbl_user WHERE id IN ($ids)";
1510
        if (!is_null($active)) {
1511
            $sql .= ' AND active='.($active ? '1' : '0');
1512
        }
1513
1514
        if (!is_null($order)) {
1515
            $order = Database::escape_string($order);
1516
            $sql .= ' ORDER BY '.$order;
1517
        }
1518
1519
        if (!is_null($limit)) {
1520
            $limit = Database::escape_string($limit);
1521
            $sql .= ' LIMIT '.$limit;
1522
        }
1523
1524
        $rs = Database::query($sql);
1525
        $result = [];
1526
        while ($row = Database::fetch_array($rs)) {
1527
            $result[] = $row;
1528
        }
1529
1530
        return $result;
1531
    }
1532
1533
    /**
1534
     * Get a list of users of which the given conditions match with an = 'cond'
1535
     * @param array $conditions a list of condition (example : status=>STUDENT)
1536
     * @param array $order_by a list of fields on which sort
1537
     * @return array An array with all users of the platform.
1538
     * @todo security filter order by
1539
     */
1540
    public static function get_user_list(
1541
        $conditions = [],
1542
        $order_by = [],
1543
        $limit_from = false,
1544
        $limit_to = false
1545
    ) {
1546
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1547
        $userUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1548
        $return_array = [];
1549
        $sql = "SELECT user.* FROM $user_table user ";
1550
1551
        if (api_is_multiple_url_enabled()) {
1552
            $urlId = api_get_current_access_url_id();
1553
            $sql .= " INNER JOIN $userUrlTable url_user
1554
                      ON (user.user_id = url_user.user_id)
1555
                      WHERE url_user.access_url_id = $urlId";
1556
        } else {
1557
            $sql .= " WHERE 1=1 ";
1558
        }
1559
1560
        if (count($conditions) > 0) {
1561
            foreach ($conditions as $field => $value) {
1562
                $field = Database::escape_string($field);
1563
                $value = Database::escape_string($value);
1564
                $sql .= " AND $field = '$value'";
1565
            }
1566
        }
1567
1568
        if (count($order_by) > 0) {
1569
            $sql .= ' ORDER BY '.Database::escape_string(implode(',', $order_by), null, false);
1570
        }
1571
1572
        if (is_numeric($limit_from) && is_numeric($limit_from)) {
1573
            $limit_from = intval($limit_from);
1574
            $limit_to = intval($limit_to);
1575
            $sql .= " LIMIT $limit_from, $limit_to";
1576
        }
1577
        $sql_result = Database::query($sql);
1578
        while ($result = Database::fetch_array($sql_result)) {
1579
            $result['complete_name'] = api_get_person_name($result['firstname'], $result['lastname']);
1580
            $return_array[] = $result;
1581
        }
1582
        return $return_array;
1583
    }
1584
1585
    /**
1586
     * Get a list of users of which the given conditions match with a LIKE '%cond%'
1587
     * @param array $conditions a list of condition (exemple : status=>STUDENT)
1588
     * @param array $order_by a list of fields on which sort
1589
     * @return array An array with all users of the platform.
1590
     * @todo optional course code parameter, optional sorting parameters...
1591
     * @todo security filter order_by
1592
     */
1593
    public static function get_user_list_like(
1594
        $conditions = [],
1595
        $order_by = [],
1596
        $simple_like = false,
1597
        $condition = 'AND'
1598
    ) {
1599
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1600
        $tblAccessUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1601
        $return_array = [];
1602
        $sql_query = "SELECT user.id FROM $user_table user ";
1603
1604
        if (api_is_multiple_url_enabled()) {
1605
            $sql_query .= " INNER JOIN $tblAccessUrlRelUser auru ON auru.user_id = user.id ";
1606
        }
1607
1608
        if (count($conditions) > 0) {
1609
            $sql_query .= ' WHERE ';
1610
            $temp_conditions = [];
1611
            foreach ($conditions as $field => $value) {
1612
                $field = Database::escape_string($field);
1613
                $value = Database::escape_string($value);
1614
                if ($simple_like) {
1615
                    $temp_conditions[] = $field." LIKE '$value%'";
1616
                } else {
1617
                    $temp_conditions[] = $field.' LIKE \'%'.$value.'%\'';
1618
                }
1619
            }
1620
            if (!empty($temp_conditions)) {
1621
                $sql_query .= implode(' '.$condition.' ', $temp_conditions);
1622
            }
1623
1624
            if (api_is_multiple_url_enabled()) {
1625
                $sql_query .= ' AND auru.access_url_id = '.api_get_current_access_url_id();
1626
            }
1627
        } else {
1628
            if (api_is_multiple_url_enabled()) {
1629
                $sql_query .= ' WHERE auru.access_url_id = '.api_get_current_access_url_id();
1630
            }
1631
        }
1632
        if (count($order_by) > 0) {
1633
            $sql_query .= ' ORDER BY '.Database::escape_string(implode(',', $order_by), null, false);
1634
        }
1635
1636
        $sql_result = Database::query($sql_query);
1637
        while ($result = Database::fetch_array($sql_result)) {
1638
            $userInfo = api_get_user_info($result['id']);
1639
            $return_array[] = $userInfo;
1640
        }
1641
1642
        return $return_array;
1643
    }
1644
1645
    /**
1646
     * Get user picture URL or path from user ID (returns an array).
1647
     * The return format is a complete path, enabling recovery of the directory
1648
     * with dirname() or the file with basename(). This also works for the
1649
     * functions dealing with the user's productions, as they are located in
1650
     * the same directory.
1651
     * @param   integer   $id User ID
1652
     * @param   string    $type Type of path to return (can be 'system', 'web')
1653
     * @param   array $userInfo user information to avoid query the DB
1654
     * returns the /main/img/unknown.jpg image set it at true
1655
     *
1656
     * @return    array     Array of 2 elements: 'dir' and 'file' which contain
1657
     * the dir and file as the name implies if image does not exist it will
1658
     * return the unknow image if anonymous parameter is true if not it returns an empty array
1659
     */
1660
    public static function get_user_picture_path_by_id(
1661
        $id,
1662
        $type = 'web',
1663
        $userInfo = []
1664
    ) {
1665
        switch ($type) {
1666
            case 'system': // Base: absolute system path.
1667
                $base = api_get_path(SYS_CODE_PATH);
1668
                break;
1669
            case 'web': // Base: absolute web path.
1670
            default:
1671
                $base = api_get_path(WEB_CODE_PATH);
1672
                break;
1673
        }
1674
1675
        $anonymousPath = [
1676
            'dir' => $base.'img/',
1677
            'file' => 'unknown.jpg',
1678
            'email' => ''
1679
        ];
1680
1681
        if (empty($id) || empty($type)) {
1682
            return $anonymousPath;
1683
        }
1684
1685
        $id = intval($id);
1686
        if (empty($userInfo)) {
1687
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
1688
            $sql = "SELECT email, picture_uri FROM $user_table
1689
                    WHERE id=".$id;
1690
            $res = Database::query($sql);
1691
1692
            if (!Database::num_rows($res)) {
1693
                return $anonymousPath;
1694
            }
1695
            $user = Database::fetch_array($res);
1696
            if (empty($user['picture_uri'])) {
1697
                return $anonymousPath;
1698
            }
1699
        } else {
1700
            $user = $userInfo;
1701
        }
1702
1703
        $pictureFilename = trim($user['picture_uri']);
1704
1705
        $dir = self::getUserPathById($id, $type);
1706
1707
        return [
1708
            'dir' => $dir,
1709
            'file' => $pictureFilename,
1710
            'email' => $user['email']
1711
        ];
1712
    }
1713
1714
    /**
1715
     * *** READ BEFORE REVIEW THIS FUNCTION ***
1716
     * This function is a exact copy from get_user_picture_path_by_id() and it was create it to avoid
1717
     * a recursive calls for get_user_picture_path_by_id() in another functions when you update a user picture
1718
     * in same script, so you can find this function usage in update_user_picture() function.
1719
     *
1720
     * @param   integer   $id User ID
1721
     * @param   string    $type Type of path to return (can be 'system', 'web')
1722
     * @param   array $userInfo user information to avoid query the DB
1723
     * returns the /main/img/unknown.jpg image set it at true
1724
     *
1725
     * @return    array     Array of 2 elements: 'dir' and 'file' which contain
1726
     * the dir and file as the name implies if image does not exist it will
1727
     * return the unknown image if anonymous parameter is true if not it returns an empty array
1728
     */
1729
    public static function getUserPicturePathById($id, $type = 'web', $userInfo = [])
1730
    {
1731
        switch ($type) {
1732
            case 'system': // Base: absolute system path.
1733
                $base = api_get_path(SYS_CODE_PATH);
1734
                break;
1735
            case 'web': // Base: absolute web path.
1736
            default:
1737
                $base = api_get_path(WEB_CODE_PATH);
1738
                break;
1739
        }
1740
1741
        $anonymousPath = [
1742
            'dir' => $base.'img/',
1743
            'file' => 'unknown.jpg',
1744
            'email' => ''
1745
        ];
1746
1747
        if (empty($id) || empty($type)) {
1748
            return $anonymousPath;
1749
        }
1750
1751
        $id = intval($id);
1752
1753
        if (empty($userInfo)) {
1754
            $user_table = Database::get_main_table(TABLE_MAIN_USER);
1755
            $sql = "SELECT email, picture_uri FROM $user_table WHERE id=$id";
1756
            $res = Database::query($sql);
1757
1758
            if (!Database::num_rows($res)) {
1759
                return $anonymousPath;
1760
            }
1761
            $user = Database::fetch_array($res);
1762
1763
            if (empty($user['picture_uri'])) {
1764
                return $anonymousPath;
1765
            }
1766
        } else {
1767
            $user = $userInfo;
1768
        }
1769
1770
        $pictureFilename = trim($user['picture_uri']);
1771
1772
        $dir = self::getUserPathById($id, $type);
1773
1774
        return [
1775
            'dir' => $dir,
1776
            'file' => $pictureFilename,
1777
            'email' => $user['email']
1778
        ];
1779
    }
1780
1781
    /**
1782
     * Get user path from user ID (returns an array).
1783
     * The return format is a complete path to a folder ending with "/"
1784
     * In case the first level of subdirectory of users/ does not exist, the
1785
     * function will attempt to create it. Probably not the right place to do it
1786
     * but at least it avoids headaches in many other places.
1787
     * @param   integer $id User ID
1788
     * @param   string  $type Type of path to return (can be 'system', 'web', 'last')
1789
     * @return  string  User folder path (i.e. /var/www/chamilo/app/upload/users/1/1/)
1790
     */
1791
    public static function getUserPathById($id, $type)
1792
    {
1793
        $id = intval($id);
1794
        if (!$id) {
1795
            return null;
1796
        }
1797
1798
        $userPath = "users/$id/";
1799
        if (api_get_setting('split_users_upload_directory') === 'true') {
1800
            $userPath = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
1801
            // In exceptional cases, on some portals, the intermediate base user
1802
            // directory might not have been created. Make sure it is before
1803
            // going further.
1804
1805
            $rootPath = api_get_path(SYS_UPLOAD_PATH).'users/'.substr((string) $id, 0, 1);
1806
            if (!is_dir($rootPath)) {
1807
                $perm = api_get_permissions_for_new_directories();
1808
                try {
1809
                    mkdir($rootPath, $perm);
1810
                } catch (Exception $e) {
1811
                    error_log($e->getMessage());
1812
                }
1813
            }
1814
        }
1815
        switch ($type) {
1816
            case 'system': // Base: absolute system path.
1817
                $userPath = api_get_path(SYS_UPLOAD_PATH).$userPath;
1818
                break;
1819
            case 'web': // Base: absolute web path.
1820
                $userPath = api_get_path(WEB_UPLOAD_PATH).$userPath;
1821
                break;
1822
            case 'last': // Only the last part starting with users/
1823
                break;
1824
        }
1825
1826
        return $userPath;
1827
    }
1828
1829
    /**
1830
     * Gets the current user image
1831
     * @param string $user_id
1832
     * @param int $size it can be USER_IMAGE_SIZE_SMALL,
1833
     * USER_IMAGE_SIZE_MEDIUM, USER_IMAGE_SIZE_BIG or  USER_IMAGE_SIZE_ORIGINAL
1834
     * @param bool $addRandomId
1835
     * @param array $userInfo to avoid query the DB
1836
     *
1837
     * @return string
1838
     */
1839
    public static function getUserPicture(
1840
        $user_id,
1841
        $size = USER_IMAGE_SIZE_MEDIUM,
1842
        $addRandomId = true,
1843
        $userInfo = []
1844
    ) {
1845
        // Make sure userInfo is defined. Otherwise, define it!
1846
        if (empty($userInfo) || !is_array($userInfo) || count($userInfo) == 0) {
1847
            if (empty($user_id)) {
1848
                return '';
1849
            } else {
1850
                $userInfo = api_get_user_info($user_id);
1851
            }
1852
        }
1853
1854
        $imageWebPath = self::get_user_picture_path_by_id(
1855
            $user_id,
1856
            'web',
1857
            $userInfo
1858
        );
1859
        $pictureWebFile = $imageWebPath['file'];
1860
        $pictureWebDir = $imageWebPath['dir'];
1861
1862
        $pictureAnonymousSize = '128';
1863
        $gravatarSize = 22;
1864
        $realSizeName = 'small_';
1865
1866
        switch ($size) {
1867
            case USER_IMAGE_SIZE_SMALL:
1868
                $pictureAnonymousSize = '32';
1869
                $realSizeName = 'small_';
1870
                $gravatarSize = 32;
1871
                break;
1872
            case USER_IMAGE_SIZE_MEDIUM:
1873
                $pictureAnonymousSize = '64';
1874
                $realSizeName = 'medium_';
1875
                $gravatarSize = 64;
1876
                break;
1877
            case USER_IMAGE_SIZE_ORIGINAL:
1878
                $pictureAnonymousSize = '128';
1879
                $realSizeName = '';
1880
                $gravatarSize = 128;
1881
                break;
1882
            case USER_IMAGE_SIZE_BIG:
1883
                $pictureAnonymousSize = '128';
1884
                $realSizeName = 'big_';
1885
                $gravatarSize = 128;
1886
                break;
1887
        }
1888
1889
        $gravatarEnabled = api_get_setting('gravatar_enabled');
1890
        $anonymousPath = Display::returnIconPath('unknown.png', $pictureAnonymousSize);
1891
        if ($pictureWebFile == 'unknown.jpg' || empty($pictureWebFile)) {
1892
            if ($gravatarEnabled === 'true') {
1893
                $file = self::getGravatar(
1894
                    $imageWebPath['email'],
1895
                    $gravatarSize,
1896
                    api_get_setting('gravatar_type')
1897
                );
1898
1899
                if ($addRandomId) {
1900
                    $file .= '&rand='.uniqid();
1901
                }
1902
1903
                return $file;
1904
            }
1905
1906
            return $anonymousPath;
1907
        }
1908
1909
        $pictureSysPath = self::get_user_picture_path_by_id($user_id, 'system');
1910
1911
        $file = $pictureSysPath['dir'].$realSizeName.$pictureWebFile;
1912
        $picture = '';
1913
        if (file_exists($file)) {
1914
            $picture = $pictureWebDir.$realSizeName.$pictureWebFile;
1915
        } else {
1916
            $file = $pictureSysPath['dir'].$pictureWebFile;
1917
            if (file_exists($file) && !is_dir($file)) {
1918
                $picture = $pictureWebFile['dir'].$pictureWebFile;
1919
            }
1920
        }
1921
1922
        if (empty($picture)) {
1923
            return $anonymousPath;
1924
        }
1925
1926
        if ($addRandomId) {
1927
            $picture .= '?rand='.uniqid();
1928
        }
1929
1930
        return $picture;
1931
    }
1932
1933
    /**
1934
     * Creates new user photos in various sizes of a user, or deletes user photos.
1935
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php
1936
     * @param   int $user_id The user internal identification number.
1937
     * @param   string $file The common file name for the newly created photos.
1938
     *                       It will be checked and modified for compatibility with the file system.
1939
     *                       If full name is provided, path component is ignored.
1940
     *                       If an empty name is provided, then old user photos are deleted only,
1941
     * @see     UserManager::delete_user_picture() as the prefered way for deletion.
1942
     * @param   string $source_file The full system name of the image from which user photos will be created.
1943
     * @param   string $cropParameters Optional string that contents "x,y,width,height" of a cropped image format
1944
     * @return bool Returns the resulting common file name of created images which usually should be stored in database.
1945
     * When deletion is requested returns empty string. In case of internal error or negative validation returns FALSE.
1946
     */
1947
    public static function update_user_picture(
1948
        $user_id,
1949
        $file = null,
1950
        $source_file = null,
1951
        $cropParameters = ''
1952
    ) {
1953
        if (empty($user_id)) {
1954
            return false;
1955
        }
1956
        $delete = empty($file);
1957
        if (empty($source_file)) {
1958
            $source_file = $file;
1959
        }
1960
1961
        // User-reserved directory where photos have to be placed.
1962
        $path_info = self::getUserPicturePathById($user_id, 'system');
1963
        $path = $path_info['dir'];
1964
1965
        // If this directory does not exist - we create it.
1966
        if (!file_exists($path)) {
1967
            mkdir($path, api_get_permissions_for_new_directories(), true);
1968
        }
1969
1970
        // The old photos (if any).
1971
        $old_file = $path_info['file'];
1972
1973
        // Let us delete them.
1974
        if ($old_file != 'unknown.jpg') {
1975
            if (KEEP_THE_OLD_IMAGE_AFTER_CHANGE) {
1976
                $prefix = 'saved_'.date('Y_m_d_H_i_s').'_'.uniqid('').'_';
1977
                @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

1977
                /** @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...
1978
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
1979
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
1980
                @rename($path.$old_file, $path.$prefix.$old_file);
1981
            } else {
1982
                @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

1982
                /** @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...
1983
                @unlink($path.'medium_'.$old_file);
1984
                @unlink($path.'big_'.$old_file);
1985
                @unlink($path.$old_file);
1986
            }
1987
        }
1988
1989
        // Exit if only deletion has been requested. Return an empty picture name.
1990
        if ($delete) {
1991
            return '';
0 ignored issues
show
Bug Best Practice introduced by
The expression return '' returns the type string which is incompatible with the documented return type boolean.
Loading history...
1992
        }
1993
1994
        // Validation 2.
1995
        $allowed_types = api_get_supported_image_extensions();
1996
        $file = str_replace('\\', '/', $file);
1997
        $filename = (($pos = strrpos($file, '/')) !== false) ? substr($file, $pos + 1) : $file;
1998
        $extension = strtolower(substr(strrchr($filename, '.'), 1));
1999
        if (!in_array($extension, $allowed_types)) {
2000
            return false;
2001
        }
2002
2003
        // This is the common name for the new photos.
2004
        if (KEEP_THE_NAME_WHEN_CHANGE_IMAGE && $old_file != 'unknown.jpg') {
2005
            $old_extension = strtolower(substr(strrchr($old_file, '.'), 1));
2006
            $filename = in_array($old_extension, $allowed_types) ? substr($old_file, 0, -strlen($old_extension)) : $old_file;
2007
            $filename = (substr($filename, -1) == '.') ? $filename.$extension : $filename.'.'.$extension;
2008
        } else {
2009
            $filename = api_replace_dangerous_char($filename);
2010
            if (PREFIX_IMAGE_FILENAME_WITH_UID) {
2011
                $filename = uniqid('').'_'.$filename;
2012
            }
2013
            // We always prefix user photos with user ids, so on setting
2014
            // api_get_setting('split_users_upload_directory') === 'true'
2015
            // the correspondent directories to be found successfully.
2016
            $filename = $user_id.'_'.$filename;
2017
        }
2018
2019
        //Crop the image to adjust 1:1 ratio
2020
        $image = new Image($source_file);
2021
        $image->crop($cropParameters);
2022
2023
        // Storing the new photos in 4 versions with various sizes.
2024
        $userPath = self::getUserPathById($user_id, 'system');
2025
2026
        // If this path does not exist - we create it.
2027
        if (!file_exists($userPath)) {
2028
            mkdir($userPath, api_get_permissions_for_new_directories(), true);
2029
        }
2030
        $small = new Image($source_file);
2031
        $small->resize(32);
2032
        $small->send_image($userPath.'small_'.$filename);
2033
        $medium = new Image($source_file);
2034
        $medium->resize(85);
2035
        $medium->send_image($userPath.'medium_'.$filename);
2036
        $normal = new Image($source_file);
2037
        $normal->resize(200);
2038
        $normal->send_image($userPath.$filename);
2039
2040
        $big = new Image($source_file); // This is the original picture.
2041
        $big->send_image($userPath.'big_'.$filename);
2042
2043
        $result = $small && $medium && $normal && $big;
2044
2045
        return $result ? $filename : false;
2046
    }
2047
2048
    /**
2049
     * Update User extra field file type into {user_folder}/{$extra_field}
2050
     * @param int $user_id          The user internal identification number
2051
     * @param string $extra_field   The $extra_field The extra field name
2052
     * @param null $file            The 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...
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...
2053
     * @param null $source_file The temporal filename
2054
     * @return bool|null return filename if success, but false
2055
     */
2056
    public static function update_user_extra_file(
2057
        $user_id,
2058
        $extra_field = '',
2059
        $file = null,
2060
        $source_file = null
2061
    ) {
2062
        // Add Filter
2063
        $source_file = Security::filter_filename($source_file);
2064
        $file = Security::filter_filename($file);
2065
2066
        if (empty($user_id)) {
2067
            return false;
2068
        }
2069
2070
        if (empty($source_file)) {
2071
            $source_file = $file;
2072
        }
2073
2074
        // User-reserved directory where extra file have to be placed.
2075
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2076
        $path = $path_info['dir'];
2077
        if (!empty($extra_field)) {
2078
            $path .= $extra_field.'/';
2079
        }
2080
        // If this directory does not exist - we create it.
2081
        if (!file_exists($path)) {
2082
            @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

2082
            /** @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...
2083
        }
2084
2085
        if (filter_extension($file)) {
2086
            if (@move_uploaded_file($source_file, $path.$file)) {
2087
                if ($extra_field) {
2088
                    return $extra_field.'/'.$file;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extra_field . '/' . $file returns the type string which is incompatible with the documented return type null|boolean.
Loading history...
2089
                } else {
2090
                    return $file;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $file returns the type string which is incompatible with the documented return type null|boolean.
Loading history...
2091
                }
2092
            }
2093
        }
2094
        return false; // this should be returned if anything went wrong with the upload
2095
    }
2096
2097
    /**
2098
     * Deletes user photos.
2099
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php
2100
     * @param int $user_id            The user internal identification number.
2101
     * @return mixed            Returns empty string on success, FALSE on error.
2102
     */
2103
    public static function delete_user_picture($user_id)
2104
    {
2105
        return self::update_user_picture($user_id);
2106
    }
2107
2108
    /**
2109
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
2110
     * doesn't have any.
2111
     *
2112
     * If there has been a request to remove a production, the function will return
2113
     * without building the list unless forced to do so by the optional second
2114
     * parameter. This increases performance by avoiding to read through the
2115
     * productions on the filesystem before the removal request has been carried
2116
     * out because they'll have to be re-read afterwards anyway.
2117
     *
2118
     * @param   int $user_id    User id
2119
     * @param   bool $force    Optional parameter to force building after a removal request
2120
     * @param   bool $showDelete
2121
     *
2122
     * @return  string A string containing the XHTML code to display the production list, or FALSE
2123
     */
2124
    public static function build_production_list($user_id, $force = false, $showDelete = false)
2125
    {
2126
        if (!$force && !empty($_POST['remove_production'])) {
2127
            return true; // postpone reading from the filesystem
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type string.
Loading history...
2128
        }
2129
2130
        $productions = self::get_user_productions($user_id);
2131
2132
        if (empty($productions)) {
2133
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
2134
        }
2135
2136
        $production_dir = self::getUserPathById($user_id, 'web');
2137
        $del_image = Display::returnIconPath('delete.png');
2138
        $add_image = Display::returnIconPath('archive.png');
2139
        $del_text = get_lang('Delete');
2140
        $production_list = '';
2141
        if (count($productions) > 0) {
2142
            $production_list = '<div class="files-production"><ul id="productions">';
2143
            foreach ($productions as $file) {
2144
                $production_list .= '<li>
2145
                    <img src="'.$add_image.'" />
2146
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2147
                        '.htmlentities($file).'
2148
                    </a>';
2149
                if ($showDelete) {
2150
                    $production_list .= '&nbsp;&nbsp;<input style="width:16px;" type="image" name="remove_production['.urlencode($file).']" src="'.$del_image.'" alt="'.$del_text.'" title="'.$del_text.' '.htmlentities($file).'" onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2151
                }
2152
            }
2153
            $production_list .= '</ul></div>';
2154
        }
2155
2156
        return $production_list;
2157
    }
2158
2159
    /**
2160
     * Returns an array with the user's productions.
2161
     *
2162
     * @param    $user_id    User id
2163
     * @return   array  An array containing the user's productions
2164
     */
2165
    public static function get_user_productions($user_id)
2166
    {
2167
        $production_repository = self::getUserPathById($user_id, 'system');
2168
        $productions = [];
2169
2170
        if (is_dir($production_repository)) {
2171
            $handle = opendir($production_repository);
2172
            while ($file = readdir($handle)) {
0 ignored issues
show
Bug introduced by
It seems like $handle can also be of type false; however, parameter $dir_handle of readdir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

2172
            while ($file = readdir(/** @scrutinizer ignore-type */ $handle)) {
Loading history...
2173
                if ($file == '.' ||
2174
                    $file == '..' ||
2175
                    $file == '.htaccess' ||
2176
                    is_dir($production_repository.$file)
2177
                ) {
2178
                    // skip current/parent directory and .htaccess
2179
                    continue;
2180
                }
2181
2182
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2183
                    // User's photos should not be listed as productions.
2184
                    continue;
2185
                }
2186
                $productions[] = $file;
2187
            }
2188
        }
2189
2190
        return $productions;
2191
    }
2192
2193
    /**
2194
     * Remove a user production.
2195
     *
2196
     * @param int $user_id User id
2197
     * @param string $production The production to remove
2198
     * @return bool
2199
     */
2200
    public static function remove_user_production($user_id, $production)
2201
    {
2202
        $production_path = self::get_user_picture_path_by_id($user_id, 'system');
2203
        $production_file = $production_path['dir'].$production;
2204
        if (is_file($production_file)) {
2205
            unlink($production_file);
2206
            return true;
2207
        }
2208
        return false;
2209
    }
2210
2211
    /**
2212
     * Update an extra field value for a given user
2213
     * @param    integer $userId User ID
2214
     * @param    string $variable Field variable name
2215
     * @param    string $value Field value
2216
     *
2217
     * @return    boolean    true if field updated, false otherwise
2218
     */
2219
    public static function update_extra_field_value($userId, $variable, $value = '')
2220
    {
2221
        $extraFieldValue = new ExtraFieldValue('user');
2222
        $params = [
2223
            'item_id' => $userId,
2224
            'variable' => $variable,
2225
            'value' => $value
2226
        ];
2227
2228
        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...
2229
    }
2230
2231
    /**
2232
     * Get an array of extra fields with field details (type, default value and options)
2233
     * @param    integer    Offset (from which row)
2234
     * @param    integer    Number of items
2235
     * @param    integer    Column on which sorting is made
2236
     * @param    string    Sorting direction
2237
     * @param    boolean    Optional. Whether we get all the fields or just the visible ones
2238
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2239
     * @return    array    Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2240
     */
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...
2241
    public static function get_extra_fields(
2242
        $from = 0,
2243
        $number_of_items = 0,
2244
        $column = 5,
2245
        $direction = 'ASC',
2246
        $all_visibility = true,
2247
        $field_filter = null
2248
    ) {
2249
        $fields = [];
2250
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2251
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2252
        $columns = [
2253
            'id',
2254
            'variable',
2255
            'field_type',
2256
            'display_text',
2257
            'default_value',
2258
            'field_order',
2259
            'filter'
2260
        ];
2261
        $column = intval($column);
2262
        $sort_direction = '';
2263
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2264
            $sort_direction = strtoupper($direction);
2265
        }
2266
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2267
        $sqlf = "SELECT * FROM $t_uf WHERE extra_field_type = $extraFieldType ";
2268
        if (!$all_visibility) {
2269
            $sqlf .= " AND visible_to_self = 1 ";
2270
        }
2271
        if (!is_null($field_filter)) {
2272
            $field_filter = intval($field_filter);
2273
            $sqlf .= " AND filter = $field_filter ";
2274
        }
2275
        $sqlf .= " ORDER BY ".$columns[$column]." $sort_direction ";
2276
        if ($number_of_items != 0) {
2277
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2278
        }
2279
        $resf = Database::query($sqlf);
2280
        if (Database::num_rows($resf) > 0) {
2281
            while ($rowf = Database::fetch_array($resf)) {
2282
                $fields[$rowf['id']] = [
2283
                    0 => $rowf['id'],
2284
                    1 => $rowf['variable'],
2285
                    2 => $rowf['field_type'],
2286
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2287
                    4 => $rowf['default_value'],
2288
                    5 => $rowf['field_order'],
2289
                    6 => $rowf['visible_to_self'],
2290
                    7 => $rowf['changeable'],
2291
                    8 => $rowf['filter'],
2292
                    9 => [],
2293
                    10 => '<a name="'.$rowf['id'].'"></a>',
2294
                ];
2295
2296
                $sqlo = "SELECT * FROM $t_ufo
2297
                         WHERE field_id = ".$rowf['id']."
2298
                         ORDER BY option_order ASC";
2299
                $reso = Database::query($sqlo);
2300
                if (Database::num_rows($reso) > 0) {
2301
                    while ($rowo = Database::fetch_array($reso)) {
2302
                        $fields[$rowf['id']][9][$rowo['id']] = [
2303
                            0 => $rowo['id'],
2304
                            1 => $rowo['option_value'],
2305
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2306
                            3 => $rowo['option_order']
2307
                        ];
2308
                    }
2309
                }
2310
            }
2311
        }
2312
2313
        return $fields;
2314
    }
2315
2316
    /**
2317
     * Build a list of extra file already uploaded in $user_folder/{$extra_field}/
2318
     * @param $user_id
2319
     * @param $extra_field
2320
     * @param bool $force
2321
     * @param bool $showDelete
2322
     * @return bool|string
2323
     */
2324
    public static function build_user_extra_file_list(
2325
        $user_id,
2326
        $extra_field,
2327
        $force = false,
2328
        $showDelete = false
2329
    ) {
2330
        if (!$force && !empty($_POST['remove_'.$extra_field])) {
2331
            return true; // postpone reading from the filesystem
2332
        }
2333
2334
        $extra_files = self::get_user_extra_files($user_id, $extra_field);
2335
        if (empty($extra_files)) {
2336
            return false;
2337
        }
2338
2339
        $path_info = self::get_user_picture_path_by_id($user_id, 'web');
2340
        $path = $path_info['dir'];
2341
        $del_image = Display::returnIconPath('delete.png');
2342
2343
        $del_text = get_lang('Delete');
2344
        $extra_file_list = '';
2345
        if (count($extra_files) > 0) {
2346
            $extra_file_list = '<div class="files-production"><ul id="productions">';
2347
            foreach ($extra_files as $file) {
2348
                $filename = substr($file, strlen($extra_field) + 1);
2349
                $extra_file_list .= '<li>'.Display::return_icon('archive.png').
2350
                    '<a href="'.$path.$extra_field.'/'.urlencode($filename).'" target="_blank">
2351
                        '.htmlentities($filename).
2352
                    '</a> ';
2353
                if ($showDelete) {
2354
                    $extra_file_list .= '<input style="width:16px;" type="image" name="remove_extra_'.$extra_field.'['.urlencode($file).']" src="'.$del_image.'" alt="'.$del_text.'" title="'.$del_text.' '.htmlentities($filename).'" onclick="javascript: return confirmation(\''.htmlentities($filename).'\');" /></li>';
2355
                }
2356
            }
2357
            $extra_file_list .= '</ul></div>';
2358
        }
2359
2360
        return $extra_file_list;
2361
    }
2362
2363
    /**
2364
     * Get valid filenames in $user_folder/{$extra_field}/
2365
     * @param $user_id
2366
     * @param $extra_field
2367
     * @param bool $full_path
2368
     * @return array
2369
     */
2370
    public static function get_user_extra_files($user_id, $extra_field, $full_path = false)
2371
    {
2372
        if (!$full_path) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
2373
            // Nothing to do
2374
        } else {
2375
            $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2376
            $path = $path_info['dir'];
2377
        }
2378
        $extra_data = self::get_extra_user_data_by_field($user_id, $extra_field);
2379
        $extra_files = $extra_data[$extra_field];
2380
        if (is_array($extra_files)) {
2381
            foreach ($extra_files as $key => $value) {
2382
                if (!$full_path) {
2383
                    // Relative path from user folder
2384
                    $files[] = $value;
2385
                } else {
2386
                    $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...
2387
                }
2388
            }
2389
        } elseif (!empty($extra_files)) {
2390
            if (!$full_path) {
2391
                // Relative path from user folder
2392
                $files[] = $extra_files;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$files was never initialized. Although not strictly required by PHP, it is generally a good practice to add $files = array(); before regardless.
Loading history...
2393
            } else {
2394
                $files[] = $path.$extra_files;
2395
            }
2396
        }
2397
2398
        return $files; // can be an empty array
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $files does not seem to be defined for all execution paths leading up to this point.
Loading history...
2399
    }
2400
2401
    /**
2402
     * Remove an {$extra_file} from the user folder $user_folder/{$extra_field}/
2403
     * @param int $user_id
2404
     * @param string $extra_field
2405
     * @param string $extra_file
2406
     * @return bool
2407
     */
2408
    public static function remove_user_extra_file($user_id, $extra_field, $extra_file)
2409
    {
2410
        $extra_file = Security::filter_filename($extra_file);
2411
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2412
        if (strpos($extra_file, $extra_field) !== false) {
2413
            $path_extra_file = $path_info['dir'].$extra_file;
2414
        } else {
2415
            $path_extra_file = $path_info['dir'].$extra_field.'/'.$extra_file;
2416
        }
2417
        if (is_file($path_extra_file)) {
2418
            unlink($path_extra_file);
2419
            return true;
2420
        }
2421
        return false;
2422
    }
2423
2424
    /**
2425
     * Creates a new extra field
2426
     * @param    string    $variable Field's internal variable name
2427
     * @param    int       $fieldType  Field's type
2428
     * @param    string    $displayText Field's language var name
2429
     * @param    string    $default Field's default value
2430
     * @return int
2431
     */
2432
    public static function create_extra_field(
2433
        $variable,
2434
        $fieldType,
2435
        $displayText,
2436
        $default
2437
    ) {
2438
        $extraField = new ExtraField('user');
2439
        $params = [
2440
            'variable' => $variable,
2441
            'field_type' => $fieldType,
2442
            'display_text' => $displayText,
2443
            'default_value' => $default
2444
        ];
2445
2446
        return $extraField->save($params);
2447
    }
2448
2449
    /**
2450
     * Check if a field is available
2451
     * @param    string    $variable
2452
     * @return    boolean
2453
     */
2454
    public static function is_extra_field_available($variable)
2455
    {
2456
        $extraField = new ExtraField('user');
2457
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2458
2459
        return !empty($data) ? true : false;
2460
    }
2461
2462
    /**
2463
     * Gets user extra fields data
2464
     * @param    integer    User ID
2465
     * @param    boolean    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2466
     * @param    boolean    Whether to return invisible fields as well
2467
     * @param    boolean    Whether to split multiple-selection fields or not
2468
     * @return    array    Array of fields => value for the given user
2469
     */
2470
    public static function get_extra_user_data(
2471
        $user_id,
2472
        $prefix = false,
2473
        $all_visibility = true,
2474
        $splitmultiple = false,
0 ignored issues
show
Unused Code introduced by
The parameter $splitmultiple is not used and could be removed. ( Ignorable by Annotation )

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

2474
        /** @scrutinizer ignore-unused */ $splitmultiple = false,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2475
        $field_filter = null
2476
    ) {
2477
        // A sanity check.
2478
        if (empty($user_id)) {
2479
            $user_id = 0;
2480
        } else {
2481
            if ($user_id != strval(intval($user_id))) {
2482
                return [];
2483
            }
2484
        }
2485
        $extra_data = [];
2486
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2487
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2488
        $user_id = intval($user_id);
2489
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2490
                FROM $t_uf f
2491
                WHERE
2492
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
2493
                ";
2494
        $filter_cond = '';
2495
2496
        if (!$all_visibility) {
2497
            if (isset($field_filter)) {
2498
                $field_filter = intval($field_filter);
2499
                $filter_cond .= " AND filter = $field_filter ";
2500
            }
2501
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2502
        } else {
2503
            if (isset($field_filter)) {
2504
                $field_filter = intval($field_filter);
2505
                $sql .= " AND filter = $field_filter ";
2506
            }
2507
        }
2508
2509
        $sql .= " ORDER BY f.field_order";
2510
2511
        $res = Database::query($sql);
2512
        if (Database::num_rows($res) > 0) {
2513
            while ($row = Database::fetch_array($res)) {
2514
                if ($row['type'] == self::USER_FIELD_TYPE_TAG) {
2515
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2516
                    $extra_data['extra_'.$row['fvar']] = $tags;
2517
                } else {
2518
                    $sqlu = "SELECT value as fval
2519
                            FROM $t_ufv
2520
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2521
                    $resu = Database::query($sqlu);
2522
                    // get default value
2523
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2524
                               WHERE id=".$row['id'];
2525
                    $res_df = Database::query($sql_df);
2526
2527
                    if (Database::num_rows($resu) > 0) {
2528
                        $rowu = Database::fetch_array($resu);
2529
                        $fval = $rowu['fval'];
2530
                        if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
2531
                            $fval = explode(';', $rowu['fval']);
2532
                        }
2533
                    } else {
2534
                        $row_df = Database::fetch_array($res_df);
2535
                        $fval = $row_df['fval_df'];
2536
                    }
2537
                    // We get here (and fill the $extra_data array) even if there
2538
                    // is no user with data (we fill it with default values)
2539
                    if ($prefix) {
2540
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
2541
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2542
                        } else {
2543
                            $extra_data['extra_'.$row['fvar']] = $fval;
2544
                        }
2545
                    } else {
2546
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
2547
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2548
                        } else {
2549
                            $extra_data[$row['fvar']] = $fval;
2550
                        }
2551
                    }
2552
                }
2553
            }
2554
        }
2555
2556
        return $extra_data;
2557
    }
2558
2559
    /** Get extra user data by field
2560
     * @param int    user ID
2561
     * @param string the internal variable name of the field
2562
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2563
     */
2564
    public static function get_extra_user_data_by_field(
2565
        $user_id,
2566
        $field_variable,
2567
        $prefix = false,
2568
        $all_visibility = true,
2569
        $splitmultiple = false
0 ignored issues
show
Unused Code introduced by
The parameter $splitmultiple is not used and could be removed. ( Ignorable by Annotation )

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

2569
        /** @scrutinizer ignore-unused */ $splitmultiple = false

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2570
    ) {
2571
        // A sanity check.
2572
        if (empty($user_id)) {
2573
            $user_id = 0;
2574
        } else {
2575
            if ($user_id != strval(intval($user_id))) {
2576
                return [];
2577
            }
2578
        }
2579
        $extra_data = [];
2580
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2581
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2582
        $user_id = intval($user_id);
2583
2584
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2585
                FROM $t_uf f
2586
                WHERE f.variable = '$field_variable' ";
2587
2588
        if (!$all_visibility) {
2589
            $sql .= " AND f.visible_to_self = 1 ";
2590
        }
2591
2592
        $sql .= " AND extra_field_type = ".EntityExtraField::USER_FIELD_TYPE;
2593
        $sql .= " ORDER BY f.field_order";
2594
2595
        $res = Database::query($sql);
2596
        if (Database::num_rows($res) > 0) {
2597
            while ($row = Database::fetch_array($res)) {
2598
                $sqlu = "SELECT value as fval FROM $t_ufv v 
2599
                         INNER JOIN $t_uf f
2600
                         ON (v.field_id = f.id)
2601
                         WHERE
2602
                            extra_field_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2603
                            field_id = ".$row['id']." AND
2604
                            item_id = ".$user_id;
2605
                $resu = Database::query($sqlu);
2606
                $fval = '';
2607
                if (Database::num_rows($resu) > 0) {
2608
                    $rowu = Database::fetch_array($resu);
2609
                    $fval = $rowu['fval'];
2610
                    if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
2611
                        $fval = explode(';', $rowu['fval']);
2612
                    }
2613
                }
2614
                if ($prefix) {
2615
                    $extra_data['extra_'.$row['fvar']] = $fval;
2616
                } else {
2617
                    $extra_data[$row['fvar']] = $fval;
2618
                }
2619
            }
2620
        }
2621
2622
        return $extra_data;
2623
    }
2624
2625
    /**
2626
     * Get the extra field information for a certain field (the options as well)
2627
     * @param  int     $variable The name of the field we want to know everything about
2628
     * @return array   Array containing all the information about the extra profile field
2629
     * (first level of array contains field details, then 'options' sub-array contains options details,
2630
     * as returned by the database)
2631
     * @author Julio Montoya
2632
     * @since v1.8.6
2633
     */
2634
    public static function get_extra_field_information_by_name($variable)
2635
    {
2636
        $extraField = new ExtraField('user');
2637
2638
        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...
2639
    }
2640
2641
    /**
2642
     * Get the extra field information for user tag (the options as well)
2643
     * @param  int     $variable The name of the field we want to know everything about
2644
     * @return array   Array containing all the information about the extra profile field
2645
     * (first level of array contains field details, then 'options' sub-array contains options details,
2646
     * as returned by the database)
2647
     * @author José Loguercio
2648
     * @since v1.11.0
2649
     */
2650
    public static function get_extra_field_tags_information_by_name($variable)
2651
    {
2652
        $extraField = new ExtraField('user');
2653
2654
        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...
2655
    }
2656
2657
    /**
2658
     * @param string $type
2659
     *
2660
     * @return array
2661
     */
2662
    public static function get_all_extra_field_by_type($type)
2663
    {
2664
        $extraField = new ExtraField('user');
2665
2666
        return $extraField->get_all_extra_field_by_type($type);
2667
    }
2668
2669
    /**
2670
     * Get all the extra field information of a certain field (also the options)
2671
     *
2672
     * @param int $fieldId the ID of the field we want to know everything of
2673
     * @return array $return containing all th information about the extra profile field
2674
     * @author Julio Montoya
2675
     * @deprecated
2676
     * @since v1.8.6
2677
     */
2678
    public static function get_extra_field_information($fieldId)
2679
    {
2680
        $extraField = new ExtraField('user');
2681
2682
        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...
2683
    }
2684
2685
    /**
2686
     * Get extra user data by value
2687
     * @param string $variable the internal variable name of the field
2688
     * @param string $value the internal value of the field
2689
     * @param bool $all_visibility
2690
     *
2691
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2692
     */
2693
    public static function get_extra_user_data_by_value($variable, $value, $all_visibility = true)
0 ignored issues
show
Unused Code introduced by
The parameter $all_visibility is not used and could be removed. ( Ignorable by Annotation )

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

2693
    public static function get_extra_user_data_by_value($variable, $value, /** @scrutinizer ignore-unused */ $all_visibility = true)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2694
    {
2695
        $extraFieldValue = new ExtraFieldValue('user');
2696
        $extraField = new ExtraField('user');
2697
2698
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2699
2700
        if (false === $info) {
2701
            return [];
2702
        }
2703
2704
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2705
            $variable,
2706
            $value,
2707
            false,
2708
            false,
2709
            true
2710
        );
2711
2712
        $result = [];
2713
        if (!empty($data)) {
2714
            foreach ($data as $item) {
2715
                $result[] = $item['item_id'];
2716
            }
2717
        }
2718
2719
        return $result;
2720
    }
2721
2722
    /**
2723
     * Get extra user data by tags value
2724
     *
2725
     * @param int $fieldId the ID of the field we want to know everything of
2726
     * @param string $tag the tag name for search
2727
     * @return array with extra data info of a user
2728
     * @author José Loguercio
2729
     * @since v1.11.0
2730
     */
2731
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2732
    {
2733
        $extraField = new ExtraField('user');
2734
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2735
        $array = [];
2736
        foreach ($result as $index => $user) {
2737
            $array[] = $user['user_id'];
2738
        }
2739
        return $array;
2740
    }
2741
2742
    /**
2743
     * Get extra user data by field variable
2744
     * @param string    $variable field variable
2745
     * @return array    data
2746
     */
2747
    public static function get_extra_user_data_by_field_variable($variable)
2748
    {
2749
        $extraInfo = self::get_extra_field_information_by_name($variable);
2750
        $field_id = intval($extraInfo['id']);
2751
2752
        $extraField = new ExtraFieldValue('user');
2753
        $data = $extraField->getValuesByFieldId($field_id);
2754
2755
        if (!empty($data) > 0) {
2756
            foreach ($data as $row) {
2757
                $user_id = $row['item_id'];
2758
                $data[$user_id] = $row;
2759
            }
2760
        }
2761
2762
        return $data;
2763
    }
2764
2765
    /**
2766
     * Get extra user data tags by field variable
2767
     *
2768
     * @param string $variable field variable
2769
     * @return array
2770
     */
2771
    public static function get_extra_user_data_for_tags($variable)
2772
    {
2773
        $data = self::get_extra_field_tags_information_by_name($variable);
2774
2775
        return $data;
2776
    }
2777
2778
    /**
2779
     * Gives a list of [session_category][session_id] for the current user.
2780
     * @param integer $user_id
2781
     * @param boolean $is_time_over whether to fill the first element or not (to give space for courses out of categories)
2782
     * @param boolean $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2783
     * @param boolean $ignoreTimeLimit ignore time start/end
2784
     * @return array  list of statuses [session_category][session_id]
2785
     *
2786
     * @todo ensure multiple access urls are managed correctly
2787
     */
2788
    public static function get_sessions_by_category(
2789
        $user_id,
2790
        $is_time_over = true,
2791
        $ignore_visibility_for_admins = false,
2792
        $ignoreTimeLimit = false
2793
    ) {
2794
        if ($user_id != strval(intval($user_id))) {
2795
            return [];
2796
        }
2797
2798
        $allowOrder = api_get_configuration_value('session_list_order');
2799
        $position = '';
2800
        if ($allowOrder) {
2801
            $position = ', s.position AS position ';
2802
        }
2803
2804
        // Get the list of sessions per user
2805
        $now = new DateTime('now', new DateTimeZone('UTC'));
2806
2807
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2808
        // join would not catch session-courses where the user is general
2809
        // session coach but which do not have students nor coaches registered
2810
2811
        // master changes
2812
        $dql = "SELECT DISTINCT
2813
                    s.id,
2814
                    s.name,
2815
                    s.accessStartDate AS access_start_date,
2816
                    s.accessEndDate AS access_end_date,
2817
                    s.duration,
2818
                    sc.id AS session_category_id,
2819
                    sc.name AS session_category_name,
2820
                    sc.dateStart AS session_category_date_start,
2821
                    sc.dateEnd AS session_category_date_end,
2822
                    s.coachAccessStartDate AS coach_access_start_date,
2823
                    s.coachAccessEndDate AS coach_access_end_date
2824
                    $position
2825
                FROM ChamiloCoreBundle:Session AS s
2826
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2827
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s
2828
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2829
                WHERE (scu.user = :user OR s.generalCoach = :user) AND url.url = :url ";
2830
2831
        $order = "ORDER BY sc.name, s.name";
2832
        $showAllSessions = api_get_configuration_value('show_all_sessions_on_my_course_page') === true;
2833
        if ($showAllSessions) {
2834
            $order = "ORDER BY s.accessStartDate";
2835
        }
2836
2837
        if ($allowOrder) {
2838
            $order = "ORDER BY s.position";
2839
        }
2840
2841
        $dql .= $order;
2842
2843
        $dql = Database::getManager()
2844
            ->createQuery($dql)
2845
            ->setParameters(
2846
                ['user' => $user_id, 'url' => api_get_current_access_url_id()]
2847
            )
2848
        ;
2849
2850
        $sessionData = $dql->getResult();
2851
        $categories = [];
2852
2853
        foreach ($sessionData as $row) {
2854
            $session_id = $row['id'];
2855
            $coachList = SessionManager::getCoachesBySession($session_id);
2856
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
2857
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
2858
            $courseList = self::get_courses_list_by_session(
2859
                $user_id,
2860
                $session_id
2861
            );
2862
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
2863
2864
            // User portal filters:
2865
            if ($ignoreTimeLimit === false) {
2866
                if ($is_time_over) {
2867
                    // History
2868
                    if ($row['duration']) {
2869
                        if ($daysLeft >= 0) {
2870
                            continue;
2871
                        }
2872
                    } else {
2873
                        if (empty($row['access_end_date'])) {
2874
                            continue;
2875
                        } else {
2876
                            if ($row['access_end_date'] > $now) {
2877
                                continue;
2878
                            }
2879
                        }
2880
                    }
2881
                } else {
2882
                    // Current user portal
2883
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
2884
                    $isCoachOfCourse = in_array($user_id, $coachList);
2885
2886
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
2887
                        // Teachers can access the session depending in the access_coach date
2888
                    } else {
2889
                        if ($row['duration']) {
2890
                            if ($daysLeft <= 0) {
2891
                                continue;
2892
                            }
2893
                        } else {
2894
                            if (isset($row['access_end_date']) &&
2895
                                !empty($row['access_end_date'])
2896
                            ) {
2897
                                if ($row['access_end_date'] <= $now) {
2898
                                    continue;
2899
                                }
2900
                            }
2901
                        }
2902
                    }
2903
                }
2904
            }
2905
2906
            $categories[$row['session_category_id']]['session_category'] = [
2907
                'id' => $row['session_category_id'],
2908
                'name' => $row['session_category_name'],
2909
                'date_start' => $categoryStart,
2910
                'date_end' => $categoryEnd
2911
            ];
2912
2913
            $visibility = api_get_session_visibility(
2914
                $session_id,
2915
                null,
2916
                $ignore_visibility_for_admins
2917
            );
2918
2919
            if ($visibility != SESSION_VISIBLE) {
2920
                // Course Coach session visibility.
2921
                $blockedCourseCount = 0;
2922
                $closedVisibilityList = [
2923
                    COURSE_VISIBILITY_CLOSED,
2924
                    COURSE_VISIBILITY_HIDDEN
2925
                ];
2926
2927
                foreach ($courseList as $course) {
2928
                    // Checking session visibility
2929
                    $sessionCourseVisibility = api_get_session_visibility(
2930
                        $session_id,
2931
                        $course['real_id'],
2932
                        $ignore_visibility_for_admins
2933
                    );
2934
2935
                    $courseIsVisible = !in_array(
2936
                        $course['visibility'],
2937
                        $closedVisibilityList
2938
                    );
2939
                    if ($courseIsVisible === false || $sessionCourseVisibility == SESSION_INVISIBLE) {
2940
                        $blockedCourseCount++;
2941
                    }
2942
                }
2943
2944
                // If all courses are blocked then no show in the list.
2945
                if ($blockedCourseCount === count($courseList)) {
2946
                    $visibility = SESSION_INVISIBLE;
2947
                } else {
2948
                    $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...
2949
                }
2950
            }
2951
2952
            switch ($visibility) {
2953
                case SESSION_VISIBLE_READ_ONLY:
2954
                case SESSION_VISIBLE:
2955
                case SESSION_AVAILABLE:
2956
                    break;
2957
                case SESSION_INVISIBLE:
2958
                    if ($ignore_visibility_for_admins === false) {
2959
                        continue 2;
2960
                    }
2961
            }
2962
2963
            $categories[$row['session_category_id']]['sessions'][$row['id']] = [
2964
                'session_name' => $row['name'],
2965
                'session_id' => $row['id'],
2966
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
2967
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
2968
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
2969
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
2970
                'courses' => $courseList
2971
            ];
2972
        }
2973
2974
        return $categories;
2975
    }
2976
2977
    /**
2978
     * Gives a list of [session_id-course_code] => [status] for the current user.
2979
     * @param integer $user_id
2980
     * @param int $sessionLimit
2981
     * @return array  list of statuses (session_id-course_code => status)
2982
     */
2983
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
2984
    {
2985
        // Database Table Definitions
2986
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2987
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2988
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2989
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
2990
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2991
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2992
2993
        if ($user_id != strval(intval($user_id))) {
2994
            return [];
2995
        }
2996
2997
        // We filter the courses from the URL
2998
        $join_access_url = $where_access_url = '';
2999
3000
        if (api_get_multiple_access_url()) {
3001
            $access_url_id = api_get_current_access_url_id();
3002
            if ($access_url_id != -1) {
3003
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3004
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
3005
                $where_access_url = " AND access_url_id = $access_url_id ";
3006
            }
3007
        }
3008
3009
        // Courses in which we subscribed out of any session
3010
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3011
3012
        $sql = "SELECT
3013
                    course.code,
3014
                    course_rel_user.status course_rel_status,
3015
                    course_rel_user.sort sort,
3016
                    course_rel_user.user_course_cat user_course_cat
3017
                 FROM $tbl_course_user course_rel_user
3018
                 LEFT JOIN $tbl_course course
3019
                 ON course.id = course_rel_user.c_id
3020
                 LEFT JOIN $tbl_user_course_category user_course_category
3021
                 ON course_rel_user.user_course_cat = user_course_category.id
3022
                 $join_access_url
3023
                 WHERE
3024
                    course_rel_user.user_id = '".$user_id."' AND
3025
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
3026
                    $where_access_url
3027
                 ORDER BY user_course_category.sort, course_rel_user.sort, course.title ASC";
3028
3029
        $course_list_sql_result = Database::query($sql);
3030
3031
        $personal_course_list = [];
3032
        if (Database::num_rows($course_list_sql_result) > 0) {
3033
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3034
                $course_info = api_get_course_info($result_row['code']);
3035
                $result_row['course_info'] = $course_info;
3036
                $personal_course_list[] = $result_row;
3037
            }
3038
        }
3039
3040
        $coachCourseConditions = '';
3041
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
3042
        if (api_is_allowed_to_create_course()) {
3043
            $sessionListFromCourseCoach = [];
3044
            $sql = " SELECT DISTINCT session_id
3045
                    FROM $tbl_session_course_user
3046
                    WHERE user_id = $user_id AND status = 2 ";
3047
3048
            $result = Database::query($sql);
3049
            if (Database::num_rows($result)) {
3050
                $result = Database::store_result($result);
3051
                foreach ($result as $session) {
3052
                    $sessionListFromCourseCoach[] = $session['session_id'];
3053
                }
3054
            }
3055
            if (!empty($sessionListFromCourseCoach)) {
3056
                $condition = implode("','", $sessionListFromCourseCoach);
3057
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
3058
            }
3059
        }
3060
3061
        // Get the list of sessions where the user is subscribed
3062
        // This is divided into two different queries
3063
        $sessions = [];
3064
        $sessionLimitRestriction = '';
3065
        if (!empty($sessionLimit)) {
3066
            $sessionLimit = (int) $sessionLimit;
3067
            $sessionLimitRestriction = "LIMIT $sessionLimit";
3068
        }
3069
3070
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
3071
                FROM $tbl_session_user su INNER JOIN $tbl_session s
3072
                ON (s.id = su.session_id)
3073
                WHERE (
3074
                    su.user_id = $user_id AND
3075
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
3076
                )
3077
                $coachCourseConditions
3078
                ORDER BY access_start_date, access_end_date, name
3079
                $sessionLimitRestriction
3080
        ";
3081
3082
        $result = Database::query($sql);
3083
        if (Database::num_rows($result) > 0) {
3084
            while ($row = Database::fetch_assoc($result)) {
3085
                $sessions[$row['id']] = $row;
3086
            }
3087
        }
3088
3089
        $sql = "SELECT DISTINCT
3090
                id, name, access_start_date, access_end_date
3091
                FROM $tbl_session s
3092
                WHERE (
3093
                    id_coach = $user_id
3094
                )
3095
                $coachCourseConditions
3096
                ORDER BY access_start_date, access_end_date, name";
3097
3098
        $result = Database::query($sql);
3099
        if (Database::num_rows($result) > 0) {
3100
            while ($row = Database::fetch_assoc($result)) {
3101
                if (empty($sessions[$row['id']])) {
3102
                    $sessions[$row['id']] = $row;
3103
                }
3104
            }
3105
        }
3106
3107
        if (api_is_allowed_to_create_course()) {
3108
            foreach ($sessions as $enreg) {
3109
                $session_id = $enreg['id'];
3110
                $session_visibility = api_get_session_visibility($session_id);
3111
3112
                if ($session_visibility == SESSION_INVISIBLE) {
3113
                    continue;
3114
                }
3115
3116
                // This query is horribly slow when more than a few thousand
3117
                // users and just a few sessions to which they are subscribed
3118
                $sql = "SELECT DISTINCT
3119
                        course.code code,
3120
                        course.title i,
3121
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
3122
                        email, course.course_language l,
3123
                        1 sort,
3124
                        category_code user_course_cat,
3125
                        access_start_date,
3126
                        access_end_date,
3127
                        session.id as session_id,
3128
                        session.name as session_name
3129
                    FROM $tbl_session_course_user as session_course_user
3130
                    INNER JOIN $tbl_course AS course
3131
                        ON course.id = session_course_user.c_id
3132
                    INNER JOIN $tbl_session as session
3133
                        ON session.id = session_course_user.session_id
3134
                    LEFT JOIN $tbl_user as user
3135
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
3136
                    WHERE
3137
                        session_course_user.session_id = $session_id AND (
3138
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
3139
                            OR session.id_coach = $user_id
3140
                        )
3141
                    ORDER BY i";
3142
                $course_list_sql_result = Database::query($sql);
3143
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3144
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3145
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3146
                    $personal_course_list[$key] = $result_row;
3147
                }
3148
            }
3149
        }
3150
3151
        foreach ($sessions as $enreg) {
3152
            $session_id = $enreg['id'];
3153
            $session_visibility = api_get_session_visibility($session_id);
3154
            if ($session_visibility == SESSION_INVISIBLE) {
3155
                continue;
3156
            }
3157
3158
            /* This query is very similar to the above query,
3159
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3160
            $sql = "SELECT DISTINCT
3161
                course.code code,
3162
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
3163
                email,
3164
                course.course_language l,
3165
                1 sort,
3166
                category_code user_course_cat,
3167
                access_start_date,
3168
                access_end_date,
3169
                session.id as session_id,
3170
                session.name as session_name,
3171
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
3172
            FROM $tbl_session_course_user as session_course_user
3173
            INNER JOIN $tbl_course AS course
3174
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
3175
            INNER JOIN $tbl_session as session 
3176
            ON session_course_user.session_id = session.id
3177
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
3178
            WHERE session_course_user.user_id = $user_id
3179
            ORDER BY i";
3180
3181
            $course_list_sql_result = Database::query($sql);
3182
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3183
                $result_row['course_info'] = api_get_course_info($result_row['code']);
3184
                $key = $result_row['session_id'].' - '.$result_row['code'];
3185
                if (!isset($personal_course_list[$key])) {
3186
                    $personal_course_list[$key] = $result_row;
3187
                }
3188
            }
3189
        }
3190
3191
        return $personal_course_list;
3192
    }
3193
3194
    /**
3195
     * Gives a list of courses for the given user in the given session
3196
     * @param integer $user_id
3197
     * @param integer $session_id
3198
     * @return array  list of statuses (session_id-course_code => status)
3199
     */
3200
    public static function get_courses_list_by_session($user_id, $session_id)
3201
    {
3202
        // Database Table Definitions
3203
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3204
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
3205
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3206
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3207
3208
        $user_id = intval($user_id);
3209
        $session_id = intval($session_id);
3210
        //we filter the courses from the URL
3211
        $join_access_url = $where_access_url = '';
3212
3213
        if (api_get_multiple_access_url()) {
3214
            $urlId = api_get_current_access_url_id();
3215
            if ($urlId != -1) {
3216
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3217
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
3218
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
3219
            }
3220
        }
3221
3222
        /* This query is very similar to the query below, but it will check the
3223
        session_rel_course_user table if there are courses registered
3224
        to our user or not */
3225
        $sql = "SELECT DISTINCT
3226
                    c.visibility,
3227
                    c.id as real_id,
3228
                    c.code as course_code,
3229
                    sc.position
3230
                FROM $tbl_session_course_user as scu
3231
                INNER JOIN $tbl_session_course sc
3232
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3233
                INNER JOIN $tableCourse as c
3234
                ON (scu.c_id = c.id)
3235
                $join_access_url
3236
                WHERE
3237
                    scu.user_id = $user_id AND
3238
                    scu.session_id = $session_id
3239
                    $where_access_url
3240
                ORDER BY sc.position ASC, c.id";
3241
3242
        $personal_course_list = [];
3243
        $courses = [];
3244
3245
        $result = Database::query($sql);
3246
        if (Database::num_rows($result) > 0) {
3247
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3248
                $result_row['status'] = 5;
3249
                if (!in_array($result_row['real_id'], $courses)) {
3250
                    $personal_course_list[] = $result_row;
3251
                    $courses[] = $result_row['real_id'];
3252
                }
3253
            }
3254
        }
3255
3256
        if (api_is_allowed_to_create_course()) {
3257
            $sql = "SELECT DISTINCT
3258
                        c.visibility, 
3259
                        c.id as real_id,
3260
                        c.code as course_code,
3261
                        sc.position
3262
                    FROM $tbl_session_course_user as scu
3263
                    INNER JOIN $tbl_session as s
3264
                    ON (scu.session_id = s.id)
3265
                    INNER JOIN $tbl_session_course sc
3266
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3267
                    INNER JOIN $tableCourse as c
3268
                    ON (scu.c_id = c.id)
3269
                    $join_access_url
3270
                    WHERE
3271
                      s.id = $session_id AND
3272
                      (
3273
                        (scu.user_id = $user_id AND scu.status = 2) OR
3274
                        s.id_coach = $user_id
3275
                      )
3276
                    $where_access_url
3277
                    ORDER BY sc.position ASC";
3278
            $result = Database::query($sql);
3279
3280
            if (Database::num_rows($result) > 0) {
3281
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3282
                    $result_row['status'] = 2;
3283
                    if (!in_array($result_row['real_id'], $courses)) {
3284
                        $personal_course_list[] = $result_row;
3285
                        $courses[] = $result_row['real_id'];
3286
                    }
3287
                }
3288
            }
3289
        }
3290
3291
        if (api_is_drh()) {
3292
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3293
            $sessionList = array_keys($sessionList);
3294
            if (in_array($session_id, $sessionList)) {
3295
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3296
                if (!empty($courseList)) {
3297
                    foreach ($courseList as $course) {
3298
                        if (!in_array($course['id'], $courses)) {
3299
                            $personal_course_list[] = $course;
3300
                        }
3301
                    }
3302
                }
3303
            }
3304
        } else {
3305
            //check if user is general coach for this session
3306
            $sessionInfo = api_get_session_info($session_id);
3307
            if ($sessionInfo['id_coach'] == $user_id) {
3308
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3309
                if (!empty($courseList)) {
3310
                    foreach ($courseList as $course) {
3311
                        if (!in_array($course['id'], $courses)) {
3312
                            $personal_course_list[] = $course;
3313
                        }
3314
                    }
3315
                }
3316
            }
3317
        }
3318
3319
        return $personal_course_list;
3320
    }
3321
3322
    /**
3323
     * Get user id from a username
3324
     * @param string $username
3325
     * @return int User ID (or false if not found)
3326
     */
3327
    public static function get_user_id_from_username($username)
3328
    {
3329
        if (empty($username)) {
3330
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3331
        }
3332
        $username = trim($username);
3333
        $username = Database::escape_string($username);
3334
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3335
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3336
        $res = Database::query($sql);
3337
3338
        if ($res === false) {
3339
            return false;
3340
        }
3341
        if (Database::num_rows($res) !== 1) {
3342
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3343
        }
3344
        $row = Database::fetch_array($res);
3345
3346
        return $row['id'];
3347
    }
3348
3349
    /**
3350
     * Get the users files upload from his share_folder
3351
     * @param    string  $user_id   User ID
3352
     * @param   string  $course course directory
3353
     * @param   string  $resourcetype resourcetype: images, all
3354
     * @return    string
3355
     */
3356
    public static function get_user_upload_files_by_course(
3357
        $user_id,
3358
        $course,
3359
        $resourcetype = 'all'
3360
    ) {
3361
        $return = '';
3362
        if (!empty($user_id) && !empty($course)) {
3363
            $user_id = intval($user_id);
3364
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3365
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3366
            $file_list = [];
3367
3368
            if (is_dir($path)) {
3369
                $handle = opendir($path);
3370
                while ($file = readdir($handle)) {
0 ignored issues
show
Bug introduced by
It seems like $handle can also be of type false; however, parameter $dir_handle of readdir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

3370
                while ($file = readdir(/** @scrutinizer ignore-type */ $handle)) {
Loading history...
3371
                    if ($file == '.' || $file == '..' || $file == '.htaccess' || is_dir($path.$file)) {
3372
                        continue; // skip current/parent directory and .htaccess
3373
                    }
3374
                    $file_list[] = $file;
3375
                }
3376
                if (count($file_list) > 0) {
3377
                    $return = "<h4>$course</h4>";
3378
                    $return .= '<ul class="thumbnails">';
3379
                }
3380
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
3381
                foreach ($file_list as $file) {
3382
                    if ($resourcetype == "all") {
3383
                        $return .= '<li><a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
3384
                    } elseif ($resourcetype == "images") {
3385
                        //get extension
3386
                        $ext = explode('.', $file);
3387
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
3388
                            $return .= '<li class="span2">
3389
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
3390
                                                <img src="'.$web_path.urlencode($file).'" >
3391
                                            </a>
3392
                                        </li>';
3393
                        }
3394
                    }
3395
                }
3396
                if (count($file_list) > 0) {
3397
                    $return .= '</ul>';
3398
                }
3399
            }
3400
        }
3401
3402
        return $return;
3403
    }
3404
3405
    /**
3406
     * Gets the API key (or keys) and return them into an array
3407
     * @param int     Optional user id (defaults to the result of api_get_user_id())
3408
     * @param string $api_service
3409
     * @return  array   Non-indexed array containing the list of API keys for this user, or FALSE on error
3410
     */
3411
    public static function get_api_keys($user_id = null, $api_service = 'dokeos')
3412
    {
3413
        if ($user_id != strval(intval($user_id))) {
3414
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
3415
        }
3416
        if (empty($user_id)) {
3417
            $user_id = api_get_user_id();
3418
        }
3419
        if ($user_id === false) {
3420
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
3421
        }
3422
        $service_name = Database::escape_string($api_service);
3423
        if (is_string($service_name) === false) {
3424
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
3425
        }
3426
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3427
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
3428
        $res = Database::query($sql);
3429
        if ($res === false) {
3430
            return false;
3431
        } //error during query
3432
        $num = Database::num_rows($res);
3433
        if ($num == 0) {
3434
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
3435
        }
3436
        $list = [];
3437
        while ($row = Database::fetch_array($res)) {
3438
            $list[$row['id']] = $row['api_key'];
3439
        }
3440
        return $list;
3441
    }
3442
3443
    /**
3444
     * Adds a new API key to the users' account
3445
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
3446
     * @param string $api_service
3447
     * @return  boolean True on success, false on failure
3448
     */
3449
    public static function add_api_key($user_id = null, $api_service = 'dokeos')
3450
    {
3451
        if ($user_id != strval(intval($user_id))) {
3452
            return false;
3453
        }
3454
        if (empty($user_id)) {
3455
            $user_id = api_get_user_id();
3456
        }
3457
        if ($user_id === false) {
3458
            return false;
3459
        }
3460
        $service_name = Database::escape_string($api_service);
3461
        if (is_string($service_name) === false) {
3462
            return false;
3463
        }
3464
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3465
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
3466
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
3467
        $res = Database::query($sql);
3468
        if ($res === false) {
3469
            return false;
3470
        } //error during query
3471
        $num = Database::insert_id();
3472
        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...
3473
    }
3474
3475
    /**
3476
     * Deletes an API key from the user's account
3477
     * @param   int     API key's internal ID
3478
     * @return  boolean True on success, false on failure
3479
     */
3480
    public static function delete_api_key($key_id)
3481
    {
3482
        if ($key_id != strval(intval($key_id))) {
3483
            return false;
3484
        }
3485
        if ($key_id === false) {
3486
            return false;
3487
        }
3488
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3489
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
3490
        $res = Database::query($sql);
3491
        if ($res === false) {
3492
            return false;
3493
        } //error during query
3494
        $num = Database::num_rows($res);
3495
        if ($num !== 1) {
3496
            return false;
3497
        }
3498
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
3499
        $res = Database::query($sql);
3500
        if ($res === false) {
3501
            return false;
3502
        } //error during query
3503
3504
        return true;
3505
    }
3506
3507
    /**
3508
     * Regenerate an API key from the user's account
3509
     * @param   int     user ID (defaults to the results of api_get_user_id())
3510
     * @param   string  API key's internal ID
3511
     * @return  int        num
3512
     */
3513
    public static function update_api_key($user_id, $api_service)
3514
    {
3515
        if ($user_id != strval(intval($user_id))) {
3516
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3517
        }
3518
        if ($user_id === false) {
3519
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3520
        }
3521
        $service_name = Database::escape_string($api_service);
3522
        if (is_string($service_name) === false) {
3523
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3524
        }
3525
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3526
        $sql = "SELECT id FROM $t_api 
3527
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3528
        $res = Database::query($sql);
3529
        $num = Database::num_rows($res);
3530
        if ($num == 1) {
3531
            $id_key = Database::fetch_array($res, 'ASSOC');
3532
            self::delete_api_key($id_key['id']);
3533
            $num = self::add_api_key($user_id, $api_service);
3534
        } elseif ($num == 0) {
3535
            $num = self::add_api_key($user_id, $api_service);
3536
        }
3537
3538
        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...
3539
    }
3540
3541
    /**
3542
     * @param   int     user ID (defaults to the results of api_get_user_id())
3543
     * @param   string    API key's internal ID
3544
     * @return  int    row ID, or return false if not found
3545
     */
3546
    public static function get_api_key_id($user_id, $api_service)
3547
    {
3548
        if ($user_id != strval(intval($user_id))) {
3549
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3550
        }
3551
        if ($user_id === false) {
3552
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3553
        }
3554
        if (empty($api_service)) {
3555
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3556
        }
3557
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3558
        $api_service = Database::escape_string($api_service);
3559
        $sql = "SELECT id FROM $t_api 
3560
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3561
        $res = Database::query($sql);
3562
        if (Database::num_rows($res) < 1) {
3563
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3564
        }
3565
        $row = Database::fetch_array($res, 'ASSOC');
3566
3567
        return $row['id'];
3568
    }
3569
3570
    /**
3571
     * Checks if a user_id is platform admin
3572
     * @param   int user ID
3573
     * @return  boolean True if is admin, false otherwise
3574
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
3575
     */
3576
    public static function is_admin($user_id)
3577
    {
3578
        if (empty($user_id) || $user_id != strval(intval($user_id))) {
3579
            return false;
3580
        }
3581
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3582
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3583
        $res = Database::query($sql);
3584
3585
        return Database::num_rows($res) === 1;
3586
    }
3587
3588
    /**
3589
     * Get the total count of users
3590
     * @param   int     Status of users to be counted
3591
     * @param   int     Access URL ID (optional)
3592
     * @return    mixed    Number of users or false on error
3593
     */
3594
    public static function get_number_of_users($status = 0, $access_url_id = 1)
3595
    {
3596
        $t_u = Database::get_main_table(TABLE_MAIN_USER);
3597
        $t_a = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3598
3599
        if (api_is_multiple_url_enabled()) {
3600
            $sql = "SELECT count(u.id) 
3601
                    FROM $t_u u 
3602
                    INNER JOIN $t_a url_user
3603
                    ON (u.id = url_user.user_id)
3604
                    WHERE url_user.access_url_id = $access_url_id                
3605
            ";
3606
        } else {
3607
            $sql = "SELECT count(u.id) 
3608
                    FROM $t_u u
3609
                    WHERE 1 = 1 ";
3610
        }
3611
        if (is_int($status) && $status > 0) {
3612
            $sql .= " AND u.status = $status ";
3613
        }
3614
        $res = Database::query($sql);
3615
        if (Database::num_rows($res) === 1) {
3616
            return (int) Database::result($res, 0, 0);
3617
        }
3618
        return false;
3619
    }
3620
3621
    /**
3622
     * @author Isaac flores <[email protected]>
3623
     * @param string The email administrator
3624
     * @param integer The user id
3625
     * @param string The message title
3626
     * @param string The content message
3627
     */
3628
    public static function send_message_in_outbox(
3629
        $email_administrator,
3630
        $user_id,
3631
        $title,
3632
        $content
3633
    ) {
3634
        $table_message = Database::get_main_table(TABLE_MESSAGE);
3635
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3636
        $title = api_utf8_decode($title);
3637
        $content = api_utf8_decode($content);
3638
        $email_administrator = Database::escape_string($email_administrator);
3639
        //message in inbox
3640
        $sql_message_outbox = 'SELECT id from '.$table_user.' WHERE email="'.$email_administrator.'" ';
3641
        //$num_row_query = Database::num_rows($sql_message_outbox);
3642
        $res_message_outbox = Database::query($sql_message_outbox);
3643
        $array_users_administrator = [];
3644
        while ($row_message_outbox = Database::fetch_array($res_message_outbox, 'ASSOC')) {
3645
            $array_users_administrator[] = $row_message_outbox['id'];
3646
        }
3647
        //allow to insert messages in outbox
3648
        for ($i = 0; $i < count($array_users_administrator); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
3649
            $sql = "INSERT INTO $table_message (user_sender_id, user_receiver_id, msg_status, send_date, title, content ) ".
3650
                " VALUES (".
3651
                "'".(int) $user_id."', '".(int) ($array_users_administrator[$i])."', '4', '".api_get_utc_datetime()."','".Database::escape_string($title)."','".Database::escape_string($content)."'".
3652
                ")";
3653
            Database::query($sql);
3654
        }
3655
    }
3656
3657
    /**
3658
     * Gets the tags of a specific field_id
3659
     * USER TAGS
3660
     *
3661
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
3662
     *
3663
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
3664
     *    Called it "books" for example.
3665
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
3666
     * 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
3667
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
3668
     * 5. Test and enjoy.
3669
     *
3670
     * @param string $tag
3671
     * @param int $field_id field_id
3672
     * @param string $return_format how we are going to result value in array or in a string (json)
3673
     * @param $limit
3674
     *
3675
     * @return mixed
3676
     *
3677
     */
3678
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
3679
    {
3680
        // database table definition
3681
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3682
        $field_id = intval($field_id);
3683
        $limit = intval($limit);
3684
        $tag = trim(Database::escape_string($tag));
3685
3686
        // all the information of the field
3687
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
3688
                WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag LIMIT $limit";
3689
        $result = Database::query($sql);
3690
        $return = [];
3691
        if (Database::num_rows($result) > 0) {
3692
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3693
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
3694
            }
3695
        }
3696
        if ($return_format === 'json') {
3697
            $return = json_encode($return);
3698
        }
3699
3700
        return $return;
3701
    }
3702
3703
    /**
3704
     * @param int $field_id
3705
     * @param int $limit
3706
     *
3707
     * @return array
3708
     */
3709
    public static function get_top_tags($field_id, $limit = 100)
3710
    {
3711
        // database table definition
3712
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3713
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3714
        $field_id = intval($field_id);
3715
        $limit = intval($limit);
3716
        // all the information of the field
3717
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
3718
                INNER JOIN $table_user_tag ut
3719
                ON(ut.id = uv.tag_id)
3720
                WHERE field_id = $field_id
3721
                GROUP BY tag_id
3722
                ORDER BY count DESC
3723
                LIMIT $limit";
3724
        $result = Database::query($sql);
3725
        $return = [];
3726
        if (Database::num_rows($result) > 0) {
3727
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3728
                $return[] = $row;
3729
            }
3730
        }
3731
        return $return;
3732
    }
3733
3734
    /**
3735
     * Get user's tags
3736
     * @param int $user_id
3737
     * @param int $field_id
3738
     *
3739
     * @return array
3740
     */
3741
    public static function get_user_tags($user_id, $field_id)
3742
    {
3743
        // database table definition
3744
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3745
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3746
        $field_id = intval($field_id);
3747
        $user_id = intval($user_id);
3748
3749
        // all the information of the field
3750
        $sql = "SELECT ut.id, tag, count
3751
                FROM $table_user_tag ut
3752
                INNER JOIN $table_user_tag_values uv
3753
                ON (uv.tag_id=ut.ID)
3754
                WHERE field_id = $field_id AND user_id = $user_id
3755
                ORDER BY tag";
3756
        $result = Database::query($sql);
3757
        $return = [];
3758
        if (Database::num_rows($result) > 0) {
3759
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3760
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3761
            }
3762
        }
3763
3764
        return $return;
3765
    }
3766
3767
    /**
3768
     * Get user's tags
3769
     * @param int $user_id
3770
     * @param int $field_id
3771
     * @param bool $show_links show links or not
3772
     *
3773
     * @return array
3774
     */
3775
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
3776
    {
3777
        // database table definition
3778
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3779
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3780
        $field_id = intval($field_id);
3781
        $user_id = intval($user_id);
3782
3783
        // all the information of the field
3784
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
3785
                INNER JOIN $table_user_tag_values uv
3786
                ON (uv.tag_id = ut.id)
3787
                WHERE field_id = $field_id AND user_id = $user_id
3788
                ORDER BY tag";
3789
3790
        $result = Database::query($sql);
3791
        $return = [];
3792
        if (Database::num_rows($result) > 0) {
3793
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3794
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
3795
            }
3796
        }
3797
        $user_tags = $return;
3798
        $tag_tmp = [];
3799
        foreach ($user_tags as $tag) {
3800
            if ($show_links) {
3801
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
3802
                    $tag['tag'].
3803
                '</a>';
3804
            } else {
3805
                $tag_tmp[] = $tag['tag'];
3806
            }
3807
        }
3808
3809
        if (is_array($user_tags) && count($user_tags) > 0) {
3810
            $return = implode(', ', $tag_tmp);
3811
        } else {
3812
            return '';
0 ignored issues
show
Bug Best Practice introduced by
The expression return '' returns the type string which is incompatible with the documented return type array.
Loading history...
3813
        }
3814
3815
        return $return;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $return returns the type string which is incompatible with the documented return type array.
Loading history...
3816
    }
3817
3818
    /**
3819
     * Get the tag id
3820
     * @param int $tag
3821
     * @param int $field_id
3822
     * @return int returns 0 if fails otherwise the tag id
3823
     */
3824
    public static function get_tag_id($tag, $field_id)
3825
    {
3826
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3827
        $tag = Database::escape_string($tag);
3828
        $field_id = intval($field_id);
3829
        //with COLLATE latin1_bin to select query in a case sensitive mode
3830
        $sql = "SELECT id FROM $table_user_tag
3831
                WHERE tag LIKE '$tag' AND field_id = $field_id";
3832
        $result = Database::query($sql);
3833
        if (Database::num_rows($result) > 0) {
3834
            $row = Database::fetch_array($result, 'ASSOC');
3835
3836
            return $row['id'];
3837
        } else {
3838
            return 0;
3839
        }
3840
    }
3841
3842
    /**
3843
     * Get the tag id
3844
     * @param int $tag_id
3845
     * @param int $field_id
3846
     *
3847
     * @return int 0 if fails otherwise the tag id
3848
     */
3849
    public static function get_tag_id_from_id($tag_id, $field_id)
3850
    {
3851
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3852
        $tag_id = intval($tag_id);
3853
        $field_id = intval($field_id);
3854
        $sql = "SELECT id FROM $table_user_tag
3855
                WHERE id = '$tag_id' AND field_id = $field_id";
3856
        $result = Database::query($sql);
3857
        if (Database::num_rows($result) > 0) {
3858
            $row = Database::fetch_array($result, 'ASSOC');
3859
            return $row['id'];
3860
        } else {
3861
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
3862
        }
3863
    }
3864
3865
    /**
3866
     * Adds a user-tag value
3867
     * @param mixed $tag
3868
     * @param int $user_id
3869
     * @param int $field_id field id of the tag
3870
     * @return bool
3871
     */
3872
    public static function add_tag($tag, $user_id, $field_id)
3873
    {
3874
        // database table definition
3875
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3876
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3877
        $tag = trim(Database::escape_string($tag));
3878
        $user_id = intval($user_id);
3879
        $field_id = intval($field_id);
3880
3881
        $tag_id = self::get_tag_id($tag, $field_id);
3882
3883
        /* IMPORTANT
3884
         *  @todo we don't create tags with numbers
3885
         *
3886
         */
3887
        if (is_numeric($tag)) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
3888
            //the form is sending an id this means that the user select it from the list so it MUST exists
3889
            /* $new_tag_id = self::get_tag_id_from_id($tag,$field_id);
3890
              if ($new_tag_id !== false) {
3891
              $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $new_tag_id";
3892
              $result = Database::query($sql);
3893
              $last_insert_id = $new_tag_id;
3894
              } else {
3895
              $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
3896
              $result = Database::query($sql);
3897
              $last_insert_id = Database::insert_id();
3898
              } */
3899
        }
3900
3901
        //this is a new tag
3902
        if ($tag_id == 0) {
3903
            //the tag doesn't exist
3904
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
3905
            Database::query($sql);
3906
            $last_insert_id = Database::insert_id();
3907
        } else {
3908
            //the tag exists we update it
3909
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
3910
            Database::query($sql);
3911
            $last_insert_id = $tag_id;
3912
        }
3913
3914
        if (!empty($last_insert_id) && ($last_insert_id != 0)) {
3915
            //we insert the relationship user-tag
3916
            $sql = "SELECT tag_id FROM $table_user_tag_values
3917
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
3918
            $result = Database::query($sql);
3919
            //if the relationship does not exist we create it
3920
            if (Database::num_rows($result) == 0) {
3921
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
3922
                Database::query($sql);
3923
            }
3924
        }
3925
    }
3926
3927
    /**
3928
     * Deletes an user tag
3929
     * @param int $user_id
3930
     * @param int $field_id
3931
     **/
3932
    public static function delete_user_tags($user_id, $field_id)
3933
    {
3934
        // database table definition
3935
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3936
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3937
        $tags = self::get_user_tags($user_id, $field_id);
3938
        if (is_array($tags) && count($tags) > 0) {
3939
            foreach ($tags as $key => $tag) {
3940
                if ($tag['count'] > '0') {
3941
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
3942
                    Database::query($sql);
3943
                }
3944
                $sql = "DELETE FROM $table_user_tag_values
3945
                        WHERE user_id = $user_id AND tag_id = $key";
3946
                Database::query($sql);
3947
            }
3948
        }
3949
    }
3950
3951
    /**
3952
     * Process the tag list comes from the UserManager::update_extra_field_value() function
3953
     * @param array $tags the tag list that will be added
3954
     * @param int $user_id
3955
     * @param int $field_id
3956
     *
3957
     * @return bool
3958
     */
3959
    public static function process_tags($tags, $user_id, $field_id)
3960
    {
3961
        // We loop the tags and add it to the DB
3962
        if (is_array($tags)) {
3963
            foreach ($tags as $tag) {
3964
                self::add_tag($tag, $user_id, $field_id);
3965
            }
3966
        } else {
3967
            self::add_tag($tags, $user_id, $field_id);
3968
        }
3969
3970
        return true;
3971
    }
3972
3973
    /**
3974
     * Returns a list of all administrators
3975
     *
3976
     * @return array
3977
     */
3978
    public static function get_all_administrators()
3979
    {
3980
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3981
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
3982
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3983
        $access_url_id = api_get_current_access_url_id();
3984
        if (api_get_multiple_access_url()) {
3985
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3986
                    FROM $tbl_url_rel_user as url
3987
                    INNER JOIN $table_admin as admin
3988
                    ON (admin.user_id=url.user_id)
3989
                    INNER JOIN $table_user u
3990
                    ON (u.id=admin.user_id)
3991
                    WHERE access_url_id ='".$access_url_id."'";
3992
        } else {
3993
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3994
                    FROM $table_admin as admin
3995
                    INNER JOIN $table_user u
3996
                    ON (u.id=admin.user_id)";
3997
        }
3998
        $result = Database::query($sql);
3999
        $return = [];
4000
        if (Database::num_rows($result) > 0) {
4001
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4002
                $return[$row['user_id']] = $row;
4003
            }
4004
        }
4005
4006
        return $return;
4007
    }
4008
4009
    /**
4010
     * Search an user (tags, first name, last name and email )
4011
     * @param string $tag
4012
     * @param int $field_id field id of the tag
4013
     * @param int $from where to start in the query
4014
     * @param int $number_of_items
4015
     * @param bool $getCount get count or not
4016
     * @return array
4017
     */
4018
    public static function get_all_user_tags(
4019
        $tag,
4020
        $field_id = 0,
4021
        $from = 0,
4022
        $number_of_items = 10,
4023
        $getCount = false
4024
    ) {
4025
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
4026
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4027
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4028
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4029
4030
        $field_id = intval($field_id);
4031
        $from = intval($from);
4032
        $number_of_items = intval($number_of_items);
4033
4034
        $where_field = "";
4035
        $where_extra_fields = self::get_search_form_where_extra_fields();
4036
        if ($field_id != 0) {
4037
            $where_field = " field_id = $field_id AND ";
4038
        }
4039
4040
        // all the information of the field
4041
        if ($getCount) {
4042
            $select = "SELECT count(DISTINCT u.id) count";
4043
        } else {
4044
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, tag, picture_uri";
4045
        }
4046
4047
        $sql = " $select
4048
                FROM $user_table u
4049
                INNER JOIN $access_url_rel_user_table url_rel_user
4050
                ON (u.id = url_rel_user.user_id)
4051
                LEFT JOIN $table_user_tag_values uv
4052
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
4053
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
4054
                WHERE
4055
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
4056
                    (
4057
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
4058
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
4059
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
4060
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
4061
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
4062
                     )
4063
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
4064
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
4065
4066
        $keyword_active = true;
4067
        // only active users
4068
        if ($keyword_active) {
4069
            $sql .= " AND u.active='1'";
4070
        }
4071
        // avoid anonymous
4072
        $sql .= " AND u.status <> 6 ";
4073
        $sql .= " ORDER BY username";
4074
        $sql .= " LIMIT $from , $number_of_items";
4075
4076
        $result = Database::query($sql);
4077
        $return = [];
4078
4079
        if (Database::num_rows($result) > 0) {
4080
            if ($getCount) {
4081
                $row = Database::fetch_array($result, 'ASSOC');
4082
                return $row['count'];
4083
            }
4084
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4085
                if (isset($return[$row['id']]) &&
4086
                    !empty($return[$row['id']]['tag'])
4087
                ) {
4088
                    $url = Display::url(
4089
                        $row['tag'],
4090
                        api_get_path(WEB_PATH).'main/social/search.php?q='.$row['tag'],
4091
                        ['class' => 'tag']
4092
                    );
4093
                    $row['tag'] = $url;
4094
                }
4095
                $return[$row['id']] = $row;
4096
            }
4097
        }
4098
4099
        return $return;
4100
    }
4101
4102
    /**
4103
      * Get extra filterable user fields (only type select)
4104
      * @return array
4105
      */
4106
    public static function getExtraFilterableFields()
4107
    {
4108
        $extraFieldList = self::get_extra_fields();
4109
        $fields = [];
4110
        if (is_array($extraFieldList)) {
4111
            foreach ($extraFieldList as $extraField) {
4112
                // If is enabled to filter and is a "<select>" field type
4113
                if ($extraField[8] == 1 && $extraField[2] == 4) {
4114
                    $fields[] = [
4115
                        'name' => $extraField[3],
4116
                        'variable' => $extraField[1],
4117
                        'data' => $extraField[9]
4118
                    ];
4119
                }
4120
            }
4121
        }
4122
4123
        if (is_array($fields) && count($fields) > 0) {
4124
            return $fields;
4125
        }
4126
    }
4127
4128
    /**
4129
      * Get extra where clauses for finding users based on extra filtrable user fields (type select)
4130
      * @return string With AND clauses based on user's ID which have the values to search in extra user fields
4131
      */
4132
    public static function get_search_form_where_extra_fields()
4133
    {
4134
        $useExtraFields = false;
4135
        $extraFields = self::getExtraFilterableFields();
4136
        $extraFieldResult = [];
4137
        if (is_array($extraFields) && count($extraFields) > 0) {
4138
            foreach ($extraFields as $extraField) {
4139
                $varName = 'field_'.$extraField['variable'];
4140
                if (self::is_extra_field_available($extraField['variable'])) {
4141
                    if (isset($_GET[$varName]) && $_GET[$varName] != '0') {
4142
                        $useExtraFields = true;
4143
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
4144
                            $extraField['variable'],
4145
                            $_GET[$varName]
4146
                        );
4147
                    }
4148
                }
4149
            }
4150
        }
4151
4152
        if ($useExtraFields) {
4153
            $finalResult = [];
4154
            if (count($extraFieldResult) > 1) {
4155
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
4156
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
4157
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
4158
                    }
4159
                }
4160
            } else {
4161
                $finalResult = $extraFieldResult[0];
4162
            }
4163
4164
            if (is_array($finalResult) && count($finalResult) > 0) {
4165
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
4166
            } else {
4167
                //no results
4168
                $whereFilter = " AND u.id  = -1 ";
4169
            }
4170
4171
            return $whereFilter;
4172
        }
4173
    }
4174
4175
    /**
4176
     * Show the search form
4177
     * @param string $query the value of the search box
4178
     * @return string HTML form
4179
     */
4180
    public static function get_search_form($query, $defaultParams = [])
4181
    {
4182
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
4183
        $form = new FormValidator(
4184
            'search_user',
4185
            'get',
4186
            api_get_path(WEB_PATH).'main/social/search.php',
4187
            '',
4188
            [],
4189
            FormValidator::LAYOUT_HORIZONTAL
4190
        );
4191
4192
        $form->addText('q', get_lang('UsersGroups'), false, [
4193
            "id" => "q"
4194
        ]);
4195
        $options = [
4196
            0 => get_lang('Select'),
4197
            1 => get_lang('User'),
4198
            2 => get_lang('Group'),
4199
        ];
4200
        $form->addSelect(
4201
            'search_type',
4202
            get_lang('Type'),
4203
            $options,
4204
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
4205
        );
4206
4207
        // Extra fields
4208
        $extraFields = self::getExtraFilterableFields();
4209
        $defaults = [];
4210
        if (is_array($extraFields) && count($extraFields) > 0) {
4211
            foreach ($extraFields as $extraField) {
4212
                $varName = 'field_'.$extraField['variable'];
4213
                $options = [
4214
                    0 => get_lang('Select')
4215
                ];
4216
                foreach ($extraField['data'] as $option) {
4217
                    $checked = '';
4218
                    if (isset($_GET[$varName])) {
4219
                        if ($_GET[$varName] == $option[1]) {
4220
                            $defaults[$option[1]] = true;
4221
                        }
4222
                    }
4223
4224
                    $options[$option[1]] = $option[1];
4225
                }
4226
                $form->addSelect($varName, $extraField['name'], $options);
4227
            }
4228
        }
4229
4230
        $defaults['search_type'] = intval($searchType);
4231
        $defaults['q'] = api_htmlentities(Security::remove_XSS($query));
4232
4233
        if (!empty($defaultParams)) {
4234
            $defaults = array_merge($defaults, $defaultParams);
4235
        }
4236
        $form->setDefaults($defaults);
4237
        $form->addButtonSearch(get_lang('Search'));
4238
4239
        $js = '<script>
4240
        extra_field_toogle();
4241
        function extra_field_toogle() {
4242
            if (jQuery("select[name=search_type]").val() != "1") { jQuery(".extra_field").hide(); } else { jQuery(".extra_field").show(); }
4243
        }
4244
        </script>';
4245
4246
        return $js.$form->returnForm();
4247
    }
4248
4249
    /**
4250
     * Shows the user menu
4251
     */
4252
    public static function show_menu()
4253
    {
4254
        echo '<div class="actions">';
4255
        echo '<a href="/main/auth/profile.php">'.Display::return_icon('profile.png').' '.get_lang('PersonalData').'</a>';
4256
        echo '<a href="/main/messages/inbox.php">'.Display::return_icon('inbox.png').' '.get_lang('Inbox').'</a>';
4257
        echo '<a href="/main/messages/outbox.php">'.Display::return_icon('outbox.png').' '.get_lang('Outbox').'</a>';
4258
        echo '<span style="float:right; padding-top:7px;">'.
4259
        '<a href="/main/auth/profile.php?show=1">'.Display::return_icon('edit.gif').' '.get_lang('Configuration').'</a>';
4260
        '</span>';
4261
        echo '</div>';
4262
    }
4263
4264
    /**
4265
     * Allow to register contact to social network
4266
     * @param int $friend_id user friend id
4267
     * @param int $my_user_id user id
4268
     * @param int $relation_type relation between users see constants definition
4269
     * @return bool
4270
     */
4271
    public static function relate_users($friend_id, $my_user_id, $relation_type)
4272
    {
4273
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4274
4275
        $friend_id = intval($friend_id);
4276
        $my_user_id = intval($my_user_id);
4277
        $relation_type = intval($relation_type);
4278
4279
        $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
4280
                WHERE
4281
                    friend_user_id='.$friend_id.' AND
4282
                    user_id='.$my_user_id.' AND
4283
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
4284
        $result = Database::query($sql);
4285
        $row = Database::fetch_array($result, 'ASSOC');
4286
        $current_date = api_get_utc_datetime();
4287
4288
        if ($row['count'] == 0) {
4289
            $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
4290
                    VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
4291
            Database::query($sql);
4292
            return true;
4293
        }
4294
4295
        $sql = 'SELECT COUNT(*) as count, relation_type  FROM '.$tbl_my_friend.'
4296
                WHERE
4297
                    friend_user_id='.$friend_id.' AND
4298
                    user_id='.$my_user_id.' AND
4299
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
4300
        $result = Database::query($sql);
4301
        $row = Database::fetch_array($result, 'ASSOC');
4302
4303
        if ($row['count'] == 1) {
4304
            //only for the case of a RRHH or a Student BOSS
4305
            if ($row['relation_type'] != $relation_type && ($relation_type == USER_RELATION_TYPE_RRHH || $relation_type == USER_RELATION_TYPE_BOSS)) {
4306
                $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
4307
                        VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
4308
            } else {
4309
                $sql = 'UPDATE '.$tbl_my_friend.' SET relation_type='.$relation_type.'
4310
                        WHERE friend_user_id='.$friend_id.' AND user_id='.$my_user_id;
4311
            }
4312
            Database::query($sql);
4313
4314
            return true;
4315
        }
4316
4317
        return false;
4318
    }
4319
4320
    /**
4321
     * Deletes a contact
4322
     * @param int user friend id
4323
     * @param bool true will delete ALL friends relationship from $friend_id
4324
     * @param string $with_status_condition
4325
     * @author isaac flores paz <[email protected]>
4326
     * @author Julio Montoya <[email protected]> Cleaning code
4327
     */
4328
    public static function remove_user_rel_user(
4329
        $friend_id,
4330
        $real_removed = false,
4331
        $with_status_condition = ''
4332
    ) {
4333
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4334
        $tbl_my_message = Database::get_main_table(TABLE_MESSAGE);
4335
        $friend_id = intval($friend_id);
4336
4337
        if ($real_removed) {
4338
            $extra_condition = '';
4339
            if ($with_status_condition != '') {
4340
                $extra_condition = ' AND relation_type = '.intval($with_status_condition);
4341
            }
4342
            $sql = 'DELETE FROM '.$tbl_my_friend.'
4343
                    WHERE 
4344
                        relation_type <> '.USER_RELATION_TYPE_RRHH.' AND 
4345
                        friend_user_id='.$friend_id.' '.$extra_condition;
4346
            Database::query($sql);
4347
            $sql = 'DELETE FROM '.$tbl_my_friend.'
4348
                   WHERE 
4349
                    relation_type <> '.USER_RELATION_TYPE_RRHH.' AND 
4350
                    user_id='.$friend_id.' '.$extra_condition;
4351
            Database::query($sql);
4352
        } else {
4353
            $user_id = api_get_user_id();
4354
            $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
4355
                    WHERE
4356
                        user_id='.$user_id.' AND
4357
                        relation_type NOT IN('.USER_RELATION_TYPE_DELETED.', '.USER_RELATION_TYPE_RRHH.') AND
4358
                        friend_user_id='.$friend_id;
4359
            $result = Database::query($sql);
4360
            $row = Database::fetch_array($result, 'ASSOC');
4361
            if ($row['count'] == 1) {
4362
                //Delete user rel user
4363
                $sql_i = 'UPDATE '.$tbl_my_friend.' SET relation_type='.USER_RELATION_TYPE_DELETED.'
4364
                          WHERE user_id='.$user_id.' AND friend_user_id='.$friend_id;
4365
                $sql_j = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
4366
                          WHERE user_receiver_id='.$user_id.' AND user_sender_id='.$friend_id.' AND update_date="0000-00-00 00:00:00" ';
4367
                //Delete user
4368
                $sql_ij = 'UPDATE '.$tbl_my_friend.'  SET relation_type='.USER_RELATION_TYPE_DELETED.'
4369
                           WHERE user_id='.$friend_id.' AND friend_user_id='.$user_id;
4370
                $sql_ji = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
4371
                           WHERE user_receiver_id='.$friend_id.' AND user_sender_id='.$user_id.' AND update_date="0000-00-00 00:00:00" ';
4372
                Database::query($sql_i);
4373
                Database::query($sql_j);
4374
                Database::query($sql_ij);
4375
                Database::query($sql_ji);
4376
            }
4377
        }
4378
    }
4379
4380
    /**
4381
     * @param int $userId
4382
     * @return array
4383
     */
4384
    public static function getDrhListFromUser($userId)
4385
    {
4386
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4387
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4388
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4389
        $userId = intval($userId);
4390
4391
        $orderBy = null;
4392
        if (api_is_western_name_order()) {
4393
            $orderBy .= " ORDER BY firstname, lastname ";
4394
        } else {
4395
            $orderBy .= " ORDER BY lastname, firstname ";
4396
        }
4397
4398
        $sql = "SELECT u.id, username, u.firstname, u.lastname
4399
                FROM $tblUser u
4400
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
4401
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
4402
                WHERE
4403
                    access_url_id = ".api_get_current_access_url_id()." AND
4404
                    uru.user_id = '$userId' AND
4405
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4406
                $orderBy
4407
                ";
4408
        $result = Database::query($sql);
4409
4410
        return Database::store_result($result);
4411
    }
4412
4413
    /**
4414
     * get users followed by human resource manager
4415
     * @param int $userId
4416
     * @param int $userStatus (STUDENT, COURSEMANAGER, etc)
4417
     * @param bool $getOnlyUserId
4418
     * @param bool $getSql
4419
     * @param bool $getCount
4420
     * @param int $from
4421
     * @param int $numberItems
4422
     * @param int $column
4423
     * @param string $direction
4424
     * @param int $active
4425
     * @param string $lastConnectionDate
4426
     * @return array     users
4427
     */
4428
    public static function get_users_followed_by_drh(
4429
        $userId,
4430
        $userStatus = 0,
4431
        $getOnlyUserId = false,
4432
        $getSql = false,
4433
        $getCount = false,
4434
        $from = null,
4435
        $numberItems = null,
4436
        $column = null,
4437
        $direction = null,
4438
        $active = null,
4439
        $lastConnectionDate = null
4440
    ) {
4441
        return self::getUsersFollowedByUser(
4442
            $userId,
4443
            $userStatus,
4444
            $getOnlyUserId,
4445
            $getSql,
4446
            $getCount,
4447
            $from,
4448
            $numberItems,
4449
            $column,
4450
            $direction,
4451
            $active,
4452
            $lastConnectionDate,
4453
            DRH
4454
        );
4455
    }
4456
4457
    /**
4458
    * Get users followed by human resource manager
4459
    * @param int $userId
4460
    * @param int  $userStatus Filter users by status (STUDENT, COURSEMANAGER, etc)
4461
    * @param bool $getOnlyUserId
4462
    * @param bool $getSql
4463
    * @param bool $getCount
4464
    * @param int $from
4465
    * @param int $numberItems
4466
    * @param int $column
4467
    * @param string $direction
4468
    * @param int $active
4469
    * @param string $lastConnectionDate
4470
    * @param int $status the function is called by who? COURSEMANAGER, DRH?
4471
    * @param string $keyword
4472
     *
4473
    * @return array user list
4474
    */
4475
    public static function getUsersFollowedByUser(
4476
        $userId,
4477
        $userStatus = null,
4478
        $getOnlyUserId = false,
4479
        $getSql = false,
4480
        $getCount = false,
4481
        $from = null,
4482
        $numberItems = null,
4483
        $column = null,
4484
        $direction = null,
4485
        $active = null,
4486
        $lastConnectionDate = null,
4487
        $status = null,
4488
        $keyword = null
4489
    ) {
4490
        // Database Table Definitions
4491
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4492
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4493
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4494
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4495
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4496
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4497
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4498
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4499
4500
        $userId = intval($userId);
4501
        $limitCondition = '';
4502
4503
        if (isset($from) && isset($numberItems)) {
4504
            $from = intval($from);
4505
            $numberItems = intval($numberItems);
4506
            $limitCondition = "LIMIT $from, $numberItems";
4507
        }
4508
4509
        $column = Database::escape_string($column);
4510
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
4511
4512
        $userConditions = '';
4513
        if (!empty($userStatus)) {
4514
            $userConditions .= ' AND u.status = '.intval($userStatus);
4515
        }
4516
4517
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
4518
        if ($getOnlyUserId) {
4519
            $select = " SELECT DISTINCT u.id user_id";
4520
        }
4521
4522
        $masterSelect = "SELECT DISTINCT * FROM ";
4523
4524
        if ($getCount) {
4525
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
4526
            $select = " SELECT DISTINCT(u.id) user_id";
4527
        }
4528
4529
        if (!is_null($active)) {
4530
            $active = intval($active);
4531
            $userConditions .= " AND u.active = $active ";
4532
        }
4533
4534
        if (!empty($keyword)) {
4535
            $keyword = Database::escape_string($keyword);
4536
            $userConditions .= " AND (
4537
                u.username LIKE '%$keyword%' OR
4538
                u.firstname LIKE '%$keyword%' OR
4539
                u.lastname LIKE '%$keyword%' OR
4540
                u.official_code LIKE '%$keyword%' OR
4541
                u.email LIKE '%$keyword%'
4542
            )";
4543
        }
4544
4545
        if (!empty($lastConnectionDate)) {
4546
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
4547
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
4548
        }
4549
4550
        $courseConditions = null;
4551
        $sessionConditionsCoach = null;
4552
        $sessionConditionsTeacher = null;
4553
        $drhConditions = null;
4554
        $teacherSelect = null;
4555
4556
        switch ($status) {
4557
            case DRH:
4558
                $drhConditions .= " AND
4559
                    friend_user_id = '$userId' AND
4560
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4561
                ";
4562
                break;
4563
            case COURSEMANAGER:
4564
                $drhConditions .= " AND
4565
                    friend_user_id = '$userId' AND
4566
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4567
                ";
4568
4569
                $sessionConditionsCoach .= " AND
4570
                    (s.id_coach = '$userId')
4571
                ";
4572
4573
                $sessionConditionsTeacher .= " AND
4574
                    (scu.status = 2 AND scu.user_id = '$userId')
4575
                ";
4576
4577
                $teacherSelect =
4578
                "UNION ALL (
4579
                        $select
4580
                        FROM $tbl_user u
4581
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
4582
                        WHERE
4583
                            (
4584
                                sru.session_id IN (
4585
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
4586
                                    $tbl_session_rel_access_url session_rel_access_rel_user
4587
                                    ON session_rel_access_rel_user.session_id = s.id
4588
                                    WHERE access_url_id = ".api_get_current_access_url_id()."
4589
                                    $sessionConditionsCoach                                  
4590
                                ) OR sru.session_id IN (
4591
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
4592
                                    INNER JOIN $tbl_session_rel_access_url url
4593
                                    ON (url.session_id = s.id)
4594
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
4595
                                    ON (scu.session_id = s.id)
4596
                                    WHERE access_url_id = ".api_get_current_access_url_id()."
4597
                                    $sessionConditionsTeacher
4598
                                )
4599
                            )                            
4600
                            $userConditions
4601
                    )
4602
                    UNION ALL(
4603
                        $select
4604
                        FROM $tbl_user u
4605
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
4606
                        WHERE cu.c_id IN (
4607
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
4608
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
4609
                        )
4610
                        $userConditions
4611
                    )"
4612
                ;
4613
                break;
4614
            case STUDENT_BOSS:
4615
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
4616
                break;
4617
            case HRM_REQUEST:
4618
                $drhConditions .= " AND
4619
                    friend_user_id = '$userId' AND
4620
                    relation_type = '".USER_RELATION_TYPE_HRM_REQUEST."'
4621
                ";
4622
                break;
4623
        }
4624
4625
        $join = null;
4626
        $sql = " $masterSelect
4627
                (
4628
                    (
4629
                        $select
4630
                        FROM $tbl_user u
4631
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
4632
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
4633
                        $join
4634
                        WHERE
4635
                            access_url_id = ".api_get_current_access_url_id()."
4636
                            $drhConditions
4637
                            $userConditions
4638
                    )
4639
                    $teacherSelect
4640
4641
                ) as t1";
4642
4643
        if ($getSql) {
4644
            return $sql;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $sql returns the type string which is incompatible with the documented return type array.
Loading history...
4645
        }
4646
        if ($getCount) {
4647
            $result = Database::query($sql);
4648
            $row = Database::fetch_array($result);
4649
            return $row['count'];
4650
        }
4651
4652
        $orderBy = null;
4653
        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...
4654
            if (api_is_western_name_order()) {
4655
                $orderBy .= " ORDER BY firstname, lastname ";
4656
            } else {
4657
                $orderBy .= " ORDER BY lastname, firstname ";
4658
            }
4659
4660
            if (!empty($column) && !empty($direction)) {
4661
                // Fixing order due the UNIONs
4662
                $column = str_replace('u.', '', $column);
4663
                $orderBy = " ORDER BY $column $direction ";
4664
            }
4665
        }
4666
4667
        $sql .= $orderBy;
4668
        $sql .= $limitCondition;
4669
4670
        $result = Database::query($sql);
4671
        $users = [];
4672
        if (Database::num_rows($result) > 0) {
4673
            while ($row = Database::fetch_array($result)) {
4674
                $users[$row['user_id']] = $row;
4675
            }
4676
        }
4677
4678
        return $users;
4679
    }
4680
4681
    /**
4682
     * Subscribes users to human resource manager (Dashboard feature)
4683
     * @param int $hr_dept_id
4684
     * @param array $users_id
4685
     * @param bool $deleteOtherAssignedUsers
4686
     * @return int
4687
     */
4688
    public static function subscribeUsersToHRManager(
4689
        $hr_dept_id,
4690
        $users_id,
4691
        $deleteOtherAssignedUsers = true
4692
    ) {
4693
        return self::subscribeUsersToUser(
4694
            $hr_dept_id,
4695
            $users_id,
4696
            USER_RELATION_TYPE_RRHH,
4697
            false,
4698
            $deleteOtherAssignedUsers
4699
        );
4700
    }
4701
4702
    /**
4703
     * Register request to assign users to HRM
4704
     * @param int $hrmId The HRM ID
4705
     * @param int $usersId The users ID
4706
     * @return int
4707
     */
4708
    public static function requestUsersToHRManager($hrmId, $usersId)
4709
    {
4710
        return self::subscribeUsersToUser(
4711
            $hrmId,
4712
            $usersId,
4713
            USER_RELATION_TYPE_HRM_REQUEST,
4714
            false,
4715
            false
4716
        );
4717
    }
4718
4719
    /**
4720
     * Remove the requests for assign a user to a HRM
4721
     * @param User $hrmId
4722
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
4723
     */
4724
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
4725
    {
4726
        $users = implode(', ', $usersId);
4727
        Database::getManager()
4728
            ->createQuery('
4729
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
4730
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
4731
            ')
4732
            ->execute(['hrm_id' => $hrmId, 'relation_type' => USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
4733
    }
4734
4735
    /**
4736
     * Add subscribed users to a user by relation type
4737
     * @param int $userId The user id
4738
     * @param array $subscribedUsersId The id of suscribed users
4739
     * @param string $relationType The relation type
4740
     * @param bool $deleteUsersBeforeInsert
4741
     * @param bool $deleteOtherAssignedUsers
4742
     * @return int
4743
     */
4744
    public static function subscribeUsersToUser(
4745
        $userId,
4746
        $subscribedUsersId,
4747
        $relationType,
4748
        $deleteUsersBeforeInsert = false,
4749
        $deleteOtherAssignedUsers = true
4750
    ) {
4751
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4752
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4753
4754
        $userId = intval($userId);
4755
        $relationType = intval($relationType);
4756
        $affectedRows = 0;
4757
4758
        if ($deleteOtherAssignedUsers) {
4759
            if (api_get_multiple_access_url()) {
4760
                // Deleting assigned users to hrm_id
4761
                $sql = "SELECT s.user_id 
4762
                        FROM $userRelUserTable s 
4763
                        INNER JOIN $userRelAccessUrlTable a
4764
                        ON (a.user_id = s.user_id) 
4765
                        WHERE 
4766
                            friend_user_id = $userId AND 
4767
                            relation_type = $relationType AND 
4768
                            access_url_id = ".api_get_current_access_url_id();
4769
            } else {
4770
                $sql = "SELECT user_id 
4771
                        FROM $userRelUserTable 
4772
                        WHERE 
4773
                            friend_user_id = $userId AND 
4774
                            relation_type = $relationType";
4775
            }
4776
            $result = Database::query($sql);
4777
4778
            if (Database::num_rows($result) > 0) {
4779
                while ($row = Database::fetch_array($result)) {
4780
                    $sql = "DELETE FROM $userRelUserTable 
4781
                            WHERE
4782
                                user_id = {$row['user_id']} AND 
4783
                                friend_user_id = $userId AND 
4784
                                relation_type = $relationType";
4785
                    Database::query($sql);
4786
                }
4787
            }
4788
        }
4789
4790
        if ($deleteUsersBeforeInsert) {
4791
            $sql = "DELETE FROM $userRelUserTable 
4792
                    WHERE 
4793
                        user_id = $userId AND
4794
                        relation_type = $relationType";
4795
            Database::query($sql);
4796
        }
4797
4798
        // Inserting new user list
4799
        if (is_array($subscribedUsersId)) {
4800
            foreach ($subscribedUsersId as $subscribedUserId) {
4801
                $subscribedUserId = intval($subscribedUserId);
4802
                $sql = "SELECT id FROM $userRelUserTable
4803
                        WHERE user_id = $subscribedUserId
4804
                        AND friend_user_id = $userId
4805
                        AND relation_type = $relationType";
4806
                $result = Database::query($sql);
4807
                $num = Database::num_rows($result);
4808
                if ($num === 0) {
4809
                    $date = api_get_utc_datetime();
4810
                    $sql = "INSERT INTO $userRelUserTable (user_id, friend_user_id, relation_type, last_edit)
4811
                        VALUES ($subscribedUserId, $userId, $relationType, '$date')";
4812
                    $result = Database::query($sql);
4813
                    $affectedRows += Database::affected_rows($result);
4814
                }
4815
            }
4816
        }
4817
4818
        return $affectedRows;
4819
    }
4820
4821
    /**
4822
     * This function check if an user is followed by human resources manager
4823
     * @param     int     $user_id
4824
     * @param    int      $hr_dept_id  Human resources manager
4825
     * @return    bool
4826
     */
4827
    public static function is_user_followed_by_drh($user_id, $hr_dept_id)
4828
    {
4829
        // Database table and variables Definitions
4830
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4831
        $user_id = intval($user_id);
4832
        $hr_dept_id = intval($hr_dept_id);
4833
        $result = false;
4834
4835
        $sql = "SELECT user_id FROM $tbl_user_rel_user
4836
                WHERE
4837
                    user_id = $user_id AND
4838
                    friend_user_id = $hr_dept_id AND
4839
                    relation_type = ".USER_RELATION_TYPE_RRHH;
4840
        $rs = Database::query($sql);
4841
        if (Database::num_rows($rs) > 0) {
4842
            $result = true;
4843
        }
4844
        return $result;
4845
    }
4846
4847
    /**
4848
     * get user id of teacher or session administrator
4849
     * @param array $courseInfo
4850
     *
4851
     * @return int The user id
4852
     */
4853
    public static function get_user_id_of_course_admin_or_session_admin($courseInfo)
4854
    {
4855
        $session = api_get_session_id();
4856
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4857
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4858
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4859
        $courseId = $courseInfo['real_id'];
4860
4861
        if ($session == 0 || is_null($session)) {
4862
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
4863
                    INNER JOIN '.$table_course_user.' ru
4864
                    ON ru.user_id = u.id
4865
                    WHERE
4866
                        ru.status = 1 AND
4867
                        ru.c_id = "'.$courseId.'" ';
4868
            $rs = Database::query($sql);
4869
            $num_rows = Database::num_rows($rs);
4870
            if ($num_rows == 1) {
4871
                $row = Database::fetch_array($rs);
4872
                return $row['uid'];
4873
            } else {
4874
                $my_num_rows = $num_rows;
4875
                $my_user_id = Database::result($rs, $my_num_rows - 1, 'uid');
4876
4877
                return $my_user_id;
4878
            }
4879
        } elseif ($session > 0) {
4880
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
4881
                    INNER JOIN '.$table_session_course_user.' sru
4882
                    ON sru.user_id=u.id
4883
                    WHERE
4884
                        sru.c_id="'.$courseId.'" AND
4885
                        sru.status=2';
4886
            $rs = Database::query($sql);
4887
            $row = Database::fetch_array($rs);
4888
4889
            return $row['uid'];
4890
        }
4891
    }
4892
4893
    /**
4894
     * Determines if a user is a gradebook certified
4895
     * @param int $cat_id The category id of gradebook
4896
     * @param int $user_id The user id
4897
     * @return boolean
4898
     */
4899
    public static function is_user_certified($cat_id, $user_id)
4900
    {
4901
        $table_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4902
        $sql = 'SELECT path_certificate FROM '.$table_certificate.'
4903
                WHERE
4904
                    cat_id="'.intval($cat_id).'" AND
4905
                    user_id="'.intval($user_id).'"';
4906
        $rs = Database::query($sql);
4907
        $row = Database::fetch_array($rs);
4908
        if ($row['path_certificate'] == '' || is_null($row['path_certificate'])) {
4909
            return false;
4910
        } else {
4911
            return true;
4912
        }
4913
    }
4914
4915
    /**
4916
     * Gets the info about a gradebook certificate for a user by course
4917
     * @param string $course_code The course code
4918
     * @param int $user_id The user id
4919
     * @return array  if there is not information return false
4920
     */
4921
    public static function get_info_gradebook_certificate($course_code, $user_id)
4922
    {
4923
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4924
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
4925
        $session_id = api_get_session_id();
4926
4927
        if (empty($session_id)) {
4928
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
4929
        } else {
4930
            $session_condition = " AND session_id = $session_id";
4931
        }
4932
4933
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.' 
4934
                WHERE cat_id = (
4935
                    SELECT id FROM '.$tbl_grade_category.'
4936
                    WHERE
4937
                        course_code = "'.Database::escape_string($course_code).'" '.$session_condition.' 
4938
                    LIMIT 1 
4939
                ) AND user_id='.intval($user_id);
4940
4941
        $rs = Database::query($sql);
4942
        if (Database::num_rows($rs) > 0) {
4943
            $row = Database::fetch_array($rs, 'ASSOC');
4944
            $score = $row['score_certificate'];
4945
            $category_id = $row['cat_id'];
4946
            $cat = Category::load($category_id);
4947
            $displayscore = ScoreDisplay::instance();
4948
            if (isset($cat) && $displayscore->is_custom()) {
4949
                $grade = $displayscore->display_score(
4950
                    [$score, $cat[0]->get_weight()],
4951
                    SCORE_DIV_PERCENT_WITH_CUSTOM
4952
                );
4953
            } else {
4954
                $grade = $displayscore->display_score(
4955
                    [$score, $cat[0]->get_weight()]
4956
                );
4957
            }
4958
            $row['grade'] = $grade;
4959
4960
            return $row;
4961
        }
4962
4963
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
4964
    }
4965
4966
    /**
4967
     * Gets the user path of user certificated
4968
     * @param int The user id
4969
     * @return array  containing path_certificate and cat_id
4970
     */
4971
    public static function get_user_path_certificate($user_id)
4972
    {
4973
        $my_certificate = [];
4974
        $table_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
4975
        $table_gradebook_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
4976
4977
        $session_id = api_get_session_id();
4978
        $user_id = intval($user_id);
4979
        if ($session_id == 0 || is_null($session_id)) {
4980
            $sql_session = 'AND (session_id='.intval($session_id).' OR isnull(session_id)) ';
4981
        } elseif ($session_id > 0) {
4982
            $sql_session = 'AND session_id='.intval($session_id);
4983
        } else {
4984
            $sql_session = '';
4985
        }
4986
        $sql = "SELECT tc.path_certificate,tc.cat_id,tgc.course_code,tgc.name
4987
                FROM $table_certificate tc, $table_gradebook_category tgc
4988
                WHERE tgc.id = tc.cat_id AND tc.user_id = $user_id
4989
                ORDER BY tc.date_certificate DESC 
4990
                LIMIT 5";
4991
4992
        $rs = Database::query($sql);
4993
        while ($row = Database::fetch_array($rs)) {
4994
            $my_certificate[] = $row;
4995
        }
4996
        return $my_certificate;
4997
    }
4998
4999
    /**
5000
     * This function check if the user is a coach inside session course
5001
     * @param  int  $user_id    User id
5002
     * @param  int  $courseId
5003
     * @param  int  $session_id
5004
     * @return bool    True if the user is a coach
5005
     *
5006
     */
5007
    public static function is_session_course_coach($user_id, $courseId, $session_id)
5008
    {
5009
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5010
        // Protect data
5011
        $user_id = intval($user_id);
5012
        $courseId = intval($courseId);
5013
        $session_id = intval($session_id);
5014
        $result = false;
5015
5016
        $sql = "SELECT session_id FROM $table
5017
                WHERE
5018
                  session_id = $session_id AND
5019
                  c_id = $courseId AND
5020
                  user_id = $user_id AND
5021
                  status = 2 ";
5022
        $res = Database::query($sql);
5023
5024
        if (Database::num_rows($res) > 0) {
5025
            $result = true;
5026
        }
5027
        return $result;
5028
    }
5029
5030
    /**
5031
     * This function returns an icon path that represents the favicon of the website of which the url given.
5032
     * Defaults to the current Chamilo favicon
5033
     * @param    string    $url1 URL of website where to look for favicon.ico
5034
     * @param    string    $url2 Optional second URL of website where to look for favicon.ico
5035
     * @return    string    Path of icon to load
5036
     */
5037
    public static function get_favicon_from_url($url1, $url2 = null)
5038
    {
5039
        $icon_link = '';
5040
        $url = $url1;
5041
        if (empty($url1)) {
5042
            $url = $url2;
5043
            if (empty($url)) {
5044
                $url = api_get_access_url(api_get_current_access_url_id());
5045
                $url = $url[0];
5046
            }
5047
        }
5048
        if (!empty($url)) {
5049
            $pieces = parse_url($url);
5050
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
5051
        }
5052
        return $icon_link;
5053
    }
5054
5055
    /**
5056
     *
5057
     * @param int $student_id
5058
     * @param int $years
5059
     * @param bool $warning_message  show warning_message
5060
     * @param bool $return_timestamp return_timestamp
5061
     */
5062
    public static function delete_inactive_student(
5063
        $student_id,
5064
        $years = 2,
5065
        $warning_message = false,
5066
        $return_timestamp = false
5067
    ) {
5068
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
5069
        $sql = 'SELECT login_date FROM '.$tbl_track_login.'
5070
                WHERE login_user_id = '.intval($student_id).'
5071
                ORDER BY login_date DESC LIMIT 0,1';
5072
        if (empty($years)) {
5073
            $years = 1;
5074
        }
5075
        $inactive_time = $years * 31536000; //1 year
5076
        $rs = Database::query($sql);
5077
        if (Database::num_rows($rs) > 0) {
5078
            if ($last_login_date = Database::result($rs, 0, 0)) {
5079
                $last_login_date = api_get_local_time(
5080
                    $last_login_date,
5081
                    null,
5082
                    date_default_timezone_get()
5083
                );
5084
                if ($return_timestamp) {
5085
                    return api_strtotime($last_login_date);
5086
                } else {
5087
                    if (!$warning_message) {
5088
                        return api_format_date($last_login_date, DATE_FORMAT_SHORT);
5089
                    } else {
5090
                        $timestamp = api_strtotime($last_login_date);
5091
                        $currentTimestamp = time();
5092
5093
                        //If the last connection is > than 7 days, the text is red
5094
                        //345600 = 7 days in seconds 63072000= 2 ans
5095
                        // if ($currentTimestamp - $timestamp > 184590 )
5096
                        if ($currentTimestamp - $timestamp > $inactive_time && self::delete_user($student_id)) {
5097
                            echo Display::return_message(get_lang('UserDeleted'));
5098
                            echo '<p>', 'id', $student_id, ':', $last_login_date, '</p>';
5099
                        }
5100
                    }
5101
                }
5102
            }
5103
        }
5104
        return false;
5105
    }
5106
5107
    /**
5108
     * @return array
5109
     */
5110
    public static function get_user_field_types()
5111
    {
5112
        $types = [];
5113
        $types[self::USER_FIELD_TYPE_TEXT] = get_lang('FieldTypeText');
5114
        $types[self::USER_FIELD_TYPE_TEXTAREA] = get_lang('FieldTypeTextarea');
5115
        $types[self::USER_FIELD_TYPE_RADIO] = get_lang('FieldTypeRadio');
5116
        $types[self::USER_FIELD_TYPE_SELECT] = get_lang('FieldTypeSelect');
5117
        $types[self::USER_FIELD_TYPE_SELECT_MULTIPLE] = get_lang('FieldTypeSelectMultiple');
5118
        $types[self::USER_FIELD_TYPE_DATE] = get_lang('FieldTypeDate');
5119
        $types[self::USER_FIELD_TYPE_DATETIME] = get_lang('FieldTypeDatetime');
5120
        $types[self::USER_FIELD_TYPE_DOUBLE_SELECT] = get_lang('FieldTypeDoubleSelect');
5121
        $types[self::USER_FIELD_TYPE_DIVIDER] = get_lang('FieldTypeDivider');
5122
        $types[self::USER_FIELD_TYPE_TAG] = get_lang('FieldTypeTag');
5123
        $types[self::USER_FIELD_TYPE_TIMEZONE] = get_lang('FieldTypeTimezone');
5124
        $types[self::USER_FIELD_TYPE_SOCIAL_PROFILE] = get_lang('FieldTypeSocialProfile');
5125
        $types[self::USER_FIELD_TYPE_FILE] = get_lang('FieldTypeFile');
5126
        $types[self::USER_FIELD_TYPE_MOBILE_PHONE_NUMBER] = get_lang('FieldTypeMobilePhoneNumber');
5127
5128
        return $types;
5129
    }
5130
5131
    /**
5132
     * @param User $user
5133
     */
5134
    public static function add_user_as_admin(User $user)
5135
    {
5136
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
5137
        if ($user) {
5138
            $userId = $user->getId();
5139
5140
            if (!self::is_admin($userId)) {
5141
                $sql = "INSERT INTO $table_admin SET user_id = $userId";
5142
                Database::query($sql);
5143
            }
5144
5145
            $user->addRole('ROLE_SUPER_ADMIN');
5146
            self::getManager()->updateUser($user, true);
5147
        }
5148
    }
5149
5150
    /**
5151
     * @param int $userId
5152
     */
5153
    public static function remove_user_admin($userId)
5154
    {
5155
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
5156
        $userId = intval($userId);
5157
        if (self::is_admin($userId)) {
5158
            $sql = "DELETE FROM $table_admin WHERE user_id = $userId";
5159
            Database::query($sql);
5160
        }
5161
    }
5162
5163
    /**
5164
     * @param string $from
5165
     * @param string $to
5166
     */
5167
    public static function update_all_user_languages($from, $to)
5168
    {
5169
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5170
        $from = Database::escape_string($from);
5171
        $to = Database::escape_string($to);
5172
5173
        if (!empty($to) && !empty($from)) {
5174
            $sql = "UPDATE $table_user SET language = '$to'
5175
                    WHERE language = '$from'";
5176
            Database::query($sql);
5177
        }
5178
    }
5179
5180
    /**
5181
     * Subscribe boss to students
5182
     *
5183
     * @param int $bossId The boss id
5184
     * @param array $usersId The users array
5185
     * @return int Affected rows
5186
     */
5187
    public static function subscribeBossToUsers($bossId, $usersId)
5188
    {
5189
        return self::subscribeUsersToUser(
5190
            $bossId,
5191
            $usersId,
5192
            USER_RELATION_TYPE_BOSS
5193
        );
5194
    }
5195
5196
    /**
5197
     * Subscribe boss to students
5198
     *
5199
     * @param int $studentId
5200
     * @param array $bossList
5201
     * @param bool $sendNotification
5202
     * @return int Affected rows
5203
     */
5204
    public static function subscribeUserToBossList(
5205
        $studentId,
5206
        $bossList,
5207
        $sendNotification = false
5208
    ) {
5209
        if ($bossList) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $bossList 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...
5210
            $studentId = (int) $studentId;
5211
            $studentInfo = api_get_user_info($studentId);
5212
5213
            if (empty($studentInfo)) {
5214
                return false;
5215
            }
5216
5217
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5218
            $sql = "DELETE FROM $userRelUserTable 
5219
                    WHERE user_id = $studentId AND relation_type = ".USER_RELATION_TYPE_BOSS;
5220
            Database::query($sql);
5221
5222
            foreach ($bossList as $bossId) {
5223
                $bossId = (int) $bossId;
5224
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
5225
                        VALUES ($studentId, $bossId, ".USER_RELATION_TYPE_BOSS.")";
5226
                $insertId = Database::query($sql);
5227
5228
                if ($insertId && $sendNotification) {
5229
                    $name = $studentInfo['complete_name'];
5230
                    $url = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?student='.$studentId;
5231
                    $url = Display::url($url, $url);
5232
                    $subject = sprintf(get_lang('UserXHasBeenAssignedToBoss'), $name);
5233
                    $message = sprintf(get_lang('UserXHasBeenAssignedToBossWithUrlX'), $name, $url);
5234
                    MessageManager::send_message_simple(
5235
                        $bossId,
5236
                        $subject,
5237
                        $message
5238
                    );
5239
                }
5240
            }
5241
        }
5242
    }
5243
5244
    /**
5245
     * Get users followed by student boss
5246
     * @param int $userId
5247
     * @param int $userStatus (STUDENT, COURSEMANAGER, etc)
5248
     * @param bool $getOnlyUserId
5249
     * @param bool $getSql
5250
     * @param bool $getCount
5251
     * @param int $from
5252
     * @param int $numberItems
5253
     * @param int $column
5254
     * @param string $direction
5255
     * @param int $active
5256
     * @param string $lastConnectionDate
5257
     * @return array     users
5258
     */
5259
    public static function getUsersFollowedByStudentBoss(
5260
        $userId,
5261
        $userStatus = 0,
5262
        $getOnlyUserId = false,
5263
        $getSql = false,
5264
        $getCount = false,
5265
        $from = null,
5266
        $numberItems = null,
5267
        $column = null,
5268
        $direction = null,
5269
        $active = null,
5270
        $lastConnectionDate = null
5271
    ) {
5272
        return self::getUsersFollowedByUser(
5273
            $userId,
5274
            $userStatus,
5275
            $getOnlyUserId,
5276
            $getSql,
5277
            $getCount,
5278
            $from,
5279
            $numberItems,
5280
            $column,
5281
            $direction,
5282
            $active,
5283
            $lastConnectionDate,
5284
            STUDENT_BOSS
5285
        );
5286
    }
5287
5288
    /**
5289
     * Get the teacher (users with COURSEMANGER status) list
5290
     * @return array The list
5291
     */
5292
    public static function getTeachersList()
5293
    {
5294
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
5295
        $resultData = Database::select(
5296
            'user_id, lastname, firstname, username',
5297
            $userTable,
5298
            [
5299
            'where' => [
5300
                'status = ?' => COURSEMANAGER
5301
            ]
5302
        ]
5303
        );
5304
5305
        foreach ($resultData as &$teacherData) {
5306
            $teacherData['completeName'] = api_get_person_name(
5307
                $teacherData['firstname'],
5308
                $teacherData['lastname']
5309
            );
5310
        }
5311
5312
        return $resultData;
5313
    }
5314
5315
    /**
5316
     * @return array
5317
     */
5318
    public static function getOfficialCodeGrouped()
5319
    {
5320
        $user = Database::get_main_table(TABLE_MAIN_USER);
5321
        $sql = "SELECT DISTINCT official_code
5322
                FROM $user
5323
                GROUP BY official_code";
5324
        $result = Database::query($sql);
5325
        $values = Database::store_result($result, 'ASSOC');
5326
        $result = [];
5327
        foreach ($values as $value) {
5328
            $result[$value['official_code']] = $value['official_code'];
5329
        }
5330
        return $result;
5331
    }
5332
5333
    /**
5334
     * @param string $officialCode
5335
     * @return array
5336
     */
5337
    public static function getUsersByOfficialCode($officialCode)
5338
    {
5339
        $user = Database::get_main_table(TABLE_MAIN_USER);
5340
        $officialCode = Database::escape_string($officialCode);
5341
5342
        $sql = "SELECT DISTINCT id
5343
                FROM $user
5344
                WHERE official_code = '$officialCode'
5345
                ";
5346
        $result = Database::query($sql);
5347
5348
        $users = [];
5349
        while ($row = Database::fetch_array($result)) {
5350
            $users[] = $row['id'];
5351
        }
5352
        return $users;
5353
    }
5354
5355
    /**
5356
     * Calc the expended time (in seconds) by a user in a course
5357
     * @param int $userId The user id
5358
     * @param int $courseId The course id
5359
     * @param int $sessionId Optional. The session id
5360
     * @param string $from Optional. From date
5361
     * @param string $until Optional. Until date
5362
     * @return int The time
5363
     */
5364
    public static function getTimeSpentInCourses(
5365
        $userId,
5366
        $courseId,
5367
        $sessionId = 0,
5368
        $from = '',
5369
        $until = ''
5370
    ) {
5371
        $userId = intval($userId);
5372
        $sessionId = intval($sessionId);
5373
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5374
        $whereConditions = [
5375
            'user_id = ? ' => $userId,
5376
            'AND c_id = ? ' => $courseId,
5377
            'AND session_id = ? ' => $sessionId
5378
        ];
5379
5380
        if (!empty($from) && !empty($until)) {
5381
            $whereConditions["AND (login_course_date >= '?' "] = $from;
5382
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
5383
        }
5384
5385
        $trackResult = Database::select(
5386
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
5387
            $trackCourseAccessTable,
5388
            [
5389
                'where' => $whereConditions
5390
            ],
5391
            'first'
5392
        );
5393
5394
        if ($trackResult != false) {
5395
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
5396
        }
5397
5398
        return 0;
5399
    }
5400
5401
    /**
5402
     * Get the boss user ID from a followed user id
5403
     * @param $userId
5404
     * @return bool
5405
     */
5406
    public static function getFirstStudentBoss($userId)
5407
    {
5408
        $userId = intval($userId);
5409
        if ($userId > 0) {
5410
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5411
            $row = Database::select(
5412
                'DISTINCT friend_user_id AS boss_id',
5413
                $userRelTable,
5414
                [
5415
                    'where' => [
5416
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
5417
                            $userId,
5418
                            USER_RELATION_TYPE_BOSS,
5419
                        ]
5420
                    ]
5421
                ]
5422
            );
5423
            if (!empty($row)) {
5424
                return $row[0]['boss_id'];
5425
            }
5426
        }
5427
5428
        return false;
5429
    }
5430
5431
    /**
5432
     * Get the boss user ID from a followed user id
5433
     * @param $userId
5434
     * @return array
5435
     */
5436
    public static function getStudentBossList($userId)
5437
    {
5438
        $userId = (int) $userId;
5439
        if ($userId > 0) {
5440
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5441
            $result = Database::select(
5442
                'DISTINCT friend_user_id AS boss_id',
5443
                $userRelTable,
5444
                [
5445
                    'where' => [
5446
                        'user_id = ? AND relation_type = ? ' => [
5447
                            $userId,
5448
                            USER_RELATION_TYPE_BOSS,
5449
                        ]
5450
                    ]
5451
                ]
5452
            );
5453
5454
            return $result;
5455
        }
5456
5457
        return [];
5458
    }
5459
5460
    /**
5461
     * @param int $bossId
5462
     * @param int $studentId
5463
     *
5464
     * @return bool
5465
     */
5466
    public static function userIsBossOfStudent($bossId, $studentId)
5467
    {
5468
        $result = false;
5469
        $bossList = self::getStudentBossList($studentId);
5470
        if (!empty($bossList)) {
5471
            $bossList = array_column($bossList, 'boss_id');
5472
            if (in_array($bossId, $bossList)) {
5473
                $result = true;
5474
            }
5475
        }
5476
5477
        return $result;
5478
    }
5479
5480
    /**
5481
     * Get either a Gravatar URL or complete image tag for a specified email address.
5482
     *
5483
     * @param string $email The email address
5484
     * @param string $s Size in pixels, defaults to 80px [ 1 - 2048 ]
5485
     * @param string $d Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
5486
     * @param string $r Maximum rating (inclusive) [ g | pg | r | x ]
5487
     * @param bool $img True to return a complete IMG tag False for just the URL
5488
     * @param array $atts Optional, additional key/value attributes to include in the IMG tag
5489
     * @return String containing either just a URL or a complete image tag
5490
     * @source http://gravatar.com/site/implement/images/php/
5491
     */
5492
    private static function getGravatar(
5493
        $email,
5494
        $s = 80,
5495
        $d = 'mm',
5496
        $r = 'g',
5497
        $img = false,
5498
        $atts = []
5499
    ) {
5500
        $url = 'http://www.gravatar.com/avatar/';
5501
        if (!empty($_SERVER['HTTPS'])) {
5502
            $url = 'https://secure.gravatar.com/avatar/';
5503
        }
5504
        $url .= md5(strtolower(trim($email)));
5505
        $url .= "?s=$s&d=$d&r=$r";
5506
        if ($img) {
5507
            $url = '<img src="'.$url.'"';
5508
            foreach ($atts as $key => $val) {
5509
                $url .= ' '.$key.'="'.$val.'"';
5510
            }
5511
            $url .= ' />';
5512
        }
5513
        return $url;
5514
    }
5515
5516
    /**
5517
     * Displays the name of the user and makes the link to the user profile
5518
     * @param array $userInfo
5519
     *
5520
     * @return string
5521
     */
5522
    public static function getUserProfileLink($userInfo)
5523
    {
5524
        if (isset($userInfo) && isset($userInfo['user_id'])) {
5525
            return Display::url(
5526
                $userInfo['complete_name_with_username'],
5527
                $userInfo['profile_url']
5528
            );
5529
        } else {
5530
            return get_lang('Anonymous');
5531
        }
5532
    }
5533
5534
    /**
5535
     * Displays the name of the user and makes the link to the user profile
5536
     *
5537
     * @param $userInfo
5538
     *
5539
     * @return string
5540
     */
5541
    public static function getUserProfileLinkWithPicture($userInfo)
5542
    {
5543
        return Display::url(
5544
            Display::img($userInfo['avatar']),
5545
            $userInfo['profile_url']
5546
        );
5547
    }
5548
5549
    /**
5550
     * Get users whose name matches $firstname and $lastname
5551
     * @param string $firstname Firstname to search
5552
     * @param string $lastname Lastname to search
5553
     * @return array The user list
5554
     */
5555
    public static function getUsersByName($firstname, $lastname)
5556
    {
5557
        $firstname = Database::escape_string($firstname);
5558
        $lastname = Database::escape_string($lastname);
5559
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
5560
5561
        $sql = <<<SQL
5562
            SELECT id, username, lastname, firstname
5563
            FROM $userTable
5564
            WHERE 
5565
                firstname LIKE '$firstname%' AND
5566
                lastname LIKE '$lastname%'
5567
SQL;
5568
        $result = Database::query($sql);
5569
        $users = [];
5570
        while ($resultData = Database::fetch_object($result)) {
5571
            $users[] = $resultData;
5572
        }
5573
5574
        return $users;
5575
    }
5576
5577
    /**
5578
     * @param int $optionSelected
5579
     * @return string
5580
     */
5581
    public static function getUserSubscriptionTab($optionSelected = 1)
5582
    {
5583
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
5584
        if (($allowAdmin === 'true' && api_is_allowed_to_edit()) ||
5585
            api_is_platform_admin()
5586
        ) {
5587
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
5588
5589
            $headers = [
5590
                [
5591
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
5592
                    'content' => get_lang('Students'),
5593
                ],
5594
                [
5595
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
5596
                    'content' => get_lang('Teachers'),
5597
                ],
5598
                /*[
5599
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
5600
                    'content' => get_lang('Students'),
5601
                ],
5602
                [
5603
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
5604
                    'content' => get_lang('Teachers'),
5605
                ],*/
5606
                [
5607
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
5608
                    'content' => get_lang('Groups'),
5609
                ],
5610
                [
5611
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
5612
                    'content' => get_lang('Classes'),
5613
                ]
5614
            ];
5615
5616
            return Display::tabsOnlyLink($headers, $optionSelected);
5617
        }
5618
    }
5619
5620
    /**
5621
     * Make sure this function is protected because it does NOT check password!
5622
     *
5623
     * This function defines globals.
5624
     * @param  int $userId
5625
     * @param bool $checkIfUserCanLoginAs
5626
     * @return bool
5627
     * @author Evie Embrechts
5628
     * @author Yannick Warnier <[email protected]>
5629
    */
5630
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
5631
    {
5632
        $userId = intval($userId);
5633
        $userInfo = api_get_user_info($userId);
5634
5635
        // Check if the user is allowed to 'login_as'
5636
        $canLoginAs = true;
5637
        if ($checkIfUserCanLoginAs) {
5638
            $canLoginAs = api_can_login_as($userId);
5639
        }
5640
5641
        if (!$canLoginAs || empty($userInfo)) {
5642
            return false;
5643
        }
5644
5645
        if ($userId) {
5646
            // Logout the current user
5647
            self::loginDelete(api_get_user_id());
5648
5649
            Session::erase('_user');
5650
            Session::erase('is_platformAdmin');
5651
            Session::erase('is_allowedCreateCourse');
5652
            Session::erase('_uid');
5653
5654
            // Cleaning session variables
5655
            $_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...
5656
            $_user['lastName'] = $userInfo['lastname'];
5657
            $_user['mail'] = $userInfo['email'];
5658
            $_user['official_code'] = $userInfo['official_code'];
5659
            $_user['picture_uri'] = $userInfo['picture_uri'];
5660
            $_user['user_id'] = $userId;
5661
            $_user['id'] = $userId;
5662
            $_user['status'] = $userInfo['status'];
5663
5664
            // Filling session variables with new data
5665
            Session::write('_uid', $userId);
5666
            Session::write('_user', $userInfo);
5667
            Session::write('is_platformAdmin', (bool) UserManager::is_admin($userId));
5668
            Session::write('is_allowedCreateCourse', (bool) ($userInfo['status'] == 1));
5669
            // will be useful later to know if the user is actually an admin or not (example reporting)
5670
            Session::write('login_as', true);
5671
5672
            return true;
5673
        }
5674
5675
        return false;
5676
    }
5677
5678
    /**
5679
     * Remove all login records from the track_e_online stats table,
5680
     * for the given user ID.
5681
     * @param int $userId User ID
5682
     * @return void
5683
     */
5684
    public static function loginDelete($userId)
5685
    {
5686
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5687
        $userId = intval($userId);
5688
        $query = "DELETE FROM ".$online_table." WHERE login_user_id = $userId";
5689
        Database::query($query);
5690
    }
5691
5692
    /**
5693
     * Login as first admin user registered in the platform
5694
     * @return array
5695
     */
5696
    public static function logInAsFirstAdmin()
5697
    {
5698
        $adminList = self::get_all_administrators();
5699
5700
        if (!empty($adminList)) {
5701
            $userInfo = current($adminList);
5702
            if (!empty($userInfo)) {
5703
                $result = self::loginAsUser($userInfo['user_id'], false);
5704
                if ($result && api_is_platform_admin()) {
5705
                    return api_get_user_info();
5706
                }
5707
            }
5708
        }
5709
5710
        return [];
5711
    }
5712
5713
    /**
5714
     * Check if user is teacher of a student based in their courses
5715
     * @param $teacherId
5716
     * @param $studentId
5717
     * @return array
5718
     */
5719
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
5720
    {
5721
        $courses = CourseManager::getCoursesFollowedByUser(
5722
            $teacherId,
5723
            COURSEMANAGER
5724
        );
5725
        if (empty($courses)) {
5726
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
5727
        }
5728
5729
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
5730
        if (empty($coursesFromUser)) {
5731
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
5732
        }
5733
5734
        $coursesCodeList = array_column($courses, 'code');
5735
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
5736
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
5737
        $commonCourses = array_filter($commonCourses);
5738
5739
        if (!empty($commonCourses)) {
5740
            return $commonCourses;
5741
        }
5742
5743
        return [];
5744
    }
5745
5746
    /**
5747
     * @param int $teacherId
5748
     * @param int $studentId
5749
     * @return bool
5750
     */
5751
    public static function isTeacherOfStudent($teacherId, $studentId)
5752
    {
5753
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
5754
            $teacherId,
5755
            $studentId
5756
        );
5757
5758
        if (!empty($courses)) {
5759
            return true;
5760
        }
5761
5762
        return false;
5763
    }
5764
5765
    /**
5766
     * Send user confirmation mail
5767
     *
5768
     * @param User $user
5769
     */
5770
    public static function sendUserConfirmationMail(User $user)
5771
    {
5772
        $uniqueId = api_get_unique_id();
5773
        $user->setConfirmationToken($uniqueId);
5774
5775
        Database::getManager()->persist($user);
5776
        Database::getManager()->flush();
5777
5778
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
5779
        $mailSubject = get_lang('RegistrationConfirmation');
5780
        $mailBody = get_lang('RegistrationConfirmationEmailMessage')
5781
            .PHP_EOL
5782
            .Display::url($url, $url);
5783
5784
        api_mail_html(
5785
            $user->getCompleteName(),
5786
            $user->getEmail(),
5787
            $mailSubject,
5788
            $mailBody
5789
        );
5790
        Display::addFlash(Display::return_message(get_lang('CheckYourEmailAndFollowInstructions')));
5791
    }
5792
}
5793